Blender V5.0
view2d_ops.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 <cmath>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_userdef_types.h"
15
16#include "BLI_math_base.h"
17#include "BLI_math_vector.h"
18#include "BLI_time.h" /* USER_ZOOM_CONTINUE */
19#include "BLI_utildefines.h"
20
21#include "BKE_context.hh"
22
23#include "RNA_access.hh"
24#include "RNA_define.hh"
25
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "ED_screen.hh"
30
31#include "UI_interface.hh"
32#include "UI_view2d.hh"
33
34#include "view2d_intern.hh"
35
36/* -------------------------------------------------------------------- */
39
40static bool view2d_poll(bContext *C)
41{
42 ARegion *region = CTX_wm_region(C);
43
44 return (region != nullptr) && (region->v2d.flag & V2D_IS_INIT);
45}
46
52static float view2d_scroll_delta_y_snap_page_size(const View2D &v2d, const float delta_y)
53{
54 const float page_size = view2d_page_size_y(v2d);
55 const int delta_pages = int(delta_y / (page_size * 0.5f));
56
57 /* Apply no change, don't update last coordinates. */
58 if (abs(delta_pages) < 1) {
59 return 0.0f;
60 }
61
62 /* Snap the delta to a multiple of a page size. */
63 return delta_pages * page_size;
64}
65
67
68/* -------------------------------------------------------------------- */
71
80
93
95 float facx, facy;
96
97 /* options for version 1 */
104
108
111
112 /* View2D Edge Panning */
115};
116
118{
119 ARegion *region = CTX_wm_region(C);
120
121 /* check if there's a region in context to work with */
122 if (region == nullptr) {
123 return false;
124 }
125
126 View2D *v2d = &region->v2d;
127
128 /* check that 2d-view can pan */
129 if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) {
130 return false;
131 }
132
133 /* view can pan */
134 return true;
135}
136
137/* initialize panning customdata */
139{
140 /* Should've been checked before. */
142
143 /* set custom-data for operator */
145 op->customdata = vpd;
146
147 /* set pointers to owners */
148 vpd->screen = CTX_wm_screen(C);
149 vpd->area = CTX_wm_area(C);
150 vpd->region = CTX_wm_region(C);
151 vpd->v2d = &vpd->region->v2d;
152
153 /* calculate translation factor - based on size of view */
154 const float winx = float(BLI_rcti_size_x(&vpd->region->winrct) + 1);
155 const float winy = float(BLI_rcti_size_y(&vpd->region->winrct) + 1);
156 vpd->facx = BLI_rctf_size_x(&vpd->v2d->cur) / winx;
157 vpd->facy = BLI_rctf_size_y(&vpd->v2d->cur) / winy;
158
159 vpd->v2d->flag |= V2D_IS_NAVIGATING;
160
161 vpd->do_category_scroll = false;
162}
163
164/* apply transform to view (i.e. adjust 'cur' rect) */
165static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy)
166{
167 View2D *v2d = vpd->v2d;
168
169 /* calculate amount to move view by */
170 dx *= vpd->facx;
171 dy *= vpd->facy;
172
173 if (!vpd->do_category_scroll) {
174 /* only move view on an axis if change is allowed */
175 if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) {
176 v2d->cur.xmin += dx;
177 v2d->cur.xmax += dx;
178 }
179 if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) {
180 v2d->cur.ymin += dy;
181 v2d->cur.ymax += dy;
182 }
183 }
184 else {
185 vpd->region->category_scroll -= dy;
186 }
187
188 /* Inform v2d about changes after this operation. */
190
191 /* don't rebuild full tree in outliner, since we're just changing our view */
193
194 /* request updates to be done... */
196
197 UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY);
198}
199
201{
202 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
203
204 view_pan_apply_ex(C, vpd, RNA_int_get(op->ptr, "deltax"), RNA_int_get(op->ptr, "deltay"));
205}
206
207/* Cleanup temp custom-data. */
208static void view_pan_exit(wmOperator *op)
209{
210 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
211 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
212 MEM_freeN(vpd);
213 op->customdata = nullptr;
214}
215
217
218/* -------------------------------------------------------------------- */
221
222/* for 'redo' only, with no user input */
224{
225 view_pan_init(C, op);
226 view_pan_apply(C, op);
227 view_pan_exit(op);
228 return OPERATOR_FINISHED;
229}
230
231/* set up modal operator and relevant settings */
233{
234 wmWindow *window = CTX_wm_window(C);
235
236 /* set up customdata */
237 view_pan_init(C, op);
238
239 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
240 View2D *v2d = vpd->v2d;
241
242 /* set initial settings */
243 vpd->startx = vpd->lastx = event->xy[0];
244 vpd->starty = vpd->lasty = event->xy[1];
245 vpd->invoke_event = event->type;
246
248
249 if (event->type == MOUSEPAN) {
250 RNA_int_set(op->ptr, "deltax", event->prev_xy[0] - event->xy[0]);
251 RNA_int_set(op->ptr, "deltay", event->prev_xy[1] - event->xy[1]);
252
253 view_pan_apply(C, op);
254 view_pan_exit(op);
255 return OPERATOR_FINISHED;
256 }
257
258 RNA_int_set(op->ptr, "deltax", 0);
259 RNA_int_set(op->ptr, "deltay", 0);
260
261 if (WM_cursor_modal_is_set_ok(window)) {
262 if (v2d->keepofs & V2D_LOCKOFS_X) {
264 }
265 else if (v2d->keepofs & V2D_LOCKOFS_Y) {
267 }
268 else {
270 }
271 vpd->own_cursor = true;
272 }
273
274 /* add temp handler */
276
278}
279
280/* handle user input - calculations of mouse-movement
281 * need to be done here, not in the apply callback! */
283{
284 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
285 View2D *v2d = vpd->v2d;
286
287 /* execute the events */
288 switch (event->type) {
289 case MOUSEMOVE: {
290 /* calculate new delta transform, then store mouse-coordinates for next-time */
291 int deltax = vpd->lastx - event->xy[0];
292 int deltay = vpd->lasty - event->xy[1];
293
294 /* Page snapping: When panning for more than half a page size, snap to the next page. */
295 if (v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) {
296 deltay = view2d_scroll_delta_y_snap_page_size(*v2d, deltay);
297 }
298
299 if (deltax != 0) {
300 vpd->lastx = event->xy[0];
301 }
302 if (deltay != 0) {
303 vpd->lasty = event->xy[1];
304 }
305
306 if (deltax || deltay) {
307 RNA_int_set(op->ptr, "deltax", deltax);
308 RNA_int_set(op->ptr, "deltay", deltay);
309 view_pan_apply(C, op);
310 }
311 break;
312 }
313 /* XXX: Mode switching isn't implemented. See comments in 36818.
314 * switch to zoom */
315#if 0
316 case LEFTMOUSE:
317 if (event->val == KM_PRESS) {
318 /* calculate overall delta mouse-movement for redo */
319 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
320 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
321
322 const bool own_cursor = vpd->own_cursor;
323 view_pan_exit(op);
324 if (own_cursor) {
326 }
328 C, "VIEW2D_OT_zoom", blender::wm::OpCallContext::InvokeDefault, nullptr, event);
329 return OPERATOR_FINISHED;
330 }
331#endif
332 default:
333 if (ELEM(event->type, vpd->invoke_event, EVT_ESCKEY)) {
334 if (event->val == KM_RELEASE) {
335 /* calculate overall delta mouse-movement for redo */
336 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
337 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
338
339 const bool own_cursor = vpd->own_cursor;
340 view_pan_exit(op);
341 if (own_cursor) {
343 }
344 return OPERATOR_FINISHED;
345 }
346 }
347 break;
348 }
349
351}
352
353static void view_pan_cancel(bContext * /*C*/, wmOperator *op)
354{
355 view_pan_exit(op);
356}
357
359{
360 /* identifiers */
361 ot->name = "Pan View";
362 ot->description = "Pan the view";
363 ot->idname = "VIEW2D_OT_pan";
364
365 /* API callbacks. */
366 ot->exec = view_pan_exec;
367 ot->invoke = view_pan_invoke;
368 ot->modal = view_pan_modal;
369 ot->cancel = view_pan_cancel;
370 ot->poll = view_pan_poll;
371
372 /* operator is modal */
374
375 /* rna - must keep these in sync with the other operators */
376 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
377 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
378}
379
381
382/* -------------------------------------------------------------------- */
388
389/* set up modal operator and relevant settings */
391 wmOperator *op,
392 const wmEvent * /*event*/)
393{
394 op->customdata = MEM_callocN(sizeof(View2DEdgePanData), "View2DEdgePanData");
395 View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
397
399
401}
402
404{
405 View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
406
407 wmWindow *source_win = CTX_wm_window(C);
408 int event_xy_target[2];
409 wmWindow *target_win = WM_window_find_under_cursor(source_win, event->xy, &event_xy_target[0]);
410
411 /* Exit if we release the mouse button, hit escape, or enter a different window. */
412 if (event->val == KM_RELEASE || event->type == EVT_ESCKEY || source_win != target_win) {
413 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
414 MEM_SAFE_FREE(vpd);
415 op->customdata = nullptr;
417 }
418
420
421 /* This operator is supposed to run together with some drag action.
422 * On successful handling, always pass events on to other handlers. */
424}
425
426static void view_edge_pan_cancel(bContext * /*C*/, wmOperator *op)
427{
428 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
429 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
430 MEM_SAFE_FREE(vpd);
431 op->customdata = nullptr;
432}
433
435{
436 /* identifiers */
437 ot->name = "View Edge Pan";
438 ot->description = "Pan the view when the mouse is held at an edge";
439 ot->idname = "VIEW2D_OT_edge_pan";
440
441 /* API callbacks. */
442 ot->invoke = view_edge_pan_invoke;
443 ot->modal = view_edge_pan_modal;
444 ot->cancel = view_edge_pan_cancel;
445 ot->poll = view2d_edge_pan_poll;
446
447 /* operator is modal */
448 ot->flag = OPTYPE_INTERNAL;
450}
451
453
454/* -------------------------------------------------------------------- */
457
458/* this operator only needs this single callback, where it calls the view_pan_*() methods */
460{
461 /* initialize default settings (and validate if ok to run) */
462 view_pan_init(C, op);
463
464 /* also, check if can pan in horizontal axis */
465 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
466 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
467 view_pan_exit(op);
469 }
470
471 /* set RNA-Props - only movement in positive x-direction */
472 RNA_int_set(op->ptr, "deltax", 40 * UI_SCALE_FAC);
473 RNA_int_set(op->ptr, "deltay", 0);
474
475 /* apply movement, then we're done */
476 view_pan_apply(C, op);
477 view_pan_exit(op);
478
479 return OPERATOR_FINISHED;
480}
481
483{
484 /* identifiers */
485 ot->name = "Scroll Right";
486 ot->description = "Scroll the view right";
487 ot->idname = "VIEW2D_OT_scroll_right";
488
489 /* API callbacks. */
491 ot->poll = view_pan_poll;
492
493 /* rna - must keep these in sync with the other operators */
494 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
495 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
496}
497
498/* this operator only needs this single callback, where it calls the view_pan_*() methods */
500{
501 /* initialize default settings (and validate if ok to run) */
502 view_pan_init(C, op);
503
504 /* also, check if can pan in horizontal axis */
505 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
506 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
507 view_pan_exit(op);
509 }
510
511 /* set RNA-Props - only movement in negative x-direction */
512 RNA_int_set(op->ptr, "deltax", -40 * UI_SCALE_FAC);
513 RNA_int_set(op->ptr, "deltay", 0);
514
515 /* apply movement, then we're done */
516 view_pan_apply(C, op);
517 view_pan_exit(op);
518
519 return OPERATOR_FINISHED;
520}
521
523{
524 /* identifiers */
525 ot->name = "Scroll Left";
526 ot->description = "Scroll the view left";
527 ot->idname = "VIEW2D_OT_scroll_left";
528
529 /* API callbacks. */
530 ot->exec = view_scrollleft_exec;
531 ot->poll = view_pan_poll;
532
533 /* rna - must keep these in sync with the other operators */
534 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
535 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
536}
537
538/* this operator only needs this single callback, where it calls the view_pan_*() methods */
540{
541 /* initialize default settings (and validate if ok to run) */
542 view_pan_init(C, op);
543
544 /* also, check if can pan in vertical axis */
545 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
546 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
547 view_pan_exit(op);
549 }
550
551 const wmWindow *win = CTX_wm_window(C);
553 win->eventstate->xy);
554
555 /* set RNA-Props */
556 RNA_int_set(op->ptr, "deltax", 0);
557 RNA_int_set(op->ptr, "deltay", -40 * UI_SCALE_FAC);
558
559 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
560 const bool use_page_size = (vpd->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) ||
561 (RNA_property_is_set(op->ptr, prop) &&
562 RNA_property_boolean_get(op->ptr, prop));
563 if (use_page_size) {
564 const ARegion *region = CTX_wm_region(C);
565 const int page_size = view2d_page_size_y(region->v2d);
566 RNA_int_set(op->ptr, "deltay", -page_size);
567 }
568
569 /* apply movement, then we're done */
570 view_pan_apply(C, op);
571 view_pan_exit(op);
572
573 return OPERATOR_FINISHED;
574}
575
577{
578 /* identifiers */
579 ot->name = "Scroll Down";
580 ot->description = "Scroll the view down";
581 ot->idname = "VIEW2D_OT_scroll_down";
582
583 /* API callbacks. */
584 ot->exec = view_scrolldown_exec;
585 ot->poll = view_pan_poll;
586
587 /* rna - must keep these in sync with the other operators */
588 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
589 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
590 RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll down one page");
591}
592
593/* this operator only needs this single callback, where it calls the view_pan_*() methods */
595{
596 /* initialize default settings (and validate if ok to run) */
597 view_pan_init(C, op);
598
599 /* also, check if can pan in vertical axis */
600 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
601 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
602 view_pan_exit(op);
604 }
605
606 const wmWindow *win = CTX_wm_window(C);
608 win->eventstate->xy);
609
610 /* set RNA-Props */
611 RNA_int_set(op->ptr, "deltax", 0);
612 RNA_int_set(op->ptr, "deltay", 40 * UI_SCALE_FAC);
613
614 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
615 const bool use_page_size = (vpd->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) ||
616 (RNA_property_is_set(op->ptr, prop) &&
617 RNA_property_boolean_get(op->ptr, prop));
618 if (use_page_size) {
619 const ARegion *region = CTX_wm_region(C);
620 const int page_size = view2d_page_size_y(region->v2d);
621 RNA_int_set(op->ptr, "deltay", page_size);
622 }
623
624 /* apply movement, then we're done */
625 view_pan_apply(C, op);
626 view_pan_exit(op);
627
628 return OPERATOR_FINISHED;
629}
630
632{
633 /* identifiers */
634 ot->name = "Scroll Up";
635 ot->description = "Scroll the view up";
636 ot->idname = "VIEW2D_OT_scroll_up";
637
638 /* API callbacks. */
639 ot->exec = view_scrollup_exec;
640 ot->poll = view_pan_poll;
641
642 /* rna - must keep these in sync with the other operators */
643 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
644 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
645 RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll up one page");
646}
647
649
650/* -------------------------------------------------------------------- */
653
667
672 View2D *v2d; /* view2d we're operating in */
674
675 /* needed for continuous zoom */
678
679 int lastx, lasty; /* previous x/y values of mouse in window */
680 int invoke_event; /* event type that invoked, for modal exits */
681 float dx, dy; /* running tally of previous delta values (for obtaining final zoom) */
682 float mx_2d, my_2d; /* initial mouse location in v2d coords */
685};
686
691static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
692{
693 ScrArea *area = CTX_wm_area(C);
694
695 r_do_zoom_xy[0] = true;
696 r_do_zoom_xy[1] = true;
697
698 /* default not to zoom the sequencer vertically */
699 if (area && area->spacetype == SPACE_SEQ) {
700 ARegion *region = CTX_wm_region(C);
701
702 if (region && region->regiontype == RGN_TYPE_WINDOW) {
703 r_do_zoom_xy[1] = false;
704 }
705 }
706}
707
708/* check if step-zoom can be applied */
710{
711 ARegion *region = CTX_wm_region(C);
712
713 /* check if there's a region in context to work with */
714 if (region == nullptr) {
715 return false;
716 }
717
718 /* Do not show that in 3DView context. */
719 if (CTX_wm_region_view3d(C)) {
720 return false;
721 }
722
723 View2D *v2d = &region->v2d;
724
725 /* check that 2d-view is zoomable */
726 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y)) {
727 return false;
728 }
729
730 /* view is zoomable */
731 return true;
732}
733
734/* initialize panning customdata */
736{
737 /* Should've been checked before. */
739
740 /* set custom-data for operator */
742 op->customdata = vzd;
743
744 /* set pointers to owners */
745 vzd->region = CTX_wm_region(C);
746 vzd->v2d = &vzd->region->v2d;
747 /* False by default. Interactive callbacks (ie invoke()) can set it to true. */
748 vzd->zoom_to_mouse_pos = false;
749
750 vzd->v2d->flag |= V2D_IS_NAVIGATING;
751}
752
753/* apply transform to view (i.e. adjust 'cur' rect) */
755 v2dViewZoomData *vzd,
756 const float facx,
757 const float facy)
758{
759 ARegion *region = CTX_wm_region(C);
760 View2D *v2d = &region->v2d;
761 const rctf cur_old = v2d->cur;
762 const int snap_test = ED_region_snap_size_test(region);
763 const bool do_keepofs = !(v2d->flag & V2D_ZOOM_IGNORE_KEEPOFS);
764
765 /* calculate amount to move view by, ensuring symmetry so the
766 * old zoom level is restored after zooming back the same amount
767 */
768 float dx, dy;
769 if (facx >= 0.0f) {
770 dx = BLI_rctf_size_x(&v2d->cur) * facx;
771 dy = BLI_rctf_size_y(&v2d->cur) * facy;
772 }
773 else {
774 dx = (BLI_rctf_size_x(&v2d->cur) / (1.0f + 2.0f * facx)) * facx;
775 dy = (BLI_rctf_size_y(&v2d->cur) / (1.0f + 2.0f * facy)) * facy;
776 }
777
778 /* Only resize view on an axis if change is allowed. */
779 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
780 if ((v2d->keepofs & V2D_LOCKOFS_X) && do_keepofs) {
781 v2d->cur.xmax -= 2 * dx;
782 }
783 else if ((v2d->keepofs & V2D_KEEPOFS_X) && do_keepofs) {
784 if (v2d->align & V2D_ALIGN_NO_POS_X) {
785 v2d->cur.xmin += 2 * dx;
786 }
787 else {
788 v2d->cur.xmax -= 2 * dx;
789 }
790 }
791 else {
792
793 v2d->cur.xmin += dx;
794 v2d->cur.xmax -= dx;
795
796 if (vzd->zoom_to_mouse_pos) {
797 /* get zoom fac the same way as in
798 * ui_view2d_curRect_validate_resize - better keep in sync! */
799 const float zoomx = float(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur);
800
801 /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
802 if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
803 IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom))
804 {
805 const float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old);
806 const float mval_faci = 1.0f - mval_fac;
807 const float ofs = (mval_fac * dx) - (mval_faci * dx);
808
809 v2d->cur.xmin += ofs;
810 v2d->cur.xmax += ofs;
811 }
812 }
813 }
814 }
815 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
816 if ((v2d->keepofs & V2D_LOCKOFS_Y) && do_keepofs) {
817 v2d->cur.ymax -= 2 * dy;
818 }
819 else if ((v2d->keepofs & V2D_KEEPOFS_Y) && do_keepofs) {
820 if (v2d->align & V2D_ALIGN_NO_POS_Y) {
821 v2d->cur.ymin += 2 * dy;
822 }
823 else {
824 v2d->cur.ymax -= 2 * dy;
825 }
826 }
827 else {
828
829 v2d->cur.ymin += dy;
830 v2d->cur.ymax -= dy;
831
832 if (vzd->zoom_to_mouse_pos) {
833 /* get zoom fac the same way as in
834 * ui_view2d_curRect_validate_resize - better keep in sync! */
835 const float zoomy = float(BLI_rcti_size_y(&v2d->mask) + 1) / BLI_rctf_size_y(&v2d->cur);
836
837 /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
838 if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
839 IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom))
840 {
841 const float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old);
842 const float mval_faci = 1.0f - mval_fac;
843 const float ofs = (mval_fac * dy) - (mval_faci * dy);
844
845 v2d->cur.ymin += ofs;
846 v2d->cur.ymax += ofs;
847 }
848 }
849 }
850 }
851
852 /* Inform v2d about changes after this operation. */
854
855 if (ED_region_snap_size_apply(region, snap_test)) {
856 ScrArea *area = CTX_wm_area(C);
857 ED_area_tag_redraw(area);
859 }
860
861 /* request updates to be done... */
864}
865
867{
868 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
870 C, vzd, RNA_float_get(op->ptr, "zoomfacx"), RNA_float_get(op->ptr, "zoomfacy"));
871}
872
874
875/* -------------------------------------------------------------------- */
878
879/* Cleanup temp custom-data. */
881{
882 ScrArea *area = CTX_wm_area(C);
883 /* Some areas change font sizes when zooming, so clear glyph cache. */
884 if (area && !ELEM(area->spacetype, SPACE_GRAPH, SPACE_ACTION)) {
886 }
887
888 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
889 vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
890 MEM_SAFE_FREE(vzd);
891 op->customdata = nullptr;
892}
893
894/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
896{
897 if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
899 }
900
901 bool do_zoom_xy[2];
902 view_zoom_axis_lock_defaults(C, do_zoom_xy);
903
904 /* set RNA-Props - zooming in by uniform factor */
905 RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? 0.0375f : 0.0f);
906 RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? 0.0375f : 0.0f);
907
908 /* apply movement, then we're done */
910
912
913 return OPERATOR_FINISHED;
914}
915
917{
919
920 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
921
922 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
923 ARegion *region = CTX_wm_region(C);
924
925 /* store initial mouse position (in view space) */
927 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
928 vzd->zoom_to_mouse_pos = true;
929 }
930
931 return view_zoomin_exec(C, op);
932}
933
935{
936 PropertyRNA *prop;
937
938 /* identifiers */
939 ot->name = "Zoom In";
940 ot->description = "Zoom in the view";
941 ot->idname = "VIEW2D_OT_zoom_in";
942
943 /* API callbacks. */
944 ot->invoke = view_zoomin_invoke;
945 ot->exec = view_zoomin_exec;
946 ot->poll = view_zoom_poll;
947
948 /* rna - must keep these in sync with the other operators */
949 prop = RNA_def_float(
950 ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
952 prop = RNA_def_float(
953 ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
955}
956
957/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
959{
960 bool do_zoom_xy[2];
961
962 if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
964 }
965
966 view_zoom_axis_lock_defaults(C, do_zoom_xy);
967
968 /* set RNA-Props - zooming in by uniform factor */
969 RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? -0.0375f : 0.0f);
970 RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? -0.0375f : 0.0f);
971
972 /* apply movement, then we're done */
974
976
977 return OPERATOR_FINISHED;
978}
979
981{
983
984 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
985
986 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
987 ARegion *region = CTX_wm_region(C);
988
989 /* store initial mouse position (in view space) */
991 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
992 vzd->zoom_to_mouse_pos = true;
993 }
994
995 return view_zoomout_exec(C, op);
996}
997
999{
1000 PropertyRNA *prop;
1001
1002 /* identifiers */
1003 ot->name = "Zoom Out";
1004 ot->description = "Zoom out the view";
1005 ot->idname = "VIEW2D_OT_zoom_out";
1006
1007 /* API callbacks. */
1008 ot->invoke = view_zoomout_invoke;
1009 ot->exec = view_zoomout_exec;
1010
1011 ot->poll = view_zoom_poll;
1012
1013 /* rna - must keep these in sync with the other operators */
1014 prop = RNA_def_float(
1015 ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
1017 prop = RNA_def_float(
1018 ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
1020}
1021
1023
1024/* -------------------------------------------------------------------- */
1027
1034
1035/* apply transform to view (i.e. adjust 'cur' rect) */
1037{
1038 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1039 View2D *v2d = vzd->v2d;
1040 const int snap_test = ED_region_snap_size_test(vzd->region);
1041
1042 const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
1043 const bool zoom_to_pos = use_cursor_init && vzd->zoom_to_mouse_pos;
1044
1045 /* get amount to move view by */
1046 float dx = RNA_float_get(op->ptr, "deltax") / UI_SCALE_FAC;
1047 float dy = RNA_float_get(op->ptr, "deltay") / UI_SCALE_FAC;
1048
1049 /* Check if the 'timer' is initialized, as zooming with the trackpad
1050 * never uses the "Continuous" zoom method, and the 'timer' is not initialized. */
1051 if ((U.viewzoom == USER_ZOOM_CONTINUE) && vzd->timer) { /* XXX store this setting as RNA prop? */
1052 const double time = BLI_time_now_seconds();
1053 const float time_step = float(time - vzd->timer_lastdraw);
1054
1055 dx *= time_step * 5.0f;
1056 dy *= time_step * 5.0f;
1057
1058 vzd->timer_lastdraw = time;
1059 }
1060
1061 /* only move view on an axis if change is allowed */
1062 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1063 if (v2d->keepofs & V2D_LOCKOFS_X) {
1064 v2d->cur.xmax -= 2 * dx;
1065 }
1066 else {
1067 if (zoom_to_pos) {
1068 const float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
1069 const float mval_faci = 1.0f - mval_fac;
1070 const float ofs = (mval_fac * dx) - (mval_faci * dx);
1071
1072 v2d->cur.xmin += ofs + dx;
1073 v2d->cur.xmax += ofs - dx;
1074 }
1075 else {
1076 v2d->cur.xmin += dx;
1077 v2d->cur.xmax -= dx;
1078 }
1079 }
1080 }
1081 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1082 if (v2d->keepofs & V2D_LOCKOFS_Y) {
1083 v2d->cur.ymax -= 2 * dy;
1084 }
1085 else {
1086 if (zoom_to_pos) {
1087 const float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
1088 const float mval_faci = 1.0f - mval_fac;
1089 const float ofs = (mval_fac * dy) - (mval_faci * dy);
1090
1091 v2d->cur.ymin += ofs + dy;
1092 v2d->cur.ymax += ofs - dy;
1093 }
1094 else {
1095 v2d->cur.ymin += dy;
1096 v2d->cur.ymax -= dy;
1097 }
1098 }
1099 }
1100
1101 /* Inform v2d about changes after this operation. */
1103
1104 if (ED_region_snap_size_apply(vzd->region, snap_test)) {
1105 ScrArea *area = CTX_wm_area(C);
1106 ED_area_tag_redraw(area);
1108 }
1109
1110 /* request updates to be done... */
1113}
1114
1115/* Cleanup temp custom-data. */
1117{
1119
1120 if (op->customdata) {
1121 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1122 vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
1123
1124 if (vzd->timer) {
1126 }
1127
1128 MEM_freeN(vzd);
1129 op->customdata = nullptr;
1130 }
1131}
1132
1134{
1135 view_zoomdrag_exit(C, op);
1136}
1137
1138/* for 'redo' only, with no user input */
1146
1147/* set up modal operator and relevant settings */
1149{
1150 wmWindow *window = CTX_wm_window(C);
1151
1152 /* set up customdata */
1153 view_zoomdrag_init(C, op);
1154
1155 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1156 View2D *v2d = vzd->v2d;
1157
1158 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
1159 ARegion *region = CTX_wm_region(C);
1160
1161 /* Store initial mouse position (in view space). */
1163 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
1164 vzd->zoom_to_mouse_pos = true;
1165 }
1166
1167 if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
1168 vzd->lastx = event->prev_xy[0];
1169 vzd->lasty = event->prev_xy[1];
1170
1171 float facx, facy;
1172 float zoomfac = 0.01f;
1173
1174 /* Some view2d's (graph) don't have min/max zoom, or extreme ones. */
1175 if (v2d->maxzoom > 0.0f) {
1176 zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1177 }
1178
1179 if (event->type == MOUSEPAN) {
1180 facx = zoomfac * WM_event_absolute_delta_x(event);
1181 facy = zoomfac * WM_event_absolute_delta_y(event);
1182
1183 if (U.uiflag & USER_ZOOM_INVERT) {
1184 facx *= -1.0f;
1185 facy *= -1.0f;
1186 }
1187 }
1188 else { /* MOUSEZOOM */
1189 facx = facy = zoomfac * WM_event_absolute_delta_x(event);
1190 }
1191
1192 /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1193 * any will be scaled uniformly. */
1194 if (((v2d->keepzoom & (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y)) == 0) &&
1195 (v2d->keepzoom & V2D_KEEPASPECT))
1196 {
1197 if (U.uiflag & USER_ZOOM_HORIZ) {
1198 facy = 0.0f;
1199 }
1200 else {
1201 facx = 0.0f;
1202 }
1203 }
1204
1205 /* support trackpad zoom to always zoom entirely - the v2d code uses portrait or
1206 * landscape exceptions */
1207 if (v2d->keepzoom & V2D_KEEPASPECT) {
1208 if (fabsf(facx) > fabsf(facy)) {
1209 facy = facx;
1210 }
1211 else {
1212 facx = facy;
1213 }
1214 }
1215
1216 const float dx = facx * BLI_rctf_size_x(&v2d->cur);
1217 const float dy = facy * BLI_rctf_size_y(&v2d->cur);
1218
1219 RNA_float_set(op->ptr, "deltax", dx);
1220 RNA_float_set(op->ptr, "deltay", dy);
1221
1223 view_zoomdrag_exit(C, op);
1224 return OPERATOR_FINISHED;
1225 }
1226
1227 /* set initial settings */
1228 vzd->lastx = event->xy[0];
1229 vzd->lasty = event->xy[1];
1230 RNA_float_set(op->ptr, "deltax", 0);
1231 RNA_float_set(op->ptr, "deltay", 0);
1232
1233 /* for modal exit test */
1234 vzd->invoke_event = event->type;
1235
1236 if (WM_cursor_modal_is_set_ok(window)) {
1237 if (v2d->keepofs & V2D_LOCKOFS_X) {
1239 }
1240 else if (v2d->keepofs & V2D_LOCKOFS_Y) {
1242 }
1243 else {
1245 }
1246 vzd->own_cursor = true;
1247 }
1248
1249 /* add temp handler */
1251
1252 if (U.viewzoom == USER_ZOOM_CONTINUE) {
1253 /* needs a timer to continue redrawing */
1254 vzd->timer = WM_event_timer_add(CTX_wm_manager(C), window, TIMER, 0.01f);
1256 }
1257
1259}
1260
1261/* handle user input - calculations of mouse-movement need to be done here,
1262 * not in the apply callback! */
1264{
1265 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1266 View2D *v2d = vzd->v2d;
1267
1268 /* execute the events */
1269 if (event->type == TIMER && event->customdata == vzd->timer) {
1271 }
1272 else if (event->type == MOUSEMOVE) {
1273 float dx, dy;
1274 float zoomfac = 0.01f;
1275
1276 /* some view2d's (graph) don't have min/max zoom, or extreme ones */
1277 if (v2d->maxzoom > 0.0f) {
1278 zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1279 }
1280
1281 /* calculate new delta transform, based on zooming mode */
1282 if (U.viewzoom == USER_ZOOM_SCALE) {
1283 /* 'scale' zooming */
1284 float dist;
1285 float len_old[2];
1286 float len_new[2];
1287
1288 /* x-axis transform */
1289 dist = BLI_rcti_size_x(&v2d->mask) / 2.0f;
1290 len_old[0] = zoomfac * fabsf(vzd->lastx - vzd->region->winrct.xmin - dist);
1291 len_new[0] = zoomfac * fabsf(event->xy[0] - vzd->region->winrct.xmin - dist);
1292
1293 /* y-axis transform */
1294 dist = BLI_rcti_size_y(&v2d->mask) / 2.0f;
1295 len_old[1] = zoomfac * fabsf(vzd->lasty - vzd->region->winrct.ymin - dist);
1296 len_new[1] = zoomfac * fabsf(event->xy[1] - vzd->region->winrct.ymin - dist);
1297
1298 /* Calculate distance */
1299 if (v2d->keepzoom & V2D_KEEPASPECT) {
1300 dist = len_v2(len_new) - len_v2(len_old);
1301 dx = dy = dist;
1302 }
1303 else {
1304 dx = len_new[0] - len_old[0];
1305 dy = len_new[1] - len_old[1];
1306 }
1307
1308 dx *= BLI_rctf_size_x(&v2d->cur);
1309 dy *= BLI_rctf_size_y(&v2d->cur);
1310 }
1311 else { /* USER_ZOOM_CONTINUE or USER_ZOOM_DOLLY */
1312 float facx = zoomfac * (event->xy[0] - vzd->lastx);
1313 float facy = zoomfac * (event->xy[1] - vzd->lasty);
1314
1315 /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1316 * any will be scaled uniformly */
1317 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0 && (v2d->keepzoom & V2D_LOCKZOOM_Y) == 0 &&
1318 (v2d->keepzoom & V2D_KEEPASPECT))
1319 {
1320 if (U.uiflag & USER_ZOOM_HORIZ) {
1321 facy = 0.0f;
1322 }
1323 else {
1324 facx = 0.0f;
1325 }
1326 }
1327
1328 /* support zoom to always zoom entirely - the v2d code uses portrait or
1329 * landscape exceptions */
1330 if (v2d->keepzoom & V2D_KEEPASPECT) {
1331 if (fabsf(facx) > fabsf(facy)) {
1332 facy = facx;
1333 }
1334 else {
1335 facx = facy;
1336 }
1337 }
1338
1339 dx = facx * BLI_rctf_size_x(&v2d->cur);
1340 dy = facy * BLI_rctf_size_y(&v2d->cur);
1341 }
1342
1343 if (U.uiflag & USER_ZOOM_INVERT) {
1344 dx *= -1.0f;
1345 dy *= -1.0f;
1346 }
1347
1348 /* set transform amount, and add current deltas to stored total delta (for redo) */
1349 RNA_float_set(op->ptr, "deltax", dx);
1350 RNA_float_set(op->ptr, "deltay", dy);
1351
1352 vzd->dx += dx;
1353 vzd->dy += dy;
1354
1355 /* Store mouse coordinates for next time, if not doing continuous zoom:
1356 * - Continuous zoom only depends on distance of mouse
1357 * to starting point to determine rate of change.
1358 */
1359 if (U.viewzoom != USER_ZOOM_CONTINUE) { /* XXX store this setting as RNA prop? */
1360 vzd->lastx = event->xy[0];
1361 vzd->lasty = event->xy[1];
1362 }
1363
1364 /* apply zooming */
1366 }
1367 else if (ELEM(event->type, vzd->invoke_event, EVT_ESCKEY)) {
1368 if (event->val == KM_RELEASE) {
1369
1370 /* for redo, store the overall deltas - need to respect zoom-locks here... */
1371 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1372 RNA_float_set(op->ptr, "deltax", vzd->dx);
1373 }
1374 else {
1375 RNA_float_set(op->ptr, "deltax", 0);
1376 }
1377
1378 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1379 RNA_float_set(op->ptr, "deltay", vzd->dy);
1380 }
1381 else {
1382 RNA_float_set(op->ptr, "deltay", 0);
1383 }
1384
1385 /* free customdata */
1386 const bool own_cursor = vzd->own_cursor;
1387 view_zoomdrag_exit(C, op);
1388 if (own_cursor) {
1390 }
1391
1392 return OPERATOR_FINISHED;
1393 }
1394 }
1395
1397}
1398
1400{
1401 PropertyRNA *prop;
1402 /* identifiers */
1403 ot->name = "Zoom 2D View";
1404 ot->description = "Zoom in/out the view";
1405 ot->idname = "VIEW2D_OT_zoom";
1406
1407 /* API callbacks. */
1408 ot->exec = view_zoomdrag_exec;
1409 ot->invoke = view_zoomdrag_invoke;
1410 ot->modal = view_zoomdrag_modal;
1411 ot->cancel = view_zoomdrag_cancel;
1412
1413 ot->poll = view_zoom_poll;
1414
1415 /* operator is repeatable */
1417
1418 /* rna - must keep these in sync with the other operators */
1419 prop = RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1421 prop = RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1423
1425}
1426
1428
1429/* -------------------------------------------------------------------- */
1441
1443{
1444 ARegion *region = CTX_wm_region(C);
1445 View2D *v2d = &region->v2d;
1446 rctf cur_new = v2d->cur;
1447 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
1448
1449 /* convert coordinates of rect to `tot` rect coordinates */
1450 rctf rect;
1452 UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
1453
1454 /* check if zooming in/out view */
1455 const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
1456
1457 if (zoom_in) {
1458 /* zoom in:
1459 * - `cur` rect will be defined by the coordinates of the border region
1460 * - just set the `cur` rect to have the same coordinates as the border region
1461 * if zoom is allowed to be changed
1462 */
1463 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1464 cur_new.xmin = rect.xmin;
1465 cur_new.xmax = rect.xmax;
1466 }
1467 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1468 cur_new.ymin = rect.ymin;
1469 cur_new.ymax = rect.ymax;
1470 }
1471 }
1472 else {
1473 /* zoom out:
1474 * - the current `cur` rect coordinates are going to end up where the `rect` ones are,
1475 * but the `cur` rect coordinates will need to be adjusted to take in more of the view
1476 * - calculate zoom factor, and adjust using center-point
1477 */
1478 float zoom, center, size;
1479
1480 /* TODO: is this zoom factor calculation valid?
1481 * It seems to produce same results every time... */
1482 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1483 size = BLI_rctf_size_x(&cur_new);
1484 zoom = size / BLI_rctf_size_x(&rect);
1485 center = BLI_rctf_cent_x(&cur_new);
1486
1487 cur_new.xmin = center - (size * zoom);
1488 cur_new.xmax = center + (size * zoom);
1489 }
1490 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1491 size = BLI_rctf_size_y(&cur_new);
1492 zoom = size / BLI_rctf_size_y(&rect);
1493 center = BLI_rctf_cent_y(&cur_new);
1494
1495 cur_new.ymin = center - (size * zoom);
1496 cur_new.ymax = center + (size * zoom);
1497 }
1498 }
1499
1500 UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx);
1501
1502 return OPERATOR_FINISHED;
1503}
1504
1506{
1507 /* identifiers */
1508 ot->name = "Zoom to Border";
1509 ot->description = "Zoom in the view to the nearest item contained in the border";
1510 ot->idname = "VIEW2D_OT_zoom_border";
1511
1512 /* API callbacks. */
1513 ot->invoke = WM_gesture_box_invoke;
1514 ot->exec = view_borderzoom_exec;
1515 ot->modal = WM_gesture_box_modal;
1516 ot->cancel = WM_gesture_box_cancel;
1517
1518 ot->poll = view_zoom_poll;
1519
1520 /* rna */
1522}
1523
1525
1526/* -------------------------------------------------------------------- */
1529
1530#ifdef WITH_INPUT_NDOF
1531static wmOperatorStatus view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1532{
1533 if (event->type != NDOF_MOTION) {
1534 return OPERATOR_CANCELLED;
1535 }
1536
1537 const wmNDOFMotionData &ndof = *static_cast<const wmNDOFMotionData *>(event->customdata);
1538
1539 /* tune these until it feels right */
1540 const float zoom_sensitivity = 0.5f;
1541 const float pan_speed = NDOF_PIXELS_PER_SECOND;
1542 blender::float3 pan_vec = WM_event_ndof_translation_get_for_navigation(ndof);
1543 const bool has_translate = !is_zero_v2(pan_vec) && view_pan_poll(C);
1544 const bool has_zoom = (pan_vec[2] != 0.0f) && view_zoom_poll(C);
1545
1546 if (has_translate) {
1547 mul_v2_fl(pan_vec, ndof.time_delta * pan_speed);
1548
1549 view_pan_init(C, op);
1550
1551 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
1552 view_pan_apply_ex(C, vpd, pan_vec[0], pan_vec[1]);
1553
1554 view_pan_exit(op);
1555 }
1556
1557 if (has_zoom) {
1558 float zoom_factor = zoom_sensitivity * ndof.time_delta * -pan_vec[2];
1559
1560 bool do_zoom_xy[2];
1561 view_zoom_axis_lock_defaults(C, do_zoom_xy);
1562
1563 view_zoomdrag_init(C, op);
1564
1565 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1567 C, vzd, do_zoom_xy[0] ? zoom_factor : 0.0f, do_zoom_xy[1] ? zoom_factor : 0.0f);
1568
1569 view_zoomstep_exit(C, op);
1570 }
1571
1572 return OPERATOR_FINISHED;
1573}
1574
1575static void VIEW2D_OT_ndof(wmOperatorType *ot)
1576{
1577 /* identifiers */
1578 ot->name = "NDOF Pan/Zoom";
1579 ot->idname = "VIEW2D_OT_ndof";
1580 ot->description = "Use a 3D mouse device to pan/zoom the view";
1581
1582 /* API callbacks. */
1583 ot->invoke = view2d_ndof_invoke;
1584 ot->poll = view2d_poll;
1585
1586 /* flags */
1587 ot->flag = OPTYPE_LOCK_BYPASS;
1588}
1589#endif /* WITH_INPUT_NDOF */
1590
1592
1593/* -------------------------------------------------------------------- */
1596
1602
1611static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
1612{
1613 const float size_a[2] = {BLI_rctf_size_x(rect_a), BLI_rctf_size_y(rect_a)};
1614 const float size_b[2] = {BLI_rctf_size_x(rect_b), BLI_rctf_size_y(rect_b)};
1615 const float cent_a[2] = {BLI_rctf_cent_x(rect_a), BLI_rctf_cent_y(rect_a)};
1616 const float cent_b[2] = {BLI_rctf_cent_x(rect_b), BLI_rctf_cent_y(rect_b)};
1617
1618 float fac_max = 0.0f;
1619
1620 for (int i = 0; i < 2; i++) {
1621 /* axis translation normalized to scale */
1622 float tfac = fabsf(cent_a[i] - cent_b[i]) / min_ff(size_a[i], size_b[i]);
1623 fac_max = max_ff(fac_max, tfac);
1624 if (fac_max >= 1.0f) {
1625 break;
1626 }
1627
1628 /* axis scale difference, x2 so doubling or half gives 1.0f */
1629 tfac = (1.0f - (min_ff(size_a[i], size_b[i]) / max_ff(size_a[i], size_b[i]))) * 2.0f;
1630 fac_max = max_ff(fac_max, tfac);
1631 if (fac_max >= 1.0f) {
1632 break;
1633 }
1634 }
1635 return min_ff(fac_max, 1.0f);
1636}
1637
1639 ARegion *region,
1640 const rctf *cur,
1641 const int smooth_viewtx)
1642{
1644 wmWindow *win = CTX_wm_window(C);
1645
1646 View2D *v2d = &region->v2d;
1647 SmoothView2DStore sms = {{0}};
1648 bool ok = false;
1649 float fac = 1.0f;
1650
1651 /* initialize sms */
1652 sms.new_cur = v2d->cur;
1653
1654 /* store the options we want to end with */
1655 if (cur) {
1656 sms.new_cur = *cur;
1657 }
1658
1659 if (cur) {
1660 fac = smooth_view_rect_to_fac(&v2d->cur, cur);
1661 }
1662
1663 if (smooth_viewtx && fac > FLT_EPSILON) {
1664 bool changed = false;
1665
1666 if (BLI_rctf_compare(&sms.new_cur, &v2d->cur, FLT_EPSILON) == false) {
1667 changed = true;
1668 }
1669
1670 /* The new view is different from the old one
1671 * so animate the view */
1672 if (changed) {
1673 sms.orig_cur = v2d->cur;
1674
1675 sms.time_allowed = double(smooth_viewtx) / 1000.0;
1676
1677 /* scale the time allowed the change in view */
1678 sms.time_allowed *= double(fac);
1679
1680 /* keep track of running timer! */
1681 if (v2d->sms == nullptr) {
1682 v2d->sms = MEM_new<SmoothView2DStore>(__func__);
1683 }
1684 *v2d->sms = sms;
1685 if (v2d->smooth_timer) {
1686 WM_event_timer_remove(wm, win, v2d->smooth_timer);
1687 }
1688 /* TIMER1 is hard-coded in key-map. */
1689 v2d->smooth_timer = WM_event_timer_add(wm, win, TIMER1, 1.0 / 100.0);
1690
1691 ok = true;
1692 }
1693 }
1694
1695 /* if we get here nothing happens */
1696 if (ok == false) {
1697 v2d->cur = sms.new_cur;
1698
1702 }
1703}
1704
1705/* only meant for timer usage */
1707 wmOperator * /*op*/,
1708 const wmEvent *event)
1709{
1710 wmWindow *win = CTX_wm_window(C);
1711 ARegion *region = CTX_wm_region(C);
1712 View2D *v2d = &region->v2d;
1713 SmoothView2DStore *sms = v2d->sms;
1714
1715 /* escape if not our timer */
1716 if (v2d->smooth_timer == nullptr || v2d->smooth_timer != event->customdata) {
1717 return OPERATOR_PASS_THROUGH;
1718 }
1719
1720 float step;
1721 if (sms->time_allowed != 0.0) {
1723 }
1724 else {
1725 step = 1.0f;
1726 }
1727
1728 /* end timer */
1729 if (step >= 1.0f) {
1730 v2d->cur = sms->new_cur;
1731
1732 MEM_delete(v2d->sms);
1733 v2d->sms = nullptr;
1734
1736 v2d->smooth_timer = nullptr;
1737
1738 /* Event handling won't know if a UI item has been moved under the pointer. */
1740 }
1741 else {
1742 /* ease in/out */
1743 step = (3.0f * step * step - 2.0f * step * step * step);
1744
1745 BLI_rctf_interp(&v2d->cur, &sms->orig_cur, &sms->new_cur, step);
1746 }
1747
1751
1752 if (v2d->sms == nullptr) {
1754 }
1755
1756 return OPERATOR_FINISHED;
1757}
1758
1760{
1761 /* identifiers */
1762 ot->name = "Smooth View 2D";
1763 ot->idname = "VIEW2D_OT_smoothview";
1764
1765 /* API callbacks. */
1766 ot->invoke = view2d_smoothview_invoke;
1767 ot->poll = view2d_poll;
1768
1769 /* flags */
1770 ot->flag = OPTYPE_INTERNAL;
1771
1772 /* rna */
1774}
1775
1777
1778/* -------------------------------------------------------------------- */
1781
1791
1792/* customdata for scroller-invoke data */
1798
1801
1802 /* XXX find some way to provide visual feedback of this (active color?) */
1804 short zone;
1805
1807 float fac;
1811 float delta;
1812
1817
1820};
1821
1822/* quick enum for vsm->zone (scroller handles) */
1823enum {
1829} /*eV2DScrollerHandle_Zone*/;
1830
1837static short scrollbar_zone_get(int mouse, int sh_min, int sh_max)
1838{
1839 /* Check if mouse is in either zoom handle. */
1840 bool in_max = ((mouse >= (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1841 (mouse <= (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1842 bool in_min = ((mouse <= (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1843 (mouse >= (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1844
1845 /* Check if mouse is in scrollbar or outside, on the scrollbar track. */
1846 bool in_bar = ((mouse < (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1847 (mouse > (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1848 const bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1849 const bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1850
1851 if (in_bar) {
1852 return SCROLLHANDLE_BAR;
1853 }
1854 if (in_max) {
1855 return SCROLLHANDLE_MAX;
1856 }
1857 if (in_min) {
1858 return SCROLLHANDLE_MIN;
1859 }
1860 if (out_min) {
1862 }
1863 if (out_max) {
1865 }
1866
1867 /* unlikely to happen, though we just cover it in case */
1868 return SCROLLHANDLE_BAR;
1869}
1870
1872{
1873 const wmWindow *win = CTX_wm_window(C);
1874 if (!(win && win->eventstate)) {
1875 return false;
1876 }
1877 if (!view2d_poll(C)) {
1878 return false;
1879 }
1880 ARegion *region = CTX_wm_region(C);
1881 View2D *v2d = &region->v2d;
1882 /* Check if mouse in scroll-bars, if they're enabled. */
1883 return (UI_view2d_mouse_in_scrollers(region, v2d, win->eventstate->xy) != 0);
1884}
1885
1886/* Initialize #wmOperator.customdata for scroller manipulation operator. */
1888 wmOperator *op,
1889 const wmEvent *event,
1890 const char in_scroller)
1891{
1892 ARegion *region = CTX_wm_region(C);
1893 View2D *v2d = &region->v2d;
1894
1895 /* set custom-data for operator */
1897 op->customdata = vsm;
1898
1899 /* set general data */
1900 vsm->v2d = v2d;
1901 vsm->region = region;
1902 vsm->scroller = in_scroller;
1903
1904 /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1905 vsm->lastx = event->xy[0];
1906 vsm->lasty = event->xy[1];
1907 /* `zone` depends on where mouse is relative to bubble
1908 * - zooming must be allowed on this axis, otherwise, default to pan.
1909 */
1910 View2DScrollers scrollers;
1911 /* Reconstruct the custom scroller mask passed to #UI_view2d_scrollers_draw().
1912 *
1913 * Some editors like the File Browser, Spreadsheet or scrubbing UI already set up custom masks
1914 * for scroll-bars (they don't cover the whole region width or height), these need to be
1915 * considered, otherwise coords for `scrollbar_zone_get` later are not compatible. This
1916 * should be a reliable way to do it. Otherwise the custom scroller mask could also be stored in
1917 * #View2D.
1918 */
1919 rcti scroller_mask = v2d->hor;
1920 BLI_rcti_union(&scroller_mask, &v2d->vert);
1921
1922 view2d_scrollers_calc(v2d, &scroller_mask, &scrollers);
1923
1924 /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
1925 * moving the scroll bars has far too little effect and the view can get stuck #31476. */
1926 rctf tot_cur_union = v2d->tot;
1927 BLI_rctf_union(&tot_cur_union, &v2d->cur);
1928
1929 if (in_scroller == 'h') {
1930 /* horizontal scroller - calculate adjustment factor first */
1931 const float mask_size = float(BLI_rcti_size_x(&v2d->hor));
1932 vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size;
1933
1934 /* pixel rounding */
1935 vsm->fac_round = BLI_rctf_size_x(&v2d->cur) / float(BLI_rcti_size_x(&region->winrct) + 1);
1936
1937 /* get 'zone' (i.e. which part of scroller is activated) */
1938 vsm->zone = scrollbar_zone_get(event->mval[0], scrollers.hor_min, scrollers.hor_max);
1939
1941 /* default to scroll, as handles not usable */
1942 vsm->zone = SCROLLHANDLE_BAR;
1943 }
1944
1945 vsm->scrollbarwidth = scrollers.hor_max - scrollers.hor_min;
1946 vsm->scrollbar_orig = ((scrollers.hor_max + scrollers.hor_min) / 2) + region->winrct.xmin;
1947 }
1948 else {
1949 /* vertical scroller - calculate adjustment factor first */
1950 const float mask_size = float(BLI_rcti_size_y(&v2d->vert));
1951 vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size;
1952
1953 /* pixel rounding */
1954 vsm->fac_round = BLI_rctf_size_y(&v2d->cur) / float(BLI_rcti_size_y(&region->winrct) + 1);
1955
1956 /* Get `zone` (i.e. which part of scroller is activated). */
1957 vsm->zone = scrollbar_zone_get(event->mval[1], scrollers.vert_min, scrollers.vert_max);
1958
1960 /* default to scroll, as handles not usable */
1961 vsm->zone = SCROLLHANDLE_BAR;
1962 }
1963
1964 vsm->scrollbarwidth = scrollers.vert_max - scrollers.vert_min;
1965 vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin;
1966 }
1967
1968 vsm->v2d->flag |= V2D_IS_NAVIGATING;
1969
1971}
1972
1973/* Cleanup temp custom-data. */
1975{
1976 if (op->customdata) {
1977 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1978
1980 vsm->v2d->flag &= ~V2D_IS_NAVIGATING;
1981
1982 MEM_freeN(vsm);
1983 op->customdata = nullptr;
1984
1986 }
1987}
1988
1990{
1992}
1993
1994/* apply transform to view (i.e. adjust 'cur' rect) */
1996{
1997 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1998 View2D *v2d = vsm->v2d;
1999
2000 /* calculate amount to move view by */
2001 float temp = vsm->fac * vsm->delta;
2002
2003 /* round to pixel */
2004 temp = roundf(temp / vsm->fac_round) * vsm->fac_round;
2005
2006 /* type of movement */
2007 switch (vsm->zone) {
2008 case SCROLLHANDLE_MIN:
2009 /* only expand view on axis if zoom is allowed */
2010 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
2011 v2d->cur.xmin -= temp;
2012 }
2013 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
2014 v2d->cur.ymin -= temp;
2015 }
2016 break;
2017
2018 case SCROLLHANDLE_MAX:
2019
2020 /* only expand view on axis if zoom is allowed */
2021 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
2022 v2d->cur.xmax += temp;
2023 }
2024 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
2025 v2d->cur.ymax += temp;
2026 }
2027 break;
2028
2031 case SCROLLHANDLE_BAR:
2032 default:
2033 /* only move view on an axis if panning is allowed */
2034 if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
2035 v2d->cur.xmin += temp;
2036 v2d->cur.xmax += temp;
2037 }
2038 if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
2039 v2d->cur.ymin += temp;
2040 v2d->cur.ymax += temp;
2041 }
2042 break;
2043 }
2044
2045 /* Inform v2d about changes after this operation. */
2047
2048 /* request updates to be done... */
2051}
2052
2058{
2059 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
2060 const bool use_page_size_y = vsm->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y;
2061
2062 /* execute the events */
2063 switch (event->type) {
2064 case MOUSEMOVE: {
2065 float delta = 0.0f;
2066
2067 /* calculate new delta transform, then store mouse-coordinates for next-time */
2069 /* if using bar (i.e. 'panning') or 'max' zoom widget */
2070 switch (vsm->scroller) {
2071 case 'h': /* horizontal scroller - so only horizontal movement
2072 * ('cur' moves opposite to mouse) */
2073 delta = float(event->xy[0] - vsm->lastx);
2074 break;
2075 case 'v': /* vertical scroller - so only vertical movement
2076 * ('cur' moves opposite to mouse) */
2077 delta = float(event->xy[1] - vsm->lasty);
2078 break;
2079 }
2080 }
2081 else if (vsm->zone == SCROLLHANDLE_MIN) {
2082 /* using 'min' zoom widget */
2083 switch (vsm->scroller) {
2084 case 'h': /* horizontal scroller - so only horizontal movement
2085 * ('cur' moves with mouse) */
2086 delta = float(vsm->lastx - event->xy[0]);
2087 break;
2088 case 'v': /* vertical scroller - so only vertical movement
2089 * ('cur' moves with to mouse) */
2090 delta = float(vsm->lasty - event->xy[1]);
2091 break;
2092 }
2093 }
2094
2095 /* Page snapping: When panning for more than half a page size, snap to the next page. */
2096 if (use_page_size_y && (vsm->scroller == 'v')) {
2097 delta = view2d_scroll_delta_y_snap_page_size(*vsm->v2d, delta * vsm->fac) / vsm->fac;
2098 }
2099
2100 if (IS_EQF(delta, 0.0f)) {
2101 break;
2102 }
2103
2104 vsm->delta = delta;
2105 /* store previous coordinates */
2106 vsm->lastx = event->xy[0];
2107 vsm->lasty = event->xy[1];
2108
2110 break;
2111 }
2112 case LEFTMOUSE:
2113 case MIDDLEMOUSE:
2114 if (event->val == KM_RELEASE) {
2115 /* single-click was in empty space outside bubble, so scroll by 1 'page' */
2117 if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE) {
2118 vsm->delta = -vsm->scrollbarwidth * 0.8f;
2119 }
2120 else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE) {
2121 vsm->delta = vsm->scrollbarwidth * 0.8f;
2122 }
2123
2126 return OPERATOR_FINISHED;
2127 }
2128
2129 /* Otherwise, end the drag action. */
2130 if (vsm->lastx || vsm->lasty) {
2132 return OPERATOR_FINISHED;
2133 }
2134 }
2135 break;
2136 default: {
2137 break;
2138 }
2139 }
2140
2142}
2143
2144/* a click (or click drag in progress)
2145 * should have occurred, so check if it happened in scrollbar */
2147{
2148 ARegion *region = CTX_wm_region(C);
2149 View2D *v2d = &region->v2d;
2150
2151 /* check if mouse in scroll-bars, if they're enabled */
2152 const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy);
2153
2154 /* if in a scroller, init customdata then set modal handler which will
2155 * catch mouse-down to start doing useful stuff */
2156 if (in_scroller) {
2157 /* initialize customdata */
2158 scroller_activate_init(C, op, event, in_scroller);
2160
2161 /* Support for quick jump to location - GTK and QT do this on Linux. */
2162 if (event->type == MIDDLEMOUSE) {
2163 switch (vsm->scroller) {
2164 case 'h': /* horizontal scroller - so only horizontal movement
2165 * ('cur' moves opposite to mouse) */
2166 vsm->delta = float(event->xy[0] - vsm->scrollbar_orig);
2167 break;
2168 case 'v': /* vertical scroller - so only vertical movement
2169 * ('cur' moves opposite to mouse) */
2170 vsm->delta = float(event->xy[1] - vsm->scrollbar_orig);
2171 break;
2172 }
2174
2175 vsm->zone = SCROLLHANDLE_BAR;
2176 }
2177
2178 /* Check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
2179 * NOTE: see `view2d.cc` for latest conditions, and keep this in sync with that. */
2181 if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) == 0) ||
2182 ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) == 0))
2183 {
2184 /* switch to bar (i.e. no scaling gets handled) */
2185 vsm->zone = SCROLLHANDLE_BAR;
2186 }
2187 }
2188
2189 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
2190 if (vsm->zone == SCROLLHANDLE_BAR) {
2191 if (((vsm->scroller == 'h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
2192 ((vsm->scroller == 'v') && (v2d->keepofs & V2D_LOCKOFS_Y)))
2193 {
2194 /* free customdata initialized */
2196
2197 /* can't catch this event for ourselves, so let it go to someone else? */
2198 return OPERATOR_PASS_THROUGH;
2199 }
2200 }
2201
2202 /* zone is also inappropriate if scroller is not visible... */
2203 if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_FULLR)) ||
2204 ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_FULLR)))
2205 {
2206 /* free customdata initialized */
2208
2209 /* can't catch this event for ourselves, so let it go to someone else? */
2210 /* XXX NOTE: if handlers use mask rect to clip input, input will fail for this case. */
2211 return OPERATOR_PASS_THROUGH;
2212 }
2213
2214 /* activate the scroller */
2215 if (vsm->scroller == 'h') {
2217 }
2218 else {
2220 }
2221
2222 /* still ok, so can add */
2225 }
2226
2227 /* not in scroller, so nothing happened...
2228 * (pass through let's something else catch event) */
2229 return OPERATOR_PASS_THROUGH;
2230}
2231
2232/* LMB-Drag in Scrollers - not repeatable operator! */
2234{
2235 /* identifiers */
2236 ot->name = "Scroller Activate";
2237 ot->description = "Scroll view by mouse click and drag";
2238 ot->idname = "VIEW2D_OT_scroller_activate";
2239
2240 /* flags */
2241 ot->flag = OPTYPE_BLOCKING;
2242
2243 /* API callbacks. */
2244 ot->invoke = scroller_activate_invoke;
2245 ot->modal = scroller_activate_modal;
2246 ot->cancel = scroller_activate_cancel;
2247
2248 ot->poll = scroller_activate_poll;
2249}
2250
2252
2253/* -------------------------------------------------------------------- */
2256
2258{
2259 const uiStyle *style = UI_style_get();
2260 ARegion *region = CTX_wm_region(C);
2261 View2D *v2d = &region->v2d;
2262 const int snap_test = ED_region_snap_size_test(region);
2263
2264 region->category_scroll = 0;
2265
2266 /* zoom 1.0 */
2267 const int winx = float(BLI_rcti_size_x(&v2d->mask) + 1);
2268 const int winy = float(BLI_rcti_size_y(&v2d->mask) + 1);
2269
2270 v2d->cur.xmax = v2d->cur.xmin + winx;
2271 v2d->cur.ymax = v2d->cur.ymin + winy;
2272
2273 /* align */
2274 if (v2d->align) {
2275 /* posx and negx flags are mutually exclusive, so watch out */
2276 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
2277 v2d->cur.xmax = 0.0f;
2278 v2d->cur.xmin = -winx * style->panelzoom;
2279 }
2280 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
2281 v2d->cur.xmax = winx * style->panelzoom;
2282 v2d->cur.xmin = 0.0f;
2283 }
2284
2285 /* - posx and negx flags are mutually exclusive, so watch out */
2286 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
2287 v2d->cur.ymax = 0.0f;
2288 v2d->cur.ymin = -winy * style->panelzoom;
2289 }
2290 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
2291 v2d->cur.ymax = winy * style->panelzoom;
2292 v2d->cur.ymin = 0.0f;
2293 }
2294 }
2295
2296 /* Inform v2d about changes after this operation. */
2298
2299 if (ED_region_snap_size_apply(region, snap_test)) {
2300 ScrArea *area = CTX_wm_area(C);
2301 ED_area_tag_redraw(area);
2303 }
2304
2305 /* request updates to be done... */
2306 ED_region_tag_redraw(region);
2308
2310
2311 return OPERATOR_FINISHED;
2312}
2313
2315{
2316 /* identifiers */
2317 ot->name = "Reset View";
2318 ot->description = "Reset the view";
2319 ot->idname = "VIEW2D_OT_reset";
2320
2321 /* API callbacks. */
2322 ot->exec = reset_exec;
2323 ot->poll = view2d_poll;
2324}
2325
2327
2328/* -------------------------------------------------------------------- */
2331
2358
2360{
2361 WM_keymap_ensure(keyconf, "View2D", SPACE_EMPTY, RGN_TYPE_WINDOW);
2362}
2363
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float max_ff(float a, float b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE bool is_zero_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
void BLI_rcti_union(struct rcti *rct_a, const struct rcti *rct_b)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
void BLI_rctf_union(struct rctf *rct_a, const struct rctf *rct_b)
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:189
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:185
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
void BLI_rctf_interp(struct rctf *rect, const struct rctf *rect_a, const struct rctf *rect_b, float fac)
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
bool BLI_rctf_compare(const struct rctf *rect_a, const struct rctf *rect_b, float limit)
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define ELEM(...)
#define IS_EQF(a, b)
#define IN_RANGE_INCL(a, b, c)
@ RGN_TYPE_WINDOW
@ SPACE_ACTION
@ SPACE_SEQ
@ SPACE_EMPTY
@ SPACE_GRAPH
#define UI_SCALE_FAC
@ USER_ZOOM_INVERT
@ USER_ZOOM_TO_MOUSEPOS
@ USER_ZOOM_HORIZ
#define NDOF_PIXELS_PER_SECOND
@ USER_ZOOM_SCALE
@ USER_ZOOM_CONTINUE
@ V2D_LOCKOFS_X
@ V2D_LOCKOFS_Y
@ V2D_KEEPOFS_Y
@ V2D_KEEPOFS_X
@ V2D_IS_NAVIGATING
@ V2D_IS_INIT
@ V2D_ZOOM_IGNORE_KEEPOFS
@ V2D_SNAP_TO_PAGESIZE_Y
@ V2D_LIMITZOOM
@ V2D_LOCKZOOM_X
@ V2D_KEEPASPECT
@ V2D_LOCKZOOM_Y
@ V2D_SCROLL_V_ACTIVE
@ V2D_SCROLL_H_ACTIVE
@ V2D_SCROLL_HORIZONTAL_FULLR
@ V2D_SCROLL_VERTICAL_FULLR
@ V2D_SCROLL_VERTICAL_HANDLES
@ V2D_SCROLL_HORIZONTAL_HANDLES
@ V2D_ALIGN_NO_NEG_X
@ V2D_ALIGN_NO_NEG_Y
@ V2D_ALIGN_NO_POS_Y
@ V2D_ALIGN_NO_POS_X
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:693
int ED_region_snap_size_test(const ARegion *region)
Definition area.cc:4406
bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2])
Definition area_query.cc:93
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:638
bool ED_region_snap_size_apply(ARegion *region, int snap_flag)
Definition area.cc:4419
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
const uiStyle * UI_style_get()
void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
Definition view2d.cc:865
#define V2D_LOCK_COPY
Definition UI_view2d.hh:85
void UI_view2d_edge_pan_operator_init(bContext *C, View2DEdgePanData *vpd, wmOperator *op)
char char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2]) ATTR_NONNULL(1
void UI_view2d_curRect_changed(const bContext *C, View2D *v2d)
Definition view2d.cc:833
void UI_view2d_edge_pan_operator_properties(wmOperatorType *ot)
#define V2D_SCROLL_HANDLE_SIZE_HOTSPOT
Definition UI_view2d.hh:71
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
void void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event)
void UI_view2d_zoom_cache_reset()
Definition view2d.cc:1041
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1675
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
#define NC_SCREEN
Definition WM_types.hh:377
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_LOCK_BYPASS
Definition WM_types.hh:205
@ OPTYPE_GRAB_CURSOR_XY
Definition WM_types.hh:188
#define NA_EDITED
Definition WM_types.hh:584
#define U
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
nullptr float
#define roundf(x)
#define abs
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
VecBase< float, 3 > float3
#define fabsf
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define FLT_MAX
Definition stdcycles.h:14
float minzoom
struct SmoothView2DStore * sms
short scroll_ui
short keepzoom
struct wmTimer * smooth_timer
short keepofs
float maxzoom
float xmax
float xmin
float ymax
float ymin
int ymin
int xmin
float panelzoom
bool do_category_scroll
bScreen * screen
Definition view2d_ops.cc:86
ARegion * region
Definition view2d_ops.cc:90
ScrArea * area
Definition view2d_ops.cc:88
double edge_pan_last_time
double edge_pan_start_time_x
double edge_pan_start_time_y
wmTimer * timer
double timer_lastdraw
ARegion * region
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int xy[2]
Definition WM_types.hh:761
int mval[2]
Definition WM_types.hh:763
int prev_xy[2]
Definition WM_types.hh:820
void * customdata
Definition WM_types.hh:807
struct PointerRNA * ptr
double time_duration
Definition WM_types.hh:968
struct wmEvent * eventstate
i
Definition text_draw.cc:230
float view2d_page_size_y(const View2D &v2d)
Definition view2d.cc:77
void view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom, View2DScrollers *r_scrollers)
Definition view2d.cc:1391
bool view2d_edge_pan_poll(bContext *C)
static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
static wmOperatorStatus view_scrolldown_exec(bContext *C, wmOperator *op)
static wmOperatorStatus scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void VIEW2D_OT_scroll_left(wmOperatorType *ot)
static wmOperatorStatus view_zoomin_exec(bContext *C, wmOperator *op)
static void scroller_activate_apply(bContext *C, wmOperator *op)
void UI_view2d_smooth_view(const bContext *C, ARegion *region, const rctf *cur, const int smooth_viewtx)
static bool view_zoom_poll(bContext *C)
static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
void ED_operatortypes_view2d()
static void view_edge_pan_cancel(bContext *, wmOperator *op)
static wmOperatorStatus view_scrollright_exec(bContext *C, wmOperator *op)
static wmOperatorStatus view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void VIEW2D_OT_scroll_down(wmOperatorType *ot)
static wmOperatorStatus view_zoomout_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void view_zoomstep_apply(bContext *C, wmOperator *op)
static void VIEW2D_OT_pan(wmOperatorType *ot)
static short scrollbar_zone_get(int mouse, int sh_min, int sh_max)
@ SCROLLHANDLE_BAR
@ SCROLLHANDLE_MIN_OUTSIDE
@ SCROLLHANDLE_MAX
@ SCROLLHANDLE_MIN
@ SCROLLHANDLE_MAX_OUTSIDE
static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const float facx, const float facy)
static wmOperatorStatus view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void view_zoomstep_exit(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_out(wmOperatorType *ot)
static wmOperatorStatus view_pan_exec(bContext *C, wmOperator *op)
static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy)
static void scroller_activate_exit(bContext *C, wmOperator *op)
static wmOperatorStatus view_borderzoom_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
static void view_pan_exit(wmOperator *op)
static void VIEW2D_OT_zoom(wmOperatorType *ot)
static bool view2d_poll(bContext *C)
Definition view2d_ops.cc:40
static void view_zoomdrag_exit(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_in(wmOperatorType *ot)
static wmOperatorStatus view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus view_zoomout_exec(bContext *C, wmOperator *op)
static void view_zoomdrag_apply(bContext *C, wmOperator *op)
static wmOperatorStatus view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void view_pan_cancel(bContext *, wmOperator *op)
static void scroller_activate_cancel(bContext *C, wmOperator *op)
static float view2d_scroll_delta_y_snap_page_size(const View2D &v2d, const float delta_y)
Definition view2d_ops.cc:52
void ED_keymap_view2d(wmKeyConfig *keyconf)
static wmOperatorStatus view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
static void view_pan_init(bContext *C, wmOperator *op)
static wmOperatorStatus view_scrollup_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_scroll_right(wmOperatorType *ot)
static wmOperatorStatus reset_exec(bContext *C, wmOperator *)
static wmOperatorStatus view_zoomdrag_exec(bContext *C, wmOperator *op)
static void view_pan_apply(bContext *C, wmOperator *op)
static bool view_pan_poll(bContext *C)
static wmOperatorStatus view_scrollleft_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_edge_pan(wmOperatorType *ot)
static void view_zoomdrag_cancel(bContext *C, wmOperator *op)
static void VIEW2D_OT_reset(wmOperatorType *ot)
static wmOperatorStatus view2d_smoothview_invoke(bContext *C, wmOperator *, const wmEvent *event)
static bool scroller_activate_poll(bContext *C)
static void VIEW2D_OT_smoothview(wmOperatorType *ot)
static wmOperatorStatus scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void scroller_activate_init(bContext *C, wmOperator *op, const wmEvent *event, const char in_scroller)
static void view_zoomdrag_init(bContext *C, wmOperator *op)
static wmOperatorStatus view_zoomin_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
bool WM_cursor_modal_is_set_ok(const wmWindow *win)
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:52
@ WM_CURSOR_NS_SCROLL
Definition wm_cursors.hh:53
@ WM_CURSOR_EW_SCROLL
Definition wm_cursors.hh:54
int WM_event_absolute_delta_y(const wmEvent *event)
int WM_event_absolute_delta_x(const wmEvent *event)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_mousemove(wmWindow *win)
@ MOUSEPAN
@ TIMER
@ MOUSEZOOM
@ TIMER1
@ MOUSEMOVE
@ LEFTMOUSE
@ NDOF_MOTION
@ MIDDLEMOUSE
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:895
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_border_to_rctf(wmOperator *op, rctf *r_rect)
void WM_operator_properties_use_cursor_init(wmOperatorType *ot)
void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
int WM_operator_smooth_viewtx_get(const wmOperator *op)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
wmWindow * WM_window_find_under_cursor(wmWindow *win, const int event_xy[2], int r_event_xy_other[2])
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)