Blender V4.3
view2d.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cfloat>
10#include <climits>
11#include <cmath>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "DNA_scene_types.h"
17#include "DNA_userdef_types.h"
18
19#include "BLI_link_utils.h"
20#include "BLI_listbase.h"
21#include "BLI_math_matrix.h"
22#include "BLI_memarena.h"
23#include "BLI_rect.h"
24#include "BLI_utildefines.h"
25
26#include "BKE_context.hh"
27#include "BKE_global.hh"
28#include "BKE_screen.hh"
29
30#include "GPU_immediate.hh"
31#include "GPU_matrix.hh"
32#include "GPU_state.hh"
33
34#include "WM_api.hh"
35
36#include "BLF_api.hh"
37
38#include "ED_screen.hh"
39
40#include "UI_interface.hh"
41#include "UI_view2d.hh"
42
43#include "view2d_intern.hh"
44
45static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize);
46
47/* -------------------------------------------------------------------- */
50
52{
53 const float min = float(INT_MIN);
54 const float max = float(INT_MAX);
55
56 if (UNLIKELY(f < min)) {
57 return min;
58 }
59 if (UNLIKELY(f > max)) {
60 return int(max);
61 }
62 return int(f);
63}
64
69BLI_INLINE void clamp_rctf_to_rcti(rcti *dst, const rctf *src)
70{
71 dst->xmin = clamp_float_to_int(src->xmin);
72 dst->xmax = clamp_float_to_int(src->xmax);
73 dst->ymin = clamp_float_to_int(src->ymin);
74 dst->ymax = clamp_float_to_int(src->ymax);
75}
76
77float view2d_page_size_y(const View2D &v2d)
78{
79 return v2d.page_size_y ? v2d.page_size_y : BLI_rcti_size_y(&v2d.mask);
80}
81
82/* XXX still unresolved: scrolls hide/unhide vs region mask handling */
83/* XXX there's V2D_SCROLL_HORIZONTAL_HIDE and V2D_SCROLL_HORIZONTAL_FULLR ... */
84
86
87/* -------------------------------------------------------------------- */
90
98static int view2d_scroll_mapped(int scroll)
99{
100 if (scroll & V2D_SCROLL_HORIZONTAL_FULLR) {
101 scroll &= ~V2D_SCROLL_HORIZONTAL;
102 }
103 if (scroll & V2D_SCROLL_VERTICAL_FULLR) {
104 scroll &= ~V2D_SCROLL_VERTICAL;
105 }
106 return scroll;
107}
108
109void UI_view2d_mask_from_win(const View2D *v2d, rcti *r_mask)
110{
111 r_mask->xmin = 0;
112 r_mask->ymin = 0;
113 r_mask->xmax = v2d->winx - 1; /* -1 yes! masks are pixels */
114 r_mask->ymax = v2d->winy - 1;
115}
116
122static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
123{
124 int scroll;
125
126 /* mask - view frame */
127 UI_view2d_mask_from_win(v2d, &v2d->mask);
128 if (mask_scroll == nullptr) {
129 mask_scroll = &v2d->mask;
130 }
131
132 /* check size if hiding flag is set: */
135 if (BLI_rctf_size_x(&v2d->tot) > BLI_rctf_size_x(&v2d->cur)) {
137 }
138 else {
140 }
141 }
142 }
143 if (v2d->scroll & V2D_SCROLL_VERTICAL_HIDE) {
144 if (!(v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES)) {
145 if (BLI_rctf_size_y(&v2d->tot) + 0.01f > BLI_rctf_size_y(&v2d->cur)) {
147 }
148 else {
150 }
151 }
152 }
153
154 /* Do not use mapped scroll here because we want to update scroller rects
155 * even if they are not displayed. For initialization purposes. See #75003. */
156 scroll = v2d->scroll;
157
158 /* Scrollers are based off region-size:
159 * - they can only be on one to two edges of the region they define
160 * - if they overlap, they must not occupy the corners (which are reserved for other widgets)
161 */
162 if (scroll) {
163 float scroll_width, scroll_height;
164
165 UI_view2d_scroller_size_get(v2d, false, &scroll_width, &scroll_height);
166
167 /* vertical scroller */
168 if (scroll & V2D_SCROLL_LEFT) {
169 /* on left-hand edge of region */
170 v2d->vert = *mask_scroll;
171 v2d->vert.xmax = scroll_width;
172 }
173 else if (scroll & V2D_SCROLL_RIGHT) {
174 /* on right-hand edge of region */
175 v2d->vert = *mask_scroll;
176 v2d->vert.xmax++; /* one pixel extra... was leaving a minor gap... */
177 v2d->vert.xmin = v2d->vert.xmax - scroll_width;
178 }
179
180 /* horizontal scroller */
181 if (scroll & V2D_SCROLL_BOTTOM) {
182 /* on bottom edge of region */
183 v2d->hor = *mask_scroll;
184 v2d->hor.ymax = scroll_height;
185 }
186 else if (scroll & V2D_SCROLL_TOP) {
187 /* on upper edge of region */
188 v2d->hor = *mask_scroll;
189 v2d->hor.ymin = v2d->hor.ymax - scroll_height;
190 }
191
192 /* adjust vertical scroller if there's a horizontal scroller, to leave corner free */
193 if (scroll & V2D_SCROLL_VERTICAL) {
194 if (scroll & V2D_SCROLL_BOTTOM) {
195 /* on bottom edge of region */
196 v2d->vert.ymin = v2d->hor.ymax;
197 }
198 else if (scroll & V2D_SCROLL_TOP) {
199 /* on upper edge of region */
200 v2d->vert.ymax = v2d->hor.ymin;
201 }
202 }
203 }
204}
205
207
208/* -------------------------------------------------------------------- */
211
212void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
213{
214 bool tot_changed = false, do_init;
215 const uiStyle *style = UI_style_get();
216
217 do_init = (v2d->flag & V2D_IS_INIT) == 0;
218
219 /* see eView2D_CommonViewTypes in UI_view2d.hh for available view presets */
220 switch (type) {
221 /* 'standard view' - optimum setup for 'standard' view behavior,
222 * that should be used new views as basis for their
223 * own unique View2D settings, which should be used instead of this in most cases...
224 */
226 /* for now, aspect ratio should be maintained,
227 * and zoom is clamped within sane default limits */
229 v2d->minzoom = 0.01f;
230 v2d->maxzoom = 1000.0f;
231
232 /* View2D tot rect and cur should be same size,
233 * and aligned using 'standard' OpenGL coordinates for now:
234 * - region can resize 'tot' later to fit other data
235 * - keeptot is only within bounds, as strict locking is not that critical
236 * - view is aligned for (0,0) -> (winx-1, winy-1) setup
237 */
240 if (do_init) {
241 v2d->tot.xmin = v2d->tot.ymin = 0.0f;
242 v2d->tot.xmax = float(winx - 1);
243 v2d->tot.ymax = float(winy - 1);
244
245 v2d->cur = v2d->tot;
246 }
247 /* scrollers - should we have these by default? */
248 /* XXX for now, we don't override this, or set it either! */
249 break;
250 }
251 /* 'list/channel view' - zoom, aspect ratio, and alignment restrictions are set here */
252 case V2D_COMMONVIEW_LIST: {
253 /* zoom + aspect ratio are locked */
255 v2d->minzoom = v2d->maxzoom = 1.0f;
256
257 /* tot rect has strictly regulated placement, and must only occur in +/- quadrant */
260 tot_changed = do_init;
261
262 /* scroller settings are currently not set here... that is left for regions... */
263 break;
264 }
265 /* 'stack view' - practically the same as list/channel view,
266 * except is located in the pos y half instead.
267 * Zoom, aspect ratio, and alignment restrictions are set here. */
269 /* zoom + aspect ratio are locked */
271 v2d->minzoom = v2d->maxzoom = 1.0f;
272
273 /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
276 tot_changed = do_init;
277
278 /* scroller settings are currently not set here... that is left for regions... */
279 break;
280 }
281 /* 'header' regions - zoom, aspect ratio,
282 * alignment, and panning restrictions are set here */
284 /* zoom + aspect ratio are locked */
286 v2d->minzoom = v2d->maxzoom = 1.0f;
287
288 if (do_init) {
289 v2d->tot.xmin = 0.0f;
290 v2d->tot.xmax = winx;
291 v2d->tot.ymin = 0.0f;
292 v2d->tot.ymax = winy;
293 v2d->cur = v2d->tot;
294
295 v2d->min[0] = v2d->max[0] = float(winx - 1);
296 v2d->min[1] = v2d->max[1] = float(winy - 1);
297 }
298 /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
301 tot_changed = do_init;
302
303 /* panning in y-axis is prohibited */
304 v2d->keepofs = V2D_LOCKOFS_Y;
305
306 /* absolutely no scrollers allowed */
307 v2d->scroll = 0;
308 break;
309 }
310 /* panels view, with horizontal/vertical align */
312
313 /* for now, aspect ratio should be maintained,
314 * and zoom is clamped within sane default limits */
316 v2d->minzoom = 0.5f;
317 v2d->maxzoom = 2.0f;
318
321
322 /* NOTE: scroll is being flipped in #ED_region_panels() drawing. */
324
325 if (do_init) {
326 const float panelzoom = (style) ? style->panelzoom : 1.0f;
327
328 v2d->tot.xmin = 0.0f;
329 v2d->tot.xmax = winx;
330
331 v2d->tot.ymax = 0.0f;
332 v2d->tot.ymin = -winy;
333
334 v2d->cur.xmin = 0.0f;
335 v2d->cur.xmax = (winx)*panelzoom;
336
337 v2d->cur.ymax = 0.0f;
338 v2d->cur.ymin = (-winy) * panelzoom;
339 }
340 break;
341 }
342 /* other view types are completely defined using their own settings already */
343 default:
344 /* we don't do anything here,
345 * as settings should be fine, but just make sure that rect */
346 break;
347 }
348
349 /* set initialized flag so that View2D doesn't get reinitialized next time again */
350 v2d->flag |= V2D_IS_INIT;
351
352 /* store view size */
353 v2d->winx = winx;
354 v2d->winy = winy;
355
356 view2d_masks(v2d, nullptr);
357
358 if (do_init) {
359 /* Visible by default. */
360 v2d->alpha_hor = v2d->alpha_vert = 255;
361 }
362
363 /* set 'tot' rect before setting cur? */
364 /* XXX confusing stuff here still */
365 if (tot_changed) {
366 view2d_totRect_set_resize(v2d, winx, winy, !do_init);
367 }
368 else {
370 }
371}
372
377/* XXX pre2.5 -> this used to be called #test_view2d() */
378static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize)
379{
380 float totwidth, totheight, curwidth, curheight, width, height;
381 float winx, winy;
382 rctf *cur, *tot;
383
384 /* use mask as size of region that View2D resides in, as it takes into account
385 * scroll-bars already - keep in sync with `zoomx/zoomy` in #view_zoomstep_apply_ex! */
386 winx = float(BLI_rcti_size_x(&v2d->mask) + 1);
387 winy = float(BLI_rcti_size_y(&v2d->mask) + 1);
388
389 /* get pointers to rcts for less typing */
390 cur = &v2d->cur;
391 tot = &v2d->tot;
392
393 /* we must satisfy the following constraints (in decreasing order of importance):
394 * - alignment restrictions are respected
395 * - cur must not fall outside of tot
396 * - axis locks (zoom and offset) must be maintained
397 * - zoom must not be excessive (check either sizes or zoom values)
398 * - aspect ratio should be respected (NOTE: this is quite closely related to zoom too)
399 */
400
401 /* Step 1: if keepzoom, adjust the sizes of the rects only
402 * - firstly, we calculate the sizes of the rects
403 * - curwidth and curheight are saved as reference... modify width and height values here
404 */
405 totwidth = BLI_rctf_size_x(tot);
406 totheight = BLI_rctf_size_y(tot);
407 /* Keep in sync with `zoomx/zoomy` in #view_zoomstep_apply_ex! */
408 curwidth = width = BLI_rctf_size_x(cur);
409 curheight = height = BLI_rctf_size_y(cur);
410
411 /* if zoom is locked, size on the appropriate axis is reset to mask size */
412 if (v2d->keepzoom & V2D_LOCKZOOM_X) {
413 width = winx;
414 }
415 if (v2d->keepzoom & V2D_LOCKZOOM_Y) {
416 height = winy;
417 }
418
419 /* values used to divide, so make it safe
420 * NOTE: width and height must use FLT_MIN instead of 1, otherwise it is impossible to
421 * get enough resolution in Graph Editor for editing some curves
422 */
423 if (width < FLT_MIN) {
424 width = 1;
425 }
426 if (height < FLT_MIN) {
427 height = 1;
428 }
429 if (winx < 1) {
430 winx = 1;
431 }
432 if (winy < 1) {
433 winy = 1;
434 }
435
436 /* V2D_LIMITZOOM indicates that zoom level should be preserved when the window size changes */
437 if (resize && (v2d->keepzoom & V2D_KEEPZOOM)) {
438 float zoom, oldzoom;
439
440 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
441 zoom = winx / width;
442 oldzoom = v2d->oldwinx / curwidth;
443
444 if (oldzoom != zoom) {
445 width *= zoom / oldzoom;
446 }
447 }
448
449 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
450 zoom = winy / height;
451 oldzoom = v2d->oldwiny / curheight;
452
453 if (oldzoom != zoom) {
454 height *= zoom / oldzoom;
455 }
456 }
457 }
458 /* keepzoom (V2D_LIMITZOOM set), indicates that zoom level on each axis must not exceed limits
459 * NOTE: in general, it is not expected that the lock-zoom will be used in conjunction with this
460 */
461 else if (v2d->keepzoom & V2D_LIMITZOOM) {
462
463 /* check if excessive zoom on x-axis */
464 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
465 const float zoom = winx / width;
466 if (zoom < v2d->minzoom) {
467 width = winx / v2d->minzoom;
468 }
469 else if (zoom > v2d->maxzoom) {
470 width = winx / v2d->maxzoom;
471 }
472 }
473
474 /* check if excessive zoom on y-axis */
475 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
476 const float zoom = winy / height;
477 if (zoom < v2d->minzoom) {
478 height = winy / v2d->minzoom;
479 }
480 else if (zoom > v2d->maxzoom) {
481 height = winy / v2d->maxzoom;
482 }
483 }
484 }
485 else {
486 /* make sure sizes don't exceed that of the min/max sizes
487 * (even though we're not doing zoom clamping) */
488 CLAMP(width, v2d->min[0], v2d->max[0]);
489 CLAMP(height, v2d->min[1], v2d->max[1]);
490 }
491
492 /* check if we should restore aspect ratio (if view size changed) */
493 if (v2d->keepzoom & V2D_KEEPASPECT) {
494 bool do_x = false, do_y = false, do_cur;
495 float curRatio, winRatio;
496
497 /* when a window edge changes, the aspect ratio can't be used to
498 * find which is the best new 'cur' rect. that's why it stores 'old'
499 */
500 if (winx != v2d->oldwinx) {
501 do_x = true;
502 }
503 if (winy != v2d->oldwiny) {
504 do_y = true;
505 }
506
507 curRatio = height / width;
508 winRatio = winy / winx;
509
510 /* Both sizes change (area/region maximized). */
511 if (do_x == do_y) {
512 if (do_x && do_y) {
513 /* here is 1,1 case, so all others must be 0,0 */
514 if (fabsf(winx - v2d->oldwinx) > fabsf(winy - v2d->oldwiny)) {
515 do_y = false;
516 }
517 else {
518 do_x = false;
519 }
520 }
521 else if (winRatio > curRatio) {
522 do_x = false;
523 }
524 else {
525 do_x = true;
526 }
527 }
528 do_cur = do_x;
529 // do_win = do_y; /* UNUSED. */
530
531 if (do_cur) {
532 if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winx != v2d->oldwinx)) {
533 /* Special exception for Outliner (and later channel-lists):
534 * - The view may be moved left to avoid contents
535 * being pushed out of view when view shrinks.
536 * - The keeptot code will make sure cur->xmin will not be less than tot->xmin
537 * (which cannot be allowed).
538 * - width is not adjusted for changed ratios here.
539 */
540 if (winx < v2d->oldwinx) {
541 const float temp = v2d->oldwinx - winx;
542
543 cur->xmin -= temp;
544 cur->xmax -= temp;
545
546 /* Width does not get modified, as keep-aspect here is just set to make
547 * sure visible area adjusts to changing view shape! */
548 }
549 }
550 else {
551 /* portrait window: correct for x */
552 width = height / winRatio;
553 }
554 }
555 else {
556 if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winy != v2d->oldwiny)) {
557 /* special exception for Outliner (and later channel-lists):
558 * - Currently, no actions need to be taken here...
559 */
560
561 if (winy < v2d->oldwiny) {
562 const float temp = v2d->oldwiny - winy;
563
564 if (v2d->align & V2D_ALIGN_NO_NEG_Y) {
565 cur->ymin -= temp;
566 cur->ymax -= temp;
567 }
568 else { /* Assume V2D_ALIGN_NO_POS_Y or combination */
569 cur->ymin += temp;
570 cur->ymax += temp;
571 }
572 }
573 }
574 else {
575 /* landscape window: correct for y */
576 height = width * winRatio;
577 }
578 }
579
580 /* store region size for next time */
581 v2d->oldwinx = short(winx);
582 v2d->oldwiny = short(winy);
583 }
584
585 /* Step 2: apply new sizes to cur rect,
586 * but need to take into account alignment settings here... */
587 if ((width != curwidth) || (height != curheight)) {
588 float temp, dh;
589
590 /* Resize from center-point, unless otherwise specified. */
591 if (width != curwidth) {
592 if (v2d->keepofs & V2D_LOCKOFS_X) {
593 cur->xmax += width - BLI_rctf_size_x(cur);
594 }
595 else if (v2d->keepofs & V2D_KEEPOFS_X) {
596 if (v2d->align & V2D_ALIGN_NO_POS_X) {
597 cur->xmin -= width - BLI_rctf_size_x(cur);
598 }
599 else {
600 cur->xmax += width - BLI_rctf_size_x(cur);
601 }
602 }
603 else {
604 temp = BLI_rctf_cent_x(cur);
605 dh = width * 0.5f;
606
607 cur->xmin = temp - dh;
608 cur->xmax = temp + dh;
609 }
610 }
611 if (height != curheight) {
612 if (v2d->keepofs & V2D_LOCKOFS_Y) {
613 cur->ymax += height - BLI_rctf_size_y(cur);
614 }
615 else if (v2d->keepofs & V2D_KEEPOFS_Y) {
616 if (v2d->align & V2D_ALIGN_NO_POS_Y) {
617 cur->ymin -= height - BLI_rctf_size_y(cur);
618 }
619 else {
620 cur->ymax += height - BLI_rctf_size_y(cur);
621 }
622 }
623 else {
624 temp = BLI_rctf_cent_y(cur);
625 dh = height * 0.5f;
626
627 cur->ymin = temp - dh;
628 cur->ymax = temp + dh;
629 }
630 }
631 }
632
633 /* Step 3: adjust so that it doesn't fall outside of bounds of 'tot' */
634 if (v2d->keeptot) {
635 float temp, diff;
636
637 /* recalculate extents of cur */
638 curwidth = BLI_rctf_size_x(cur);
639 curheight = BLI_rctf_size_y(cur);
640
641 /* width */
642 if ((curwidth > totwidth) &&
644 {
645 /* if zoom doesn't have to be maintained, just clamp edges */
646 if (cur->xmin < tot->xmin) {
647 cur->xmin = tot->xmin;
648 }
649 if (cur->xmax > tot->xmax) {
650 cur->xmax = tot->xmax;
651 }
652 }
653 else if (v2d->keeptot == V2D_KEEPTOT_STRICT) {
654 /* This is an exception for the outliner (and later channel-lists, headers)
655 * - must clamp within tot rect (absolutely no excuses)
656 * --> therefore, cur->xmin must not be less than tot->xmin
657 */
658 if (cur->xmin < tot->xmin) {
659 /* move cur across so that it sits at minimum of tot */
660 temp = tot->xmin - cur->xmin;
661
662 cur->xmin += temp;
663 cur->xmax += temp;
664 }
665 else if (cur->xmax > tot->xmax) {
666 /* - only offset by difference of cur-xmax and tot-xmax if that would not move
667 * cur-xmin to lie past tot-xmin
668 * - otherwise, simply shift to tot-xmin???
669 */
670 temp = cur->xmax - tot->xmax;
671
672 if ((cur->xmin - temp) < tot->xmin) {
673 /* only offset by difference from cur-min and tot-min */
674 temp = cur->xmin - tot->xmin;
675
676 cur->xmin -= temp;
677 cur->xmax -= temp;
678 }
679 else {
680 cur->xmin -= temp;
681 cur->xmax -= temp;
682 }
683 }
684 }
685 else {
686 /* This here occurs when:
687 * - width too big, but maintaining zoom (i.e. widths cannot be changed)
688 * - width is OK, but need to check if outside of boundaries
689 *
690 * So, resolution is to just shift view by the gap between the extremities.
691 * We favor moving the 'minimum' across, as that's origin for most things.
692 * (XXX: in the past, max was favored... if there are bugs, swap!)
693 */
694 if ((cur->xmin < tot->xmin) && (cur->xmax > tot->xmax)) {
695 /* outside boundaries on both sides,
696 * so take middle-point of tot, and place in balanced way */
697 temp = BLI_rctf_cent_x(tot);
698 diff = curwidth * 0.5f;
699
700 cur->xmin = temp - diff;
701 cur->xmax = temp + diff;
702 }
703 else if (cur->xmin < tot->xmin) {
704 /* move cur across so that it sits at minimum of tot */
705 temp = tot->xmin - cur->xmin;
706
707 cur->xmin += temp;
708 cur->xmax += temp;
709 }
710 else if (cur->xmax > tot->xmax) {
711 /* - only offset by difference of cur-xmax and tot-xmax if that would not move
712 * cur-xmin to lie past tot-xmin
713 * - otherwise, simply shift to tot-xmin???
714 */
715 temp = cur->xmax - tot->xmax;
716
717 if ((cur->xmin - temp) < tot->xmin) {
718 /* only offset by difference from cur-min and tot-min */
719 temp = cur->xmin - tot->xmin;
720
721 cur->xmin -= temp;
722 cur->xmax -= temp;
723 }
724 else {
725 cur->xmin -= temp;
726 cur->xmax -= temp;
727 }
728 }
729 }
730
731 /* height */
732 if ((curheight > totheight) &&
734 {
735 /* if zoom doesn't have to be maintained, just clamp edges */
736 if (cur->ymin < tot->ymin) {
737 cur->ymin = tot->ymin;
738 }
739 if (cur->ymax > tot->ymax) {
740 cur->ymax = tot->ymax;
741 }
742 }
743 else {
744 /* This here occurs when:
745 * - height too big, but maintaining zoom (i.e. heights cannot be changed)
746 * - height is OK, but need to check if outside of boundaries
747 *
748 * So, resolution is to just shift view by the gap between the extremities.
749 * We favor moving the 'minimum' across, as that's origin for most things.
750 */
751 if ((cur->ymin < tot->ymin) && (cur->ymax > tot->ymax)) {
752 /* outside boundaries on both sides,
753 * so take middle-point of tot, and place in balanced way */
754 temp = BLI_rctf_cent_y(tot);
755 diff = curheight * 0.5f;
756
757 cur->ymin = temp - diff;
758 cur->ymax = temp + diff;
759 }
760 else if (cur->ymin < tot->ymin) {
761 /* there's still space remaining, so shift up */
762 temp = tot->ymin - cur->ymin;
763
764 cur->ymin += temp;
765 cur->ymax += temp;
766 }
767 else if (cur->ymax > tot->ymax) {
768 /* there's still space remaining, so shift down */
769 temp = cur->ymax - tot->ymax;
770
771 cur->ymin -= temp;
772 cur->ymax -= temp;
773 }
774 }
775 }
776
777 /* Step 4: Make sure alignment restrictions are respected */
778 if (v2d->align) {
779 /* If alignment flags are set (but keeptot is not), they must still be respected, as although
780 * they don't specify any particular bounds to stay within, they do define ranges which are
781 * invalid.
782 *
783 * Here, we only check to make sure that on each axis, the 'cur' rect doesn't stray into these
784 * invalid zones, otherwise we offset.
785 */
786
787 /* handle width - posx and negx flags are mutually exclusive, so watch out */
788 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
789 /* width is in negative-x half */
790 if (v2d->cur.xmax > 0) {
791 v2d->cur.xmin -= v2d->cur.xmax;
792 v2d->cur.xmax = 0.0f;
793 }
794 }
795 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
796 /* width is in positive-x half */
797 if (v2d->cur.xmin < 0) {
798 v2d->cur.xmax -= v2d->cur.xmin;
799 v2d->cur.xmin = 0.0f;
800 }
801 }
802
803 /* handle height - posx and negx flags are mutually exclusive, so watch out */
804 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
805 /* height is in negative-y half */
806 if (v2d->cur.ymax > 0) {
807 v2d->cur.ymin -= v2d->cur.ymax;
808 v2d->cur.ymax = 0.0f;
809 }
810 }
811 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
812 /* height is in positive-y half */
813 if (v2d->cur.ymin < 0) {
814 v2d->cur.ymax -= v2d->cur.ymin;
815 v2d->cur.ymin = 0.0f;
816 }
817 }
818 }
819
820 /* set masks */
821 view2d_masks(v2d, nullptr);
822}
823
828
830{
832
833 ARegion *region = CTX_wm_region(C);
834
835 if (region->type->on_view2d_changed != nullptr) {
836 region->type->on_view2d_changed(C, region);
837 }
838}
839
841{
842 const float cur_height_y = BLI_rctf_size_y(&v2d->cur);
843
844 if (BLI_rctf_size_y(&v2d->cur) > BLI_rctf_size_y(&v2d->tot)) {
845 v2d->cur.ymin = -cur_height_y;
846 v2d->cur.ymax = 0;
847 }
848 else if (v2d->cur.ymin < v2d->tot.ymin) {
849 v2d->cur.ymin = v2d->tot.ymin;
850 v2d->cur.ymax = v2d->cur.ymin + cur_height_y;
851 }
852}
853
854/* ------------------ */
855
860
861void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
862{
863 /* don't continue if no view syncing to be done */
865 return;
866 }
867
868 /* check if doing within area syncing (i.e. channels/vertical) */
869 if ((v2dcur->flag & V2D_VIEWSYNC_AREA_VERTICAL) && (area)) {
870 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
871 /* don't operate on self */
872 if (v2dcur != &region->v2d) {
873 /* only if view has vertical locks enabled */
874 if (region->v2d.flag & V2D_VIEWSYNC_AREA_VERTICAL) {
875 if (flag == V2D_LOCK_COPY) {
876 /* other views with locks on must copy active */
877 region->v2d.cur.ymin = v2dcur->cur.ymin;
878 region->v2d.cur.ymax = v2dcur->cur.ymax;
879 }
880 else { /* V2D_LOCK_SET */
881 /* active must copy others */
882 v2dcur->cur.ymin = region->v2d.cur.ymin;
883 v2dcur->cur.ymax = region->v2d.cur.ymax;
884 }
885
886 /* region possibly changed, so refresh */
888 }
889 }
890 }
891 }
892
893 /* check if doing whole screen syncing (i.e. time/horizontal) */
894 if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) {
895 LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
896 if (!UI_view2d_area_supports_sync(area_iter)) {
897 continue;
898 }
899 LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) {
900 /* don't operate on self */
901 if (v2dcur != &region->v2d) {
902 /* only if view has horizontal locks enabled */
903 if (region->v2d.flag & V2D_VIEWSYNC_SCREEN_TIME) {
904 if (flag == V2D_LOCK_COPY) {
905 /* other views with locks on must copy active */
906 region->v2d.cur.xmin = v2dcur->cur.xmin;
907 region->v2d.cur.xmax = v2dcur->cur.xmax;
908 }
909 else { /* V2D_LOCK_SET */
910 /* active must copy others */
911 v2dcur->cur.xmin = region->v2d.cur.xmin;
912 v2dcur->cur.xmax = region->v2d.cur.xmax;
913 }
914
915 /* region possibly changed, so refresh */
917 }
918 }
919 }
920 }
921 }
922}
923
925{
926 float width, height;
927
928 /* assume width and height of 'cur' rect by default, should be same size as mask */
929 width = float(BLI_rcti_size_x(&v2d->mask) + 1);
930 height = float(BLI_rcti_size_y(&v2d->mask) + 1);
931
932 /* handle width - posx and negx flags are mutually exclusive, so watch out */
933 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
934 /* width is in negative-x half */
935 v2d->cur.xmin = -width;
936 v2d->cur.xmax = 0.0f;
937 }
938 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
939 /* width is in positive-x half */
940 v2d->cur.xmin = 0.0f;
941 v2d->cur.xmax = width;
942 }
943 else {
944 /* width is centered around (x == 0) */
945 const float dx = width / 2.0f;
946
947 v2d->cur.xmin = -dx;
948 v2d->cur.xmax = dx;
949 }
950
951 /* handle height - posx and negx flags are mutually exclusive, so watch out */
952 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
953 /* height is in negative-y half */
954 v2d->cur.ymin = -height;
955 v2d->cur.ymax = 0.0f;
956 }
957 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
958 /* height is in positive-y half */
959 v2d->cur.ymin = 0.0f;
960 v2d->cur.ymax = height;
961 }
962 else {
963 /* height is centered around (y == 0) */
964 const float dy = height / 2.0f;
965
966 v2d->cur.ymin = -dy;
967 v2d->cur.ymax = dy;
968 }
969}
970
971/* ------------------ */
972
973void view2d_totRect_set_resize(View2D *v2d, int width, int height, bool resize)
974{
975 /* don't do anything if either value is 0 */
976 width = abs(width);
977 height = abs(height);
978
979 if (ELEM(0, width, height)) {
980 if (G.debug & G_DEBUG) {
981 /* XXX: temp debug info. */
982 printf("Error: View2D totRect set exiting: v2d=%p width=%d height=%d\n",
983 (void *)v2d,
984 width,
985 height);
986 }
987 return;
988 }
989
990 /* handle width - posx and negx flags are mutually exclusive, so watch out */
991 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
992 /* width is in negative-x half */
993 v2d->tot.xmin = float(-width);
994 v2d->tot.xmax = 0.0f;
995 }
996 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
997 /* width is in positive-x half */
998 v2d->tot.xmin = 0.0f;
999 v2d->tot.xmax = float(width);
1000 }
1001 else {
1002 /* width is centered around (x == 0) */
1003 const float dx = float(width) / 2.0f;
1004
1005 v2d->tot.xmin = -dx;
1006 v2d->tot.xmax = dx;
1007 }
1008
1009 /* handle height - posx and negx flags are mutually exclusive, so watch out */
1010 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
1011 /* height is in negative-y half */
1012 v2d->tot.ymin = float(-height);
1013 v2d->tot.ymax = 0.0f;
1014 }
1015 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
1016 /* height is in positive-y half */
1017 v2d->tot.ymin = 0.0f;
1018 v2d->tot.ymax = float(height);
1019 }
1020 else {
1021 /* height is centered around (y == 0) */
1022 const float dy = float(height) / 2.0f;
1023
1024 v2d->tot.ymin = -dy;
1025 v2d->tot.ymax = dy;
1026 }
1027
1028 /* make sure that 'cur' rect is in a valid state as a result of these changes */
1030}
1031
1032void UI_view2d_totRect_set(View2D *v2d, int width, int height)
1033{
1034 view2d_totRect_set_resize(v2d, width, height, false);
1035}
1036
1038{
1039 /* TODO(sergey): This way we avoid threading conflict with sequencer rendering
1040 * text strip. But ideally we want to make glyph cache to be fully safe
1041 * for threading.
1042 */
1043 if (G.is_rendering) {
1044 return;
1045 }
1046 /* While scaling we can accumulate fonts at many sizes (~20 or so).
1047 * Not an issue with embedded font, but can use over 500Mb with i18n ones! See #38244. */
1048
1049 /* NOTE: only some views draw text, we could check for this case to avoid cleaning cache. */
1051}
1052
1054
1055/* -------------------------------------------------------------------- */
1058
1059/* mapping function to ensure 'cur' draws extended over the area where sliders are */
1060static void view2d_map_cur_using_mask(const View2D *v2d, rctf *r_curmasked)
1061{
1062 *r_curmasked = v2d->cur;
1063
1064 if (view2d_scroll_mapped(v2d->scroll)) {
1065 const float sizex = BLI_rcti_size_x(&v2d->mask);
1066 const float sizey = BLI_rcti_size_y(&v2d->mask);
1067
1068 /* prevent tiny or narrow regions to get
1069 * invalid coordinates - mask can get negative even... */
1070 if (sizex > 0.0f && sizey > 0.0f) {
1071 const float dx = BLI_rctf_size_x(&v2d->cur) / (sizex + 1);
1072 const float dy = BLI_rctf_size_y(&v2d->cur) / (sizey + 1);
1073
1074 if (v2d->mask.xmin != 0) {
1075 r_curmasked->xmin -= dx * float(v2d->mask.xmin);
1076 }
1077 if (v2d->mask.xmax + 1 != v2d->winx) {
1078 r_curmasked->xmax += dx * float(v2d->winx - v2d->mask.xmax - 1);
1079 }
1080
1081 if (v2d->mask.ymin != 0) {
1082 r_curmasked->ymin -= dy * float(v2d->mask.ymin);
1083 }
1084 if (v2d->mask.ymax + 1 != v2d->winy) {
1085 r_curmasked->ymax += dy * float(v2d->winy - v2d->mask.ymax - 1);
1086 }
1087 }
1088 }
1089}
1090
1092{
1093 rctf curmasked;
1094 const int sizex = BLI_rcti_size_x(&v2d->mask);
1095 const int sizey = BLI_rcti_size_y(&v2d->mask);
1096 const float eps = 0.001f;
1097 float xofs = 0.0f, yofs = 0.0f;
1098
1099 /* Pixel offsets (-GLA_PIXEL_OFS) are needed to get 1:1
1100 * correspondence with pixels for smooth UI drawing,
1101 * but only applied where requested.
1102 */
1103 /* XXX brecht: instead of zero at least use a tiny offset, otherwise
1104 * pixel rounding is effectively random due to float inaccuracy */
1105 if (sizex > 0) {
1106 xofs = eps * BLI_rctf_size_x(&v2d->cur) / sizex;
1107 }
1108 if (sizey > 0) {
1109 yofs = eps * BLI_rctf_size_y(&v2d->cur) / sizey;
1110 }
1111
1112 /* apply mask-based adjustments to cur rect (due to scrollers),
1113 * to eliminate scaling artifacts */
1114 view2d_map_cur_using_mask(v2d, &curmasked);
1115
1116 BLI_rctf_translate(&curmasked, -xofs, -yofs);
1117
1118 /* XXX ton: this flag set by outliner, for icons */
1119 if (v2d->flag & V2D_PIXELOFS_X) {
1120 curmasked.xmin = floorf(curmasked.xmin) - (eps + xofs);
1121 curmasked.xmax = floorf(curmasked.xmax) - (eps + xofs);
1122 }
1123 if (v2d->flag & V2D_PIXELOFS_Y) {
1124 curmasked.ymin = floorf(curmasked.ymin) - (eps + yofs);
1125 curmasked.ymax = floorf(curmasked.ymax) - (eps + yofs);
1126 }
1127
1128 /* set matrix on all appropriate axes */
1129 wmOrtho2(curmasked.xmin, curmasked.xmax, curmasked.ymin, curmasked.ymax);
1130}
1131
1132void UI_view2d_view_orthoSpecial(ARegion *region, View2D *v2d, const bool xaxis)
1133{
1134 rctf curmasked;
1135 float xofs, yofs;
1136
1137 /* Pixel offsets (-GLA_PIXEL_OFS) are needed to get 1:1
1138 * correspondence with pixels for smooth UI drawing,
1139 * but only applied where requested.
1140 */
1141 /* XXX(ton): temp. */
1142 xofs = 0.0f; // (v2d->flag & V2D_PIXELOFS_X) ? GLA_PIXEL_OFS : 0.0f;
1143 yofs = 0.0f; // (v2d->flag & V2D_PIXELOFS_Y) ? GLA_PIXEL_OFS : 0.0f;
1144
1145 /* apply mask-based adjustments to cur rect (due to scrollers),
1146 * to eliminate scaling artifacts */
1147 view2d_map_cur_using_mask(v2d, &curmasked);
1148
1149 /* only set matrix with 'cur' coordinates on relevant axes */
1150 if (xaxis) {
1151 wmOrtho2(curmasked.xmin - xofs, curmasked.xmax - xofs, -yofs, region->winy - yofs);
1152 }
1153 else {
1154 wmOrtho2(-xofs, region->winx - xofs, curmasked.ymin - yofs, curmasked.ymax - yofs);
1155 }
1156}
1157
1159{
1160 ARegion *region = CTX_wm_region(C);
1161 const int width = BLI_rcti_size_x(&region->winrct) + 1;
1162 const int height = BLI_rcti_size_y(&region->winrct) + 1;
1163
1164 wmOrtho2(0.0f, float(width), 0.0f, float(height));
1166
1167 // ED_region_pixelspace(CTX_wm_region(C));
1168}
1169
1171
1172/* -------------------------------------------------------------------- */
1175
1177 const View2D *v2d, int colorid, float step, int level_size, int totlevels)
1178{
1179 /* Exit if there is nothing to draw */
1180 if (totlevels == 0) {
1181 return;
1182 }
1183
1184 int offset = -10;
1185 float lstep = step;
1186 uchar grid_line_color[3];
1187
1188 /* Make an estimate of at least how many vertices will be needed */
1189 uint vertex_count = 4;
1190 vertex_count += 2 * (int((v2d->cur.xmax - v2d->cur.xmin) / lstep) + 1);
1191 vertex_count += 2 * (int((v2d->cur.ymax - v2d->cur.ymin) / lstep) + 1);
1192
1197
1198 GPU_line_width(1.0f);
1199
1201 immBeginAtMost(GPU_PRIM_LINES, vertex_count);
1202
1203 for (int level = 0; level < totlevels; level++) {
1204 /* Blend the background color (colorid) with the grid color, to avoid either too low contrast
1205 * or high contrast grid lines. This only has an effect if colorid != TH_GRID. */
1206 UI_GetThemeColorBlendShade3ubv(colorid, TH_GRID, 0.25f, offset, grid_line_color);
1207
1208 int i = int(v2d->cur.xmin / lstep);
1209 if (v2d->cur.xmin > 0.0f) {
1210 i++;
1211 }
1212 float start = i * lstep;
1213
1214 for (; start < v2d->cur.xmax; start += lstep, i++) {
1215 if (i == 0 || (level < totlevels - 1 && i % level_size == 0)) {
1216 continue;
1217 }
1218
1220 immVertex2f(pos, start, v2d->cur.ymin);
1221 immAttr3ubv(color, grid_line_color);
1222 immVertex2f(pos, start, v2d->cur.ymax);
1223 }
1224
1225 i = int(v2d->cur.ymin / lstep);
1226 if (v2d->cur.ymin > 0.0f) {
1227 i++;
1228 }
1229 start = i * lstep;
1230
1231 for (; start < v2d->cur.ymax; start += lstep, i++) {
1232 if (i == 0 || (level < totlevels - 1 && i % level_size == 0)) {
1233 continue;
1234 }
1235
1237 immVertex2f(pos, v2d->cur.xmin, start);
1238 immAttr3ubv(color, grid_line_color);
1239 immVertex2f(pos, v2d->cur.xmax, start);
1240 }
1241
1242 lstep *= level_size;
1243 offset -= 6;
1244 }
1245
1246 /* X and Y axis */
1248 colorid, TH_GRID, 0.5f, -18 + ((totlevels - 1) * -6), grid_line_color);
1249
1251 immVertex2f(pos, 0.0f, v2d->cur.ymin);
1252 immAttr3ubv(color, grid_line_color);
1253 immVertex2f(pos, 0.0f, v2d->cur.ymax);
1254
1256 immVertex2f(pos, v2d->cur.xmin, 0.0f);
1257 immAttr3ubv(color, grid_line_color);
1258 immVertex2f(pos, v2d->cur.xmax, 0.0f);
1259
1260 immEnd();
1262}
1263
1265 const float step, const float min, const float max, float *r_start, int *r_count)
1266{
1267 *r_start = min;
1268 if (*r_start < 0.0f) {
1269 *r_start += -float(fmod(min, step));
1270 }
1271 else {
1272 *r_start += step - float(fabs(fmod(min, step)));
1273 }
1274
1275 if (*r_start > max) {
1276 *r_count = 0;
1277 }
1278 else {
1279 *r_count = (max - *r_start) / step + 1;
1280 }
1281}
1282
1284 const int grid_color_id,
1285 const float min_step,
1286 const int grid_subdivisions)
1287{
1288 BLI_assert(grid_subdivisions >= 0 && grid_subdivisions < 4);
1289 if (grid_subdivisions == 0) {
1290 return;
1291 }
1292
1293 const float zoom_x = float(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur);
1294
1299
1300 /* Scaling the dots fully with the zoom looks too busy, but a bit of size variation is nice. */
1301 const float min_point_size = 2.0f * U.pixelsize;
1302 const float point_size_factor = 1.5f;
1303 const float max_point_size = point_size_factor * min_point_size;
1304
1305 /* Each consecutive grid level is five times larger than the previous. */
1306 const int subdivision_scale = 5;
1307
1308 const float view_level = logf(min_step / zoom_x) / logf(subdivision_scale);
1309 const int largest_visible_level = int(view_level);
1310
1311 for (int level_offset = 0; level_offset <= grid_subdivisions; level_offset++) {
1312 const int level = largest_visible_level - level_offset;
1313
1314 if (level < 0) {
1315 break;
1316 }
1317
1318 const float level_scale = powf(subdivision_scale, level);
1319 const float point_size_precise = min_point_size * level_scale * zoom_x;
1320 const float point_size_draw = ceilf(
1321 clamp_f(point_size_precise, min_point_size, max_point_size));
1322
1323 /* Offset point by this amount to better align centers as size changes. */
1324 const float point_size_offset = (point_size_draw / 2.0f) - U.pixelsize;
1325
1326 /* To compensate the for the clamped point_size we adjust the alpha to make the overall
1327 * brightness of the grid background more consistent. */
1328 const float alpha = pow2f(point_size_precise / point_size_draw);
1329
1330 /* Make sure we don't draw points once the alpha gets too low. */
1331 const float alpha_cutoff = 0.01f;
1332 if (alpha < alpha_cutoff) {
1333 break;
1334 }
1335 const float alpha_clamped = clamp_f((1.0f + alpha_cutoff) * alpha - alpha_cutoff, 0.0f, 1.0f);
1336
1337 /* If we have don't draw enough subdivision levels so they fade out naturally, we apply an
1338 * additional fade to the last level to avoid pop in. */
1339 const bool last_level = level_offset == grid_subdivisions;
1340 const float subdivision_fade = last_level ? (1.0f - fractf(view_level)) : 1.0f;
1341
1342 float color[4];
1343 UI_GetThemeColor3fv(grid_color_id, color);
1344 color[3] = alpha_clamped * subdivision_fade;
1345
1346 const float step = min_step * level_scale;
1347 int count_x;
1348 float start_x;
1349
1350 /* Count points that fit in viewport. */
1351 grid_axis_start_and_count(step, v2d->cur.xmin, v2d->cur.xmax, &start_x, &count_x);
1352 int count_y;
1353 float start_y;
1354 grid_axis_start_and_count(step, v2d->cur.ymin, v2d->cur.ymax, &start_y, &count_y);
1355 if (count_x == 0 || count_y == 0) {
1356 continue;
1357 }
1358
1359 immUniform1f("size", point_size_draw);
1360 immUniform4fv("color", color);
1361 immBegin(GPU_PRIM_POINTS, count_x * count_y);
1362
1363 /* Theoretically drawing on top of lower grid levels could be avoided, but it would also
1364 * increase the complexity of this loop, which isn't worth the time at the moment. */
1365 for (int i_y = 0; i_y < count_y; i_y++) {
1366 const float y = start_y + step * i_y;
1367 for (int i_x = 0; i_x < count_x; i_x++) {
1368 const float x = start_x + step * i_x;
1369 immVertex2f(pos, x + point_size_offset, y + point_size_offset);
1370 }
1371 }
1372
1373 immEnd();
1374 }
1375
1378}
1379
1381
1382/* -------------------------------------------------------------------- */
1385
1386void view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom, View2DScrollers *r_scrollers)
1387{
1388 rcti vert, hor;
1389 float fac1, fac2, totsize, scrollsize;
1390 const int scroll = view2d_scroll_mapped(v2d->scroll);
1391 int smaller;
1392
1393 /* Always update before drawing (for dynamically sized scrollers). */
1394 view2d_masks(v2d, mask_custom);
1395
1396 vert = v2d->vert;
1397 hor = v2d->hor;
1398
1399 /* slider rects need to be smaller than region and not interfere with splitter areas */
1400 hor.xmin += UI_HEADER_OFFSET;
1401 hor.xmax -= UI_HEADER_OFFSET;
1402 vert.ymin += UI_HEADER_OFFSET;
1403 vert.ymax -= UI_HEADER_OFFSET;
1404
1405 /* width of sliders */
1406 smaller = int(0.1f * U.widget_unit);
1407 if (scroll & V2D_SCROLL_BOTTOM) {
1408 hor.ymin += smaller;
1409 }
1410 else {
1411 hor.ymax -= smaller;
1412 }
1413
1414 if (scroll & V2D_SCROLL_LEFT) {
1415 vert.xmin += smaller;
1416 }
1417 else {
1418 vert.xmax -= smaller;
1419 }
1420
1423
1424 /* store in scrollers, used for drawing */
1425 r_scrollers->vert = vert;
1426 r_scrollers->hor = hor;
1427
1428 /* scroller 'buttons':
1429 * - These should always remain within the visible region of the scroll-bar
1430 * - They represent the region of 'tot' that is visible in 'cur'
1431 */
1432
1433 /* horizontal scrollers */
1434 if (scroll & V2D_SCROLL_HORIZONTAL) {
1435 /* scroller 'button' extents */
1436 totsize = BLI_rctf_size_x(&v2d->tot);
1437 scrollsize = float(BLI_rcti_size_x(&hor));
1438 if (totsize == 0.0f) {
1439 totsize = 1.0f; /* avoid divide by zero */
1440 }
1441
1442 fac1 = (v2d->cur.xmin - v2d->tot.xmin) / totsize;
1443 if (fac1 <= 0.0f) {
1444 r_scrollers->hor_min = hor.xmin;
1445 }
1446 else {
1447 r_scrollers->hor_min = int(hor.xmin + (fac1 * scrollsize));
1448 }
1449
1450 fac2 = (v2d->cur.xmax - v2d->tot.xmin) / totsize;
1451 if (fac2 >= 1.0f) {
1452 r_scrollers->hor_max = hor.xmax;
1453 }
1454 else {
1455 r_scrollers->hor_max = int(hor.xmin + (fac2 * scrollsize));
1456 }
1457
1458 /* prevent inverted sliders */
1459 if (r_scrollers->hor_min > r_scrollers->hor_max) {
1460 r_scrollers->hor_min = r_scrollers->hor_max;
1461 }
1462 /* prevent sliders from being too small to grab */
1463 if ((r_scrollers->hor_max - r_scrollers->hor_min) < V2D_SCROLL_THUMB_SIZE_MIN) {
1464 r_scrollers->hor_max = r_scrollers->hor_min + V2D_SCROLL_THUMB_SIZE_MIN;
1465
1466 CLAMP(r_scrollers->hor_max, hor.xmin + V2D_SCROLL_THUMB_SIZE_MIN, hor.xmax);
1467 CLAMP(r_scrollers->hor_min, hor.xmin, hor.xmax - V2D_SCROLL_THUMB_SIZE_MIN);
1468 }
1469 }
1470
1471 /* vertical scrollers */
1472 if (scroll & V2D_SCROLL_VERTICAL) {
1473 /* scroller 'button' extents */
1474 totsize = BLI_rctf_size_y(&v2d->tot);
1475 scrollsize = float(BLI_rcti_size_y(&vert));
1476 if (totsize == 0.0f) {
1477 totsize = 1.0f; /* avoid divide by zero */
1478 }
1479
1480 fac1 = (v2d->cur.ymin - v2d->tot.ymin) / totsize;
1481 if (fac1 <= 0.0f) {
1482 r_scrollers->vert_min = vert.ymin;
1483 }
1484 else {
1485 r_scrollers->vert_min = int(vert.ymin + (fac1 * scrollsize));
1486 }
1487
1488 fac2 = (v2d->cur.ymax - v2d->tot.ymin) / totsize;
1489 if (fac2 >= 1.0f) {
1490 r_scrollers->vert_max = vert.ymax;
1491 }
1492 else {
1493 r_scrollers->vert_max = int(vert.ymin + (fac2 * scrollsize));
1494 }
1495
1496 /* prevent inverted sliders */
1497 if (r_scrollers->vert_min > r_scrollers->vert_max) {
1498 r_scrollers->vert_min = r_scrollers->vert_max;
1499 }
1500 /* prevent sliders from being too small to grab */
1501 if ((r_scrollers->vert_max - r_scrollers->vert_min) < V2D_SCROLL_THUMB_SIZE_MIN) {
1502 r_scrollers->vert_max = r_scrollers->vert_min + V2D_SCROLL_THUMB_SIZE_MIN;
1503
1504 CLAMP(r_scrollers->vert_max, vert.ymin + V2D_SCROLL_THUMB_SIZE_MIN, vert.ymax);
1505 CLAMP(r_scrollers->vert_min, vert.ymin, vert.ymax - V2D_SCROLL_THUMB_SIZE_MIN);
1506 }
1507 }
1508}
1509
1510void UI_view2d_scrollers_draw_ex(View2D *v2d, const rcti *mask_custom, bool use_full_hide)
1511{
1512 View2DScrollers scrollers;
1513 view2d_scrollers_calc(v2d, mask_custom, &scrollers);
1514 bTheme *btheme = UI_GetTheme();
1515 rcti vert, hor;
1516 const int scroll = view2d_scroll_mapped(v2d->scroll);
1517 const char emboss_alpha = btheme->tui.widget_emboss[3];
1518 const float alpha_min = use_full_hide ? 0.0f : V2D_SCROLL_MIN_ALPHA;
1519
1520 uchar scrollers_back_color[4];
1521
1522 /* Color for scroll-bar backs. */
1523 UI_GetThemeColor4ubv(TH_BACK, scrollers_back_color);
1524
1525 /* make copies of rects for less typing */
1526 vert = scrollers.vert;
1527 hor = scrollers.hor;
1528
1529 /* Horizontal scroll-bar. */
1530 if (scroll & V2D_SCROLL_HORIZONTAL) {
1531 uiWidgetColors wcol = btheme->tui.wcol_scroll;
1532 /* 0..255 -> min...1 */
1533 const float alpha_fac = ((v2d->alpha_hor / 255.0f) * (1.0f - alpha_min)) + alpha_min;
1534 rcti slider;
1535 int state;
1536
1537 slider.xmin = scrollers.hor_min;
1538 slider.xmax = scrollers.hor_max;
1539 slider.ymin = hor.ymin;
1540 slider.ymax = hor.ymax;
1541
1543
1544 wcol.inner[3] *= alpha_fac;
1545 wcol.item[3] *= alpha_fac;
1546 wcol.outline[3] = 0;
1547 btheme->tui.widget_emboss[3] = 0; /* will be reset later */
1548
1549 /* show zoom handles if:
1550 * - zooming on x-axis is allowed (no scroll otherwise)
1551 * - slider bubble is large enough (no overdraw confusion)
1552 * - scale is shown on the scroller
1553 * (workaround to make sure that button windows don't show these,
1554 * and only the time-grids with their zoom-ability can do so).
1555 */
1556 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0 && (v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) &&
1558 {
1560 }
1561
1562 UI_draw_widget_scroll(&wcol, &hor, &slider, state);
1563 }
1564
1565 /* Vertical scroll-bar. */
1566 if (scroll & V2D_SCROLL_VERTICAL) {
1567 uiWidgetColors wcol = btheme->tui.wcol_scroll;
1568 rcti slider;
1569 /* 0..255 -> min...1 */
1570 const float alpha_fac = ((v2d->alpha_vert / 255.0f) * (1.0f - alpha_min)) + alpha_min;
1571 int state;
1572
1573 slider.xmin = vert.xmin;
1574 slider.xmax = vert.xmax;
1575 slider.ymin = scrollers.vert_min;
1576 slider.ymax = scrollers.vert_max;
1577
1579
1580 wcol.inner[3] *= alpha_fac;
1581 wcol.item[3] *= alpha_fac;
1582 wcol.outline[3] = 0;
1583 btheme->tui.widget_emboss[3] = 0; /* will be reset later */
1584
1585 /* show zoom handles if:
1586 * - zooming on y-axis is allowed (no scroll otherwise)
1587 * - slider bubble is large enough (no overdraw confusion)
1588 * - scale is shown on the scroller
1589 * (workaround to make sure that button windows don't show these,
1590 * and only the time-grids with their zoomability can do so)
1591 */
1592 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0 && (v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) &&
1594 {
1596 }
1597
1598 UI_draw_widget_scroll(&wcol, &vert, &slider, state);
1599 }
1600
1601 /* Was changed above, so reset. */
1602 btheme->tui.widget_emboss[3] = emboss_alpha;
1603}
1604
1605void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
1606{
1607 UI_view2d_scrollers_draw_ex(v2d, mask_custom, false);
1608}
1609
1611
1612/* -------------------------------------------------------------------- */
1615
1616void UI_view2d_listview_view_to_cell(float columnwidth,
1617 float rowheight,
1618 float startx,
1619 float starty,
1620 float viewx,
1621 float viewy,
1622 int *r_column,
1623 int *r_row)
1624{
1625 if (r_column) {
1626 if (columnwidth > 0) {
1627 /* Columns go from left to right (x increases). */
1628 *r_column = floorf((viewx - startx) / columnwidth);
1629 }
1630 else {
1631 *r_column = 0;
1632 }
1633 }
1634
1635 if (r_row) {
1636 if (rowheight > 0) {
1637 /* Rows got from top to bottom (y decreases). */
1638 *r_row = floorf((starty - viewy) / rowheight);
1639 }
1640 else {
1641 *r_row = 0;
1642 }
1643 }
1644}
1645
1647
1648/* -------------------------------------------------------------------- */
1651
1652float UI_view2d_region_to_view_x(const View2D *v2d, float x)
1653{
1654 return (v2d->cur.xmin +
1655 (BLI_rctf_size_x(&v2d->cur) * (x - v2d->mask.xmin) / BLI_rcti_size_x(&v2d->mask)));
1656}
1657float UI_view2d_region_to_view_y(const View2D *v2d, float y)
1658{
1659 return (v2d->cur.ymin +
1660 (BLI_rctf_size_y(&v2d->cur) * (y - v2d->mask.ymin) / BLI_rcti_size_y(&v2d->mask)));
1661}
1662
1664 const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y)
1665{
1666 *r_view_x = UI_view2d_region_to_view_x(v2d, x);
1667 *r_view_y = UI_view2d_region_to_view_y(v2d, y);
1668}
1669
1670void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst)
1671{
1672 const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)};
1673 const float mask_size[2] = {float(BLI_rcti_size_x(&v2d->mask)),
1674 float(BLI_rcti_size_y(&v2d->mask))};
1675
1676 rect_dst->xmin = (v2d->cur.xmin +
1677 (cur_size[0] * (rect_src->xmin - v2d->mask.xmin) / mask_size[0]));
1678 rect_dst->xmax = (v2d->cur.xmin +
1679 (cur_size[0] * (rect_src->xmax - v2d->mask.xmin) / mask_size[0]));
1680 rect_dst->ymin = (v2d->cur.ymin +
1681 (cur_size[1] * (rect_src->ymin - v2d->mask.ymin) / mask_size[1]));
1682 rect_dst->ymax = (v2d->cur.ymin +
1683 (cur_size[1] * (rect_src->ymax - v2d->mask.ymin) / mask_size[1]));
1684}
1685
1686float UI_view2d_view_to_region_x(const View2D *v2d, float x)
1687{
1688 return (v2d->mask.xmin +
1689 (((x - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur)) * BLI_rcti_size_x(&v2d->mask)));
1690}
1691float UI_view2d_view_to_region_y(const View2D *v2d, float y)
1692{
1693 return (v2d->mask.ymin +
1694 (((y - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur)) * BLI_rcti_size_y(&v2d->mask)));
1695}
1696
1698 const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y)
1699{
1700 /* express given coordinates as proportional values */
1701 x = (x - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
1702 y = (y - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
1703
1704 /* check if values are within bounds */
1705 if ((x >= 0.0f) && (x <= 1.0f) && (y >= 0.0f) && (y <= 1.0f)) {
1706 *r_region_x = int(v2d->mask.xmin + (x * BLI_rcti_size_x(&v2d->mask)));
1707 *r_region_y = int(v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask)));
1708
1709 return true;
1710 }
1711
1712 /* set initial value in case coordinate lies outside of bounds */
1713 *r_region_x = *r_region_y = V2D_IS_CLIPPED;
1714
1715 return false;
1716}
1717
1719 const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y)
1720{
1721 /* Step 1: express given coordinates as proportional values. */
1722 x = (x - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
1723 y = (y - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
1724
1725 /* Step 2: convert proportional distances to screen coordinates. */
1726 x = v2d->mask.xmin + (x * BLI_rcti_size_x(&v2d->mask));
1727 y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask));
1728
1729 /* Although we don't clamp to lie within region bounds, we must avoid exceeding size of ints. */
1730 *r_region_x = clamp_float_to_int(x);
1731 *r_region_y = clamp_float_to_int(y);
1732}
1733
1735 const View2D *v2d, float x, float y, float *r_region_x, float *r_region_y)
1736{
1737 /* express given coordinates as proportional values */
1738 x = (x - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
1739 y = (y - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
1740
1741 /* convert proportional distances to screen coordinates */
1742 *r_region_x = v2d->mask.xmin + (x * BLI_rcti_size_x(&v2d->mask));
1743 *r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask));
1744}
1745
1747 const float xy_a[2],
1748 const float xy_b[2],
1749 int r_region_a[2],
1750 int r_region_b[2])
1751{
1752 rctf rect_unit;
1753 rect_unit.xmin = rect_unit.ymin = 0.0f;
1754 rect_unit.xmax = rect_unit.ymax = 1.0f;
1755
1756 /* Express given coordinates as proportional values. */
1757 const float s_a[2] = {
1758 (xy_a[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
1759 (xy_a[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
1760 };
1761 const float s_b[2] = {
1762 (xy_b[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
1763 (xy_b[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
1764 };
1765
1766 /* Set initial value in case coordinates lie outside bounds. */
1767 r_region_a[0] = r_region_b[0] = r_region_a[1] = r_region_b[1] = V2D_IS_CLIPPED;
1768
1769 if (BLI_rctf_isect_segment(&rect_unit, s_a, s_b)) {
1770 r_region_a[0] = int(v2d->mask.xmin + (s_a[0] * BLI_rcti_size_x(&v2d->mask)));
1771 r_region_a[1] = int(v2d->mask.ymin + (s_a[1] * BLI_rcti_size_y(&v2d->mask)));
1772 r_region_b[0] = int(v2d->mask.xmin + (s_b[0] * BLI_rcti_size_x(&v2d->mask)));
1773 r_region_b[1] = int(v2d->mask.ymin + (s_b[1] * BLI_rcti_size_y(&v2d->mask)));
1774
1775 return true;
1776 }
1777
1778 return false;
1779}
1780
1781void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst)
1782{
1783 const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)};
1784 const float mask_size[2] = {float(BLI_rcti_size_x(&v2d->mask)),
1785 float(BLI_rcti_size_y(&v2d->mask))};
1786 rctf rect_tmp;
1787
1788 /* Step 1: express given coordinates as proportional values. */
1789 rect_tmp.xmin = (rect_src->xmin - v2d->cur.xmin) / cur_size[0];
1790 rect_tmp.xmax = (rect_src->xmax - v2d->cur.xmin) / cur_size[0];
1791 rect_tmp.ymin = (rect_src->ymin - v2d->cur.ymin) / cur_size[1];
1792 rect_tmp.ymax = (rect_src->ymax - v2d->cur.ymin) / cur_size[1];
1793
1794 /* Step 2: convert proportional distances to screen coordinates. */
1795 rect_tmp.xmin = v2d->mask.xmin + (rect_tmp.xmin * mask_size[0]);
1796 rect_tmp.xmax = v2d->mask.xmin + (rect_tmp.xmax * mask_size[0]);
1797 rect_tmp.ymin = v2d->mask.ymin + (rect_tmp.ymin * mask_size[1]);
1798 rect_tmp.ymax = v2d->mask.ymin + (rect_tmp.ymax * mask_size[1]);
1799
1800 clamp_rctf_to_rcti(rect_dst, &rect_tmp);
1801}
1802
1803void UI_view2d_view_to_region_m4(const View2D *v2d, float matrix[4][4])
1804{
1805 rctf mask;
1806 unit_m4(matrix);
1807 BLI_rctf_rcti_copy(&mask, &v2d->mask);
1809}
1810
1811bool UI_view2d_view_to_region_rcti_clip(const View2D *v2d, const rctf *rect_src, rcti *rect_dst)
1812{
1813 const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)};
1814 const float mask_size[2] = {float(BLI_rcti_size_x(&v2d->mask)),
1815 float(BLI_rcti_size_y(&v2d->mask))};
1816 rctf rect_tmp;
1817
1818 BLI_assert(rect_src->xmin <= rect_src->xmax && rect_src->ymin <= rect_src->ymax);
1819
1820 /* Step 1: express given coordinates as proportional values. */
1821 rect_tmp.xmin = (rect_src->xmin - v2d->cur.xmin) / cur_size[0];
1822 rect_tmp.xmax = (rect_src->xmax - v2d->cur.xmin) / cur_size[0];
1823 rect_tmp.ymin = (rect_src->ymin - v2d->cur.ymin) / cur_size[1];
1824 rect_tmp.ymax = (rect_src->ymax - v2d->cur.ymin) / cur_size[1];
1825
1826 if (((rect_tmp.xmax < 0.0f) || (rect_tmp.xmin > 1.0f) || (rect_tmp.ymax < 0.0f) ||
1827 (rect_tmp.ymin > 1.0f)) == 0)
1828 {
1829 /* Step 2: convert proportional distances to screen coordinates. */
1830 rect_tmp.xmin = v2d->mask.xmin + (rect_tmp.xmin * mask_size[0]);
1831 rect_tmp.xmax = v2d->mask.ymin + (rect_tmp.xmax * mask_size[0]);
1832 rect_tmp.ymin = v2d->mask.ymin + (rect_tmp.ymin * mask_size[1]);
1833 rect_tmp.ymax = v2d->mask.ymin + (rect_tmp.ymax * mask_size[1]);
1834
1835 clamp_rctf_to_rcti(rect_dst, &rect_tmp);
1836
1837 return true;
1838 }
1839
1840 rect_dst->xmin = rect_dst->xmax = rect_dst->ymin = rect_dst->ymax = V2D_IS_CLIPPED;
1841 return false;
1842}
1843
1845
1846/* -------------------------------------------------------------------- */
1849
1851{
1852 ScrArea *area = CTX_wm_area(C);
1853 ARegion *region = CTX_wm_region(C);
1854
1855 if (area == nullptr) {
1856 return nullptr;
1857 }
1858 if (region == nullptr) {
1859 return nullptr;
1860 }
1861 return &(region->v2d);
1862}
1863
1865{
1866 ScrArea *area = CTX_wm_area(C);
1867 ARegion *region = CTX_wm_region(C);
1868
1869 if (area == nullptr) {
1870 return nullptr;
1871 }
1872 if (region == nullptr) {
1873 return nullptr;
1874 }
1875 if (region->regiontype != RGN_TYPE_WINDOW) {
1877 return region_win ? &(region_win->v2d) : nullptr;
1878 }
1879 return &(region->v2d);
1880}
1881
1882void UI_view2d_scroller_size_get(const View2D *v2d, bool mapped, float *r_x, float *r_y)
1883{
1884 const int scroll = (mapped) ? view2d_scroll_mapped(v2d->scroll) : v2d->scroll;
1885
1886 if (r_x) {
1887 if (scroll & V2D_SCROLL_VERTICAL) {
1889 *r_x = ((*r_x - V2D_SCROLL_MIN_WIDTH) * (v2d->alpha_vert / 255.0f)) + V2D_SCROLL_MIN_WIDTH;
1890 }
1891 else {
1892 *r_x = 0;
1893 }
1894 }
1895 if (r_y) {
1896 if (scroll & V2D_SCROLL_HORIZONTAL) {
1899 *r_y = ((*r_y - V2D_SCROLL_MIN_WIDTH) * (v2d->alpha_hor / 255.0f)) + V2D_SCROLL_MIN_WIDTH;
1900 }
1901 else {
1902 *r_y = 0;
1903 }
1904 }
1905}
1906
1907void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
1908{
1909 if (r_x) {
1910 *r_x = UI_view2d_scale_get_x(v2d);
1911 }
1912 if (r_y) {
1913 *r_y = UI_view2d_scale_get_y(v2d);
1914 }
1915}
1917{
1918 return BLI_rcti_size_x(&v2d->mask) / BLI_rctf_size_x(&v2d->cur);
1919}
1921{
1922 return BLI_rcti_size_y(&v2d->mask) / BLI_rctf_size_y(&v2d->cur);
1923}
1924void UI_view2d_scale_get_inverse(const View2D *v2d, float *r_x, float *r_y)
1925{
1926 if (r_x) {
1927 *r_x = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
1928 }
1929 if (r_y) {
1930 *r_y = BLI_rctf_size_y(&v2d->cur) / BLI_rcti_size_y(&v2d->mask);
1931 }
1932}
1933
1934void UI_view2d_center_get(const View2D *v2d, float *r_x, float *r_y)
1935{
1936 /* get center */
1937 if (r_x) {
1938 *r_x = BLI_rctf_cent_x(&v2d->cur);
1939 }
1940 if (r_y) {
1941 *r_y = BLI_rctf_cent_y(&v2d->cur);
1942 }
1943}
1944void UI_view2d_center_set(View2D *v2d, float x, float y)
1945{
1946 BLI_rctf_recenter(&v2d->cur, x, y);
1947
1948 /* make sure that 'cur' rect is in a valid state as a result of these changes */
1950}
1951
1952void UI_view2d_offset(View2D *v2d, float xfac, float yfac)
1953{
1954 if (xfac != -1.0f) {
1955 const float xsize = BLI_rctf_size_x(&v2d->cur);
1956 const float xmin = v2d->tot.xmin;
1957 const float xmax = v2d->tot.xmax - xsize;
1958
1959 v2d->cur.xmin = (xmin * (1.0f - xfac)) + (xmax * xfac);
1960 v2d->cur.xmax = v2d->cur.xmin + xsize;
1961 }
1962
1963 if (yfac != -1.0f) {
1964 const float ysize = BLI_rctf_size_y(&v2d->cur);
1965 const float ymin = v2d->tot.ymin;
1966 const float ymax = v2d->tot.ymax - ysize;
1967
1968 v2d->cur.ymin = (ymin * (1.0f - yfac)) + (ymax * yfac);
1969 v2d->cur.ymax = v2d->cur.ymin + ysize;
1970 }
1971
1973}
1974
1976{
1977 const float cur_size_y = BLI_rctf_size_y(&v2d->cur);
1978 const float page_size_y = view2d_page_size_y(*v2d);
1979
1980 v2d->cur.ymax = roundf(v2d->cur.ymax / page_size_y) * page_size_y;
1981 v2d->cur.ymin = v2d->cur.ymax - cur_size_y;
1982
1984}
1985
1987 const View2D *v2d,
1988 const int xy[2],
1989 int *r_scroll)
1990{
1991 const int scroll = view2d_scroll_mapped(v2d->scroll);
1992 *r_scroll = scroll;
1993
1994 if (scroll) {
1995 /* Move to region-coordinates. */
1996 const int co[2] = {
1997 xy[0] - region->winrct.xmin,
1998 xy[1] - region->winrct.ymin,
1999 };
2000 if (scroll & V2D_SCROLL_HORIZONTAL) {
2001 if (IN_2D_HORIZ_SCROLL(v2d, co)) {
2002 return 'h';
2003 }
2004 }
2005 if (scroll & V2D_SCROLL_VERTICAL) {
2006 if (IN_2D_VERT_SCROLL(v2d, co)) {
2007 return 'v';
2008 }
2009 }
2010 }
2011
2012 return 0;
2013}
2014
2016 const View2D *v2d,
2017 const rcti *rect,
2018 int *r_scroll)
2019{
2020 const int scroll = view2d_scroll_mapped(v2d->scroll);
2021 *r_scroll = scroll;
2022
2023 if (scroll) {
2024 /* Move to region-coordinates. */
2025 rcti rect_region = *rect;
2026 BLI_rcti_translate(&rect_region, -region->winrct.xmin, region->winrct.ymin);
2027 if (scroll & V2D_SCROLL_HORIZONTAL) {
2028 if (IN_2D_HORIZ_SCROLL_RECT(v2d, &rect_region)) {
2029 return 'h';
2030 }
2031 }
2032 if (scroll & V2D_SCROLL_VERTICAL) {
2033 if (IN_2D_VERT_SCROLL_RECT(v2d, &rect_region)) {
2034 return 'v';
2035 }
2036 }
2037 }
2038
2039 return 0;
2040}
2041
2042char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2])
2043{
2044 int scroll_dummy = 0;
2045 return UI_view2d_mouse_in_scrollers_ex(region, v2d, xy, &scroll_dummy);
2046}
2047
2048char UI_view2d_rect_in_scrollers(const ARegion *region, const View2D *v2d, const rcti *rect)
2049{
2050 int scroll_dummy = 0;
2051 return UI_view2d_rect_in_scrollers_ex(region, v2d, rect, &scroll_dummy);
2052}
2053
2055
2056/* -------------------------------------------------------------------- */
2059
2062 union {
2064 int pack;
2067 int mval[2];
2068
2069 /* str is allocated past the end */
2070 char str[0];
2071};
2072
2073/* assumes caches are used correctly, so for time being no local storage in v2d */
2075static View2DString *g_v2d_strings = nullptr;
2076
2078 View2D *v2d, float x, float y, const char *str, size_t str_len, const uchar col[4])
2079{
2080 int mval[2];
2081
2082 BLI_assert(str_len == strlen(str));
2083
2084 if (UI_view2d_view_to_region_clip(v2d, x, y, &mval[0], &mval[1])) {
2085 const int alloc_len = str_len + 1;
2086
2087 if (g_v2d_strings_arena == nullptr) {
2089 }
2090
2091 View2DString *v2s = static_cast<View2DString *>(
2093
2095
2096 v2s->col.pack = *((const int *)col);
2097
2098 memset(&v2s->rect, 0, sizeof(v2s->rect));
2099
2100 v2s->mval[0] = mval[0];
2101 v2s->mval[1] = mval[1];
2102
2103 memcpy(v2s->str, str, alloc_len);
2104 }
2105}
2106
2108 View2D *v2d, const rctf *rect_view, const char *str, size_t str_len, const uchar col[4])
2109{
2110 rcti rect;
2111
2112 BLI_assert(str_len == strlen(str));
2113
2114 if (UI_view2d_view_to_region_rcti_clip(v2d, rect_view, &rect)) {
2115 const int alloc_len = str_len + 1;
2116
2117 if (g_v2d_strings_arena == nullptr) {
2119 }
2120
2121 View2DString *v2s = static_cast<View2DString *>(
2123
2125
2126 v2s->col.pack = *((const int *)col);
2127
2128 v2s->rect = rect;
2129
2130 v2s->mval[0] = v2s->rect.xmin;
2131 v2s->mval[1] = v2s->rect.ymin;
2132
2133 memcpy(v2s->str, str, alloc_len);
2134 }
2135}
2136
2138{
2139 View2DString *v2s;
2140 int col_pack_prev = 0;
2141
2142 /* investigate using BLF_ascender() */
2143 const int font_id = BLF_default();
2144
2146 const float default_height = g_v2d_strings ? BLF_height(font_id, "28", 3) : 0.0f;
2147
2149
2150 for (v2s = g_v2d_strings; v2s; v2s = v2s->next) {
2151 int xofs = 0, yofs;
2152
2153 yofs = ceil(0.5f * (BLI_rcti_size_y(&v2s->rect) - default_height));
2154 if (yofs < 1) {
2155 yofs = 1;
2156 }
2157
2158 if (col_pack_prev != v2s->col.pack) {
2159 BLF_color4ubv(font_id, v2s->col.ub);
2160 col_pack_prev = v2s->col.pack;
2161 }
2162
2163 /* Don't use clipping if `v2s->rect` is not set. */
2164 if (BLI_rcti_size_x(&v2s->rect) == 0 && BLI_rcti_size_y(&v2s->rect) == 0) {
2165 BLF_draw_default(float(v2s->mval[0] + xofs),
2166 float(v2s->mval[1] + yofs),
2167 0.0,
2168 v2s->str,
2170 }
2171 else {
2172 BLF_enable(font_id, BLF_CLIPPING);
2174 font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4);
2176 v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX);
2177 BLF_disable(font_id, BLF_CLIPPING);
2178 }
2179 }
2180 g_v2d_strings = nullptr;
2181
2182 if (g_v2d_strings_arena) {
2184 g_v2d_strings_arena = nullptr;
2185 }
2186}
2187
ScrArea * CTX_wm_area(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
@ G_DEBUG
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:815
@ BLF_CLIPPING
Definition BLF_api.hh:362
int BLF_set_default()
float BLF_height(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:815
void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax)
Definition blf.cc:881
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:393
void BLF_disable(int fontid, int option)
Definition blf.cc:321
int BLF_default()
void BLF_cache_clear()
Definition blf.cc:99
void BLF_enable(int fontid, int option)
Definition blf.cc:312
void BLF_color4ubv(int fontid, const unsigned char rgba[4])
Definition blf.cc:435
void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL()
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
#define LISTBASE_FOREACH(type, var, list)
MINLINE float pow2f(float x)
MINLINE float clamp_f(float value, float min, float max)
void unit_m4(float m[4][4])
Definition rct.c:1127
void * BLI_memarena_alloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_rctf_translate(struct rctf *rect, float x, float y)
Definition rct.c:567
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:184
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:180
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.c:560
void BLI_rctf_transform_calc_m4_pivot_min(const rctf *dst, const rctf *src, float matrix[4][4])
Definition rct.c:555
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
void BLI_rctf_recenter(struct rctf *rect, float x, float y)
Definition rct.c:596
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
void BLI_rctf_rcti_copy(struct rctf *dst, const struct rcti *src)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
unsigned char uchar
unsigned int uint
#define CLAMP(a, b, c)
#define CLAMP_MAX(a, c)
#define UNLIKELY(x)
#define ELEM(...)
@ RGN_TYPE_WINDOW
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_GRAPH
@ V2D_LIMITZOOM
@ V2D_LOCKZOOM_X
@ V2D_KEEPZOOM
@ V2D_KEEPASPECT
@ V2D_LOCKZOOM_Y
@ V2D_SCROLL_LEFT
@ V2D_SCROLL_HORIZONTAL_FULLR
@ V2D_SCROLL_HORIZONTAL_HIDE
@ V2D_SCROLL_VERTICAL_FULLR
@ V2D_SCROLL_VERTICAL_HIDE
@ V2D_SCROLL_HORIZONTAL
@ V2D_SCROLL_TOP
@ V2D_SCROLL_VERTICAL_HANDLES
@ V2D_SCROLL_RIGHT
@ V2D_SCROLL_BOTTOM
@ V2D_SCROLL_HORIZONTAL_HANDLES
@ V2D_SCROLL_VERTICAL
@ V2D_ALIGN_NO_NEG_X
@ V2D_ALIGN_NO_NEG_Y
@ V2D_ALIGN_NO_POS_Y
@ V2D_ALIGN_NO_POS_X
@ V2D_SCROLL_V_ACTIVE
@ V2D_SCROLL_H_ACTIVE
@ V2D_LOCKOFS_X
@ V2D_LOCKOFS_Y
@ V2D_KEEPOFS_Y
@ V2D_KEEPOFS_X
@ V2D_VIEWSYNC_SCREEN_TIME
@ V2D_PIXELOFS_X
@ V2D_IS_INIT
@ V2D_PIXELOFS_Y
@ V2D_VIEWSYNC_AREA_VERTICAL
@ V2D_KEEPTOT_BOUNDS
@ V2D_KEEPTOT_STRICT
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:653
void immAttr3ubv(uint attr_id, const unsigned char data[3])
void immEnd()
void immUnbindProgram()
void immAttrSkip(uint attr_id)
void immVertex2f(uint attr_id, float x, float y)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immUniform4fv(const char *name, const float data[4])
void immBegin(GPUPrimType, uint vertex_len)
void GPU_matrix_identity_set()
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA
@ GPU_SHADER_3D_FLAT_COLOR
void GPU_program_point_size(bool enable)
Definition gpu_state.cc:175
void GPU_line_width(float width)
Definition gpu_state.cc:161
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ GPU_COMP_U8
Read Guarded memory(de)allocation.
#define MEM_SIZE_OPTIMAL(size)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
#define C
Definition RandGen.cpp:29
#define UI_HEADER_OFFSET
@ UI_SCROLL_PRESSED
@ UI_SCROLL_ARROWS
const uiStyle * UI_style_get()
void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_GRID
@ TH_BACK
void UI_GetThemeColorBlendShade3ubv(int colorid1, int colorid2, float fac, int offset, unsigned char col[3])
bTheme * UI_GetTheme()
void UI_GetThemeColor4ubv(int colorid, unsigned char col[4])
#define V2D_SCROLL_HEIGHT
Definition UI_view2d.hh:53
#define V2D_LOCK_COPY
Definition UI_view2d.hh:85
#define V2D_SCROLL_MIN_ALPHA
Definition UI_view2d.hh:57
#define V2D_SCROLL_HANDLE_HEIGHT
Definition UI_view2d.hh:67
#define V2D_SCROLL_THUMB_SIZE_MIN
Definition UI_view2d.hh:74
#define IN_2D_HORIZ_SCROLL(v2d, co)
Definition UI_view2d.hh:95
#define V2D_SCROLL_WIDTH
Definition UI_view2d.hh:54
#define V2D_SCROLL_HANDLE_WIDTH
Definition UI_view2d.hh:68
#define IN_2D_VERT_SCROLL(v2d, co)
Definition UI_view2d.hh:94
#define IN_2D_VERT_SCROLL_RECT(v2d, rct)
Definition UI_view2d.hh:97
#define V2D_SCROLL_HANDLE_SIZE_HOTSPOT
Definition UI_view2d.hh:71
#define V2D_SCROLL_MIN_WIDTH
Definition UI_view2d.hh:60
@ V2D_COMMONVIEW_LIST
Definition UI_view2d.hh:35
@ V2D_COMMONVIEW_STACK
Definition UI_view2d.hh:37
@ V2D_COMMONVIEW_HEADER
Definition UI_view2d.hh:39
@ V2D_COMMONVIEW_STANDARD
Definition UI_view2d.hh:33
@ V2D_COMMONVIEW_PANELS_UI
Definition UI_view2d.hh:41
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
#define IN_2D_HORIZ_SCROLL_RECT(v2d, rct)
Definition UI_view2d.hh:98
#define U
#define printf
#define logf(x)
#define powf(x, y)
#define ceilf(x)
#define floorf(x)
#define fabsf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint col
format
MINLINE float fractf(float a)
ccl_device_inline float2 fmod(const float2 a, const float b)
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 ceil(const float3 a)
ccl_device_inline float4 mask(const int4 mask, const float4 a)
static ulong state[N]
#define G(x, y, z)
const btScalar eps
Definition poly34.cpp:11
#define min(a, b)
Definition sort.c:32
void(* on_view2d_changed)(const bContext *C, ARegion *region)
struct ARegionType * type
ListBase regionbase
uiWidgetColors wcol_scroll
unsigned char widget_emboss[4]
char str[0]
Definition view2d.cc:2070
union View2DString::@255132311327275077075273105072153113125144146057 col
int mval[2]
Definition view2d.cc:2067
View2DString * next
Definition view2d.cc:2061
uchar ub[4]
Definition view2d.cc:2063
char alpha_vert
float minzoom
short keeptot
short scroll_ui
short oldwiny
float max[2]
short keepzoom
short keepofs
float min[2]
short oldwinx
float maxzoom
float page_size_y
ListBase areabase
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
unsigned char inner[4]
unsigned char outline[4]
unsigned char item[4]
float max
ccl_device_inline int abs(int x)
Definition util/math.h:120
char UI_view2d_rect_in_scrollers(const ARegion *region, const View2D *v2d, const rcti *rect)
Definition view2d.cc:2048
void UI_view2d_curRect_validate(View2D *v2d)
Definition view2d.cc:824
void view2d_totRect_set_resize(View2D *v2d, int width, int height, bool resize)
Definition view2d.cc:973
void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
Definition view2d.cc:861
static void grid_axis_start_and_count(const float step, const float min, const float max, float *r_start, int *r_count)
Definition view2d.cc:1264
View2D * UI_view2d_fromcontext_rwin(const bContext *C)
Definition view2d.cc:1864
void UI_view2d_scale_get_inverse(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1924
void UI_view2d_curRect_reset(View2D *v2d)
Definition view2d.cc:924
static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize)
Definition view2d.cc:378
bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, const float xy_a[2], const float xy_b[2], int r_region_a[2], int r_region_b[2])
Definition view2d.cc:1746
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y)
Definition view2d.cc:1663
void UI_view2d_view_orthoSpecial(ARegion *region, View2D *v2d, const bool xaxis)
Definition view2d.cc:1132
char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2])
Definition view2d.cc:2042
void UI_view2d_multi_grid_draw(const View2D *v2d, int colorid, float step, int level_size, int totlevels)
Definition view2d.cc:1176
static View2DString * g_v2d_strings
Definition view2d.cc:2075
float view2d_page_size_y(const View2D &v2d)
Definition view2d.cc:77
void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst)
Definition view2d.cc:1781
bool UI_view2d_view_to_region_clip(const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y)
Definition view2d.cc:1697
bool UI_view2d_view_to_region_rcti_clip(const View2D *v2d, const rctf *rect_src, rcti *rect_dst)
Definition view2d.cc:1811
char UI_view2d_mouse_in_scrollers_ex(const ARegion *region, const View2D *v2d, const int xy[2], int *r_scroll)
Definition view2d.cc:1986
void UI_view2d_curRect_clamp_y(View2D *v2d)
Definition view2d.cc:840
void UI_view2d_dot_grid_draw(const View2D *v2d, const int grid_color_id, const float min_step, const int grid_subdivisions)
Definition view2d.cc:1283
void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
Definition view2d.cc:1605
BLI_INLINE void clamp_rctf_to_rcti(rcti *dst, const rctf *src)
Definition view2d.cc:69
void UI_view2d_view_restore(const bContext *C)
Definition view2d.cc:1158
void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
Definition view2d.cc:212
void UI_view2d_curRect_changed(const bContext *C, View2D *v2d)
Definition view2d.cc:829
void UI_view2d_text_cache_draw(ARegion *region)
Definition view2d.cc:2137
float UI_view2d_region_to_view_y(const View2D *v2d, float y)
Definition view2d.cc:1657
void UI_view2d_totRect_set(View2D *v2d, int width, int height)
Definition view2d.cc:1032
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst)
Definition view2d.cc:1670
static int view2d_scroll_mapped(int scroll)
Definition view2d.cc:98
float UI_view2d_view_to_region_y(const View2D *v2d, float y)
Definition view2d.cc:1691
void UI_view2d_listview_view_to_cell(float columnwidth, float rowheight, float startx, float starty, float viewx, float viewy, int *r_column, int *r_row)
Definition view2d.cc:1616
void UI_view2d_center_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1934
void UI_view2d_view_ortho(const View2D *v2d)
Definition view2d.cc:1091
static MemArena * g_v2d_strings_arena
Definition view2d.cc:2074
void UI_view2d_view_to_region(const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y)
Definition view2d.cc:1718
View2D * UI_view2d_fromcontext(const bContext *C)
Definition view2d.cc:1850
float UI_view2d_scale_get_y(const View2D *v2d)
Definition view2d.cc:1920
void UI_view2d_scroller_size_get(const View2D *v2d, bool mapped, float *r_x, float *r_y)
Definition view2d.cc:1882
void UI_view2d_offset(View2D *v2d, float xfac, float yfac)
Definition view2d.cc:1952
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1907
bool UI_view2d_area_supports_sync(ScrArea *area)
Definition view2d.cc:856
void UI_view2d_mask_from_win(const View2D *v2d, rcti *r_mask)
Definition view2d.cc:109
char UI_view2d_rect_in_scrollers_ex(const ARegion *region, const View2D *v2d, const rcti *rect, int *r_scroll)
Definition view2d.cc:2015
void UI_view2d_view_to_region_m4(const View2D *v2d, float matrix[4][4])
Definition view2d.cc:1803
void UI_view2d_view_to_region_fl(const View2D *v2d, float x, float y, float *r_region_x, float *r_region_y)
Definition view2d.cc:1734
void UI_view2d_text_cache_add(View2D *v2d, float x, float y, const char *str, size_t str_len, const uchar col[4])
Definition view2d.cc:2077
void UI_view2d_offset_y_snap_to_closest_page(View2D *v2d)
Definition view2d.cc:1975
void UI_view2d_center_set(View2D *v2d, float x, float y)
Definition view2d.cc:1944
float UI_view2d_region_to_view_x(const View2D *v2d, float x)
Definition view2d.cc:1652
float UI_view2d_view_to_region_x(const View2D *v2d, float x)
Definition view2d.cc:1686
void UI_view2d_zoom_cache_reset()
Definition view2d.cc:1037
void view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom, View2DScrollers *r_scrollers)
Definition view2d.cc:1386
static void view2d_map_cur_using_mask(const View2D *v2d, rctf *r_curmasked)
Definition view2d.cc:1060
BLI_INLINE int clamp_float_to_int(const float f)
Definition view2d.cc:51
void UI_view2d_scrollers_draw_ex(View2D *v2d, const rcti *mask_custom, bool use_full_hide)
Definition view2d.cc:1510
static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
Definition view2d.cc:122
void UI_view2d_text_cache_add_rectf(View2D *v2d, const rctf *rect_view, const char *str, size_t str_len, const uchar col[4])
Definition view2d.cc:2107
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1916
int xy[2]
Definition wm_draw.cc:170
void wmOrtho2(float x1, float x2, float y1, float y2)
void wmOrtho2_region_pixelspace(const ARegion *region)
uint8_t flag
Definition wm_window.cc:138