Blender  V3.3
view2d_ops.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. All rights reserved. */
3 
8 #include <cmath>
9 
10 #include "MEM_guardedalloc.h"
11 
12 #include "DNA_userdef_types.h"
14 
15 #include "BLI_blenlib.h"
16 #include "BLI_math_base.h"
17 #include "BLI_math_vector.h"
18 #include "BLI_utildefines.h"
19 
20 #include "BKE_context.h"
21 
22 #include "RNA_access.h"
23 #include "RNA_define.h"
24 
25 #include "WM_api.h"
26 #include "WM_types.h"
27 
28 #include "ED_screen.h"
29 
30 #include "UI_interface.h"
31 #include "UI_view2d.h"
32 
33 #include "PIL_time.h" /* USER_ZOOM_CONTINUE */
34 
35 /* -------------------------------------------------------------------- */
39 static bool view2d_poll(bContext *C)
40 {
41  ARegion *region = CTX_wm_region(C);
42 
43  return (region != nullptr) && (region->v2d.flag & V2D_IS_INIT);
44 }
45 
48 /* -------------------------------------------------------------------- */
73 
75  float facx, facy;
76 
77  /* options for version 1 */
79  int startx, starty;
81  int lastx, lasty;
84 
86  short in_scroller;
87 
88  /* View2D Edge Panning */
91 };
92 
93 static bool view_pan_poll(bContext *C)
94 {
95  ARegion *region = CTX_wm_region(C);
96 
97  /* check if there's a region in context to work with */
98  if (region == nullptr) {
99  return false;
100  }
101 
102  View2D *v2d = &region->v2d;
103 
104  /* check that 2d-view can pan */
105  if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) {
106  return false;
107  }
108 
109  /* view can pan */
110  return true;
111 }
112 
113 /* initialize panning customdata */
114 static void view_pan_init(bContext *C, wmOperator *op)
115 {
116  /* Should've been checked before. */
118 
119  /* set custom-data for operator */
120  v2dViewPanData *vpd = MEM_cnew<v2dViewPanData>(__func__);
121  op->customdata = vpd;
122 
123  /* set pointers to owners */
124  vpd->screen = CTX_wm_screen(C);
125  vpd->area = CTX_wm_area(C);
126  vpd->region = CTX_wm_region(C);
127  vpd->v2d = &vpd->region->v2d;
128 
129  /* calculate translation factor - based on size of view */
130  const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1);
131  const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1);
132  vpd->facx = (BLI_rctf_size_x(&vpd->v2d->cur)) / winx;
133  vpd->facy = (BLI_rctf_size_y(&vpd->v2d->cur)) / winy;
134 
135  vpd->v2d->flag |= V2D_IS_NAVIGATING;
136 }
137 
138 /* apply transform to view (i.e. adjust 'cur' rect) */
139 static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy)
140 {
141  View2D *v2d = vpd->v2d;
142 
143  /* calculate amount to move view by */
144  dx *= vpd->facx;
145  dy *= vpd->facy;
146 
147  /* only move view on an axis if change is allowed */
148  if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) {
149  v2d->cur.xmin += dx;
150  v2d->cur.xmax += dx;
151  }
152  if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) {
153  v2d->cur.ymin += dy;
154  v2d->cur.ymax += dy;
155  }
156 
157  /* Inform v2d about changes after this operation. */
159 
160  /* don't rebuild full tree in outliner, since we're just changing our view */
162 
163  /* request updates to be done... */
165 
166  UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY);
167 }
168 
170 {
171  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
172 
173  view_pan_apply_ex(C, vpd, RNA_int_get(op->ptr, "deltax"), RNA_int_get(op->ptr, "deltay"));
174 }
175 
176 /* Cleanup temp custom-data. */
177 static void view_pan_exit(wmOperator *op)
178 {
179  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
180  vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
182 }
183 
186 /* -------------------------------------------------------------------- */
190 /* for 'redo' only, with no user input */
192 {
193  view_pan_init(C, op);
194  view_pan_apply(C, op);
195  view_pan_exit(op);
196  return OPERATOR_FINISHED;
197 }
198 
199 /* set up modal operator and relevant settings */
200 static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
201 {
202  wmWindow *window = CTX_wm_window(C);
203 
204  /* set up customdata */
205  view_pan_init(C, op);
206 
207  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
208  View2D *v2d = vpd->v2d;
209 
210  /* set initial settings */
211  vpd->startx = vpd->lastx = event->xy[0];
212  vpd->starty = vpd->lasty = event->xy[1];
213  vpd->invoke_event = event->type;
214 
215  if (event->type == MOUSEPAN) {
216  RNA_int_set(op->ptr, "deltax", event->prev_xy[0] - event->xy[0]);
217  RNA_int_set(op->ptr, "deltay", event->prev_xy[1] - event->xy[1]);
218 
219  view_pan_apply(C, op);
220  view_pan_exit(op);
221  return OPERATOR_FINISHED;
222  }
223 
224  RNA_int_set(op->ptr, "deltax", 0);
225  RNA_int_set(op->ptr, "deltay", 0);
226 
227  if (v2d->keepofs & V2D_LOCKOFS_X) {
229  }
230  else if (v2d->keepofs & V2D_LOCKOFS_Y) {
232  }
233  else {
235  }
236 
237  /* add temp handler */
239 
240  return OPERATOR_RUNNING_MODAL;
241 }
242 
243 /* handle user input - calculations of mouse-movement
244  * need to be done here, not in the apply callback! */
245 static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
246 {
247  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
248 
249  /* execute the events */
250  switch (event->type) {
251  case MOUSEMOVE: {
252  /* calculate new delta transform, then store mouse-coordinates for next-time */
253  RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->xy[0]));
254  RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->xy[1]));
255 
256  vpd->lastx = event->xy[0];
257  vpd->lasty = event->xy[1];
258 
259  view_pan_apply(C, op);
260  break;
261  }
262  /* XXX: Mode switching isn't implemented. See comments in 36818.
263  * switch to zoom */
264 #if 0
265  case LEFTMOUSE:
266  if (event->val == KM_PRESS) {
267  /* calculate overall delta mouse-movement for redo */
268  RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
269  RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
270 
271  view_pan_exit(op);
273  WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, nullptr, event);
274  return OPERATOR_FINISHED;
275  }
276 #endif
277  default:
278  if (ELEM(event->type, vpd->invoke_event, EVT_ESCKEY)) {
279  if (event->val == KM_RELEASE) {
280  /* calculate overall delta mouse-movement for redo */
281  RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
282  RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
283 
284  view_pan_exit(op);
286 
287  return OPERATOR_FINISHED;
288  }
289  }
290  break;
291  }
292 
293  return OPERATOR_RUNNING_MODAL;
294 }
295 
297 {
298  view_pan_exit(op);
299 }
300 
302 {
303  /* identifiers */
304  ot->name = "Pan View";
305  ot->description = "Pan the view";
306  ot->idname = "VIEW2D_OT_pan";
307 
308  /* api callbacks */
309  ot->exec = view_pan_exec;
313  ot->poll = view_pan_poll;
314 
315  /* operator is modal */
317 
318  /* rna - must keep these in sync with the other operators */
319  RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
320  RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
321 }
322 
325 /* -------------------------------------------------------------------- */
332 /* set up modal operator and relevant settings */
333 static int view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
334 {
335  op->customdata = MEM_callocN(sizeof(View2DEdgePanData), "View2DEdgePanData");
336  View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
338 
340 
342 }
343 
344 static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
345 {
346  View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
347 
348  if (event->val == KM_RELEASE || event->type == EVT_ESCKEY) {
349  vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
352  }
353 
354  UI_view2d_edge_pan_apply_event(C, vpd, event);
355 
356  /* This operator is supposed to run together with some drag action.
357  * On successful handling, always pass events on to other handlers. */
358  return OPERATOR_PASS_THROUGH;
359 }
360 
362 {
363  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
364  vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
366 }
367 
369 {
370  /* identifiers */
371  ot->name = "View Edge Pan";
372  ot->description = "Pan the view when the mouse is held at an edge";
373  ot->idname = "VIEW2D_OT_edge_pan";
374 
375  /* api callbacks */
380 
381  /* operator is modal */
384 }
385 
388 /* -------------------------------------------------------------------- */
392 /* this operator only needs this single callback, where it calls the view_pan_*() methods */
394 {
395  /* initialize default settings (and validate if ok to run) */
396  view_pan_init(C, op);
397 
398  /* also, check if can pan in horizontal axis */
399  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
400  if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
401  view_pan_exit(op);
402  return OPERATOR_PASS_THROUGH;
403  }
404 
405  /* set RNA-Props - only movement in positive x-direction */
406  RNA_int_set(op->ptr, "deltax", 40);
407  RNA_int_set(op->ptr, "deltay", 0);
408 
409  /* apply movement, then we're done */
410  view_pan_apply(C, op);
411  view_pan_exit(op);
412 
413  return OPERATOR_FINISHED;
414 }
415 
417 {
418  /* identifiers */
419  ot->name = "Scroll Right";
420  ot->description = "Scroll the view right";
421  ot->idname = "VIEW2D_OT_scroll_right";
422 
423  /* api callbacks */
425  ot->poll = view_pan_poll;
426 
427  /* rna - must keep these in sync with the other operators */
428  RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
429  RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
430 }
431 
432 /* this operator only needs this single callback, where it calls the view_pan_*() methods */
434 {
435  /* initialize default settings (and validate if ok to run) */
436  view_pan_init(C, op);
437 
438  /* also, check if can pan in horizontal axis */
439  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
440  if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
441  view_pan_exit(op);
442  return OPERATOR_PASS_THROUGH;
443  }
444 
445  /* set RNA-Props - only movement in negative x-direction */
446  RNA_int_set(op->ptr, "deltax", -40);
447  RNA_int_set(op->ptr, "deltay", 0);
448 
449  /* apply movement, then we're done */
450  view_pan_apply(C, op);
451  view_pan_exit(op);
452 
453  return OPERATOR_FINISHED;
454 }
455 
457 {
458  /* identifiers */
459  ot->name = "Scroll Left";
460  ot->description = "Scroll the view left";
461  ot->idname = "VIEW2D_OT_scroll_left";
462 
463  /* api callbacks */
465  ot->poll = view_pan_poll;
466 
467  /* rna - must keep these in sync with the other operators */
468  RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
469  RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
470 }
471 
472 /* this operator only needs this single callback, where it calls the view_pan_*() methods */
474 {
475  /* initialize default settings (and validate if ok to run) */
476  view_pan_init(C, op);
477 
478  /* also, check if can pan in vertical axis */
479  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
480  if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
481  view_pan_exit(op);
482  return OPERATOR_PASS_THROUGH;
483  }
484 
485  /* set RNA-Props */
486  RNA_int_set(op->ptr, "deltax", 0);
487  RNA_int_set(op->ptr, "deltay", -40);
488 
489  PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
490  if (RNA_property_is_set(op->ptr, prop) && RNA_property_boolean_get(op->ptr, prop)) {
491  ARegion *region = CTX_wm_region(C);
492  RNA_int_set(op->ptr, "deltay", region->v2d.mask.ymin - region->v2d.mask.ymax);
493  }
494 
495  /* apply movement, then we're done */
496  view_pan_apply(C, op);
497  view_pan_exit(op);
498 
499  return OPERATOR_FINISHED;
500 }
501 
503 {
504  /* identifiers */
505  ot->name = "Scroll Down";
506  ot->description = "Scroll the view down";
507  ot->idname = "VIEW2D_OT_scroll_down";
508 
509  /* api callbacks */
511  ot->poll = view_pan_poll;
512 
513  /* rna - must keep these in sync with the other operators */
514  RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
515  RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
516  RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll down one page");
517 }
518 
519 /* this operator only needs this single callback, where it calls the view_pan_*() methods */
521 {
522  /* initialize default settings (and validate if ok to run) */
523  view_pan_init(C, op);
524 
525  /* also, check if can pan in vertical axis */
526  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
527  if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
528  view_pan_exit(op);
529  return OPERATOR_PASS_THROUGH;
530  }
531 
532  /* set RNA-Props */
533  RNA_int_set(op->ptr, "deltax", 0);
534  RNA_int_set(op->ptr, "deltay", 40);
535 
536  PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
537  if (RNA_property_is_set(op->ptr, prop) && RNA_property_boolean_get(op->ptr, prop)) {
538  ARegion *region = CTX_wm_region(C);
539  RNA_int_set(op->ptr, "deltay", BLI_rcti_size_y(&region->v2d.mask));
540  }
541 
542  /* apply movement, then we're done */
543  view_pan_apply(C, op);
544  view_pan_exit(op);
545 
546  return OPERATOR_FINISHED;
547 }
548 
550 {
551  /* identifiers */
552  ot->name = "Scroll Up";
553  ot->description = "Scroll the view up";
554  ot->idname = "VIEW2D_OT_scroll_up";
555 
556  /* api callbacks */
558  ot->poll = view_pan_poll;
559 
560  /* rna - must keep these in sync with the other operators */
561  RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
562  RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
563  RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll up one page");
564 }
565 
568 /* -------------------------------------------------------------------- */
590  View2D *v2d; /* view2d we're operating in */
592 
593  /* needed for continuous zoom */
596 
597  int lastx, lasty; /* previous x/y values of mouse in window */
598  int invoke_event; /* event type that invoked, for modal exits */
599  float dx, dy; /* running tally of previous delta values (for obtaining final zoom) */
600  float mx_2d, my_2d; /* initial mouse location in v2d coords */
602 };
603 
608 static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
609 {
611 
612  r_do_zoom_xy[0] = true;
613  r_do_zoom_xy[1] = true;
614 
615  /* default not to zoom the sequencer vertically */
616  if (area && area->spacetype == SPACE_SEQ) {
617  ARegion *region = CTX_wm_region(C);
618 
619  if (region && region->regiontype == RGN_TYPE_WINDOW) {
620  r_do_zoom_xy[1] = false;
621  }
622  }
623 }
624 
625 /* check if step-zoom can be applied */
626 static bool view_zoom_poll(bContext *C)
627 {
628  ARegion *region = CTX_wm_region(C);
629 
630  /* check if there's a region in context to work with */
631  if (region == nullptr) {
632  return false;
633  }
634 
635  /* Do not show that in 3DView context. */
636  if (CTX_wm_region_view3d(C)) {
637  return false;
638  }
639 
640  View2D *v2d = &region->v2d;
641 
642  /* check that 2d-view is zoomable */
643  if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y)) {
644  return false;
645  }
646 
647  /* view is zoomable */
648  return true;
649 }
650 
651 /* initialize panning customdata */
653 {
654  /* Should've been checked before. */
656 
657  /* set custom-data for operator */
658  v2dViewZoomData *vzd = MEM_cnew<v2dViewZoomData>(__func__);
659  op->customdata = vzd;
660 
661  /* set pointers to owners */
662  vzd->region = CTX_wm_region(C);
663  vzd->v2d = &vzd->region->v2d;
664  /* False by default. Interactive callbacks (ie invoke()) can set it to true. */
665  vzd->zoom_to_mouse_pos = false;
666 
667  vzd->v2d->flag |= V2D_IS_NAVIGATING;
668 }
669 
670 /* apply transform to view (i.e. adjust 'cur' rect) */
672  v2dViewZoomData *vzd,
673  const float facx,
674  const float facy)
675 {
676  ARegion *region = CTX_wm_region(C);
677  View2D *v2d = &region->v2d;
678  const rctf cur_old = v2d->cur;
679  const int snap_test = ED_region_snap_size_test(region);
680 
681  /* calculate amount to move view by, ensuring symmetry so the
682  * old zoom level is restored after zooming back the same amount
683  */
684  float dx, dy;
685  if (facx >= 0.0f) {
686  dx = BLI_rctf_size_x(&v2d->cur) * facx;
687  dy = BLI_rctf_size_y(&v2d->cur) * facy;
688  }
689  else {
690  dx = (BLI_rctf_size_x(&v2d->cur) / (1.0f + 2.0f * facx)) * facx;
691  dy = (BLI_rctf_size_y(&v2d->cur) / (1.0f + 2.0f * facy)) * facy;
692  }
693 
694  /* only resize view on an axis if change is allowed */
695  if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
696  if (v2d->keepofs & V2D_LOCKOFS_X) {
697  v2d->cur.xmax -= 2 * dx;
698  }
699  else if (v2d->keepofs & V2D_KEEPOFS_X) {
700  if (v2d->align & V2D_ALIGN_NO_POS_X) {
701  v2d->cur.xmin += 2 * dx;
702  }
703  else {
704  v2d->cur.xmax -= 2 * dx;
705  }
706  }
707  else {
708 
709  v2d->cur.xmin += dx;
710  v2d->cur.xmax -= dx;
711 
712  if (vzd->zoom_to_mouse_pos) {
713  /* get zoom fac the same way as in
714  * ui_view2d_curRect_validate_resize - better keep in sync! */
715  const float zoomx = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur);
716 
717  /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
718  if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
719  IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom)) {
720  const float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old);
721  const float mval_faci = 1.0f - mval_fac;
722  const float ofs = (mval_fac * dx) - (mval_faci * dx);
723 
724  v2d->cur.xmin += ofs;
725  v2d->cur.xmax += ofs;
726  }
727  }
728  }
729  }
730  if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
731  if (v2d->keepofs & V2D_LOCKOFS_Y) {
732  v2d->cur.ymax -= 2 * dy;
733  }
734  else if (v2d->keepofs & V2D_KEEPOFS_Y) {
735  if (v2d->align & V2D_ALIGN_NO_POS_Y) {
736  v2d->cur.ymin += 2 * dy;
737  }
738  else {
739  v2d->cur.ymax -= 2 * dy;
740  }
741  }
742  else {
743 
744  v2d->cur.ymin += dy;
745  v2d->cur.ymax -= dy;
746 
747  if (vzd->zoom_to_mouse_pos) {
748  /* get zoom fac the same way as in
749  * ui_view2d_curRect_validate_resize - better keep in sync! */
750  const float zoomy = (float)(BLI_rcti_size_y(&v2d->mask) + 1) / BLI_rctf_size_y(&v2d->cur);
751 
752  /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
753  if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
754  IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom)) {
755  const float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old);
756  const float mval_faci = 1.0f - mval_fac;
757  const float ofs = (mval_fac * dy) - (mval_faci * dy);
758 
759  v2d->cur.ymin += ofs;
760  v2d->cur.ymax += ofs;
761  }
762  }
763  }
764  }
765 
766  /* Inform v2d about changes after this operation. */
768 
769  if (ED_region_snap_size_apply(region, snap_test)) {
773  }
774 
775  /* request updates to be done... */
778 }
779 
781 {
782  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
784  C, vzd, RNA_float_get(op->ptr, "zoomfacx"), RNA_float_get(op->ptr, "zoomfacy"));
785 }
786 
789 /* -------------------------------------------------------------------- */
793 /* Cleanup temp custom-data. */
795 {
797  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
798  vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
800 }
801 
802 /* this operator only needs this single callback, where it calls the view_zoom_*() methods */
804 {
805  if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
806  view_zoomdrag_init(C, op);
807  }
808 
809  bool do_zoom_xy[2];
810  view_zoom_axis_lock_defaults(C, do_zoom_xy);
811 
812  /* set RNA-Props - zooming in by uniform factor */
813  RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? 0.0375f : 0.0f);
814  RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? 0.0375f : 0.0f);
815 
816  /* apply movement, then we're done */
817  view_zoomstep_apply(C, op);
818 
819  view_zoomstep_exit(op);
820 
821  return OPERATOR_FINISHED;
822 }
823 
824 static int view_zoomin_invoke(bContext *C, wmOperator *op, const wmEvent *event)
825 {
826  view_zoomdrag_init(C, op);
827 
828  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
829 
830  if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
831  ARegion *region = CTX_wm_region(C);
832 
833  /* store initial mouse position (in view space) */
835  &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
836  vzd->zoom_to_mouse_pos = true;
837  }
838 
839  return view_zoomin_exec(C, op);
840 }
841 
843 {
844  PropertyRNA *prop;
845 
846  /* identifiers */
847  ot->name = "Zoom In";
848  ot->description = "Zoom in the view";
849  ot->idname = "VIEW2D_OT_zoom_in";
850 
851  /* api callbacks */
855 
856  /* rna - must keep these in sync with the other operators */
857  prop = RNA_def_float(
858  ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
860  prop = RNA_def_float(
861  ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
863 }
864 
865 /* this operator only needs this single callback, where it calls the view_zoom_*() methods */
867 {
868  bool do_zoom_xy[2];
869 
870  if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
871  view_zoomdrag_init(C, op);
872  }
873 
874  view_zoom_axis_lock_defaults(C, do_zoom_xy);
875 
876  /* set RNA-Props - zooming in by uniform factor */
877  RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? -0.0375f : 0.0f);
878  RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? -0.0375f : 0.0f);
879 
880  /* apply movement, then we're done */
881  view_zoomstep_apply(C, op);
882 
883  view_zoomstep_exit(op);
884 
885  return OPERATOR_FINISHED;
886 }
887 
888 static int view_zoomout_invoke(bContext *C, wmOperator *op, const wmEvent *event)
889 {
890  view_zoomdrag_init(C, op);
891 
892  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
893 
894  if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
895  ARegion *region = CTX_wm_region(C);
896 
897  /* store initial mouse position (in view space) */
899  &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
900  vzd->zoom_to_mouse_pos = true;
901  }
902 
903  return view_zoomout_exec(C, op);
904 }
905 
907 {
908  PropertyRNA *prop;
909 
910  /* identifiers */
911  ot->name = "Zoom Out";
912  ot->description = "Zoom out the view";
913  ot->idname = "VIEW2D_OT_zoom_out";
914 
915  /* api callbacks */
918 
920 
921  /* rna - must keep these in sync with the other operators */
922  prop = RNA_def_float(
923  ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
925  prop = RNA_def_float(
926  ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
928 }
929 
932 /* -------------------------------------------------------------------- */
943 /* apply transform to view (i.e. adjust 'cur' rect) */
945 {
946  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
947  View2D *v2d = vzd->v2d;
948  const int snap_test = ED_region_snap_size_test(vzd->region);
949 
950  const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
951  const bool zoom_to_pos = use_cursor_init && vzd->zoom_to_mouse_pos;
952 
953  /* get amount to move view by */
954  float dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac;
955  float dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac;
956 
957  /* Check if the 'timer' is initialized, as zooming with the trackpad
958  * never uses the "Continuous" zoom method, and the 'timer' is not initialized. */
959  if ((U.viewzoom == USER_ZOOM_CONTINUE) && vzd->timer) { /* XXX store this setting as RNA prop? */
960  const double time = PIL_check_seconds_timer();
961  const float time_step = (float)(time - vzd->timer_lastdraw);
962 
963  dx *= time_step * 5.0f;
964  dy *= time_step * 5.0f;
965 
966  vzd->timer_lastdraw = time;
967  }
968 
969  /* only move view on an axis if change is allowed */
970  if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
971  if (v2d->keepofs & V2D_LOCKOFS_X) {
972  v2d->cur.xmax -= 2 * dx;
973  }
974  else {
975  if (zoom_to_pos) {
976  const float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
977  const float mval_faci = 1.0f - mval_fac;
978  const float ofs = (mval_fac * dx) - (mval_faci * dx);
979 
980  v2d->cur.xmin += ofs + dx;
981  v2d->cur.xmax += ofs - dx;
982  }
983  else {
984  v2d->cur.xmin += dx;
985  v2d->cur.xmax -= dx;
986  }
987  }
988  }
989  if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
990  if (v2d->keepofs & V2D_LOCKOFS_Y) {
991  v2d->cur.ymax -= 2 * dy;
992  }
993  else {
994  if (zoom_to_pos) {
995  const float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
996  const float mval_faci = 1.0f - mval_fac;
997  const float ofs = (mval_fac * dy) - (mval_faci * dy);
998 
999  v2d->cur.ymin += ofs + dy;
1000  v2d->cur.ymax += ofs - dy;
1001  }
1002  else {
1003  v2d->cur.ymin += dy;
1004  v2d->cur.ymax -= dy;
1005  }
1006  }
1007  }
1008 
1009  /* Inform v2d about changes after this operation. */
1011 
1012  if (ED_region_snap_size_apply(vzd->region, snap_test)) {
1013  ScrArea *area = CTX_wm_area(C);
1016  }
1017 
1018  /* request updates to be done... */
1021 }
1022 
1023 /* Cleanup temp custom-data. */
1025 {
1027 
1028  if (op->customdata) {
1029  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1030  vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
1031 
1032  if (vzd->timer) {
1034  }
1035 
1036  MEM_freeN(op->customdata);
1037  op->customdata = nullptr;
1038  }
1039 }
1040 
1042 {
1043  view_zoomdrag_exit(C, op);
1044 }
1045 
1046 /* for 'redo' only, with no user input */
1048 {
1049  view_zoomdrag_init(C, op);
1050  view_zoomdrag_apply(C, op);
1051  view_zoomdrag_exit(C, op);
1052  return OPERATOR_FINISHED;
1053 }
1054 
1055 /* set up modal operator and relevant settings */
1056 static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1057 {
1058  wmWindow *window = CTX_wm_window(C);
1059 
1060  /* set up customdata */
1061  view_zoomdrag_init(C, op);
1062 
1063  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1064  View2D *v2d = vzd->v2d;
1065 
1066  if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
1067  ARegion *region = CTX_wm_region(C);
1068 
1069  /* Store initial mouse position (in view space). */
1071  &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
1072  vzd->zoom_to_mouse_pos = true;
1073  }
1074 
1075  if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
1076  vzd->lastx = event->prev_xy[0];
1077  vzd->lasty = event->prev_xy[1];
1078 
1079  float facx, facy;
1080  float zoomfac = 0.01f;
1081 
1082  /* Some view2d's (graph) don't have min/max zoom, or extreme ones. */
1083  if (v2d->maxzoom > 0.0f) {
1084  zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1085  }
1086 
1087  if (event->type == MOUSEPAN) {
1088  facx = zoomfac * WM_event_absolute_delta_x(event);
1089  facy = zoomfac * WM_event_absolute_delta_y(event);
1090 
1091  if (U.uiflag & USER_ZOOM_INVERT) {
1092  facx *= -1.0f;
1093  facy *= -1.0f;
1094  }
1095  }
1096  else { /* MOUSEZOOM */
1097  facx = facy = zoomfac * WM_event_absolute_delta_x(event);
1098  }
1099 
1100  /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1101  * any will be scaled uniformly. */
1102  if (((v2d->keepzoom & (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y)) == 0) &&
1103  (v2d->keepzoom & V2D_KEEPASPECT)) {
1104  if (U.uiflag & USER_ZOOM_HORIZ) {
1105  facy = 0.0f;
1106  }
1107  else {
1108  facx = 0.0f;
1109  }
1110  }
1111 
1112  /* support trackpad zoom to always zoom entirely - the v2d code uses portrait or
1113  * landscape exceptions */
1114  if (v2d->keepzoom & V2D_KEEPASPECT) {
1115  if (fabsf(facx) > fabsf(facy)) {
1116  facy = facx;
1117  }
1118  else {
1119  facx = facy;
1120  }
1121  }
1122 
1123  const float dx = facx * BLI_rctf_size_x(&v2d->cur);
1124  const float dy = facy * BLI_rctf_size_y(&v2d->cur);
1125 
1126  RNA_float_set(op->ptr, "deltax", dx);
1127  RNA_float_set(op->ptr, "deltay", dy);
1128 
1129  view_zoomdrag_apply(C, op);
1130  view_zoomdrag_exit(C, op);
1131  return OPERATOR_FINISHED;
1132  }
1133 
1134  /* set initial settings */
1135  vzd->lastx = event->xy[0];
1136  vzd->lasty = event->xy[1];
1137  RNA_float_set(op->ptr, "deltax", 0);
1138  RNA_float_set(op->ptr, "deltay", 0);
1139 
1140  /* for modal exit test */
1141  vzd->invoke_event = event->type;
1142 
1143  if (v2d->keepofs & V2D_LOCKOFS_X) {
1145  }
1146  else if (v2d->keepofs & V2D_LOCKOFS_Y) {
1148  }
1149  else {
1151  }
1152 
1153  /* add temp handler */
1155 
1156  if (U.viewzoom == USER_ZOOM_CONTINUE) {
1157  /* needs a timer to continue redrawing */
1158  vzd->timer = WM_event_add_timer(CTX_wm_manager(C), window, TIMER, 0.01f);
1160  }
1161 
1162  return OPERATOR_RUNNING_MODAL;
1163 }
1164 
1165 /* handle user input - calculations of mouse-movement need to be done here,
1166  * not in the apply callback! */
1167 static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event)
1168 {
1169  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1170  View2D *v2d = vzd->v2d;
1171 
1172  /* execute the events */
1173  if (event->type == TIMER && event->customdata == vzd->timer) {
1174  view_zoomdrag_apply(C, op);
1175  }
1176  else if (event->type == MOUSEMOVE) {
1177  float dx, dy;
1178  float zoomfac = 0.01f;
1179 
1180  /* some view2d's (graph) don't have min/max zoom, or extreme ones */
1181  if (v2d->maxzoom > 0.0f) {
1182  zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1183  }
1184 
1185  /* calculate new delta transform, based on zooming mode */
1186  if (U.viewzoom == USER_ZOOM_SCALE) {
1187  /* 'scale' zooming */
1188  float dist;
1189  float len_old[2];
1190  float len_new[2];
1191 
1192  /* x-axis transform */
1193  dist = BLI_rcti_size_x(&v2d->mask) / 2.0f;
1194  len_old[0] = zoomfac * fabsf(vzd->lastx - vzd->region->winrct.xmin - dist);
1195  len_new[0] = zoomfac * fabsf(event->xy[0] - vzd->region->winrct.xmin - dist);
1196 
1197  /* y-axis transform */
1198  dist = BLI_rcti_size_y(&v2d->mask) / 2.0f;
1199  len_old[1] = zoomfac * fabsf(vzd->lasty - vzd->region->winrct.ymin - dist);
1200  len_new[1] = zoomfac * fabsf(event->xy[1] - vzd->region->winrct.ymin - dist);
1201 
1202  /* Calculate distance */
1203  if (v2d->keepzoom & V2D_KEEPASPECT) {
1204  dist = len_v2(len_new) - len_v2(len_old);
1205  dx = dy = dist;
1206  }
1207  else {
1208  dx = len_new[0] - len_old[0];
1209  dy = len_new[1] - len_old[1];
1210  }
1211 
1212  dx *= BLI_rctf_size_x(&v2d->cur);
1213  dy *= BLI_rctf_size_y(&v2d->cur);
1214  }
1215  else { /* USER_ZOOM_CONTINUE or USER_ZOOM_DOLLY */
1216  float facx = zoomfac * (event->xy[0] - vzd->lastx);
1217  float facy = zoomfac * (event->xy[1] - vzd->lasty);
1218 
1219  /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1220  * any will be scaled uniformly */
1221  if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0 && (v2d->keepzoom & V2D_LOCKZOOM_Y) == 0 &&
1222  (v2d->keepzoom & V2D_KEEPASPECT)) {
1223  if (U.uiflag & USER_ZOOM_HORIZ) {
1224  facy = 0.0f;
1225  }
1226  else {
1227  facx = 0.0f;
1228  }
1229  }
1230 
1231  /* support zoom to always zoom entirely - the v2d code uses portrait or
1232  * landscape exceptions */
1233  if (v2d->keepzoom & V2D_KEEPASPECT) {
1234  if (fabsf(facx) > fabsf(facy)) {
1235  facy = facx;
1236  }
1237  else {
1238  facx = facy;
1239  }
1240  }
1241 
1242  dx = facx * BLI_rctf_size_x(&v2d->cur);
1243  dy = facy * BLI_rctf_size_y(&v2d->cur);
1244  }
1245 
1246  if (U.uiflag & USER_ZOOM_INVERT) {
1247  dx *= -1.0f;
1248  dy *= -1.0f;
1249  }
1250 
1251  /* set transform amount, and add current deltas to stored total delta (for redo) */
1252  RNA_float_set(op->ptr, "deltax", dx);
1253  RNA_float_set(op->ptr, "deltay", dy);
1254 
1255  vzd->dx += dx;
1256  vzd->dy += dy;
1257 
1258  /* Store mouse coordinates for next time, if not doing continuous zoom:
1259  * - Continuous zoom only depends on distance of mouse
1260  * to starting point to determine rate of change.
1261  */
1262  if (U.viewzoom != USER_ZOOM_CONTINUE) { /* XXX store this setting as RNA prop? */
1263  vzd->lastx = event->xy[0];
1264  vzd->lasty = event->xy[1];
1265  }
1266 
1267  /* apply zooming */
1268  view_zoomdrag_apply(C, op);
1269  }
1270  else if (ELEM(event->type, vzd->invoke_event, EVT_ESCKEY)) {
1271  if (event->val == KM_RELEASE) {
1272 
1273  /* for redo, store the overall deltas - need to respect zoom-locks here... */
1274  if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1275  RNA_float_set(op->ptr, "deltax", vzd->dx);
1276  }
1277  else {
1278  RNA_float_set(op->ptr, "deltax", 0);
1279  }
1280 
1281  if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1282  RNA_float_set(op->ptr, "deltay", vzd->dy);
1283  }
1284  else {
1285  RNA_float_set(op->ptr, "deltay", 0);
1286  }
1287 
1288  /* free customdata */
1289  view_zoomdrag_exit(C, op);
1291 
1292  return OPERATOR_FINISHED;
1293  }
1294  }
1295 
1296  return OPERATOR_RUNNING_MODAL;
1297 }
1298 
1300 {
1301  PropertyRNA *prop;
1302  /* identifiers */
1303  ot->name = "Zoom 2D View";
1304  ot->description = "Zoom in/out the view";
1305  ot->idname = "VIEW2D_OT_zoom";
1306 
1307  /* api callbacks */
1312 
1313  ot->poll = view_zoom_poll;
1314 
1315  /* operator is repeatable */
1317 
1318  /* rna - must keep these in sync with the other operators */
1319  prop = RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1321  prop = RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1323 
1325 }
1326 
1329 /* -------------------------------------------------------------------- */
1345 {
1346  ARegion *region = CTX_wm_region(C);
1347  View2D *v2d = &region->v2d;
1348  rctf cur_new = v2d->cur;
1349  const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
1350 
1351  /* convert coordinates of rect to 'tot' rect coordinates */
1352  rctf rect;
1354  UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
1355 
1356  /* check if zooming in/out view */
1357  const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
1358 
1359  if (zoom_in) {
1360  /* zoom in:
1361  * - 'cur' rect will be defined by the coordinates of the border region
1362  * - just set the 'cur' rect to have the same coordinates as the border region
1363  * if zoom is allowed to be changed
1364  */
1365  if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1366  cur_new.xmin = rect.xmin;
1367  cur_new.xmax = rect.xmax;
1368  }
1369  if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1370  cur_new.ymin = rect.ymin;
1371  cur_new.ymax = rect.ymax;
1372  }
1373  }
1374  else {
1375  /* zoom out:
1376  * - the current 'cur' rect coordinates are going to end up where the 'rect' ones are,
1377  * but the 'cur' rect coordinates will need to be adjusted to take in more of the view
1378  * - calculate zoom factor, and adjust using center-point
1379  */
1380  float zoom, center, size;
1381 
1382  /* TODO: is this zoom factor calculation valid?
1383  * It seems to produce same results every time... */
1384  if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1385  size = BLI_rctf_size_x(&cur_new);
1386  zoom = size / BLI_rctf_size_x(&rect);
1387  center = BLI_rctf_cent_x(&cur_new);
1388 
1389  cur_new.xmin = center - (size * zoom);
1390  cur_new.xmax = center + (size * zoom);
1391  }
1392  if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1393  size = BLI_rctf_size_y(&cur_new);
1394  zoom = size / BLI_rctf_size_y(&rect);
1395  center = BLI_rctf_cent_y(&cur_new);
1396 
1397  cur_new.ymin = center - (size * zoom);
1398  cur_new.ymax = center + (size * zoom);
1399  }
1400  }
1401 
1402  UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx);
1403 
1404  return OPERATOR_FINISHED;
1405 }
1406 
1408 {
1409  /* identifiers */
1410  ot->name = "Zoom to Border";
1411  ot->description = "Zoom in the view to the nearest item contained in the border";
1412  ot->idname = "VIEW2D_OT_zoom_border";
1413 
1414  /* api callbacks */
1419 
1420  ot->poll = view_zoom_poll;
1421 
1422  /* rna */
1424 }
1425 
1428 /* -------------------------------------------------------------------- */
1432 #ifdef WITH_INPUT_NDOF
1433 static int view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1434 {
1435  if (event->type != NDOF_MOTION) {
1436  return OPERATOR_CANCELLED;
1437  }
1438 
1439  const wmNDOFMotionData *ndof = static_cast<const wmNDOFMotionData *>(event->customdata);
1440 
1441  /* tune these until it feels right */
1442  const float zoom_sensitivity = 0.5f;
1443  const float speed = 10.0f; /* match view3d ortho */
1444  const bool has_translate = !is_zero_v2(ndof->tvec) && view_pan_poll(C);
1445  const bool has_zoom = (ndof->tvec[2] != 0.0f) && view_zoom_poll(C);
1446 
1447  if (has_translate) {
1448  float pan_vec[3];
1449 
1450  WM_event_ndof_pan_get(ndof, pan_vec, false);
1451 
1452  pan_vec[0] *= speed;
1453  pan_vec[1] *= speed;
1454 
1455  view_pan_init(C, op);
1456 
1457  v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
1458  view_pan_apply_ex(C, vpd, pan_vec[0], pan_vec[1]);
1459 
1460  view_pan_exit(op);
1461  }
1462 
1463  if (has_zoom) {
1464  float zoom_factor = zoom_sensitivity * ndof->dt * -ndof->tvec[2];
1465 
1466  bool do_zoom_xy[2];
1467 
1468  if (U.ndof_flag & NDOF_ZOOM_INVERT) {
1469  zoom_factor = -zoom_factor;
1470  }
1471 
1472  view_zoom_axis_lock_defaults(C, do_zoom_xy);
1473 
1474  view_zoomdrag_init(C, op);
1475 
1476  v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1478  C, vzd, do_zoom_xy[0] ? zoom_factor : 0.0f, do_zoom_xy[1] ? zoom_factor : 0.0f);
1479 
1480  view_zoomstep_exit(op);
1481  }
1482 
1483  return OPERATOR_FINISHED;
1484 }
1485 
1486 static void VIEW2D_OT_ndof(wmOperatorType *ot)
1487 {
1488  /* identifiers */
1489  ot->name = "NDOF Pan/Zoom";
1490  ot->idname = "VIEW2D_OT_ndof";
1491  ot->description = "Use a 3D mouse device to pan/zoom the view";
1492 
1493  /* api callbacks */
1494  ot->invoke = view2d_ndof_invoke;
1495  ot->poll = view2d_poll;
1496 
1497  /* flags */
1499 }
1500 #endif /* WITH_INPUT_NDOF */
1501 
1504 /* -------------------------------------------------------------------- */
1510 
1512 };
1513 
1522 static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
1523 {
1524  const float size_a[2] = {BLI_rctf_size_x(rect_a), BLI_rctf_size_y(rect_a)};
1525  const float size_b[2] = {BLI_rctf_size_x(rect_b), BLI_rctf_size_y(rect_b)};
1526  const float cent_a[2] = {BLI_rctf_cent_x(rect_a), BLI_rctf_cent_y(rect_a)};
1527  const float cent_b[2] = {BLI_rctf_cent_x(rect_b), BLI_rctf_cent_y(rect_b)};
1528 
1529  float fac_max = 0.0f;
1530 
1531  for (int i = 0; i < 2; i++) {
1532  /* axis translation normalized to scale */
1533  float tfac = fabsf(cent_a[i] - cent_b[i]) / min_ff(size_a[i], size_b[i]);
1534  fac_max = max_ff(fac_max, tfac);
1535  if (fac_max >= 1.0f) {
1536  break;
1537  }
1538 
1539  /* axis scale difference, x2 so doubling or half gives 1.0f */
1540  tfac = (1.0f - (min_ff(size_a[i], size_b[i]) / max_ff(size_a[i], size_b[i]))) * 2.0f;
1541  fac_max = max_ff(fac_max, tfac);
1542  if (fac_max >= 1.0f) {
1543  break;
1544  }
1545  }
1546  return min_ff(fac_max, 1.0f);
1547 }
1548 
1550  ARegion *region,
1551  const rctf *cur,
1552  const int smooth_viewtx)
1553 {
1555  wmWindow *win = CTX_wm_window(C);
1556 
1557  View2D *v2d = &region->v2d;
1558  SmoothView2DStore sms = {{0}};
1559  bool ok = false;
1560  float fac = 1.0f;
1561 
1562  /* initialize sms */
1563  sms.new_cur = v2d->cur;
1564 
1565  /* store the options we want to end with */
1566  if (cur) {
1567  sms.new_cur = *cur;
1568  }
1569 
1570  if (cur) {
1571  fac = smooth_view_rect_to_fac(&v2d->cur, cur);
1572  }
1573 
1574  if (smooth_viewtx && fac > FLT_EPSILON) {
1575  bool changed = false;
1576 
1577  if (BLI_rctf_compare(&sms.new_cur, &v2d->cur, FLT_EPSILON) == false) {
1578  changed = true;
1579  }
1580 
1581  /* The new view is different from the old one
1582  * so animate the view */
1583  if (changed) {
1584  sms.orig_cur = v2d->cur;
1585 
1586  sms.time_allowed = (double)smooth_viewtx / 1000.0;
1587 
1588  /* scale the time allowed the change in view */
1589  sms.time_allowed *= (double)fac;
1590 
1591  /* keep track of running timer! */
1592  if (v2d->sms == nullptr) {
1593  v2d->sms = MEM_new<SmoothView2DStore>(__func__);
1594  }
1595  *v2d->sms = sms;
1596  if (v2d->smooth_timer) {
1597  WM_event_remove_timer(wm, win, v2d->smooth_timer);
1598  }
1599  /* TIMER1 is hard-coded in key-map. */
1600  v2d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0);
1601 
1602  ok = true;
1603  }
1604  }
1605 
1606  /* if we get here nothing happens */
1607  if (ok == false) {
1608  v2d->cur = sms.new_cur;
1609 
1613  }
1614 }
1615 
1616 /* only meant for timer usage */
1618 {
1619  wmWindow *win = CTX_wm_window(C);
1620  ARegion *region = CTX_wm_region(C);
1621  View2D *v2d = &region->v2d;
1622  SmoothView2DStore *sms = v2d->sms;
1623 
1624  /* escape if not our timer */
1625  if (v2d->smooth_timer == nullptr || v2d->smooth_timer != event->customdata) {
1626  return OPERATOR_PASS_THROUGH;
1627  }
1628 
1629  float step;
1630  if (sms->time_allowed != 0.0) {
1631  step = (float)((v2d->smooth_timer->duration) / sms->time_allowed);
1632  }
1633  else {
1634  step = 1.0f;
1635  }
1636 
1637  /* end timer */
1638  if (step >= 1.0f) {
1639  v2d->cur = sms->new_cur;
1640 
1641  MEM_freeN(v2d->sms);
1642  v2d->sms = nullptr;
1643 
1645  v2d->smooth_timer = nullptr;
1646 
1647  /* Event handling won't know if a UI item has been moved under the pointer. */
1649  }
1650  else {
1651  /* ease in/out */
1652  step = (3.0f * step * step - 2.0f * step * step * step);
1653 
1654  BLI_rctf_interp(&v2d->cur, &sms->orig_cur, &sms->new_cur, step);
1655  }
1656 
1660 
1661  if (v2d->sms == nullptr) {
1663  }
1664 
1665  return OPERATOR_FINISHED;
1666 }
1667 
1669 {
1670  /* identifiers */
1671  ot->name = "Smooth View 2D";
1672  ot->idname = "VIEW2D_OT_smoothview";
1673 
1674  /* api callbacks */
1676  ot->poll = view2d_poll;
1677 
1678  /* flags */
1679  ot->flag = OPTYPE_INTERNAL;
1680 
1681  /* rna */
1683 }
1684 
1687 /* -------------------------------------------------------------------- */
1701 /* customdata for scroller-invoke data */
1707 
1709  char scroller;
1710 
1711  /* XXX find some way to provide visual feedback of this (active color?) */
1713  short zone;
1714 
1716  float fac;
1718  float fac_round;
1720  float delta;
1721 
1726 
1728  int lastx, lasty;
1729 };
1730 
1740 struct View2DScrollers {
1741  int vert_min, vert_max; /* vertical scrollbar */
1742  int hor_min, hor_max; /* horizontal scrollbar */
1743 
1744  /* These values are written into, even if we don't use them. */
1746 };
1747 
1748 /* quick enum for vsm->zone (scroller handles) */
1749 enum {
1755 } /*eV2DScrollerHandle_Zone*/;
1756 
1764 static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
1765 {
1766  /* firstly, check if
1767  * - 'bubble' fills entire scroller
1768  * - 'bubble' completely out of view on either side
1769  */
1770  bool in_view = true;
1771  if (sh_min <= sc_min && sc_max <= sh_max) {
1772  in_view = false;
1773  }
1774  else if (sh_max <= sc_min || sc_max <= sh_min) {
1775  in_view = false;
1776  }
1777 
1778  if (!in_view) {
1779  return SCROLLHANDLE_BAR;
1780  }
1781 
1782  /* check if mouse is in or past either handle */
1783  /* TODO: check if these extents are still valid or not */
1784  bool in_max = ((mouse >= (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1785  (mouse <= (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1786  bool in_min = ((mouse <= (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1787  (mouse >= (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1788  bool in_bar = ((mouse < (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1789  (mouse > (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1790  const bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1791  const bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1792 
1793  if (in_bar) {
1794  return SCROLLHANDLE_BAR;
1795  }
1796  if (in_max) {
1797  return SCROLLHANDLE_MAX;
1798  }
1799  if (in_min) {
1800  return SCROLLHANDLE_MIN;
1801  }
1802  if (out_min) {
1803  return SCROLLHANDLE_MIN_OUTSIDE;
1804  }
1805  if (out_max) {
1806  return SCROLLHANDLE_MAX_OUTSIDE;
1807  }
1808 
1809  /* unlikely to happen, though we just cover it in case */
1810  return SCROLLHANDLE_BAR;
1811 }
1812 
1814 {
1815  if (!view2d_poll(C)) {
1816  return false;
1817  }
1818 
1819  wmWindow *win = CTX_wm_window(C);
1820  ARegion *region = CTX_wm_region(C);
1821  View2D *v2d = &region->v2d;
1822  wmEvent *event = win->eventstate;
1823 
1824  /* check if mouse in scrollbars, if they're enabled */
1825  return (UI_view2d_mouse_in_scrollers(region, v2d, event->xy) != 0);
1826 }
1827 
1828 /* initialize customdata for scroller manipulation operator */
1830  wmOperator *op,
1831  const wmEvent *event,
1832  const char in_scroller)
1833 {
1834  ARegion *region = CTX_wm_region(C);
1835  View2D *v2d = &region->v2d;
1836 
1837  /* set custom-data for operator */
1838  v2dScrollerMove *vsm = MEM_cnew<v2dScrollerMove>(__func__);
1839  op->customdata = vsm;
1840 
1841  /* set general data */
1842  vsm->v2d = v2d;
1843  vsm->region = region;
1844  vsm->scroller = in_scroller;
1845 
1846  /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1847  vsm->lastx = event->xy[0];
1848  vsm->lasty = event->xy[1];
1849  /* 'zone' depends on where mouse is relative to bubble
1850  * - zooming must be allowed on this axis, otherwise, default to pan
1851  */
1852  View2DScrollers scrollers;
1853  /* Some Editors like the Filebrowser or Spreadsheet already set up custom masks for scrollbars
1854  * (they dont cover the whole region width or height), these need to be considered, otherwise
1855  * coords for `mouse_in_scroller_handle` later are not compatible. */
1856  rcti scroller_mask = v2d->hor;
1857  BLI_rcti_union(&scroller_mask, &v2d->vert);
1858  UI_view2d_scrollers_calc(v2d, &scroller_mask, &scrollers);
1859 
1860  /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
1861  * moving the scroll bars has far too little effect and the view can get stuck T31476. */
1862  rctf tot_cur_union = v2d->tot;
1863  BLI_rctf_union(&tot_cur_union, &v2d->cur);
1864 
1865  if (in_scroller == 'h') {
1866  /* horizontal scroller - calculate adjustment factor first */
1867  const float mask_size = (float)BLI_rcti_size_x(&v2d->hor);
1868  vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size;
1869 
1870  /* pixel rounding */
1871  vsm->fac_round = (BLI_rctf_size_x(&v2d->cur)) / (float)(BLI_rcti_size_x(&region->winrct) + 1);
1872 
1873  /* get 'zone' (i.e. which part of scroller is activated) */
1875  event->mval[0], v2d->hor.xmin, v2d->hor.xmax, scrollers.hor_min, scrollers.hor_max);
1876 
1878  /* default to scroll, as handles not usable */
1879  vsm->zone = SCROLLHANDLE_BAR;
1880  }
1881 
1882  vsm->scrollbarwidth = scrollers.hor_max - scrollers.hor_min;
1883  vsm->scrollbar_orig = ((scrollers.hor_max + scrollers.hor_min) / 2) + region->winrct.xmin;
1884  }
1885  else {
1886  /* vertical scroller - calculate adjustment factor first */
1887  const float mask_size = (float)BLI_rcti_size_y(&v2d->vert);
1888  vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size;
1889 
1890  /* pixel rounding */
1891  vsm->fac_round = (BLI_rctf_size_y(&v2d->cur)) / (float)(BLI_rcti_size_y(&region->winrct) + 1);
1892 
1893  /* get 'zone' (i.e. which part of scroller is activated) */
1895  event->mval[1], v2d->vert.ymin, v2d->vert.ymax, scrollers.vert_min, scrollers.vert_max);
1896 
1898  /* default to scroll, as handles not usable */
1899  vsm->zone = SCROLLHANDLE_BAR;
1900  }
1901 
1902  vsm->scrollbarwidth = scrollers.vert_max - scrollers.vert_min;
1903  vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin;
1904  }
1905 
1906  vsm->v2d->flag |= V2D_IS_NAVIGATING;
1907 
1909 }
1910 
1911 /* Cleanup temp custom-data. */
1913 {
1914  if (op->customdata) {
1915  v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1916 
1918  vsm->v2d->flag &= ~V2D_IS_NAVIGATING;
1919 
1920  MEM_freeN(op->customdata);
1921  op->customdata = nullptr;
1922 
1924  }
1925 }
1926 
1928 {
1930 }
1931 
1932 /* apply transform to view (i.e. adjust 'cur' rect) */
1934 {
1935  v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1936  View2D *v2d = vsm->v2d;
1937 
1938  /* calculate amount to move view by */
1939  float temp = vsm->fac * vsm->delta;
1940 
1941  /* round to pixel */
1942  temp = roundf(temp / vsm->fac_round) * vsm->fac_round;
1943 
1944  /* type of movement */
1945  switch (vsm->zone) {
1946  case SCROLLHANDLE_MIN:
1947  /* only expand view on axis if zoom is allowed */
1948  if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
1949  v2d->cur.xmin -= temp;
1950  }
1951  if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
1952  v2d->cur.ymin -= temp;
1953  }
1954  break;
1955 
1956  case SCROLLHANDLE_MAX:
1957 
1958  /* only expand view on axis if zoom is allowed */
1959  if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
1960  v2d->cur.xmax += temp;
1961  }
1962  if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
1963  v2d->cur.ymax += temp;
1964  }
1965  break;
1966 
1969  case SCROLLHANDLE_BAR:
1970  default:
1971  /* only move view on an axis if panning is allowed */
1972  if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
1973  v2d->cur.xmin += temp;
1974  v2d->cur.xmax += temp;
1975  }
1976  if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
1977  v2d->cur.ymin += temp;
1978  v2d->cur.ymax += temp;
1979  }
1980  break;
1981  }
1982 
1983  /* Inform v2d about changes after this operation. */
1985 
1986  /* request updates to be done... */
1989 }
1990 
1995 static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *event)
1996 {
1997  v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1998 
1999  /* execute the events */
2000  switch (event->type) {
2001  case MOUSEMOVE: {
2002  /* calculate new delta transform, then store mouse-coordinates for next-time */
2004  /* if using bar (i.e. 'panning') or 'max' zoom widget */
2005  switch (vsm->scroller) {
2006  case 'h': /* horizontal scroller - so only horizontal movement
2007  * ('cur' moves opposite to mouse) */
2008  vsm->delta = (float)(event->xy[0] - vsm->lastx);
2009  break;
2010  case 'v': /* vertical scroller - so only vertical movement
2011  * ('cur' moves opposite to mouse) */
2012  vsm->delta = (float)(event->xy[1] - vsm->lasty);
2013  break;
2014  }
2015  }
2016  else if (vsm->zone == SCROLLHANDLE_MIN) {
2017  /* using 'min' zoom widget */
2018  switch (vsm->scroller) {
2019  case 'h': /* horizontal scroller - so only horizontal movement
2020  * ('cur' moves with mouse) */
2021  vsm->delta = (float)(vsm->lastx - event->xy[0]);
2022  break;
2023  case 'v': /* vertical scroller - so only vertical movement
2024  * ('cur' moves with to mouse) */
2025  vsm->delta = (float)(vsm->lasty - event->xy[1]);
2026  break;
2027  }
2028  }
2029 
2030  /* store previous coordinates */
2031  vsm->lastx = event->xy[0];
2032  vsm->lasty = event->xy[1];
2033 
2035  break;
2036  }
2037  case LEFTMOUSE:
2038  case MIDDLEMOUSE:
2039  if (event->val == KM_RELEASE) {
2040  /* single-click was in empty space outside bubble, so scroll by 1 'page' */
2042  if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE) {
2043  vsm->delta = -vsm->scrollbarwidth * 0.8f;
2044  }
2045  else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE) {
2046  vsm->delta = vsm->scrollbarwidth * 0.8f;
2047  }
2048 
2051  return OPERATOR_FINISHED;
2052  }
2053 
2054  /* Otherwise, end the drag action. */
2055  if (vsm->lastx || vsm->lasty) {
2057  return OPERATOR_FINISHED;
2058  }
2059  }
2060  break;
2061  }
2062 
2063  return OPERATOR_RUNNING_MODAL;
2064 }
2065 
2066 /* a click (or click drag in progress)
2067  * should have occurred, so check if it happened in scrollbar */
2068 static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2069 {
2070  ARegion *region = CTX_wm_region(C);
2071  View2D *v2d = &region->v2d;
2072 
2073  /* check if mouse in scrollbars, if they're enabled */
2074  const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy);
2075 
2076  /* if in a scroller, init customdata then set modal handler which will
2077  * catch mouse-down to start doing useful stuff */
2078  if (in_scroller) {
2079  /* initialize customdata */
2080  scroller_activate_init(C, op, event, in_scroller);
2082 
2083  /* Support for quick jump to location - GTK and QT do this on Linux. */
2084  if (event->type == MIDDLEMOUSE) {
2085  switch (vsm->scroller) {
2086  case 'h': /* horizontal scroller - so only horizontal movement
2087  * ('cur' moves opposite to mouse) */
2088  vsm->delta = (float)(event->xy[0] - vsm->scrollbar_orig);
2089  break;
2090  case 'v': /* vertical scroller - so only vertical movement
2091  * ('cur' moves opposite to mouse) */
2092  vsm->delta = (float)(event->xy[1] - vsm->scrollbar_orig);
2093  break;
2094  }
2096 
2097  vsm->zone = SCROLLHANDLE_BAR;
2098  }
2099 
2100  /* check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
2101  * NOTE: see view2d.c for latest conditions, and keep this in sync with that
2102  */
2104  if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) == 0) ||
2105  ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) == 0)) {
2106  /* switch to bar (i.e. no scaling gets handled) */
2107  vsm->zone = SCROLLHANDLE_BAR;
2108  }
2109  }
2110 
2111  /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
2112  if (vsm->zone == SCROLLHANDLE_BAR) {
2113  if (((vsm->scroller == 'h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
2114  ((vsm->scroller == 'v') && (v2d->keepofs & V2D_LOCKOFS_Y))) {
2115  /* free customdata initialized */
2117 
2118  /* can't catch this event for ourselves, so let it go to someone else? */
2119  return OPERATOR_PASS_THROUGH;
2120  }
2121  }
2122 
2123  /* zone is also inappropriate if scroller is not visible... */
2124  if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_FULLR)) ||
2125  ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_FULLR))) {
2126  /* free customdata initialized */
2128 
2129  /* can't catch this event for ourselves, so let it go to someone else? */
2130  /* XXX NOTE: if handlers use mask rect to clip input, input will fail for this case. */
2131  return OPERATOR_PASS_THROUGH;
2132  }
2133 
2134  /* activate the scroller */
2135  if (vsm->scroller == 'h') {
2137  }
2138  else {
2140  }
2141 
2142  /* still ok, so can add */
2144  return OPERATOR_RUNNING_MODAL;
2145  }
2146 
2147  /* not in scroller, so nothing happened...
2148  * (pass through let's something else catch event) */
2149  return OPERATOR_PASS_THROUGH;
2150 }
2151 
2152 /* LMB-Drag in Scrollers - not repeatable operator! */
2154 {
2155  /* identifiers */
2156  ot->name = "Scroller Activate";
2157  ot->description = "Scroll view by mouse click and drag";
2158  ot->idname = "VIEW2D_OT_scroller_activate";
2159 
2160  /* flags */
2161  ot->flag = OPTYPE_BLOCKING;
2162 
2163  /* api callbacks */
2167 
2169 }
2170 
2173 /* -------------------------------------------------------------------- */
2178 {
2179  const uiStyle *style = UI_style_get();
2180  ARegion *region = CTX_wm_region(C);
2181  View2D *v2d = &region->v2d;
2182  const int snap_test = ED_region_snap_size_test(region);
2183 
2184  /* zoom 1.0 */
2185  const int winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1);
2186  const int winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1);
2187 
2188  v2d->cur.xmax = v2d->cur.xmin + winx;
2189  v2d->cur.ymax = v2d->cur.ymin + winy;
2190 
2191  /* align */
2192  if (v2d->align) {
2193  /* posx and negx flags are mutually exclusive, so watch out */
2194  if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
2195  v2d->cur.xmax = 0.0f;
2196  v2d->cur.xmin = -winx * style->panelzoom;
2197  }
2198  else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
2199  v2d->cur.xmax = winx * style->panelzoom;
2200  v2d->cur.xmin = 0.0f;
2201  }
2202 
2203  /* - posx and negx flags are mutually exclusive, so watch out */
2204  if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
2205  v2d->cur.ymax = 0.0f;
2206  v2d->cur.ymin = -winy * style->panelzoom;
2207  }
2208  else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
2209  v2d->cur.ymax = winy * style->panelzoom;
2210  v2d->cur.ymin = 0.0f;
2211  }
2212  }
2213 
2214  /* Inform v2d about changes after this operation. */
2216 
2217  if (ED_region_snap_size_apply(region, snap_test)) {
2218  ScrArea *area = CTX_wm_area(C);
2221  }
2222 
2223  /* request updates to be done... */
2224  ED_region_tag_redraw(region);
2226 
2228 
2229  return OPERATOR_FINISHED;
2230 }
2231 
2233 {
2234  /* identifiers */
2235  ot->name = "Reset View";
2236  ot->description = "Reset the view";
2237  ot->idname = "VIEW2D_OT_reset";
2238 
2239  /* api callbacks */
2240  ot->exec = reset_exec;
2241  ot->poll = view2d_poll;
2242 }
2243 
2246 /* -------------------------------------------------------------------- */
2251 {
2254 
2259 
2262 
2265 
2266 #ifdef WITH_INPUT_NDOF
2267  WM_operatortype_append(VIEW2D_OT_ndof);
2268 #endif
2269 
2271 
2273 
2275 }
2276 
2278 {
2279  WM_keymap_ensure(keyconf, "View2D", 0, 0);
2280 }
2281 
typedef float(TangentPoint)[2]
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct bScreen * CTX_wm_screen(const bContext *C)
Definition: context.c:733
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct RegionView3D * CTX_wm_region_view3d(const bContext *C)
Definition: context.c:793
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
#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 bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2(const float a[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:190
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:181
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition: BLI_rect.h:177
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition: BLI_rect.h:186
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:194
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:198
bool BLI_rctf_compare(const struct rctf *rect_a, const struct rctf *rect_b, float limit)
#define UNUSED(x)
#define ELEM(...)
#define IN_RANGE_INCL(a, b, c)
typedef double(DMatrix)[4][4]
@ RGN_TYPE_WINDOW
@ SPACE_SEQ
@ USER_ZOOM_INVERT
@ USER_ZOOM_TO_MOUSEPOS
@ USER_ZOOM_HORIZ
@ USER_ZOOM_SCALE
@ USER_ZOOM_CONTINUE
@ NDOF_ZOOM_INVERT
@ V2D_LOCKOFS_X
@ V2D_LOCKOFS_Y
@ V2D_KEEPOFS_Y
@ V2D_KEEPOFS_X
@ V2D_SCROLL_V_ACTIVE
@ V2D_SCROLL_H_ACTIVE
@ V2D_LIMITZOOM
@ V2D_LOCKZOOM_X
@ V2D_KEEPASPECT
@ V2D_LOCKZOOM_Y
@ V2D_IS_NAVIGATING
@ V2D_IS_INIT
@ 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.c:729
int ED_region_snap_size_test(const struct ARegion *region)
void ED_region_tag_redraw_no_rebuild(struct ARegion *region)
Definition: area.c:674
bool ED_region_snap_size_apply(struct ARegion *region, int snap_flag)
Definition: area.c:3872
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
NSNotificationCenter * center
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
Platform independent time functions.
@ PROP_HIDDEN
Definition: RNA_types.h:216
#define C
Definition: RandGen.cpp:25
const struct uiStyle * UI_style_get(void)
#define V2D_LOCK_COPY
Definition: UI_view2d.h:82
void UI_view2d_edge_pan_operator_init(struct bContext *C, struct View2DEdgePanData *vpd, struct wmOperator *op)
bool UI_view2d_edge_pan_poll(struct bContext *C)
void UI_view2d_region_to_view_rctf(const struct View2D *v2d, const struct rctf *rect_src, struct rctf *rect_dst) ATTR_NONNULL()
char char UI_view2d_mouse_in_scrollers(const struct ARegion *region, const struct View2D *v2d, const int xy[2]) ATTR_NONNULL(1
#define V2D_SCROLL_HANDLE_SIZE_HOTSPOT
Definition: UI_view2d.h:72
void UI_view2d_region_to_view(const struct View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
void UI_view2d_zoom_cache_reset(void)
Definition: view2d.cc:1027
void UI_view2d_edge_pan_operator_properties(struct wmOperatorType *ot)
void UI_view2d_sync(struct bScreen *screen, struct ScrArea *area, struct View2D *v2dcur, int flag)
Definition: view2d.cc:851
void UI_view2d_curRect_changed(const struct bContext *C, struct View2D *v2d)
void void UI_view2d_edge_pan_apply_event(struct bContext *C, struct View2DEdgePanData *vpd, const struct wmEvent *event)
void UI_view2d_scrollers_calc(struct View2D *v2d, const struct rcti *mask_custom, struct View2DScrollers *r_scrollers)
@ KM_PRESS
Definition: WM_types.h:267
@ KM_RELEASE
Definition: WM_types.h:268
@ OPTYPE_INTERNAL
Definition: WM_types.h:168
@ OPTYPE_BLOCKING
Definition: WM_types.h:150
@ OPTYPE_LOCK_BYPASS
Definition: WM_types.h:171
@ OPTYPE_GRAB_CURSOR_XY
Definition: WM_types.h:154
#define NC_SCREEN
Definition: WM_types.h:327
#define NA_EDITED
Definition: WM_types.h:523
@ WM_OP_INVOKE_DEFAULT
Definition: WM_types.h:201
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
unsigned int U
Definition: btGjkEpa3.h:78
double time
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
#define fabsf(x)
Definition: metal/compat.h:219
static void area(int d1, int d2, int e1, int e2, float weights[2])
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:5271
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
Definition: rna_access.c:4921
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:717
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2153
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4910
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
Definition: rna_access.c:4968
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:3836
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1490
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3597
short regiontype
struct View2D * v2d
Definition: UI_view2d.h:474
short flag
float minzoom
struct SmoothView2DStore * sms
short align
short scroll_ui
short keepzoom
struct wmTimer * smooth_timer
short keepofs
short scroll
float maxzoom
float xmax
Definition: DNA_vec_types.h:69
float xmin
Definition: DNA_vec_types.h:69
float ymax
Definition: DNA_vec_types.h:70
float ymin
Definition: DNA_vec_types.h:70
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63
float panelzoom
ARegion * region
Definition: view2d_ops.cc:1706
bScreen * screen
Definition: view2d_ops.cc:66
View2D * v2d
Definition: view2d_ops.cc:72
ARegion * region
Definition: view2d_ops.cc:70
ScrArea * area
Definition: view2d_ops.cc:68
double edge_pan_last_time
Definition: view2d_ops.cc:89
short in_scroller
Definition: view2d_ops.cc:86
double edge_pan_start_time_x
Definition: view2d_ops.cc:90
double edge_pan_start_time_y
Definition: view2d_ops.cc:90
wmTimer * timer
Definition: view2d_ops.cc:594
double timer_lastdraw
Definition: view2d_ops.cc:595
ARegion * region
Definition: view2d_ops.cc:591
bool zoom_to_mouse_pos
Definition: view2d_ops.cc:601
short val
Definition: WM_types.h:680
int xy[2]
Definition: WM_types.h:682
int mval[2]
Definition: WM_types.h:684
int prev_xy[2]
Definition: WM_types.h:728
short type
Definition: WM_types.h:678
void * customdata
Definition: WM_types.h:715
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:935
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:927
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
struct PointerRNA * ptr
double duration
Definition: WM_types.h:872
struct wmEvent * eventstate
double PIL_check_seconds_timer(void)
Definition: time.c:64
static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
Definition: view2d_ops.cc:549
static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
Definition: view2d_ops.cc:1617
static int view_scrollup_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:520
static void VIEW2D_OT_scroll_left(wmOperatorType *ot)
Definition: view2d_ops.cc:456
static void view_pan_cancel(bContext *UNUSED(C), wmOperator *op)
Definition: view2d_ops.cc:296
static void scroller_activate_apply(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:1933
void UI_view2d_smooth_view(const bContext *C, ARegion *region, const rctf *cur, const int smooth_viewtx)
Definition: view2d_ops.cc:1549
static bool view_zoom_poll(bContext *C)
Definition: view2d_ops.cc:626
static int view_scrolldown_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:473
static int view_zoomout_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:888
static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
Definition: view2d_ops.cc:2153
static int view_zoomin_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:824
static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:344
static int view_scrollleft_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:433
static void VIEW2D_OT_scroll_down(wmOperatorType *ot)
Definition: view2d_ops.cc:502
static void view_zoomstep_apply(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:780
static void VIEW2D_OT_pan(wmOperatorType *ot)
Definition: view2d_ops.cc:301
static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const float facx, const float facy)
Definition: view2d_ops.cc:671
@ SCROLLHANDLE_BAR
Definition: view2d_ops.cc:1751
@ SCROLLHANDLE_MIN_OUTSIDE
Definition: view2d_ops.cc:1753
@ SCROLLHANDLE_MAX
Definition: view2d_ops.cc:1752
@ SCROLLHANDLE_MIN
Definition: view2d_ops.cc:1750
@ SCROLLHANDLE_MAX_OUTSIDE
Definition: view2d_ops.cc:1754
static void VIEW2D_OT_zoom_out(wmOperatorType *ot)
Definition: view2d_ops.cc:906
static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy)
Definition: view2d_ops.cc:139
static void scroller_activate_exit(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:1912
static int view_zoomin_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:803
static int view_pan_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:191
static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
Definition: view2d_ops.cc:1407
static void view_pan_exit(wmOperator *op)
Definition: view2d_ops.cc:177
static int view_scrollright_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:393
static void VIEW2D_OT_zoom(wmOperatorType *ot)
Definition: view2d_ops.cc:1299
static bool view2d_poll(bContext *C)
Definition: view2d_ops.cc:39
static void view_zoomdrag_exit(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:1024
static void VIEW2D_OT_zoom_in(wmOperatorType *ot)
Definition: view2d_ops.cc:842
static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:1167
static void view_zoomdrag_apply(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:944
static void scroller_activate_cancel(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:1927
void ED_keymap_view2d(wmKeyConfig *keyconf)
Definition: view2d_ops.cc:2277
static void view_edge_pan_cancel(bContext *UNUSED(C), wmOperator *op)
Definition: view2d_ops.cc:361
static int view_zoomout_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:866
static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
Definition: view2d_ops.cc:1522
static int view_borderzoom_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:1344
static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:1056
static void view_pan_init(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:114
static int view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
Definition: view2d_ops.cc:333
static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:2068
static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:200
static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:245
void ED_operatortypes_view2d(void)
Definition: view2d_ops.cc:2250
static void VIEW2D_OT_scroll_right(wmOperatorType *ot)
Definition: view2d_ops.cc:416
static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
Definition: view2d_ops.cc:1764
static void view_pan_apply(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:169
static bool view_pan_poll(bContext *C)
Definition: view2d_ops.cc:93
static void VIEW2D_OT_edge_pan(wmOperatorType *ot)
Definition: view2d_ops.cc:368
static void view_zoomdrag_cancel(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:1041
static void VIEW2D_OT_reset(wmOperatorType *ot)
Definition: view2d_ops.cc:2232
static int view_zoomdrag_exec(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:1047
static bool scroller_activate_poll(bContext *C)
Definition: view2d_ops.cc:1813
static void VIEW2D_OT_smoothview(wmOperatorType *ot)
Definition: view2d_ops.cc:1668
static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition: view2d_ops.cc:1995
static int reset_exec(bContext *C, wmOperator *UNUSED(op))
Definition: view2d_ops.cc:2177
static void scroller_activate_init(bContext *C, wmOperator *op, const wmEvent *event, const char in_scroller)
Definition: view2d_ops.cc:1829
static void view_zoomdrag_init(bContext *C, wmOperator *op)
Definition: view2d_ops.cc:652
static void view_zoomstep_exit(wmOperator *op)
Definition: view2d_ops.cc:794
static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
Definition: view2d_ops.cc:608
void WM_cursor_modal_set(wmWindow *win, int val)
Definition: wm_cursors.c:191
void WM_cursor_modal_restore(wmWindow *win)
Definition: wm_cursors.c:200
@ WM_CURSOR_NSEW_SCROLL
Definition: wm_cursors.h:51
@ WM_CURSOR_NS_SCROLL
Definition: wm_cursors.h:52
@ WM_CURSOR_EW_SCROLL
Definition: wm_cursors.h:53
int WM_event_absolute_delta_y(const struct wmEvent *event)
int WM_event_absolute_delta_x(const struct 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)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext 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.c:3479
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition: wm_keymap.c:852
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_use_cursor_init(wmOperatorType *ot)
void WM_operator_properties_border_to_rctf(struct wmOperator *op, rctf *rect)
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)
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1682
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1630