Blender V4.3
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
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/* -------------------------------------------------------------------- */
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) / page_size);
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
68/* -------------------------------------------------------------------- */
93
95 float facx, facy;
96
97 /* options for version 1 */
104
107
110
111 /* View2D Edge Panning */
114};
115
116static bool view_pan_poll(bContext *C)
117{
118 ARegion *region = CTX_wm_region(C);
119
120 /* check if there's a region in context to work with */
121 if (region == nullptr) {
122 return false;
123 }
124
125 View2D *v2d = &region->v2d;
126
127 /* check that 2d-view can pan */
128 if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) {
129 return false;
130 }
131
132 /* view can pan */
133 return true;
134}
135
136/* initialize panning customdata */
138{
139 /* Should've been checked before. */
141
142 /* set custom-data for operator */
143 v2dViewPanData *vpd = MEM_cnew<v2dViewPanData>(__func__);
144 op->customdata = vpd;
145
146 /* set pointers to owners */
147 vpd->screen = CTX_wm_screen(C);
148 vpd->area = CTX_wm_area(C);
149 vpd->region = CTX_wm_region(C);
150 vpd->v2d = &vpd->region->v2d;
151
152 /* calculate translation factor - based on size of view */
153 const float winx = float(BLI_rcti_size_x(&vpd->region->winrct) + 1);
154 const float winy = float(BLI_rcti_size_y(&vpd->region->winrct) + 1);
155 vpd->facx = BLI_rctf_size_x(&vpd->v2d->cur) / winx;
156 vpd->facy = BLI_rctf_size_y(&vpd->v2d->cur) / winy;
157
158 vpd->v2d->flag |= V2D_IS_NAVIGATING;
159
160 vpd->do_category_scroll = false;
161}
162
163/* apply transform to view (i.e. adjust 'cur' rect) */
164static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy)
165{
166 View2D *v2d = vpd->v2d;
167
168 /* calculate amount to move view by */
169 dx *= vpd->facx;
170 dy *= vpd->facy;
171
172 if (!vpd->do_category_scroll) {
173 /* only move view on an axis if change is allowed */
174 if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) {
175 v2d->cur.xmin += dx;
176 v2d->cur.xmax += dx;
177 }
178 if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) {
179 v2d->cur.ymin += dy;
180 v2d->cur.ymax += dy;
181 }
182 }
183 else {
184 vpd->region->category_scroll -= dy;
185 }
186
187 /* Inform v2d about changes after this operation. */
189
190 /* don't rebuild full tree in outliner, since we're just changing our view */
192
193 /* request updates to be done... */
195
196 UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY);
197}
198
200{
201 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
202
203 view_pan_apply_ex(C, vpd, RNA_int_get(op->ptr, "deltax"), RNA_int_get(op->ptr, "deltay"));
204}
205
206/* Cleanup temp custom-data. */
207static void view_pan_exit(wmOperator *op)
208{
209 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
210 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
212}
213
216/* -------------------------------------------------------------------- */
220/* for 'redo' only, with no user input */
222{
223 view_pan_init(C, op);
224 view_pan_apply(C, op);
225 view_pan_exit(op);
226 return OPERATOR_FINISHED;
227}
228
229/* set up modal operator and relevant settings */
230static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
231{
232 wmWindow *window = CTX_wm_window(C);
233
234 /* set up customdata */
235 view_pan_init(C, op);
236
237 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
238 View2D *v2d = vpd->v2d;
239
240 /* set initial settings */
241 vpd->startx = vpd->lastx = event->xy[0];
242 vpd->starty = vpd->lasty = event->xy[1];
243 vpd->invoke_event = event->type;
244
246
247 if (event->type == MOUSEPAN) {
248 RNA_int_set(op->ptr, "deltax", event->prev_xy[0] - event->xy[0]);
249 RNA_int_set(op->ptr, "deltay", event->prev_xy[1] - event->xy[1]);
250
251 view_pan_apply(C, op);
252 view_pan_exit(op);
253 return OPERATOR_FINISHED;
254 }
255
256 RNA_int_set(op->ptr, "deltax", 0);
257 RNA_int_set(op->ptr, "deltay", 0);
258
259 if (window->grabcursor == 0) {
260 if (v2d->keepofs & V2D_LOCKOFS_X) {
262 }
263 else if (v2d->keepofs & V2D_LOCKOFS_Y) {
265 }
266 else {
268 }
269 }
270
271 /* add temp handler */
273
275}
276
277/* handle user input - calculations of mouse-movement
278 * need to be done here, not in the apply callback! */
279static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
280{
281 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
282 View2D *v2d = vpd->v2d;
283
284 /* execute the events */
285 switch (event->type) {
286 case MOUSEMOVE: {
287 /* calculate new delta transform, then store mouse-coordinates for next-time */
288 int deltax = vpd->lastx - event->xy[0];
289 int deltay = vpd->lasty - event->xy[1];
290
291 /* Page snapping: When panning for more than half a page size, snap to the next page. */
292 if (v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) {
293 deltay = view2d_scroll_delta_y_snap_page_size(*v2d, deltay);
294 }
295
296 if (deltax != 0) {
297 vpd->lastx = event->xy[0];
298 }
299 if (deltay != 0) {
300 vpd->lasty = event->xy[1];
301 }
302
303 if (deltax || deltay) {
304 RNA_int_set(op->ptr, "deltax", deltax);
305 RNA_int_set(op->ptr, "deltay", deltay);
306 view_pan_apply(C, op);
307 }
308 break;
309 }
310 /* XXX: Mode switching isn't implemented. See comments in 36818.
311 * switch to zoom */
312#if 0
313 case LEFTMOUSE:
314 if (event->val == KM_PRESS) {
315 /* calculate overall delta mouse-movement for redo */
316 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
317 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
318
319 view_pan_exit(op);
321 WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, nullptr, event);
322 return OPERATOR_FINISHED;
323 }
324#endif
325 default:
326 if (ELEM(event->type, vpd->invoke_event, EVT_ESCKEY)) {
327 if (event->val == KM_RELEASE) {
328 /* calculate overall delta mouse-movement for redo */
329 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
330 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
331
332 view_pan_exit(op);
334
335 return OPERATOR_FINISHED;
336 }
337 }
338 break;
339 }
340
342}
343
344static void view_pan_cancel(bContext * /*C*/, wmOperator *op)
345{
346 view_pan_exit(op);
347}
348
350{
351 /* identifiers */
352 ot->name = "Pan View";
353 ot->description = "Pan the view";
354 ot->idname = "VIEW2D_OT_pan";
355
356 /* api callbacks */
362
363 /* operator is modal */
365
366 /* rna - must keep these in sync with the other operators */
367 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
368 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
369}
370
373/* -------------------------------------------------------------------- */
380/* set up modal operator and relevant settings */
381static int view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
382{
383 op->customdata = MEM_callocN(sizeof(View2DEdgePanData), "View2DEdgePanData");
384 View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
386
388
390}
391
392static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
393{
394 View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
395
396 wmWindow *source_win = CTX_wm_window(C);
397 int event_xy_target[2];
398 wmWindow *target_win = WM_window_find_under_cursor(source_win, event->xy, &event_xy_target[0]);
399
400 /* Exit if we release the mouse button, hit escape, or enter a different window. */
401 if (event->val == KM_RELEASE || event->type == EVT_ESCKEY || source_win != target_win) {
402 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
405 }
406
407 UI_view2d_edge_pan_apply_event(C, vpd, event);
408
409 /* This operator is supposed to run together with some drag action.
410 * On successful handling, always pass events on to other handlers. */
412}
413
414static void view_edge_pan_cancel(bContext * /*C*/, wmOperator *op)
415{
416 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
417 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
419}
420
422{
423 /* identifiers */
424 ot->name = "View Edge Pan";
425 ot->description = "Pan the view when the mouse is held at an edge";
426 ot->idname = "VIEW2D_OT_edge_pan";
427
428 /* api callbacks */
433
434 /* operator is modal */
437}
438
441/* -------------------------------------------------------------------- */
445/* this operator only needs this single callback, where it calls the view_pan_*() methods */
447{
448 /* initialize default settings (and validate if ok to run) */
449 view_pan_init(C, op);
450
451 /* also, check if can pan in horizontal axis */
452 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
453 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
454 view_pan_exit(op);
456 }
457
458 /* set RNA-Props - only movement in positive x-direction */
459 RNA_int_set(op->ptr, "deltax", 40 * UI_SCALE_FAC);
460 RNA_int_set(op->ptr, "deltay", 0);
461
462 /* apply movement, then we're done */
463 view_pan_apply(C, op);
464 view_pan_exit(op);
465
466 return OPERATOR_FINISHED;
467}
468
470{
471 /* identifiers */
472 ot->name = "Scroll Right";
473 ot->description = "Scroll the view right";
474 ot->idname = "VIEW2D_OT_scroll_right";
475
476 /* api callbacks */
479
480 /* rna - must keep these in sync with the other operators */
481 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
482 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
483}
484
485/* this operator only needs this single callback, where it calls the view_pan_*() methods */
487{
488 /* initialize default settings (and validate if ok to run) */
489 view_pan_init(C, op);
490
491 /* also, check if can pan in horizontal axis */
492 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
493 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
494 view_pan_exit(op);
496 }
497
498 /* set RNA-Props - only movement in negative x-direction */
499 RNA_int_set(op->ptr, "deltax", -40 * UI_SCALE_FAC);
500 RNA_int_set(op->ptr, "deltay", 0);
501
502 /* apply movement, then we're done */
503 view_pan_apply(C, op);
504 view_pan_exit(op);
505
506 return OPERATOR_FINISHED;
507}
508
510{
511 /* identifiers */
512 ot->name = "Scroll Left";
513 ot->description = "Scroll the view left";
514 ot->idname = "VIEW2D_OT_scroll_left";
515
516 /* api callbacks */
519
520 /* rna - must keep these in sync with the other operators */
521 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
522 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
523}
524
525/* this operator only needs this single callback, where it calls the view_pan_*() methods */
527{
528 /* initialize default settings (and validate if ok to run) */
529 view_pan_init(C, op);
530
531 /* also, check if can pan in vertical axis */
532 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
533 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
534 view_pan_exit(op);
536 }
537
538 const wmWindow *win = CTX_wm_window(C);
540 win->eventstate->xy);
541
542 /* set RNA-Props */
543 RNA_int_set(op->ptr, "deltax", 0);
544 RNA_int_set(op->ptr, "deltay", -40 * UI_SCALE_FAC);
545
546 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
547 const bool use_page_size = (vpd->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) ||
548 (RNA_property_is_set(op->ptr, prop) &&
549 RNA_property_boolean_get(op->ptr, prop));
550 if (use_page_size) {
551 const ARegion *region = CTX_wm_region(C);
552 const int page_size = view2d_page_size_y(region->v2d);
553 RNA_int_set(op->ptr, "deltay", -page_size);
554 }
555
556 /* apply movement, then we're done */
557 view_pan_apply(C, op);
558 view_pan_exit(op);
559
560 return OPERATOR_FINISHED;
561}
562
564{
565 /* identifiers */
566 ot->name = "Scroll Down";
567 ot->description = "Scroll the view down";
568 ot->idname = "VIEW2D_OT_scroll_down";
569
570 /* api callbacks */
573
574 /* rna - must keep these in sync with the other operators */
575 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
576 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
577 RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll down one page");
578}
579
580/* this operator only needs this single callback, where it calls the view_pan_*() methods */
582{
583 /* initialize default settings (and validate if ok to run) */
584 view_pan_init(C, op);
585
586 /* also, check if can pan in vertical axis */
587 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
588 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
589 view_pan_exit(op);
591 }
592
593 const wmWindow *win = CTX_wm_window(C);
595 win->eventstate->xy);
596
597 /* set RNA-Props */
598 RNA_int_set(op->ptr, "deltax", 0);
599 RNA_int_set(op->ptr, "deltay", 40 * UI_SCALE_FAC);
600
601 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
602 const bool use_page_size = (vpd->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) ||
603 (RNA_property_is_set(op->ptr, prop) &&
604 RNA_property_boolean_get(op->ptr, prop));
605 if (use_page_size) {
606 const ARegion *region = CTX_wm_region(C);
607 const int page_size = view2d_page_size_y(region->v2d);
608 RNA_int_set(op->ptr, "deltay", page_size);
609 }
610
611 /* apply movement, then we're done */
612 view_pan_apply(C, op);
613 view_pan_exit(op);
614
615 return OPERATOR_FINISHED;
616}
617
619{
620 /* identifiers */
621 ot->name = "Scroll Up";
622 ot->description = "Scroll the view up";
623 ot->idname = "VIEW2D_OT_scroll_up";
624
625 /* api callbacks */
628
629 /* rna - must keep these in sync with the other operators */
630 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
631 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
632 RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll up one page");
633}
634
637/* -------------------------------------------------------------------- */
659 View2D *v2d; /* view2d we're operating in */
661
662 /* needed for continuous zoom */
665
666 int lastx, lasty; /* previous x/y values of mouse in window */
667 int invoke_event; /* event type that invoked, for modal exits */
668 float dx, dy; /* running tally of previous delta values (for obtaining final zoom) */
669 float mx_2d, my_2d; /* initial mouse location in v2d coords */
671};
672
677static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
678{
679 ScrArea *area = CTX_wm_area(C);
680
681 r_do_zoom_xy[0] = true;
682 r_do_zoom_xy[1] = true;
683
684 /* default not to zoom the sequencer vertically */
685 if (area && area->spacetype == SPACE_SEQ) {
686 ARegion *region = CTX_wm_region(C);
687
688 if (region && region->regiontype == RGN_TYPE_WINDOW) {
689 r_do_zoom_xy[1] = false;
690 }
691 }
692}
693
694/* check if step-zoom can be applied */
696{
697 ARegion *region = CTX_wm_region(C);
698
699 /* check if there's a region in context to work with */
700 if (region == nullptr) {
701 return false;
702 }
703
704 /* Do not show that in 3DView context. */
705 if (CTX_wm_region_view3d(C)) {
706 return false;
707 }
708
709 View2D *v2d = &region->v2d;
710
711 /* check that 2d-view is zoomable */
712 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y)) {
713 return false;
714 }
715
716 /* view is zoomable */
717 return true;
718}
719
720/* initialize panning customdata */
722{
723 /* Should've been checked before. */
725
726 /* set custom-data for operator */
727 v2dViewZoomData *vzd = MEM_cnew<v2dViewZoomData>(__func__);
728 op->customdata = vzd;
729
730 /* set pointers to owners */
731 vzd->region = CTX_wm_region(C);
732 vzd->v2d = &vzd->region->v2d;
733 /* False by default. Interactive callbacks (ie invoke()) can set it to true. */
734 vzd->zoom_to_mouse_pos = false;
735
736 vzd->v2d->flag |= V2D_IS_NAVIGATING;
737}
738
739/* apply transform to view (i.e. adjust 'cur' rect) */
741 v2dViewZoomData *vzd,
742 const float facx,
743 const float facy)
744{
745 ARegion *region = CTX_wm_region(C);
746 View2D *v2d = &region->v2d;
747 const rctf cur_old = v2d->cur;
748 const int snap_test = ED_region_snap_size_test(region);
749
750 /* calculate amount to move view by, ensuring symmetry so the
751 * old zoom level is restored after zooming back the same amount
752 */
753 float dx, dy;
754 if (facx >= 0.0f) {
755 dx = BLI_rctf_size_x(&v2d->cur) * facx;
756 dy = BLI_rctf_size_y(&v2d->cur) * facy;
757 }
758 else {
759 dx = (BLI_rctf_size_x(&v2d->cur) / (1.0f + 2.0f * facx)) * facx;
760 dy = (BLI_rctf_size_y(&v2d->cur) / (1.0f + 2.0f * facy)) * facy;
761 }
762
763 /* only resize view on an axis if change is allowed */
764 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
765 if (v2d->keepofs & V2D_LOCKOFS_X) {
766 v2d->cur.xmax -= 2 * dx;
767 }
768 else if (v2d->keepofs & V2D_KEEPOFS_X) {
769 if (v2d->align & V2D_ALIGN_NO_POS_X) {
770 v2d->cur.xmin += 2 * dx;
771 }
772 else {
773 v2d->cur.xmax -= 2 * dx;
774 }
775 }
776 else {
777
778 v2d->cur.xmin += dx;
779 v2d->cur.xmax -= dx;
780
781 if (vzd->zoom_to_mouse_pos) {
782 /* get zoom fac the same way as in
783 * ui_view2d_curRect_validate_resize - better keep in sync! */
784 const float zoomx = float(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur);
785
786 /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
787 if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
788 IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom))
789 {
790 const float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old);
791 const float mval_faci = 1.0f - mval_fac;
792 const float ofs = (mval_fac * dx) - (mval_faci * dx);
793
794 v2d->cur.xmin += ofs;
795 v2d->cur.xmax += ofs;
796 }
797 }
798 }
799 }
800 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
801 if (v2d->keepofs & V2D_LOCKOFS_Y) {
802 v2d->cur.ymax -= 2 * dy;
803 }
804 else if (v2d->keepofs & V2D_KEEPOFS_Y) {
805 if (v2d->align & V2D_ALIGN_NO_POS_Y) {
806 v2d->cur.ymin += 2 * dy;
807 }
808 else {
809 v2d->cur.ymax -= 2 * dy;
810 }
811 }
812 else {
813
814 v2d->cur.ymin += dy;
815 v2d->cur.ymax -= dy;
816
817 if (vzd->zoom_to_mouse_pos) {
818 /* get zoom fac the same way as in
819 * ui_view2d_curRect_validate_resize - better keep in sync! */
820 const float zoomy = float(BLI_rcti_size_y(&v2d->mask) + 1) / BLI_rctf_size_y(&v2d->cur);
821
822 /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
823 if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
824 IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom))
825 {
826 const float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old);
827 const float mval_faci = 1.0f - mval_fac;
828 const float ofs = (mval_fac * dy) - (mval_faci * dy);
829
830 v2d->cur.ymin += ofs;
831 v2d->cur.ymax += ofs;
832 }
833 }
834 }
835 }
836
837 /* Inform v2d about changes after this operation. */
839
840 if (ED_region_snap_size_apply(region, snap_test)) {
841 ScrArea *area = CTX_wm_area(C);
842 ED_area_tag_redraw(area);
844 }
845
846 /* request updates to be done... */
849}
850
852{
853 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
855 C, vzd, RNA_float_get(op->ptr, "zoomfacx"), RNA_float_get(op->ptr, "zoomfacy"));
856}
857
860/* -------------------------------------------------------------------- */
864/* Cleanup temp custom-data. */
866{
867 ScrArea *area = CTX_wm_area(C);
868 /* Some areas change font sizes when zooming, so clear glyph cache. */
869 if (area && !ELEM(area->spacetype, SPACE_GRAPH, SPACE_ACTION)) {
871 }
872
873 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
874 vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
876}
877
878/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
880{
881 if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
882 view_zoomdrag_init(C, op);
883 }
884
885 bool do_zoom_xy[2];
886 view_zoom_axis_lock_defaults(C, do_zoom_xy);
887
888 /* set RNA-Props - zooming in by uniform factor */
889 RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? 0.0375f : 0.0f);
890 RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? 0.0375f : 0.0f);
891
892 /* apply movement, then we're done */
893 view_zoomstep_apply(C, op);
894
895 view_zoomstep_exit(C, op);
896
897 return OPERATOR_FINISHED;
898}
899
900static int view_zoomin_invoke(bContext *C, wmOperator *op, const wmEvent *event)
901{
902 view_zoomdrag_init(C, op);
903
904 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
905
906 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
907 ARegion *region = CTX_wm_region(C);
908
909 /* store initial mouse position (in view space) */
911 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
912 vzd->zoom_to_mouse_pos = true;
913 }
914
915 return view_zoomin_exec(C, op);
916}
917
919{
920 PropertyRNA *prop;
921
922 /* identifiers */
923 ot->name = "Zoom In";
924 ot->description = "Zoom in the view";
925 ot->idname = "VIEW2D_OT_zoom_in";
926
927 /* api callbacks */
931
932 /* rna - must keep these in sync with the other operators */
933 prop = RNA_def_float(
934 ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
936 prop = RNA_def_float(
937 ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
939}
940
941/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
943{
944 bool do_zoom_xy[2];
945
946 if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
947 view_zoomdrag_init(C, op);
948 }
949
950 view_zoom_axis_lock_defaults(C, do_zoom_xy);
951
952 /* set RNA-Props - zooming in by uniform factor */
953 RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? -0.0375f : 0.0f);
954 RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? -0.0375f : 0.0f);
955
956 /* apply movement, then we're done */
957 view_zoomstep_apply(C, op);
958
959 view_zoomstep_exit(C, op);
960
961 return OPERATOR_FINISHED;
962}
963
964static int view_zoomout_invoke(bContext *C, wmOperator *op, const wmEvent *event)
965{
966 view_zoomdrag_init(C, op);
967
968 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
969
970 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
971 ARegion *region = CTX_wm_region(C);
972
973 /* store initial mouse position (in view space) */
975 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
976 vzd->zoom_to_mouse_pos = true;
977 }
978
979 return view_zoomout_exec(C, op);
980}
981
983{
984 PropertyRNA *prop;
985
986 /* identifiers */
987 ot->name = "Zoom Out";
988 ot->description = "Zoom out the view";
989 ot->idname = "VIEW2D_OT_zoom_out";
990
991 /* api callbacks */
994
996
997 /* rna - must keep these in sync with the other operators */
998 prop = RNA_def_float(
999 ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
1001 prop = RNA_def_float(
1002 ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
1004}
1005
1008/* -------------------------------------------------------------------- */
1019/* apply transform to view (i.e. adjust 'cur' rect) */
1021{
1022 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1023 View2D *v2d = vzd->v2d;
1024 const int snap_test = ED_region_snap_size_test(vzd->region);
1025
1026 const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
1027 const bool zoom_to_pos = use_cursor_init && vzd->zoom_to_mouse_pos;
1028
1029 /* get amount to move view by */
1030 float dx = RNA_float_get(op->ptr, "deltax") / UI_SCALE_FAC;
1031 float dy = RNA_float_get(op->ptr, "deltay") / UI_SCALE_FAC;
1032
1033 /* Check if the 'timer' is initialized, as zooming with the trackpad
1034 * never uses the "Continuous" zoom method, and the 'timer' is not initialized. */
1035 if ((U.viewzoom == USER_ZOOM_CONTINUE) && vzd->timer) { /* XXX store this setting as RNA prop? */
1036 const double time = BLI_time_now_seconds();
1037 const float time_step = float(time - vzd->timer_lastdraw);
1038
1039 dx *= time_step * 5.0f;
1040 dy *= time_step * 5.0f;
1041
1042 vzd->timer_lastdraw = time;
1043 }
1044
1045 /* only move view on an axis if change is allowed */
1046 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1047 if (v2d->keepofs & V2D_LOCKOFS_X) {
1048 v2d->cur.xmax -= 2 * dx;
1049 }
1050 else {
1051 if (zoom_to_pos) {
1052 const float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
1053 const float mval_faci = 1.0f - mval_fac;
1054 const float ofs = (mval_fac * dx) - (mval_faci * dx);
1055
1056 v2d->cur.xmin += ofs + dx;
1057 v2d->cur.xmax += ofs - dx;
1058 }
1059 else {
1060 v2d->cur.xmin += dx;
1061 v2d->cur.xmax -= dx;
1062 }
1063 }
1064 }
1065 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1066 if (v2d->keepofs & V2D_LOCKOFS_Y) {
1067 v2d->cur.ymax -= 2 * dy;
1068 }
1069 else {
1070 if (zoom_to_pos) {
1071 const float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
1072 const float mval_faci = 1.0f - mval_fac;
1073 const float ofs = (mval_fac * dy) - (mval_faci * dy);
1074
1075 v2d->cur.ymin += ofs + dy;
1076 v2d->cur.ymax += ofs - dy;
1077 }
1078 else {
1079 v2d->cur.ymin += dy;
1080 v2d->cur.ymax -= dy;
1081 }
1082 }
1083 }
1084
1085 /* Inform v2d about changes after this operation. */
1087
1088 if (ED_region_snap_size_apply(vzd->region, snap_test)) {
1089 ScrArea *area = CTX_wm_area(C);
1090 ED_area_tag_redraw(area);
1092 }
1093
1094 /* request updates to be done... */
1097}
1098
1099/* Cleanup temp custom-data. */
1101{
1103
1104 if (op->customdata) {
1105 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1106 vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
1107
1108 if (vzd->timer) {
1110 }
1111
1112 MEM_freeN(op->customdata);
1113 op->customdata = nullptr;
1114 }
1115}
1116
1118{
1119 view_zoomdrag_exit(C, op);
1120}
1121
1122/* for 'redo' only, with no user input */
1124{
1125 view_zoomdrag_init(C, op);
1126 view_zoomdrag_apply(C, op);
1127 view_zoomdrag_exit(C, op);
1128 return OPERATOR_FINISHED;
1129}
1130
1131/* set up modal operator and relevant settings */
1132static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1133{
1134 wmWindow *window = CTX_wm_window(C);
1135
1136 /* set up customdata */
1137 view_zoomdrag_init(C, op);
1138
1139 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1140 View2D *v2d = vzd->v2d;
1141
1142 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
1143 ARegion *region = CTX_wm_region(C);
1144
1145 /* Store initial mouse position (in view space). */
1147 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
1148 vzd->zoom_to_mouse_pos = true;
1149 }
1150
1151 if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
1152 vzd->lastx = event->prev_xy[0];
1153 vzd->lasty = event->prev_xy[1];
1154
1155 float facx, facy;
1156 float zoomfac = 0.01f;
1157
1158 /* Some view2d's (graph) don't have min/max zoom, or extreme ones. */
1159 if (v2d->maxzoom > 0.0f) {
1160 zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1161 }
1162
1163 if (event->type == MOUSEPAN) {
1164 facx = zoomfac * WM_event_absolute_delta_x(event);
1165 facy = zoomfac * WM_event_absolute_delta_y(event);
1166
1167 if (U.uiflag & USER_ZOOM_INVERT) {
1168 facx *= -1.0f;
1169 facy *= -1.0f;
1170 }
1171 }
1172 else { /* MOUSEZOOM */
1173 facx = facy = zoomfac * WM_event_absolute_delta_x(event);
1174 }
1175
1176 /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1177 * any will be scaled uniformly. */
1178 if (((v2d->keepzoom & (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y)) == 0) &&
1179 (v2d->keepzoom & V2D_KEEPASPECT))
1180 {
1181 if (U.uiflag & USER_ZOOM_HORIZ) {
1182 facy = 0.0f;
1183 }
1184 else {
1185 facx = 0.0f;
1186 }
1187 }
1188
1189 /* support trackpad zoom to always zoom entirely - the v2d code uses portrait or
1190 * landscape exceptions */
1191 if (v2d->keepzoom & V2D_KEEPASPECT) {
1192 if (fabsf(facx) > fabsf(facy)) {
1193 facy = facx;
1194 }
1195 else {
1196 facx = facy;
1197 }
1198 }
1199
1200 const float dx = facx * BLI_rctf_size_x(&v2d->cur);
1201 const float dy = facy * BLI_rctf_size_y(&v2d->cur);
1202
1203 RNA_float_set(op->ptr, "deltax", dx);
1204 RNA_float_set(op->ptr, "deltay", dy);
1205
1206 view_zoomdrag_apply(C, op);
1207 view_zoomdrag_exit(C, op);
1208 return OPERATOR_FINISHED;
1209 }
1210
1211 /* set initial settings */
1212 vzd->lastx = event->xy[0];
1213 vzd->lasty = event->xy[1];
1214 RNA_float_set(op->ptr, "deltax", 0);
1215 RNA_float_set(op->ptr, "deltay", 0);
1216
1217 /* for modal exit test */
1218 vzd->invoke_event = event->type;
1219
1220 if (window->grabcursor == 0) {
1221 if (v2d->keepofs & V2D_LOCKOFS_X) {
1223 }
1224 else if (v2d->keepofs & V2D_LOCKOFS_Y) {
1226 }
1227 else {
1229 }
1230 }
1231
1232 /* add temp handler */
1234
1235 if (U.viewzoom == USER_ZOOM_CONTINUE) {
1236 /* needs a timer to continue redrawing */
1237 vzd->timer = WM_event_timer_add(CTX_wm_manager(C), window, TIMER, 0.01f);
1239 }
1240
1242}
1243
1244/* handle user input - calculations of mouse-movement need to be done here,
1245 * not in the apply callback! */
1246static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event)
1247{
1248 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1249 View2D *v2d = vzd->v2d;
1250
1251 /* execute the events */
1252 if (event->type == TIMER && event->customdata == vzd->timer) {
1253 view_zoomdrag_apply(C, op);
1254 }
1255 else if (event->type == MOUSEMOVE) {
1256 float dx, dy;
1257 float zoomfac = 0.01f;
1258
1259 /* some view2d's (graph) don't have min/max zoom, or extreme ones */
1260 if (v2d->maxzoom > 0.0f) {
1261 zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1262 }
1263
1264 /* calculate new delta transform, based on zooming mode */
1265 if (U.viewzoom == USER_ZOOM_SCALE) {
1266 /* 'scale' zooming */
1267 float dist;
1268 float len_old[2];
1269 float len_new[2];
1270
1271 /* x-axis transform */
1272 dist = BLI_rcti_size_x(&v2d->mask) / 2.0f;
1273 len_old[0] = zoomfac * fabsf(vzd->lastx - vzd->region->winrct.xmin - dist);
1274 len_new[0] = zoomfac * fabsf(event->xy[0] - vzd->region->winrct.xmin - dist);
1275
1276 /* y-axis transform */
1277 dist = BLI_rcti_size_y(&v2d->mask) / 2.0f;
1278 len_old[1] = zoomfac * fabsf(vzd->lasty - vzd->region->winrct.ymin - dist);
1279 len_new[1] = zoomfac * fabsf(event->xy[1] - vzd->region->winrct.ymin - dist);
1280
1281 /* Calculate distance */
1282 if (v2d->keepzoom & V2D_KEEPASPECT) {
1283 dist = len_v2(len_new) - len_v2(len_old);
1284 dx = dy = dist;
1285 }
1286 else {
1287 dx = len_new[0] - len_old[0];
1288 dy = len_new[1] - len_old[1];
1289 }
1290
1291 dx *= BLI_rctf_size_x(&v2d->cur);
1292 dy *= BLI_rctf_size_y(&v2d->cur);
1293 }
1294 else { /* USER_ZOOM_CONTINUE or USER_ZOOM_DOLLY */
1295 float facx = zoomfac * (event->xy[0] - vzd->lastx);
1296 float facy = zoomfac * (event->xy[1] - vzd->lasty);
1297
1298 /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1299 * any will be scaled uniformly */
1300 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0 && (v2d->keepzoom & V2D_LOCKZOOM_Y) == 0 &&
1301 (v2d->keepzoom & V2D_KEEPASPECT))
1302 {
1303 if (U.uiflag & USER_ZOOM_HORIZ) {
1304 facy = 0.0f;
1305 }
1306 else {
1307 facx = 0.0f;
1308 }
1309 }
1310
1311 /* support zoom to always zoom entirely - the v2d code uses portrait or
1312 * landscape exceptions */
1313 if (v2d->keepzoom & V2D_KEEPASPECT) {
1314 if (fabsf(facx) > fabsf(facy)) {
1315 facy = facx;
1316 }
1317 else {
1318 facx = facy;
1319 }
1320 }
1321
1322 dx = facx * BLI_rctf_size_x(&v2d->cur);
1323 dy = facy * BLI_rctf_size_y(&v2d->cur);
1324 }
1325
1326 if (U.uiflag & USER_ZOOM_INVERT) {
1327 dx *= -1.0f;
1328 dy *= -1.0f;
1329 }
1330
1331 /* set transform amount, and add current deltas to stored total delta (for redo) */
1332 RNA_float_set(op->ptr, "deltax", dx);
1333 RNA_float_set(op->ptr, "deltay", dy);
1334
1335 vzd->dx += dx;
1336 vzd->dy += dy;
1337
1338 /* Store mouse coordinates for next time, if not doing continuous zoom:
1339 * - Continuous zoom only depends on distance of mouse
1340 * to starting point to determine rate of change.
1341 */
1342 if (U.viewzoom != USER_ZOOM_CONTINUE) { /* XXX store this setting as RNA prop? */
1343 vzd->lastx = event->xy[0];
1344 vzd->lasty = event->xy[1];
1345 }
1346
1347 /* apply zooming */
1348 view_zoomdrag_apply(C, op);
1349 }
1350 else if (ELEM(event->type, vzd->invoke_event, EVT_ESCKEY)) {
1351 if (event->val == KM_RELEASE) {
1352
1353 /* for redo, store the overall deltas - need to respect zoom-locks here... */
1354 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1355 RNA_float_set(op->ptr, "deltax", vzd->dx);
1356 }
1357 else {
1358 RNA_float_set(op->ptr, "deltax", 0);
1359 }
1360
1361 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1362 RNA_float_set(op->ptr, "deltay", vzd->dy);
1363 }
1364 else {
1365 RNA_float_set(op->ptr, "deltay", 0);
1366 }
1367
1368 /* free customdata */
1369 view_zoomdrag_exit(C, op);
1371
1372 return OPERATOR_FINISHED;
1373 }
1374 }
1375
1377}
1378
1380{
1381 PropertyRNA *prop;
1382 /* identifiers */
1383 ot->name = "Zoom 2D View";
1384 ot->description = "Zoom in/out the view";
1385 ot->idname = "VIEW2D_OT_zoom";
1386
1387 /* api callbacks */
1392
1394
1395 /* operator is repeatable */
1397
1398 /* rna - must keep these in sync with the other operators */
1399 prop = RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1401 prop = RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1403
1405}
1406
1409/* -------------------------------------------------------------------- */
1423{
1424 ARegion *region = CTX_wm_region(C);
1425 View2D *v2d = &region->v2d;
1426 rctf cur_new = v2d->cur;
1427 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
1428
1429 /* convert coordinates of rect to 'tot' rect coordinates */
1430 rctf rect;
1432 UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
1433
1434 /* check if zooming in/out view */
1435 const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
1436
1437 if (zoom_in) {
1438 /* zoom in:
1439 * - 'cur' rect will be defined by the coordinates of the border region
1440 * - just set the 'cur' rect to have the same coordinates as the border region
1441 * if zoom is allowed to be changed
1442 */
1443 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1444 cur_new.xmin = rect.xmin;
1445 cur_new.xmax = rect.xmax;
1446 }
1447 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1448 cur_new.ymin = rect.ymin;
1449 cur_new.ymax = rect.ymax;
1450 }
1451 }
1452 else {
1453 /* zoom out:
1454 * - the current 'cur' rect coordinates are going to end up where the 'rect' ones are,
1455 * but the 'cur' rect coordinates will need to be adjusted to take in more of the view
1456 * - calculate zoom factor, and adjust using center-point
1457 */
1458 float zoom, center, size;
1459
1460 /* TODO: is this zoom factor calculation valid?
1461 * It seems to produce same results every time... */
1462 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1463 size = BLI_rctf_size_x(&cur_new);
1464 zoom = size / BLI_rctf_size_x(&rect);
1465 center = BLI_rctf_cent_x(&cur_new);
1466
1467 cur_new.xmin = center - (size * zoom);
1468 cur_new.xmax = center + (size * zoom);
1469 }
1470 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1471 size = BLI_rctf_size_y(&cur_new);
1472 zoom = size / BLI_rctf_size_y(&rect);
1473 center = BLI_rctf_cent_y(&cur_new);
1474
1475 cur_new.ymin = center - (size * zoom);
1476 cur_new.ymax = center + (size * zoom);
1477 }
1478 }
1479
1480 UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx);
1481
1482 return OPERATOR_FINISHED;
1483}
1484
1486{
1487 /* identifiers */
1488 ot->name = "Zoom to Border";
1489 ot->description = "Zoom in the view to the nearest item contained in the border";
1490 ot->idname = "VIEW2D_OT_zoom_border";
1491
1492 /* api callbacks */
1497
1499
1500 /* rna */
1502}
1503
1506/* -------------------------------------------------------------------- */
1510#ifdef WITH_INPUT_NDOF
1511static int view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1512{
1513 if (event->type != NDOF_MOTION) {
1514 return OPERATOR_CANCELLED;
1515 }
1516
1517 const wmNDOFMotionData *ndof = static_cast<const wmNDOFMotionData *>(event->customdata);
1518
1519 /* tune these until it feels right */
1520 const float zoom_sensitivity = 0.5f;
1521 const float speed = 10.0f; /* match view3d ortho */
1522 const bool has_translate = !is_zero_v2(ndof->tvec) && view_pan_poll(C);
1523 const bool has_zoom = (ndof->tvec[2] != 0.0f) && view_zoom_poll(C);
1524
1525 if (has_translate) {
1526 float pan_vec[3];
1527
1528 WM_event_ndof_pan_get(ndof, pan_vec, false);
1529
1530 pan_vec[0] *= speed;
1531 pan_vec[1] *= speed;
1532
1533 view_pan_init(C, op);
1534
1535 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
1536 view_pan_apply_ex(C, vpd, pan_vec[0], pan_vec[1]);
1537
1538 view_pan_exit(op);
1539 }
1540
1541 if (has_zoom) {
1542 float zoom_factor = zoom_sensitivity * ndof->dt * -ndof->tvec[2];
1543
1544 bool do_zoom_xy[2];
1545
1546 if (U.ndof_flag & NDOF_ZOOM_INVERT) {
1547 zoom_factor = -zoom_factor;
1548 }
1549
1550 view_zoom_axis_lock_defaults(C, do_zoom_xy);
1551
1552 view_zoomdrag_init(C, op);
1553
1554 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1556 C, vzd, do_zoom_xy[0] ? zoom_factor : 0.0f, do_zoom_xy[1] ? zoom_factor : 0.0f);
1557
1558 view_zoomstep_exit(C, op);
1559 }
1560
1561 return OPERATOR_FINISHED;
1562}
1563
1564static void VIEW2D_OT_ndof(wmOperatorType *ot)
1565{
1566 /* identifiers */
1567 ot->name = "NDOF Pan/Zoom";
1568 ot->idname = "VIEW2D_OT_ndof";
1569 ot->description = "Use a 3D mouse device to pan/zoom the view";
1570
1571 /* api callbacks */
1572 ot->invoke = view2d_ndof_invoke;
1573 ot->poll = view2d_poll;
1574
1575 /* flags */
1577}
1578#endif /* WITH_INPUT_NDOF */
1579
1582/* -------------------------------------------------------------------- */
1591
1600static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
1601{
1602 const float size_a[2] = {BLI_rctf_size_x(rect_a), BLI_rctf_size_y(rect_a)};
1603 const float size_b[2] = {BLI_rctf_size_x(rect_b), BLI_rctf_size_y(rect_b)};
1604 const float cent_a[2] = {BLI_rctf_cent_x(rect_a), BLI_rctf_cent_y(rect_a)};
1605 const float cent_b[2] = {BLI_rctf_cent_x(rect_b), BLI_rctf_cent_y(rect_b)};
1606
1607 float fac_max = 0.0f;
1608
1609 for (int i = 0; i < 2; i++) {
1610 /* axis translation normalized to scale */
1611 float tfac = fabsf(cent_a[i] - cent_b[i]) / min_ff(size_a[i], size_b[i]);
1612 fac_max = max_ff(fac_max, tfac);
1613 if (fac_max >= 1.0f) {
1614 break;
1615 }
1616
1617 /* axis scale difference, x2 so doubling or half gives 1.0f */
1618 tfac = (1.0f - (min_ff(size_a[i], size_b[i]) / max_ff(size_a[i], size_b[i]))) * 2.0f;
1619 fac_max = max_ff(fac_max, tfac);
1620 if (fac_max >= 1.0f) {
1621 break;
1622 }
1623 }
1624 return min_ff(fac_max, 1.0f);
1625}
1626
1628 ARegion *region,
1629 const rctf *cur,
1630 const int smooth_viewtx)
1631{
1633 wmWindow *win = CTX_wm_window(C);
1634
1635 View2D *v2d = &region->v2d;
1636 SmoothView2DStore sms = {{0}};
1637 bool ok = false;
1638 float fac = 1.0f;
1639
1640 /* initialize sms */
1641 sms.new_cur = v2d->cur;
1642
1643 /* store the options we want to end with */
1644 if (cur) {
1645 sms.new_cur = *cur;
1646 }
1647
1648 if (cur) {
1649 fac = smooth_view_rect_to_fac(&v2d->cur, cur);
1650 }
1651
1652 if (smooth_viewtx && fac > FLT_EPSILON) {
1653 bool changed = false;
1654
1655 if (BLI_rctf_compare(&sms.new_cur, &v2d->cur, FLT_EPSILON) == false) {
1656 changed = true;
1657 }
1658
1659 /* The new view is different from the old one
1660 * so animate the view */
1661 if (changed) {
1662 sms.orig_cur = v2d->cur;
1663
1664 sms.time_allowed = double(smooth_viewtx) / 1000.0;
1665
1666 /* scale the time allowed the change in view */
1667 sms.time_allowed *= double(fac);
1668
1669 /* keep track of running timer! */
1670 if (v2d->sms == nullptr) {
1671 v2d->sms = MEM_new<SmoothView2DStore>(__func__);
1672 }
1673 *v2d->sms = sms;
1674 if (v2d->smooth_timer) {
1675 WM_event_timer_remove(wm, win, v2d->smooth_timer);
1676 }
1677 /* TIMER1 is hard-coded in key-map. */
1678 v2d->smooth_timer = WM_event_timer_add(wm, win, TIMER1, 1.0 / 100.0);
1679
1680 ok = true;
1681 }
1682 }
1683
1684 /* if we get here nothing happens */
1685 if (ok == false) {
1686 v2d->cur = sms.new_cur;
1687
1691 }
1692}
1693
1694/* only meant for timer usage */
1695static int view2d_smoothview_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
1696{
1697 wmWindow *win = CTX_wm_window(C);
1698 ARegion *region = CTX_wm_region(C);
1699 View2D *v2d = &region->v2d;
1700 SmoothView2DStore *sms = v2d->sms;
1701
1702 /* escape if not our timer */
1703 if (v2d->smooth_timer == nullptr || v2d->smooth_timer != event->customdata) {
1704 return OPERATOR_PASS_THROUGH;
1705 }
1706
1707 float step;
1708 if (sms->time_allowed != 0.0) {
1709 step = float((v2d->smooth_timer->time_duration) / sms->time_allowed);
1710 }
1711 else {
1712 step = 1.0f;
1713 }
1714
1715 /* end timer */
1716 if (step >= 1.0f) {
1717 v2d->cur = sms->new_cur;
1718
1719 MEM_delete(v2d->sms);
1720 v2d->sms = nullptr;
1721
1723 v2d->smooth_timer = nullptr;
1724
1725 /* Event handling won't know if a UI item has been moved under the pointer. */
1727 }
1728 else {
1729 /* ease in/out */
1730 step = (3.0f * step * step - 2.0f * step * step * step);
1731
1732 BLI_rctf_interp(&v2d->cur, &sms->orig_cur, &sms->new_cur, step);
1733 }
1734
1738
1739 if (v2d->sms == nullptr) {
1741 }
1742
1743 return OPERATOR_FINISHED;
1744}
1745
1747{
1748 /* identifiers */
1749 ot->name = "Smooth View 2D";
1750 ot->idname = "VIEW2D_OT_smoothview";
1751
1752 /* api callbacks */
1754 ot->poll = view2d_poll;
1755
1756 /* flags */
1758
1759 /* rna */
1761}
1762
1765/* -------------------------------------------------------------------- */
1779/* customdata for scroller-invoke data */
1785
1788
1789 /* XXX find some way to provide visual feedback of this (active color?) */
1791 short zone;
1792
1794 float fac;
1798 float delta;
1799
1804
1807};
1808
1809/* quick enum for vsm->zone (scroller handles) */
1810enum {
1816} /*eV2DScrollerHandle_Zone*/;
1817
1825static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
1826{
1827 /* firstly, check if
1828 * - 'bubble' fills entire scroller
1829 * - 'bubble' completely out of view on either side
1830 */
1831 bool in_view = true;
1832 if (sh_min <= sc_min && sc_max <= sh_max) {
1833 in_view = false;
1834 }
1835 else if (sh_max <= sc_min || sc_max <= sh_min) {
1836 in_view = false;
1837 }
1838
1839 if (!in_view) {
1840 return SCROLLHANDLE_BAR;
1841 }
1842
1843 /* check if mouse is in or past either handle */
1844 /* TODO: check if these extents are still valid or not */
1845 bool in_max = ((mouse >= (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1846 (mouse <= (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1847 bool in_min = ((mouse <= (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1848 (mouse >= (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1849 bool in_bar = ((mouse < (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1850 (mouse > (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1851 const bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1852 const bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1853
1854 if (in_bar) {
1855 return SCROLLHANDLE_BAR;
1856 }
1857 if (in_max) {
1858 return SCROLLHANDLE_MAX;
1859 }
1860 if (in_min) {
1861 return SCROLLHANDLE_MIN;
1862 }
1863 if (out_min) {
1865 }
1866 if (out_max) {
1868 }
1869
1870 /* unlikely to happen, though we just cover it in case */
1871 return SCROLLHANDLE_BAR;
1872}
1873
1875{
1876 const wmWindow *win = CTX_wm_window(C);
1877 if (!(win && win->eventstate)) {
1878 return false;
1879 }
1880 if (!view2d_poll(C)) {
1881 return false;
1882 }
1883 ARegion *region = CTX_wm_region(C);
1884 View2D *v2d = &region->v2d;
1885 /* Check if mouse in scroll-bars, if they're enabled. */
1886 return (UI_view2d_mouse_in_scrollers(region, v2d, win->eventstate->xy) != 0);
1887}
1888
1889/* Initialize #wmOperator.customdata for scroller manipulation operator. */
1891 wmOperator *op,
1892 const wmEvent *event,
1893 const char in_scroller)
1894{
1895 ARegion *region = CTX_wm_region(C);
1896 View2D *v2d = &region->v2d;
1897
1898 /* set custom-data for operator */
1899 v2dScrollerMove *vsm = MEM_cnew<v2dScrollerMove>(__func__);
1900 op->customdata = vsm;
1901
1902 /* set general data */
1903 vsm->v2d = v2d;
1904 vsm->region = region;
1905 vsm->scroller = in_scroller;
1906
1907 /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1908 vsm->lastx = event->xy[0];
1909 vsm->lasty = event->xy[1];
1910 /* 'zone' depends on where mouse is relative to bubble
1911 * - zooming must be allowed on this axis, otherwise, default to pan
1912 */
1913 View2DScrollers scrollers;
1914 /* Reconstruct the custom scroller mask passed to #UI_view2d_scrollers_draw().
1915 *
1916 * Some editors like the File Browser, Spreadsheet or scrubbing UI already set up custom masks
1917 * for scroll-bars (they don't cover the whole region width or height), these need to be
1918 * considered, otherwise coords for `mouse_in_scroller_handle` later are not compatible. This
1919 * should be a reliable way to do it. Otherwise the custom scroller mask could also be stored in
1920 * #View2D.
1921 */
1922 rcti scroller_mask = v2d->hor;
1923 BLI_rcti_union(&scroller_mask, &v2d->vert);
1924
1925 view2d_scrollers_calc(v2d, &scroller_mask, &scrollers);
1926
1927 /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
1928 * moving the scroll bars has far too little effect and the view can get stuck #31476. */
1929 rctf tot_cur_union = v2d->tot;
1930 BLI_rctf_union(&tot_cur_union, &v2d->cur);
1931
1932 if (in_scroller == 'h') {
1933 /* horizontal scroller - calculate adjustment factor first */
1934 const float mask_size = float(BLI_rcti_size_x(&v2d->hor));
1935 vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size;
1936
1937 /* pixel rounding */
1938 vsm->fac_round = BLI_rctf_size_x(&v2d->cur) / float(BLI_rcti_size_x(&region->winrct) + 1);
1939
1940 /* get 'zone' (i.e. which part of scroller is activated) */
1942 event->mval[0], v2d->hor.xmin, v2d->hor.xmax, scrollers.hor_min, scrollers.hor_max);
1943
1945 /* default to scroll, as handles not usable */
1946 vsm->zone = SCROLLHANDLE_BAR;
1947 }
1948
1949 vsm->scrollbarwidth = scrollers.hor_max - scrollers.hor_min;
1950 vsm->scrollbar_orig = ((scrollers.hor_max + scrollers.hor_min) / 2) + region->winrct.xmin;
1951 }
1952 else {
1953 /* vertical scroller - calculate adjustment factor first */
1954 const float mask_size = float(BLI_rcti_size_y(&v2d->vert));
1955 vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size;
1956
1957 /* pixel rounding */
1958 vsm->fac_round = BLI_rctf_size_y(&v2d->cur) / float(BLI_rcti_size_y(&region->winrct) + 1);
1959
1960 /* get 'zone' (i.e. which part of scroller is activated) */
1962 event->mval[1], v2d->vert.ymin, v2d->vert.ymax, scrollers.vert_min, scrollers.vert_max);
1963
1965 /* default to scroll, as handles not usable */
1966 vsm->zone = SCROLLHANDLE_BAR;
1967 }
1968
1969 vsm->scrollbarwidth = scrollers.vert_max - scrollers.vert_min;
1970 vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin;
1971 }
1972
1973 vsm->v2d->flag |= V2D_IS_NAVIGATING;
1974
1976}
1977
1978/* Cleanup temp custom-data. */
1980{
1981 if (op->customdata) {
1982 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1983
1985 vsm->v2d->flag &= ~V2D_IS_NAVIGATING;
1986
1987 MEM_freeN(op->customdata);
1988 op->customdata = nullptr;
1989
1991 }
1992}
1993
1995{
1997}
1998
1999/* apply transform to view (i.e. adjust 'cur' rect) */
2001{
2002 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
2003 View2D *v2d = vsm->v2d;
2004
2005 /* calculate amount to move view by */
2006 float temp = vsm->fac * vsm->delta;
2007
2008 /* round to pixel */
2009 temp = roundf(temp / vsm->fac_round) * vsm->fac_round;
2010
2011 /* type of movement */
2012 switch (vsm->zone) {
2013 case SCROLLHANDLE_MIN:
2014 /* only expand view on axis if zoom is allowed */
2015 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
2016 v2d->cur.xmin -= temp;
2017 }
2018 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
2019 v2d->cur.ymin -= temp;
2020 }
2021 break;
2022
2023 case SCROLLHANDLE_MAX:
2024
2025 /* only expand view on axis if zoom is allowed */
2026 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
2027 v2d->cur.xmax += temp;
2028 }
2029 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
2030 v2d->cur.ymax += temp;
2031 }
2032 break;
2033
2036 case SCROLLHANDLE_BAR:
2037 default:
2038 /* only move view on an axis if panning is allowed */
2039 if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
2040 v2d->cur.xmin += temp;
2041 v2d->cur.xmax += temp;
2042 }
2043 if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
2044 v2d->cur.ymin += temp;
2045 v2d->cur.ymax += temp;
2046 }
2047 break;
2048 }
2049
2050 /* Inform v2d about changes after this operation. */
2052
2053 /* request updates to be done... */
2056}
2057
2062static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *event)
2063{
2064 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
2065 const bool use_page_size_y = vsm->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y;
2066
2067 /* execute the events */
2068 switch (event->type) {
2069 case MOUSEMOVE: {
2070 float delta = 0.0f;
2071
2072 /* calculate new delta transform, then store mouse-coordinates for next-time */
2074 /* if using bar (i.e. 'panning') or 'max' zoom widget */
2075 switch (vsm->scroller) {
2076 case 'h': /* horizontal scroller - so only horizontal movement
2077 * ('cur' moves opposite to mouse) */
2078 delta = float(event->xy[0] - vsm->lastx);
2079 break;
2080 case 'v': /* vertical scroller - so only vertical movement
2081 * ('cur' moves opposite to mouse) */
2082 delta = float(event->xy[1] - vsm->lasty);
2083 break;
2084 }
2085 }
2086 else if (vsm->zone == SCROLLHANDLE_MIN) {
2087 /* using 'min' zoom widget */
2088 switch (vsm->scroller) {
2089 case 'h': /* horizontal scroller - so only horizontal movement
2090 * ('cur' moves with mouse) */
2091 delta = float(vsm->lastx - event->xy[0]);
2092 break;
2093 case 'v': /* vertical scroller - so only vertical movement
2094 * ('cur' moves with to mouse) */
2095 delta = float(vsm->lasty - event->xy[1]);
2096 break;
2097 }
2098 }
2099
2100 /* Page snapping: When panning for more than half a page size, snap to the next page. */
2101 if (use_page_size_y && (vsm->scroller == 'v')) {
2102 delta = view2d_scroll_delta_y_snap_page_size(*vsm->v2d, delta * vsm->fac) / vsm->fac;
2103 }
2104
2105 if (IS_EQF(delta, 0.0f)) {
2106 break;
2107 }
2108
2109 vsm->delta = delta;
2110 /* store previous coordinates */
2111 vsm->lastx = event->xy[0];
2112 vsm->lasty = event->xy[1];
2113
2115 break;
2116 }
2117 case LEFTMOUSE:
2118 case MIDDLEMOUSE:
2119 if (event->val == KM_RELEASE) {
2120 /* single-click was in empty space outside bubble, so scroll by 1 'page' */
2122 if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE) {
2123 vsm->delta = -vsm->scrollbarwidth * 0.8f;
2124 }
2125 else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE) {
2126 vsm->delta = vsm->scrollbarwidth * 0.8f;
2127 }
2128
2131 return OPERATOR_FINISHED;
2132 }
2133
2134 /* Otherwise, end the drag action. */
2135 if (vsm->lastx || vsm->lasty) {
2137 return OPERATOR_FINISHED;
2138 }
2139 }
2140 break;
2141 }
2142
2144}
2145
2146/* a click (or click drag in progress)
2147 * should have occurred, so check if it happened in scrollbar */
2148static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2149{
2150 ARegion *region = CTX_wm_region(C);
2151 View2D *v2d = &region->v2d;
2152
2153 /* check if mouse in scroll-bars, if they're enabled */
2154 const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy);
2155
2156 /* if in a scroller, init customdata then set modal handler which will
2157 * catch mouse-down to start doing useful stuff */
2158 if (in_scroller) {
2159 /* initialize customdata */
2160 scroller_activate_init(C, op, event, in_scroller);
2162
2163 /* Support for quick jump to location - GTK and QT do this on Linux. */
2164 if (event->type == MIDDLEMOUSE) {
2165 switch (vsm->scroller) {
2166 case 'h': /* horizontal scroller - so only horizontal movement
2167 * ('cur' moves opposite to mouse) */
2168 vsm->delta = float(event->xy[0] - vsm->scrollbar_orig);
2169 break;
2170 case 'v': /* vertical scroller - so only vertical movement
2171 * ('cur' moves opposite to mouse) */
2172 vsm->delta = float(event->xy[1] - vsm->scrollbar_orig);
2173 break;
2174 }
2176
2177 vsm->zone = SCROLLHANDLE_BAR;
2178 }
2179
2180 /* Check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
2181 * NOTE: see `view2d.cc` for latest conditions, and keep this in sync with that. */
2183 if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) == 0) ||
2184 ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) == 0))
2185 {
2186 /* switch to bar (i.e. no scaling gets handled) */
2187 vsm->zone = SCROLLHANDLE_BAR;
2188 }
2189 }
2190
2191 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
2192 if (vsm->zone == SCROLLHANDLE_BAR) {
2193 if (((vsm->scroller == 'h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
2194 ((vsm->scroller == 'v') && (v2d->keepofs & V2D_LOCKOFS_Y)))
2195 {
2196 /* free customdata initialized */
2198
2199 /* can't catch this event for ourselves, so let it go to someone else? */
2200 return OPERATOR_PASS_THROUGH;
2201 }
2202 }
2203
2204 /* zone is also inappropriate if scroller is not visible... */
2205 if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_FULLR)) ||
2206 ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_FULLR)))
2207 {
2208 /* free customdata initialized */
2210
2211 /* can't catch this event for ourselves, so let it go to someone else? */
2212 /* XXX NOTE: if handlers use mask rect to clip input, input will fail for this case. */
2213 return OPERATOR_PASS_THROUGH;
2214 }
2215
2216 /* activate the scroller */
2217 if (vsm->scroller == 'h') {
2219 }
2220 else {
2222 }
2223
2224 /* still ok, so can add */
2227 }
2228
2229 /* not in scroller, so nothing happened...
2230 * (pass through let's something else catch event) */
2231 return OPERATOR_PASS_THROUGH;
2232}
2233
2234/* LMB-Drag in Scrollers - not repeatable operator! */
2236{
2237 /* identifiers */
2238 ot->name = "Scroller Activate";
2239 ot->description = "Scroll view by mouse click and drag";
2240 ot->idname = "VIEW2D_OT_scroller_activate";
2241
2242 /* flags */
2244
2245 /* api callbacks */
2249
2251}
2252
2255/* -------------------------------------------------------------------- */
2259static int reset_exec(bContext *C, wmOperator * /*op*/)
2260{
2261 const uiStyle *style = UI_style_get();
2262 ARegion *region = CTX_wm_region(C);
2263 View2D *v2d = &region->v2d;
2264 const int snap_test = ED_region_snap_size_test(region);
2265
2266 region->category_scroll = 0;
2267
2268 /* zoom 1.0 */
2269 const int winx = float(BLI_rcti_size_x(&v2d->mask) + 1);
2270 const int winy = float(BLI_rcti_size_y(&v2d->mask) + 1);
2271
2272 v2d->cur.xmax = v2d->cur.xmin + winx;
2273 v2d->cur.ymax = v2d->cur.ymin + winy;
2274
2275 /* align */
2276 if (v2d->align) {
2277 /* posx and negx flags are mutually exclusive, so watch out */
2278 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
2279 v2d->cur.xmax = 0.0f;
2280 v2d->cur.xmin = -winx * style->panelzoom;
2281 }
2282 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
2283 v2d->cur.xmax = winx * style->panelzoom;
2284 v2d->cur.xmin = 0.0f;
2285 }
2286
2287 /* - posx and negx flags are mutually exclusive, so watch out */
2288 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
2289 v2d->cur.ymax = 0.0f;
2290 v2d->cur.ymin = -winy * style->panelzoom;
2291 }
2292 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
2293 v2d->cur.ymax = winy * style->panelzoom;
2294 v2d->cur.ymin = 0.0f;
2295 }
2296 }
2297
2298 /* Inform v2d about changes after this operation. */
2300
2301 if (ED_region_snap_size_apply(region, snap_test)) {
2302 ScrArea *area = CTX_wm_area(C);
2303 ED_area_tag_redraw(area);
2305 }
2306
2307 /* request updates to be done... */
2308 ED_region_tag_redraw(region);
2310
2312
2313 return OPERATOR_FINISHED;
2314}
2315
2317{
2318 /* identifiers */
2319 ot->name = "Reset View";
2320 ot->description = "Reset the view";
2321 ot->idname = "VIEW2D_OT_reset";
2322
2323 /* api callbacks */
2324 ot->exec = reset_exec;
2325 ot->poll = view2d_poll;
2326}
2327
2330/* -------------------------------------------------------------------- */
2360
2362{
2363 WM_keymap_ensure(keyconf, "View2D", SPACE_EMPTY, RGN_TYPE_WINDOW);
2364}
2365
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:50
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 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:193
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:184
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:180
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
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:201
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.c:65
#define ELEM(...)
#define IS_EQF(a, b)
#define IN_RANGE_INCL(a, b, c)
typedef double(DMatrix)[4][4]
@ RGN_TYPE_WINDOW
@ SPACE_ACTION
@ SPACE_SEQ
@ SPACE_EMPTY
@ SPACE_GRAPH
@ USER_ZOOM_INVERT
@ USER_ZOOM_TO_MOUSEPOS
@ USER_ZOOM_HORIZ
@ USER_ZOOM_SCALE
@ USER_ZOOM_CONTINUE
@ NDOF_ZOOM_INVERT
#define UI_SCALE_FAC
@ V2D_ALIGN_NO_NEG_X
@ V2D_ALIGN_NO_NEG_Y
@ V2D_ALIGN_NO_POS_Y
@ V2D_ALIGN_NO_POS_X
@ V2D_IS_NAVIGATING
@ V2D_IS_INIT
@ V2D_SNAP_TO_PAGESIZE_Y
@ V2D_LOCKOFS_X
@ V2D_LOCKOFS_Y
@ V2D_KEEPOFS_Y
@ V2D_KEEPOFS_X
@ 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_LIMITZOOM
@ V2D_LOCKZOOM_X
@ V2D_KEEPASPECT
@ V2D_LOCKZOOM_Y
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:708
int ED_region_snap_size_test(const ARegion *region)
Definition area.cc:4104
bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2])
Definition area_query.cc:83
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:653
bool ED_region_snap_size_apply(ARegion *region, int snap_flag)
Definition area.cc:4117
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ PROP_HIDDEN
Definition RNA_types.hh:239
const uiStyle * UI_style_get()
void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
Definition view2d.cc:861
#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:829
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:1663
void void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event)
void UI_view2d_zoom_cache_reset()
Definition view2d.cc:1037
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1670
@ OPTYPE_INTERNAL
Definition WM_types.hh:182
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_LOCK_BYPASS
Definition WM_types.hh:185
@ OPTYPE_GRAB_CURSOR_XY
Definition WM_types.hh:168
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
#define NC_SCREEN
Definition WM_types.hh:344
#define NA_EDITED
Definition WM_types.hh:550
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
unsigned int U
Definition btGjkEpa3.h:78
double time
#define fabsf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
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 ymax
int xmin
int xmax
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
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
int mval[2]
Definition WM_types.hh:728
int prev_xy[2]
Definition WM_types.hh:785
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct PointerRNA * ptr
double time_duration
Definition WM_types.hh:925
struct wmEvent * eventstate
ccl_device_inline int abs(int x)
Definition util/math.h:120
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:1386
bool view2d_edge_pan_poll(bContext *C)
static int reset_exec(bContext *C, wmOperator *)
static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
static int view_scrollup_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_scroll_left(wmOperatorType *ot)
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 int view_scrolldown_exec(bContext *C, wmOperator *op)
static int view_zoomout_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
void ED_operatortypes_view2d()
static void view_edge_pan_cancel(bContext *, wmOperator *op)
static int view_zoomin_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int view_scrollleft_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_scroll_down(wmOperatorType *ot)
static void view_zoomstep_apply(bContext *C, wmOperator *op)
static int view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void VIEW2D_OT_pan(wmOperatorType *ot)
static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const float facx, const float facy)
static void view_zoomstep_exit(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_out(wmOperatorType *ot)
static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy)
static void scroller_activate_exit(bContext *C, wmOperator *op)
static int view_zoomin_exec(bContext *C, wmOperator *op)
static int view_pan_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
static void view_pan_exit(wmOperator *op)
static int view_scrollright_exec(bContext *C, 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 int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void view_zoomdrag_apply(bContext *C, wmOperator *op)
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 int view_zoomout_exec(bContext *C, wmOperator *op)
static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
@ SCROLLHANDLE_BAR
@ SCROLLHANDLE_MIN_OUTSIDE
@ SCROLLHANDLE_MAX
@ SCROLLHANDLE_MIN
@ SCROLLHANDLE_MAX_OUTSIDE
static int view_borderzoom_exec(bContext *C, wmOperator *op)
static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void view_pan_init(bContext *C, wmOperator *op)
static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void VIEW2D_OT_scroll_right(wmOperatorType *ot)
static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
static void view_pan_apply(bContext *C, wmOperator *op)
static bool view_pan_poll(bContext *C)
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 int view_zoomdrag_exec(bContext *C, wmOperator *op)
static bool scroller_activate_poll(bContext *C)
static int view2d_smoothview_invoke(bContext *C, wmOperator *, const wmEvent *event)
static void VIEW2D_OT_smoothview(wmOperatorType *ot)
static int scroller_activate_modal(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 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)
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:51
@ WM_CURSOR_NS_SCROLL
Definition wm_cursors.hh:52
@ WM_CURSOR_EW_SCROLL
Definition wm_cursors.hh:53
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)
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.cc:4125
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.cc:897
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)
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)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)