Blender V4.5
view2d_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cmath>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_userdef_types.h"
15
16#include "BLI_math_base.h"
17#include "BLI_math_vector.h"
18#include "BLI_time.h" /* USER_ZOOM_CONTINUE */
19#include "BLI_utildefines.h"
20
21#include "BKE_context.hh"
22
23#include "RNA_access.hh"
24#include "RNA_define.hh"
25
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "ED_screen.hh"
30
31#include "UI_interface.hh"
32#include "UI_view2d.hh"
33
34#include "view2d_intern.hh"
35
36/* -------------------------------------------------------------------- */
39
40static bool view2d_poll(bContext *C)
41{
42 ARegion *region = CTX_wm_region(C);
43
44 return (region != nullptr) && (region->v2d.flag & V2D_IS_INIT);
45}
46
52static float view2d_scroll_delta_y_snap_page_size(const View2D &v2d, const float delta_y)
53{
54 const float page_size = view2d_page_size_y(v2d);
55 const int delta_pages = int((delta_y - page_size * 0.5f) / 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
67
68/* -------------------------------------------------------------------- */
71
80
93
95 float facx, facy;
96
97 /* options for version 1 */
104
107
110
111 /* View2D Edge Panning */
114};
115
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 */
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;
211 MEM_freeN(vpd);
212 op->customdata = nullptr;
213}
214
216
217/* -------------------------------------------------------------------- */
220
221/* for 'redo' only, with no user input */
223{
224 view_pan_init(C, op);
225 view_pan_apply(C, op);
226 view_pan_exit(op);
227 return OPERATOR_FINISHED;
228}
229
230/* set up modal operator and relevant settings */
232{
233 wmWindow *window = CTX_wm_window(C);
234
235 /* set up customdata */
236 view_pan_init(C, op);
237
238 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
239 View2D *v2d = vpd->v2d;
240
241 /* set initial settings */
242 vpd->startx = vpd->lastx = event->xy[0];
243 vpd->starty = vpd->lasty = event->xy[1];
244 vpd->invoke_event = event->type;
245
247
248 if (event->type == MOUSEPAN) {
249 RNA_int_set(op->ptr, "deltax", event->prev_xy[0] - event->xy[0]);
250 RNA_int_set(op->ptr, "deltay", event->prev_xy[1] - event->xy[1]);
251
252 view_pan_apply(C, op);
253 view_pan_exit(op);
254 return OPERATOR_FINISHED;
255 }
256
257 RNA_int_set(op->ptr, "deltax", 0);
258 RNA_int_set(op->ptr, "deltay", 0);
259
260 if (window->grabcursor == 0) {
261 if (v2d->keepofs & V2D_LOCKOFS_X) {
263 }
264 else if (v2d->keepofs & V2D_LOCKOFS_Y) {
266 }
267 else {
269 }
270 }
271
272 /* add temp handler */
274
276}
277
278/* handle user input - calculations of mouse-movement
279 * need to be done here, not in the apply callback! */
281{
282 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
283 View2D *v2d = vpd->v2d;
284
285 /* execute the events */
286 switch (event->type) {
287 case MOUSEMOVE: {
288 /* calculate new delta transform, then store mouse-coordinates for next-time */
289 int deltax = vpd->lastx - event->xy[0];
290 int deltay = vpd->lasty - event->xy[1];
291
292 /* Page snapping: When panning for more than half a page size, snap to the next page. */
293 if (v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) {
294 deltay = view2d_scroll_delta_y_snap_page_size(*v2d, deltay);
295 }
296
297 if (deltax != 0) {
298 vpd->lastx = event->xy[0];
299 }
300 if (deltay != 0) {
301 vpd->lasty = event->xy[1];
302 }
303
304 if (deltax || deltay) {
305 RNA_int_set(op->ptr, "deltax", deltax);
306 RNA_int_set(op->ptr, "deltay", deltay);
307 view_pan_apply(C, op);
308 }
309 break;
310 }
311 /* XXX: Mode switching isn't implemented. See comments in 36818.
312 * switch to zoom */
313#if 0
314 case LEFTMOUSE:
315 if (event->val == KM_PRESS) {
316 /* calculate overall delta mouse-movement for redo */
317 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
318 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
319
320 view_pan_exit(op);
322 WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, nullptr, event);
323 return OPERATOR_FINISHED;
324 }
325#endif
326 default:
327 if (ELEM(event->type, vpd->invoke_event, EVT_ESCKEY)) {
328 if (event->val == KM_RELEASE) {
329 /* calculate overall delta mouse-movement for redo */
330 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
331 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
332
333 view_pan_exit(op);
335
336 return OPERATOR_FINISHED;
337 }
338 }
339 break;
340 }
341
343}
344
345static void view_pan_cancel(bContext * /*C*/, wmOperator *op)
346{
347 view_pan_exit(op);
348}
349
351{
352 /* identifiers */
353 ot->name = "Pan View";
354 ot->description = "Pan the view";
355 ot->idname = "VIEW2D_OT_pan";
356
357 /* API callbacks. */
358 ot->exec = view_pan_exec;
359 ot->invoke = view_pan_invoke;
360 ot->modal = view_pan_modal;
361 ot->cancel = view_pan_cancel;
362 ot->poll = view_pan_poll;
363
364 /* operator is modal */
366
367 /* rna - must keep these in sync with the other operators */
368 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
369 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
370}
371
373
374/* -------------------------------------------------------------------- */
380
381/* set up modal operator and relevant settings */
383 wmOperator *op,
384 const wmEvent * /*event*/)
385{
386 op->customdata = MEM_callocN(sizeof(View2DEdgePanData), "View2DEdgePanData");
387 View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
389
391
393}
394
396{
397 View2DEdgePanData *vpd = static_cast<View2DEdgePanData *>(op->customdata);
398
399 wmWindow *source_win = CTX_wm_window(C);
400 int event_xy_target[2];
401 wmWindow *target_win = WM_window_find_under_cursor(source_win, event->xy, &event_xy_target[0]);
402
403 /* Exit if we release the mouse button, hit escape, or enter a different window. */
404 if (event->val == KM_RELEASE || event->type == EVT_ESCKEY || source_win != target_win) {
405 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
406 MEM_SAFE_FREE(vpd);
407 op->customdata = nullptr;
409 }
410
412
413 /* This operator is supposed to run together with some drag action.
414 * On successful handling, always pass events on to other handlers. */
416}
417
418static void view_edge_pan_cancel(bContext * /*C*/, wmOperator *op)
419{
420 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
421 vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
422 MEM_SAFE_FREE(vpd);
423 op->customdata = nullptr;
424}
425
427{
428 /* identifiers */
429 ot->name = "View Edge Pan";
430 ot->description = "Pan the view when the mouse is held at an edge";
431 ot->idname = "VIEW2D_OT_edge_pan";
432
433 /* API callbacks. */
434 ot->invoke = view_edge_pan_invoke;
435 ot->modal = view_edge_pan_modal;
436 ot->cancel = view_edge_pan_cancel;
437 ot->poll = view2d_edge_pan_poll;
438
439 /* operator is modal */
440 ot->flag = OPTYPE_INTERNAL;
442}
443
445
446/* -------------------------------------------------------------------- */
449
450/* this operator only needs this single callback, where it calls the view_pan_*() methods */
452{
453 /* initialize default settings (and validate if ok to run) */
454 view_pan_init(C, op);
455
456 /* also, check if can pan in horizontal axis */
457 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
458 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
459 view_pan_exit(op);
461 }
462
463 /* set RNA-Props - only movement in positive x-direction */
464 RNA_int_set(op->ptr, "deltax", 40 * UI_SCALE_FAC);
465 RNA_int_set(op->ptr, "deltay", 0);
466
467 /* apply movement, then we're done */
468 view_pan_apply(C, op);
469 view_pan_exit(op);
470
471 return OPERATOR_FINISHED;
472}
473
475{
476 /* identifiers */
477 ot->name = "Scroll Right";
478 ot->description = "Scroll the view right";
479 ot->idname = "VIEW2D_OT_scroll_right";
480
481 /* API callbacks. */
483 ot->poll = view_pan_poll;
484
485 /* rna - must keep these in sync with the other operators */
486 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
487 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
488}
489
490/* this operator only needs this single callback, where it calls the view_pan_*() methods */
492{
493 /* initialize default settings (and validate if ok to run) */
494 view_pan_init(C, op);
495
496 /* also, check if can pan in horizontal axis */
497 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
498 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
499 view_pan_exit(op);
501 }
502
503 /* set RNA-Props - only movement in negative x-direction */
504 RNA_int_set(op->ptr, "deltax", -40 * UI_SCALE_FAC);
505 RNA_int_set(op->ptr, "deltay", 0);
506
507 /* apply movement, then we're done */
508 view_pan_apply(C, op);
509 view_pan_exit(op);
510
511 return OPERATOR_FINISHED;
512}
513
515{
516 /* identifiers */
517 ot->name = "Scroll Left";
518 ot->description = "Scroll the view left";
519 ot->idname = "VIEW2D_OT_scroll_left";
520
521 /* API callbacks. */
522 ot->exec = view_scrollleft_exec;
523 ot->poll = view_pan_poll;
524
525 /* rna - must keep these in sync with the other operators */
526 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
527 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
528}
529
530/* this operator only needs this single callback, where it calls the view_pan_*() methods */
532{
533 /* initialize default settings (and validate if ok to run) */
534 view_pan_init(C, op);
535
536 /* also, check if can pan in vertical axis */
537 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
538 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
539 view_pan_exit(op);
541 }
542
543 const wmWindow *win = CTX_wm_window(C);
545 win->eventstate->xy);
546
547 /* set RNA-Props */
548 RNA_int_set(op->ptr, "deltax", 0);
549 RNA_int_set(op->ptr, "deltay", -40 * UI_SCALE_FAC);
550
551 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
552 const bool use_page_size = (vpd->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) ||
553 (RNA_property_is_set(op->ptr, prop) &&
554 RNA_property_boolean_get(op->ptr, prop));
555 if (use_page_size) {
556 const ARegion *region = CTX_wm_region(C);
557 const int page_size = view2d_page_size_y(region->v2d);
558 RNA_int_set(op->ptr, "deltay", -page_size);
559 }
560
561 /* apply movement, then we're done */
562 view_pan_apply(C, op);
563 view_pan_exit(op);
564
565 return OPERATOR_FINISHED;
566}
567
569{
570 /* identifiers */
571 ot->name = "Scroll Down";
572 ot->description = "Scroll the view down";
573 ot->idname = "VIEW2D_OT_scroll_down";
574
575 /* API callbacks. */
576 ot->exec = view_scrolldown_exec;
577 ot->poll = view_pan_poll;
578
579 /* rna - must keep these in sync with the other operators */
580 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
581 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
582 RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll down one page");
583}
584
585/* this operator only needs this single callback, where it calls the view_pan_*() methods */
587{
588 /* initialize default settings (and validate if ok to run) */
589 view_pan_init(C, op);
590
591 /* also, check if can pan in vertical axis */
592 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
593 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
594 view_pan_exit(op);
596 }
597
598 const wmWindow *win = CTX_wm_window(C);
600 win->eventstate->xy);
601
602 /* set RNA-Props */
603 RNA_int_set(op->ptr, "deltax", 0);
604 RNA_int_set(op->ptr, "deltay", 40 * UI_SCALE_FAC);
605
606 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "page");
607 const bool use_page_size = (vpd->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y) ||
608 (RNA_property_is_set(op->ptr, prop) &&
609 RNA_property_boolean_get(op->ptr, prop));
610 if (use_page_size) {
611 const ARegion *region = CTX_wm_region(C);
612 const int page_size = view2d_page_size_y(region->v2d);
613 RNA_int_set(op->ptr, "deltay", page_size);
614 }
615
616 /* apply movement, then we're done */
617 view_pan_apply(C, op);
618 view_pan_exit(op);
619
620 return OPERATOR_FINISHED;
621}
622
624{
625 /* identifiers */
626 ot->name = "Scroll Up";
627 ot->description = "Scroll the view up";
628 ot->idname = "VIEW2D_OT_scroll_up";
629
630 /* API callbacks. */
631 ot->exec = view_scrollup_exec;
632 ot->poll = view_pan_poll;
633
634 /* rna - must keep these in sync with the other operators */
635 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
636 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
637 RNA_def_boolean(ot->srna, "page", false, "Page", "Scroll up one page");
638}
639
641
642/* -------------------------------------------------------------------- */
645
659
664 View2D *v2d; /* view2d we're operating in */
666
667 /* needed for continuous zoom */
670
671 int lastx, lasty; /* previous x/y values of mouse in window */
672 int invoke_event; /* event type that invoked, for modal exits */
673 float dx, dy; /* running tally of previous delta values (for obtaining final zoom) */
674 float mx_2d, my_2d; /* initial mouse location in v2d coords */
676};
677
682static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
683{
684 ScrArea *area = CTX_wm_area(C);
685
686 r_do_zoom_xy[0] = true;
687 r_do_zoom_xy[1] = true;
688
689 /* default not to zoom the sequencer vertically */
690 if (area && area->spacetype == SPACE_SEQ) {
691 ARegion *region = CTX_wm_region(C);
692
693 if (region && region->regiontype == RGN_TYPE_WINDOW) {
694 r_do_zoom_xy[1] = false;
695 }
696 }
697}
698
699/* check if step-zoom can be applied */
701{
702 ARegion *region = CTX_wm_region(C);
703
704 /* check if there's a region in context to work with */
705 if (region == nullptr) {
706 return false;
707 }
708
709 /* Do not show that in 3DView context. */
710 if (CTX_wm_region_view3d(C)) {
711 return false;
712 }
713
714 View2D *v2d = &region->v2d;
715
716 /* check that 2d-view is zoomable */
717 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y)) {
718 return false;
719 }
720
721 /* view is zoomable */
722 return true;
723}
724
725/* initialize panning customdata */
727{
728 /* Should've been checked before. */
730
731 /* set custom-data for operator */
733 op->customdata = vzd;
734
735 /* set pointers to owners */
736 vzd->region = CTX_wm_region(C);
737 vzd->v2d = &vzd->region->v2d;
738 /* False by default. Interactive callbacks (ie invoke()) can set it to true. */
739 vzd->zoom_to_mouse_pos = false;
740
741 vzd->v2d->flag |= V2D_IS_NAVIGATING;
742}
743
744/* apply transform to view (i.e. adjust 'cur' rect) */
746 v2dViewZoomData *vzd,
747 const float facx,
748 const float facy)
749{
750 ARegion *region = CTX_wm_region(C);
751 View2D *v2d = &region->v2d;
752 const rctf cur_old = v2d->cur;
753 const int snap_test = ED_region_snap_size_test(region);
754 const bool do_keepofs = !(v2d->flag & V2D_ZOOM_IGNORE_KEEPOFS);
755
756 /* calculate amount to move view by, ensuring symmetry so the
757 * old zoom level is restored after zooming back the same amount
758 */
759 float dx, dy;
760 if (facx >= 0.0f) {
761 dx = BLI_rctf_size_x(&v2d->cur) * facx;
762 dy = BLI_rctf_size_y(&v2d->cur) * facy;
763 }
764 else {
765 dx = (BLI_rctf_size_x(&v2d->cur) / (1.0f + 2.0f * facx)) * facx;
766 dy = (BLI_rctf_size_y(&v2d->cur) / (1.0f + 2.0f * facy)) * facy;
767 }
768
769 /* Only resize view on an axis if change is allowed. */
770 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
771 if ((v2d->keepofs & V2D_LOCKOFS_X) && do_keepofs) {
772 v2d->cur.xmax -= 2 * dx;
773 }
774 else if ((v2d->keepofs & V2D_KEEPOFS_X) && do_keepofs) {
775 if (v2d->align & V2D_ALIGN_NO_POS_X) {
776 v2d->cur.xmin += 2 * dx;
777 }
778 else {
779 v2d->cur.xmax -= 2 * dx;
780 }
781 }
782 else {
783
784 v2d->cur.xmin += dx;
785 v2d->cur.xmax -= dx;
786
787 if (vzd->zoom_to_mouse_pos) {
788 /* get zoom fac the same way as in
789 * ui_view2d_curRect_validate_resize - better keep in sync! */
790 const float zoomx = float(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur);
791
792 /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
793 if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
794 IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom))
795 {
796 const float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old);
797 const float mval_faci = 1.0f - mval_fac;
798 const float ofs = (mval_fac * dx) - (mval_faci * dx);
799
800 v2d->cur.xmin += ofs;
801 v2d->cur.xmax += ofs;
802 }
803 }
804 }
805 }
806 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
807 if ((v2d->keepofs & V2D_LOCKOFS_Y) && do_keepofs) {
808 v2d->cur.ymax -= 2 * dy;
809 }
810 else if ((v2d->keepofs & V2D_KEEPOFS_Y) && do_keepofs) {
811 if (v2d->align & V2D_ALIGN_NO_POS_Y) {
812 v2d->cur.ymin += 2 * dy;
813 }
814 else {
815 v2d->cur.ymax -= 2 * dy;
816 }
817 }
818 else {
819
820 v2d->cur.ymin += dy;
821 v2d->cur.ymax -= dy;
822
823 if (vzd->zoom_to_mouse_pos) {
824 /* get zoom fac the same way as in
825 * ui_view2d_curRect_validate_resize - better keep in sync! */
826 const float zoomy = float(BLI_rcti_size_y(&v2d->mask) + 1) / BLI_rctf_size_y(&v2d->cur);
827
828 /* only move view to mouse if zoom fac is inside minzoom/maxzoom */
829 if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) ||
830 IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom))
831 {
832 const float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old);
833 const float mval_faci = 1.0f - mval_fac;
834 const float ofs = (mval_fac * dy) - (mval_faci * dy);
835
836 v2d->cur.ymin += ofs;
837 v2d->cur.ymax += ofs;
838 }
839 }
840 }
841 }
842
843 /* Inform v2d about changes after this operation. */
845
846 if (ED_region_snap_size_apply(region, snap_test)) {
847 ScrArea *area = CTX_wm_area(C);
848 ED_area_tag_redraw(area);
850 }
851
852 /* request updates to be done... */
855}
856
858{
859 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
861 C, vzd, RNA_float_get(op->ptr, "zoomfacx"), RNA_float_get(op->ptr, "zoomfacy"));
862}
863
865
866/* -------------------------------------------------------------------- */
869
870/* Cleanup temp custom-data. */
872{
873 ScrArea *area = CTX_wm_area(C);
874 /* Some areas change font sizes when zooming, so clear glyph cache. */
875 if (area && !ELEM(area->spacetype, SPACE_GRAPH, SPACE_ACTION)) {
877 }
878
879 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
880 vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
881 MEM_SAFE_FREE(vzd);
882 op->customdata = nullptr;
883}
884
885/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
887{
888 if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
890 }
891
892 bool do_zoom_xy[2];
893 view_zoom_axis_lock_defaults(C, do_zoom_xy);
894
895 /* set RNA-Props - zooming in by uniform factor */
896 RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? 0.0375f : 0.0f);
897 RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? 0.0375f : 0.0f);
898
899 /* apply movement, then we're done */
901
903
904 return OPERATOR_FINISHED;
905}
906
908{
910
911 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
912
913 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
914 ARegion *region = CTX_wm_region(C);
915
916 /* store initial mouse position (in view space) */
918 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
919 vzd->zoom_to_mouse_pos = true;
920 }
921
922 return view_zoomin_exec(C, op);
923}
924
926{
927 PropertyRNA *prop;
928
929 /* identifiers */
930 ot->name = "Zoom In";
931 ot->description = "Zoom in the view";
932 ot->idname = "VIEW2D_OT_zoom_in";
933
934 /* API callbacks. */
935 ot->invoke = view_zoomin_invoke;
936 ot->exec = view_zoomin_exec;
937 ot->poll = view_zoom_poll;
938
939 /* rna - must keep these in sync with the other operators */
940 prop = RNA_def_float(
941 ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
943 prop = RNA_def_float(
944 ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
946}
947
948/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
950{
951 bool do_zoom_xy[2];
952
953 if (op->customdata == nullptr) { /* Might have been setup in _invoke() already. */
955 }
956
957 view_zoom_axis_lock_defaults(C, do_zoom_xy);
958
959 /* set RNA-Props - zooming in by uniform factor */
960 RNA_float_set(op->ptr, "zoomfacx", do_zoom_xy[0] ? -0.0375f : 0.0f);
961 RNA_float_set(op->ptr, "zoomfacy", do_zoom_xy[1] ? -0.0375f : 0.0f);
962
963 /* apply movement, then we're done */
965
967
968 return OPERATOR_FINISHED;
969}
970
972{
974
975 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
976
977 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
978 ARegion *region = CTX_wm_region(C);
979
980 /* store initial mouse position (in view space) */
982 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
983 vzd->zoom_to_mouse_pos = true;
984 }
985
986 return view_zoomout_exec(C, op);
987}
988
990{
991 PropertyRNA *prop;
992
993 /* identifiers */
994 ot->name = "Zoom Out";
995 ot->description = "Zoom out the view";
996 ot->idname = "VIEW2D_OT_zoom_out";
997
998 /* API callbacks. */
999 ot->invoke = view_zoomout_invoke;
1000 ot->exec = view_zoomout_exec;
1001
1002 ot->poll = view_zoom_poll;
1003
1004 /* rna - must keep these in sync with the other operators */
1005 prop = RNA_def_float(
1006 ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
1008 prop = RNA_def_float(
1009 ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
1011}
1012
1014
1015/* -------------------------------------------------------------------- */
1018
1025
1026/* apply transform to view (i.e. adjust 'cur' rect) */
1028{
1029 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1030 View2D *v2d = vzd->v2d;
1031 const int snap_test = ED_region_snap_size_test(vzd->region);
1032
1033 const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
1034 const bool zoom_to_pos = use_cursor_init && vzd->zoom_to_mouse_pos;
1035
1036 /* get amount to move view by */
1037 float dx = RNA_float_get(op->ptr, "deltax") / UI_SCALE_FAC;
1038 float dy = RNA_float_get(op->ptr, "deltay") / UI_SCALE_FAC;
1039
1040 /* Check if the 'timer' is initialized, as zooming with the trackpad
1041 * never uses the "Continuous" zoom method, and the 'timer' is not initialized. */
1042 if ((U.viewzoom == USER_ZOOM_CONTINUE) && vzd->timer) { /* XXX store this setting as RNA prop? */
1043 const double time = BLI_time_now_seconds();
1044 const float time_step = float(time - vzd->timer_lastdraw);
1045
1046 dx *= time_step * 5.0f;
1047 dy *= time_step * 5.0f;
1048
1049 vzd->timer_lastdraw = time;
1050 }
1051
1052 /* only move view on an axis if change is allowed */
1053 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1054 if (v2d->keepofs & V2D_LOCKOFS_X) {
1055 v2d->cur.xmax -= 2 * dx;
1056 }
1057 else {
1058 if (zoom_to_pos) {
1059 const float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur);
1060 const float mval_faci = 1.0f - mval_fac;
1061 const float ofs = (mval_fac * dx) - (mval_faci * dx);
1062
1063 v2d->cur.xmin += ofs + dx;
1064 v2d->cur.xmax += ofs - dx;
1065 }
1066 else {
1067 v2d->cur.xmin += dx;
1068 v2d->cur.xmax -= dx;
1069 }
1070 }
1071 }
1072 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1073 if (v2d->keepofs & V2D_LOCKOFS_Y) {
1074 v2d->cur.ymax -= 2 * dy;
1075 }
1076 else {
1077 if (zoom_to_pos) {
1078 const float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur);
1079 const float mval_faci = 1.0f - mval_fac;
1080 const float ofs = (mval_fac * dy) - (mval_faci * dy);
1081
1082 v2d->cur.ymin += ofs + dy;
1083 v2d->cur.ymax += ofs - dy;
1084 }
1085 else {
1086 v2d->cur.ymin += dy;
1087 v2d->cur.ymax -= dy;
1088 }
1089 }
1090 }
1091
1092 /* Inform v2d about changes after this operation. */
1094
1095 if (ED_region_snap_size_apply(vzd->region, snap_test)) {
1096 ScrArea *area = CTX_wm_area(C);
1097 ED_area_tag_redraw(area);
1099 }
1100
1101 /* request updates to be done... */
1104}
1105
1106/* Cleanup temp custom-data. */
1108{
1110
1111 if (op->customdata) {
1112 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1113 vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
1114
1115 if (vzd->timer) {
1117 }
1118
1119 MEM_freeN(vzd);
1120 op->customdata = nullptr;
1121 }
1122}
1123
1125{
1126 view_zoomdrag_exit(C, op);
1127}
1128
1129/* for 'redo' only, with no user input */
1137
1138/* set up modal operator and relevant settings */
1140{
1141 wmWindow *window = CTX_wm_window(C);
1142
1143 /* set up customdata */
1144 view_zoomdrag_init(C, op);
1145
1146 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1147 View2D *v2d = vzd->v2d;
1148
1149 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
1150 ARegion *region = CTX_wm_region(C);
1151
1152 /* Store initial mouse position (in view space). */
1154 &region->v2d, event->mval[0], event->mval[1], &vzd->mx_2d, &vzd->my_2d);
1155 vzd->zoom_to_mouse_pos = true;
1156 }
1157
1158 if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
1159 vzd->lastx = event->prev_xy[0];
1160 vzd->lasty = event->prev_xy[1];
1161
1162 float facx, facy;
1163 float zoomfac = 0.01f;
1164
1165 /* Some view2d's (graph) don't have min/max zoom, or extreme ones. */
1166 if (v2d->maxzoom > 0.0f) {
1167 zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1168 }
1169
1170 if (event->type == MOUSEPAN) {
1171 facx = zoomfac * WM_event_absolute_delta_x(event);
1172 facy = zoomfac * WM_event_absolute_delta_y(event);
1173
1174 if (U.uiflag & USER_ZOOM_INVERT) {
1175 facx *= -1.0f;
1176 facy *= -1.0f;
1177 }
1178 }
1179 else { /* MOUSEZOOM */
1180 facx = facy = zoomfac * WM_event_absolute_delta_x(event);
1181 }
1182
1183 /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1184 * any will be scaled uniformly. */
1185 if (((v2d->keepzoom & (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y)) == 0) &&
1186 (v2d->keepzoom & V2D_KEEPASPECT))
1187 {
1188 if (U.uiflag & USER_ZOOM_HORIZ) {
1189 facy = 0.0f;
1190 }
1191 else {
1192 facx = 0.0f;
1193 }
1194 }
1195
1196 /* support trackpad zoom to always zoom entirely - the v2d code uses portrait or
1197 * landscape exceptions */
1198 if (v2d->keepzoom & V2D_KEEPASPECT) {
1199 if (fabsf(facx) > fabsf(facy)) {
1200 facy = facx;
1201 }
1202 else {
1203 facx = facy;
1204 }
1205 }
1206
1207 const float dx = facx * BLI_rctf_size_x(&v2d->cur);
1208 const float dy = facy * BLI_rctf_size_y(&v2d->cur);
1209
1210 RNA_float_set(op->ptr, "deltax", dx);
1211 RNA_float_set(op->ptr, "deltay", dy);
1212
1214 view_zoomdrag_exit(C, op);
1215 return OPERATOR_FINISHED;
1216 }
1217
1218 /* set initial settings */
1219 vzd->lastx = event->xy[0];
1220 vzd->lasty = event->xy[1];
1221 RNA_float_set(op->ptr, "deltax", 0);
1222 RNA_float_set(op->ptr, "deltay", 0);
1223
1224 /* for modal exit test */
1225 vzd->invoke_event = event->type;
1226
1227 if (window->grabcursor == 0) {
1228 if (v2d->keepofs & V2D_LOCKOFS_X) {
1230 }
1231 else if (v2d->keepofs & V2D_LOCKOFS_Y) {
1233 }
1234 else {
1236 }
1237 }
1238
1239 /* add temp handler */
1241
1242 if (U.viewzoom == USER_ZOOM_CONTINUE) {
1243 /* needs a timer to continue redrawing */
1244 vzd->timer = WM_event_timer_add(CTX_wm_manager(C), window, TIMER, 0.01f);
1246 }
1247
1249}
1250
1251/* handle user input - calculations of mouse-movement need to be done here,
1252 * not in the apply callback! */
1254{
1255 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1256 View2D *v2d = vzd->v2d;
1257
1258 /* execute the events */
1259 if (event->type == TIMER && event->customdata == vzd->timer) {
1261 }
1262 else if (event->type == MOUSEMOVE) {
1263 float dx, dy;
1264 float zoomfac = 0.01f;
1265
1266 /* some view2d's (graph) don't have min/max zoom, or extreme ones */
1267 if (v2d->maxzoom > 0.0f) {
1268 zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f);
1269 }
1270
1271 /* calculate new delta transform, based on zooming mode */
1272 if (U.viewzoom == USER_ZOOM_SCALE) {
1273 /* 'scale' zooming */
1274 float dist;
1275 float len_old[2];
1276 float len_new[2];
1277
1278 /* x-axis transform */
1279 dist = BLI_rcti_size_x(&v2d->mask) / 2.0f;
1280 len_old[0] = zoomfac * fabsf(vzd->lastx - vzd->region->winrct.xmin - dist);
1281 len_new[0] = zoomfac * fabsf(event->xy[0] - vzd->region->winrct.xmin - dist);
1282
1283 /* y-axis transform */
1284 dist = BLI_rcti_size_y(&v2d->mask) / 2.0f;
1285 len_old[1] = zoomfac * fabsf(vzd->lasty - vzd->region->winrct.ymin - dist);
1286 len_new[1] = zoomfac * fabsf(event->xy[1] - vzd->region->winrct.ymin - dist);
1287
1288 /* Calculate distance */
1289 if (v2d->keepzoom & V2D_KEEPASPECT) {
1290 dist = len_v2(len_new) - len_v2(len_old);
1291 dx = dy = dist;
1292 }
1293 else {
1294 dx = len_new[0] - len_old[0];
1295 dy = len_new[1] - len_old[1];
1296 }
1297
1298 dx *= BLI_rctf_size_x(&v2d->cur);
1299 dy *= BLI_rctf_size_y(&v2d->cur);
1300 }
1301 else { /* USER_ZOOM_CONTINUE or USER_ZOOM_DOLLY */
1302 float facx = zoomfac * (event->xy[0] - vzd->lastx);
1303 float facy = zoomfac * (event->xy[1] - vzd->lasty);
1304
1305 /* Only respect user setting zoom axis if the view does not have any zoom restrictions
1306 * any will be scaled uniformly */
1307 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0 && (v2d->keepzoom & V2D_LOCKZOOM_Y) == 0 &&
1308 (v2d->keepzoom & V2D_KEEPASPECT))
1309 {
1310 if (U.uiflag & USER_ZOOM_HORIZ) {
1311 facy = 0.0f;
1312 }
1313 else {
1314 facx = 0.0f;
1315 }
1316 }
1317
1318 /* support zoom to always zoom entirely - the v2d code uses portrait or
1319 * landscape exceptions */
1320 if (v2d->keepzoom & V2D_KEEPASPECT) {
1321 if (fabsf(facx) > fabsf(facy)) {
1322 facy = facx;
1323 }
1324 else {
1325 facx = facy;
1326 }
1327 }
1328
1329 dx = facx * BLI_rctf_size_x(&v2d->cur);
1330 dy = facy * BLI_rctf_size_y(&v2d->cur);
1331 }
1332
1333 if (U.uiflag & USER_ZOOM_INVERT) {
1334 dx *= -1.0f;
1335 dy *= -1.0f;
1336 }
1337
1338 /* set transform amount, and add current deltas to stored total delta (for redo) */
1339 RNA_float_set(op->ptr, "deltax", dx);
1340 RNA_float_set(op->ptr, "deltay", dy);
1341
1342 vzd->dx += dx;
1343 vzd->dy += dy;
1344
1345 /* Store mouse coordinates for next time, if not doing continuous zoom:
1346 * - Continuous zoom only depends on distance of mouse
1347 * to starting point to determine rate of change.
1348 */
1349 if (U.viewzoom != USER_ZOOM_CONTINUE) { /* XXX store this setting as RNA prop? */
1350 vzd->lastx = event->xy[0];
1351 vzd->lasty = event->xy[1];
1352 }
1353
1354 /* apply zooming */
1356 }
1357 else if (ELEM(event->type, vzd->invoke_event, EVT_ESCKEY)) {
1358 if (event->val == KM_RELEASE) {
1359
1360 /* for redo, store the overall deltas - need to respect zoom-locks here... */
1361 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1362 RNA_float_set(op->ptr, "deltax", vzd->dx);
1363 }
1364 else {
1365 RNA_float_set(op->ptr, "deltax", 0);
1366 }
1367
1368 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1369 RNA_float_set(op->ptr, "deltay", vzd->dy);
1370 }
1371 else {
1372 RNA_float_set(op->ptr, "deltay", 0);
1373 }
1374
1375 /* free customdata */
1376 view_zoomdrag_exit(C, op);
1378
1379 return OPERATOR_FINISHED;
1380 }
1381 }
1382
1384}
1385
1387{
1388 PropertyRNA *prop;
1389 /* identifiers */
1390 ot->name = "Zoom 2D View";
1391 ot->description = "Zoom in/out the view";
1392 ot->idname = "VIEW2D_OT_zoom";
1393
1394 /* API callbacks. */
1395 ot->exec = view_zoomdrag_exec;
1396 ot->invoke = view_zoomdrag_invoke;
1397 ot->modal = view_zoomdrag_modal;
1398 ot->cancel = view_zoomdrag_cancel;
1399
1400 ot->poll = view_zoom_poll;
1401
1402 /* operator is repeatable */
1404
1405 /* rna - must keep these in sync with the other operators */
1406 prop = RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1408 prop = RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1410
1412}
1413
1415
1416/* -------------------------------------------------------------------- */
1428
1430{
1431 ARegion *region = CTX_wm_region(C);
1432 View2D *v2d = &region->v2d;
1433 rctf cur_new = v2d->cur;
1434 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
1435
1436 /* convert coordinates of rect to `tot` rect coordinates */
1437 rctf rect;
1439 UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
1440
1441 /* check if zooming in/out view */
1442 const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
1443
1444 if (zoom_in) {
1445 /* zoom in:
1446 * - `cur` rect will be defined by the coordinates of the border region
1447 * - just set the `cur` rect to have the same coordinates as the border region
1448 * if zoom is allowed to be changed
1449 */
1450 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1451 cur_new.xmin = rect.xmin;
1452 cur_new.xmax = rect.xmax;
1453 }
1454 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1455 cur_new.ymin = rect.ymin;
1456 cur_new.ymax = rect.ymax;
1457 }
1458 }
1459 else {
1460 /* zoom out:
1461 * - the current `cur` rect coordinates are going to end up where the `rect` ones are,
1462 * but the `cur` rect coordinates will need to be adjusted to take in more of the view
1463 * - calculate zoom factor, and adjust using center-point
1464 */
1465 float zoom, center, size;
1466
1467 /* TODO: is this zoom factor calculation valid?
1468 * It seems to produce same results every time... */
1469 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1470 size = BLI_rctf_size_x(&cur_new);
1471 zoom = size / BLI_rctf_size_x(&rect);
1472 center = BLI_rctf_cent_x(&cur_new);
1473
1474 cur_new.xmin = center - (size * zoom);
1475 cur_new.xmax = center + (size * zoom);
1476 }
1477 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1478 size = BLI_rctf_size_y(&cur_new);
1479 zoom = size / BLI_rctf_size_y(&rect);
1480 center = BLI_rctf_cent_y(&cur_new);
1481
1482 cur_new.ymin = center - (size * zoom);
1483 cur_new.ymax = center + (size * zoom);
1484 }
1485 }
1486
1487 UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx);
1488
1489 return OPERATOR_FINISHED;
1490}
1491
1493{
1494 /* identifiers */
1495 ot->name = "Zoom to Border";
1496 ot->description = "Zoom in the view to the nearest item contained in the border";
1497 ot->idname = "VIEW2D_OT_zoom_border";
1498
1499 /* API callbacks. */
1500 ot->invoke = WM_gesture_box_invoke;
1501 ot->exec = view_borderzoom_exec;
1502 ot->modal = WM_gesture_box_modal;
1503 ot->cancel = WM_gesture_box_cancel;
1504
1505 ot->poll = view_zoom_poll;
1506
1507 /* rna */
1509}
1510
1512
1513/* -------------------------------------------------------------------- */
1516
1517#ifdef WITH_INPUT_NDOF
1518static wmOperatorStatus view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1519{
1520 if (event->type != NDOF_MOTION) {
1521 return OPERATOR_CANCELLED;
1522 }
1523
1524 const wmNDOFMotionData &ndof = *static_cast<const wmNDOFMotionData *>(event->customdata);
1525
1526 /* tune these until it feels right */
1527 const float zoom_sensitivity = 0.5f;
1528 const float pan_speed = NDOF_PIXELS_PER_SECOND;
1529 blender::float3 pan_vec = WM_event_ndof_translation_get_for_navigation(ndof);
1530 const bool has_translate = !is_zero_v2(pan_vec) && view_pan_poll(C);
1531 const bool has_zoom = (pan_vec[2] != 0.0f) && view_zoom_poll(C);
1532
1533 if (has_translate) {
1534 mul_v2_fl(pan_vec, ndof.dt * pan_speed);
1535
1536 view_pan_init(C, op);
1537
1538 v2dViewPanData *vpd = static_cast<v2dViewPanData *>(op->customdata);
1539 view_pan_apply_ex(C, vpd, pan_vec[0], pan_vec[1]);
1540
1541 view_pan_exit(op);
1542 }
1543
1544 if (has_zoom) {
1545 float zoom_factor = zoom_sensitivity * ndof.dt * -pan_vec[2];
1546
1547 bool do_zoom_xy[2];
1548 view_zoom_axis_lock_defaults(C, do_zoom_xy);
1549
1550 view_zoomdrag_init(C, op);
1551
1552 v2dViewZoomData *vzd = static_cast<v2dViewZoomData *>(op->customdata);
1554 C, vzd, do_zoom_xy[0] ? zoom_factor : 0.0f, do_zoom_xy[1] ? zoom_factor : 0.0f);
1555
1556 view_zoomstep_exit(C, op);
1557 }
1558
1559 return OPERATOR_FINISHED;
1560}
1561
1562static void VIEW2D_OT_ndof(wmOperatorType *ot)
1563{
1564 /* identifiers */
1565 ot->name = "NDOF Pan/Zoom";
1566 ot->idname = "VIEW2D_OT_ndof";
1567 ot->description = "Use a 3D mouse device to pan/zoom the view";
1568
1569 /* API callbacks. */
1570 ot->invoke = view2d_ndof_invoke;
1571 ot->poll = view2d_poll;
1572
1573 /* flags */
1574 ot->flag = OPTYPE_LOCK_BYPASS;
1575}
1576#endif /* WITH_INPUT_NDOF */
1577
1579
1580/* -------------------------------------------------------------------- */
1583
1589
1598static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
1599{
1600 const float size_a[2] = {BLI_rctf_size_x(rect_a), BLI_rctf_size_y(rect_a)};
1601 const float size_b[2] = {BLI_rctf_size_x(rect_b), BLI_rctf_size_y(rect_b)};
1602 const float cent_a[2] = {BLI_rctf_cent_x(rect_a), BLI_rctf_cent_y(rect_a)};
1603 const float cent_b[2] = {BLI_rctf_cent_x(rect_b), BLI_rctf_cent_y(rect_b)};
1604
1605 float fac_max = 0.0f;
1606
1607 for (int i = 0; i < 2; i++) {
1608 /* axis translation normalized to scale */
1609 float tfac = fabsf(cent_a[i] - cent_b[i]) / min_ff(size_a[i], size_b[i]);
1610 fac_max = max_ff(fac_max, tfac);
1611 if (fac_max >= 1.0f) {
1612 break;
1613 }
1614
1615 /* axis scale difference, x2 so doubling or half gives 1.0f */
1616 tfac = (1.0f - (min_ff(size_a[i], size_b[i]) / max_ff(size_a[i], size_b[i]))) * 2.0f;
1617 fac_max = max_ff(fac_max, tfac);
1618 if (fac_max >= 1.0f) {
1619 break;
1620 }
1621 }
1622 return min_ff(fac_max, 1.0f);
1623}
1624
1626 ARegion *region,
1627 const rctf *cur,
1628 const int smooth_viewtx)
1629{
1631 wmWindow *win = CTX_wm_window(C);
1632
1633 View2D *v2d = &region->v2d;
1634 SmoothView2DStore sms = {{0}};
1635 bool ok = false;
1636 float fac = 1.0f;
1637
1638 /* initialize sms */
1639 sms.new_cur = v2d->cur;
1640
1641 /* store the options we want to end with */
1642 if (cur) {
1643 sms.new_cur = *cur;
1644 }
1645
1646 if (cur) {
1647 fac = smooth_view_rect_to_fac(&v2d->cur, cur);
1648 }
1649
1650 if (smooth_viewtx && fac > FLT_EPSILON) {
1651 bool changed = false;
1652
1653 if (BLI_rctf_compare(&sms.new_cur, &v2d->cur, FLT_EPSILON) == false) {
1654 changed = true;
1655 }
1656
1657 /* The new view is different from the old one
1658 * so animate the view */
1659 if (changed) {
1660 sms.orig_cur = v2d->cur;
1661
1662 sms.time_allowed = double(smooth_viewtx) / 1000.0;
1663
1664 /* scale the time allowed the change in view */
1665 sms.time_allowed *= double(fac);
1666
1667 /* keep track of running timer! */
1668 if (v2d->sms == nullptr) {
1669 v2d->sms = MEM_new<SmoothView2DStore>(__func__);
1670 }
1671 *v2d->sms = sms;
1672 if (v2d->smooth_timer) {
1673 WM_event_timer_remove(wm, win, v2d->smooth_timer);
1674 }
1675 /* TIMER1 is hard-coded in key-map. */
1676 v2d->smooth_timer = WM_event_timer_add(wm, win, TIMER1, 1.0 / 100.0);
1677
1678 ok = true;
1679 }
1680 }
1681
1682 /* if we get here nothing happens */
1683 if (ok == false) {
1684 v2d->cur = sms.new_cur;
1685
1689 }
1690}
1691
1692/* only meant for timer usage */
1694 wmOperator * /*op*/,
1695 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. */
1753 ot->invoke = view2d_smoothview_invoke;
1754 ot->poll = view2d_poll;
1755
1756 /* flags */
1757 ot->flag = OPTYPE_INTERNAL;
1758
1759 /* rna */
1761}
1762
1764
1765/* -------------------------------------------------------------------- */
1768
1778
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
1824static short scrollbar_zone_get(int mouse, int sh_min, int sh_max)
1825{
1826 /* Check if mouse is in either zoom handle. */
1827 bool in_max = ((mouse >= (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1828 (mouse <= (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1829 bool in_min = ((mouse <= (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1830 (mouse >= (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1831
1832 /* Check if mouse is in scrollbar or outside, on the scrollbar track. */
1833 bool in_bar = ((mouse < (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) &&
1834 (mouse > (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT)));
1835 const bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1836 const bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT);
1837
1838 if (in_bar) {
1839 return SCROLLHANDLE_BAR;
1840 }
1841 if (in_max) {
1842 return SCROLLHANDLE_MAX;
1843 }
1844 if (in_min) {
1845 return SCROLLHANDLE_MIN;
1846 }
1847 if (out_min) {
1849 }
1850 if (out_max) {
1852 }
1853
1854 /* unlikely to happen, though we just cover it in case */
1855 return SCROLLHANDLE_BAR;
1856}
1857
1859{
1860 const wmWindow *win = CTX_wm_window(C);
1861 if (!(win && win->eventstate)) {
1862 return false;
1863 }
1864 if (!view2d_poll(C)) {
1865 return false;
1866 }
1867 ARegion *region = CTX_wm_region(C);
1868 View2D *v2d = &region->v2d;
1869 /* Check if mouse in scroll-bars, if they're enabled. */
1870 return (UI_view2d_mouse_in_scrollers(region, v2d, win->eventstate->xy) != 0);
1871}
1872
1873/* Initialize #wmOperator.customdata for scroller manipulation operator. */
1875 wmOperator *op,
1876 const wmEvent *event,
1877 const char in_scroller)
1878{
1879 ARegion *region = CTX_wm_region(C);
1880 View2D *v2d = &region->v2d;
1881
1882 /* set custom-data for operator */
1884 op->customdata = vsm;
1885
1886 /* set general data */
1887 vsm->v2d = v2d;
1888 vsm->region = region;
1889 vsm->scroller = in_scroller;
1890
1891 /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1892 vsm->lastx = event->xy[0];
1893 vsm->lasty = event->xy[1];
1894 /* `zone` depends on where mouse is relative to bubble
1895 * - zooming must be allowed on this axis, otherwise, default to pan.
1896 */
1897 View2DScrollers scrollers;
1898 /* Reconstruct the custom scroller mask passed to #UI_view2d_scrollers_draw().
1899 *
1900 * Some editors like the File Browser, Spreadsheet or scrubbing UI already set up custom masks
1901 * for scroll-bars (they don't cover the whole region width or height), these need to be
1902 * considered, otherwise coords for `scrollbar_zone_get` later are not compatible. This
1903 * should be a reliable way to do it. Otherwise the custom scroller mask could also be stored in
1904 * #View2D.
1905 */
1906 rcti scroller_mask = v2d->hor;
1907 BLI_rcti_union(&scroller_mask, &v2d->vert);
1908
1909 view2d_scrollers_calc(v2d, &scroller_mask, &scrollers);
1910
1911 /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
1912 * moving the scroll bars has far too little effect and the view can get stuck #31476. */
1913 rctf tot_cur_union = v2d->tot;
1914 BLI_rctf_union(&tot_cur_union, &v2d->cur);
1915
1916 if (in_scroller == 'h') {
1917 /* horizontal scroller - calculate adjustment factor first */
1918 const float mask_size = float(BLI_rcti_size_x(&v2d->hor));
1919 vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size;
1920
1921 /* pixel rounding */
1922 vsm->fac_round = BLI_rctf_size_x(&v2d->cur) / float(BLI_rcti_size_x(&region->winrct) + 1);
1923
1924 /* get 'zone' (i.e. which part of scroller is activated) */
1925 vsm->zone = scrollbar_zone_get(event->mval[0], scrollers.hor_min, scrollers.hor_max);
1926
1928 /* default to scroll, as handles not usable */
1929 vsm->zone = SCROLLHANDLE_BAR;
1930 }
1931
1932 vsm->scrollbarwidth = scrollers.hor_max - scrollers.hor_min;
1933 vsm->scrollbar_orig = ((scrollers.hor_max + scrollers.hor_min) / 2) + region->winrct.xmin;
1934 }
1935 else {
1936 /* vertical scroller - calculate adjustment factor first */
1937 const float mask_size = float(BLI_rcti_size_y(&v2d->vert));
1938 vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size;
1939
1940 /* pixel rounding */
1941 vsm->fac_round = BLI_rctf_size_y(&v2d->cur) / float(BLI_rcti_size_y(&region->winrct) + 1);
1942
1943 /* Get `zone` (i.e. which part of scroller is activated). */
1944 vsm->zone = scrollbar_zone_get(event->mval[1], scrollers.vert_min, scrollers.vert_max);
1945
1947 /* default to scroll, as handles not usable */
1948 vsm->zone = SCROLLHANDLE_BAR;
1949 }
1950
1951 vsm->scrollbarwidth = scrollers.vert_max - scrollers.vert_min;
1952 vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin;
1953 }
1954
1955 vsm->v2d->flag |= V2D_IS_NAVIGATING;
1956
1958}
1959
1960/* Cleanup temp custom-data. */
1962{
1963 if (op->customdata) {
1964 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1965
1967 vsm->v2d->flag &= ~V2D_IS_NAVIGATING;
1968
1969 MEM_freeN(vsm);
1970 op->customdata = nullptr;
1971
1973 }
1974}
1975
1977{
1979}
1980
1981/* apply transform to view (i.e. adjust 'cur' rect) */
1983{
1984 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
1985 View2D *v2d = vsm->v2d;
1986
1987 /* calculate amount to move view by */
1988 float temp = vsm->fac * vsm->delta;
1989
1990 /* round to pixel */
1991 temp = roundf(temp / vsm->fac_round) * vsm->fac_round;
1992
1993 /* type of movement */
1994 switch (vsm->zone) {
1995 case SCROLLHANDLE_MIN:
1996 /* only expand view on axis if zoom is allowed */
1997 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
1998 v2d->cur.xmin -= temp;
1999 }
2000 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
2001 v2d->cur.ymin -= temp;
2002 }
2003 break;
2004
2005 case SCROLLHANDLE_MAX:
2006
2007 /* only expand view on axis if zoom is allowed */
2008 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X)) {
2009 v2d->cur.xmax += temp;
2010 }
2011 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y)) {
2012 v2d->cur.ymax += temp;
2013 }
2014 break;
2015
2018 case SCROLLHANDLE_BAR:
2019 default:
2020 /* only move view on an axis if panning is allowed */
2021 if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
2022 v2d->cur.xmin += temp;
2023 v2d->cur.xmax += temp;
2024 }
2025 if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
2026 v2d->cur.ymin += temp;
2027 v2d->cur.ymax += temp;
2028 }
2029 break;
2030 }
2031
2032 /* Inform v2d about changes after this operation. */
2034
2035 /* request updates to be done... */
2038}
2039
2045{
2046 v2dScrollerMove *vsm = static_cast<v2dScrollerMove *>(op->customdata);
2047 const bool use_page_size_y = vsm->v2d->flag & V2D_SNAP_TO_PAGESIZE_Y;
2048
2049 /* execute the events */
2050 switch (event->type) {
2051 case MOUSEMOVE: {
2052 float delta = 0.0f;
2053
2054 /* calculate new delta transform, then store mouse-coordinates for next-time */
2056 /* if using bar (i.e. 'panning') or 'max' zoom widget */
2057 switch (vsm->scroller) {
2058 case 'h': /* horizontal scroller - so only horizontal movement
2059 * ('cur' moves opposite to mouse) */
2060 delta = float(event->xy[0] - vsm->lastx);
2061 break;
2062 case 'v': /* vertical scroller - so only vertical movement
2063 * ('cur' moves opposite to mouse) */
2064 delta = float(event->xy[1] - vsm->lasty);
2065 break;
2066 }
2067 }
2068 else if (vsm->zone == SCROLLHANDLE_MIN) {
2069 /* using 'min' zoom widget */
2070 switch (vsm->scroller) {
2071 case 'h': /* horizontal scroller - so only horizontal movement
2072 * ('cur' moves with mouse) */
2073 delta = float(vsm->lastx - event->xy[0]);
2074 break;
2075 case 'v': /* vertical scroller - so only vertical movement
2076 * ('cur' moves with to mouse) */
2077 delta = float(vsm->lasty - event->xy[1]);
2078 break;
2079 }
2080 }
2081
2082 /* Page snapping: When panning for more than half a page size, snap to the next page. */
2083 if (use_page_size_y && (vsm->scroller == 'v')) {
2084 delta = view2d_scroll_delta_y_snap_page_size(*vsm->v2d, delta * vsm->fac) / vsm->fac;
2085 }
2086
2087 if (IS_EQF(delta, 0.0f)) {
2088 break;
2089 }
2090
2091 vsm->delta = delta;
2092 /* store previous coordinates */
2093 vsm->lastx = event->xy[0];
2094 vsm->lasty = event->xy[1];
2095
2097 break;
2098 }
2099 case LEFTMOUSE:
2100 case MIDDLEMOUSE:
2101 if (event->val == KM_RELEASE) {
2102 /* single-click was in empty space outside bubble, so scroll by 1 'page' */
2104 if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE) {
2105 vsm->delta = -vsm->scrollbarwidth * 0.8f;
2106 }
2107 else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE) {
2108 vsm->delta = vsm->scrollbarwidth * 0.8f;
2109 }
2110
2113 return OPERATOR_FINISHED;
2114 }
2115
2116 /* Otherwise, end the drag action. */
2117 if (vsm->lastx || vsm->lasty) {
2119 return OPERATOR_FINISHED;
2120 }
2121 }
2122 break;
2123 default: {
2124 break;
2125 }
2126 }
2127
2129}
2130
2131/* a click (or click drag in progress)
2132 * should have occurred, so check if it happened in scrollbar */
2134{
2135 ARegion *region = CTX_wm_region(C);
2136 View2D *v2d = &region->v2d;
2137
2138 /* check if mouse in scroll-bars, if they're enabled */
2139 const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy);
2140
2141 /* if in a scroller, init customdata then set modal handler which will
2142 * catch mouse-down to start doing useful stuff */
2143 if (in_scroller) {
2144 /* initialize customdata */
2145 scroller_activate_init(C, op, event, in_scroller);
2147
2148 /* Support for quick jump to location - GTK and QT do this on Linux. */
2149 if (event->type == MIDDLEMOUSE) {
2150 switch (vsm->scroller) {
2151 case 'h': /* horizontal scroller - so only horizontal movement
2152 * ('cur' moves opposite to mouse) */
2153 vsm->delta = float(event->xy[0] - vsm->scrollbar_orig);
2154 break;
2155 case 'v': /* vertical scroller - so only vertical movement
2156 * ('cur' moves opposite to mouse) */
2157 vsm->delta = float(event->xy[1] - vsm->scrollbar_orig);
2158 break;
2159 }
2161
2162 vsm->zone = SCROLLHANDLE_BAR;
2163 }
2164
2165 /* Check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
2166 * NOTE: see `view2d.cc` for latest conditions, and keep this in sync with that. */
2168 if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) == 0) ||
2169 ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) == 0))
2170 {
2171 /* switch to bar (i.e. no scaling gets handled) */
2172 vsm->zone = SCROLLHANDLE_BAR;
2173 }
2174 }
2175
2176 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
2177 if (vsm->zone == SCROLLHANDLE_BAR) {
2178 if (((vsm->scroller == 'h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
2179 ((vsm->scroller == 'v') && (v2d->keepofs & V2D_LOCKOFS_Y)))
2180 {
2181 /* free customdata initialized */
2183
2184 /* can't catch this event for ourselves, so let it go to someone else? */
2185 return OPERATOR_PASS_THROUGH;
2186 }
2187 }
2188
2189 /* zone is also inappropriate if scroller is not visible... */
2190 if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_FULLR)) ||
2191 ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_FULLR)))
2192 {
2193 /* free customdata initialized */
2195
2196 /* can't catch this event for ourselves, so let it go to someone else? */
2197 /* XXX NOTE: if handlers use mask rect to clip input, input will fail for this case. */
2198 return OPERATOR_PASS_THROUGH;
2199 }
2200
2201 /* activate the scroller */
2202 if (vsm->scroller == 'h') {
2204 }
2205 else {
2207 }
2208
2209 /* still ok, so can add */
2212 }
2213
2214 /* not in scroller, so nothing happened...
2215 * (pass through let's something else catch event) */
2216 return OPERATOR_PASS_THROUGH;
2217}
2218
2219/* LMB-Drag in Scrollers - not repeatable operator! */
2221{
2222 /* identifiers */
2223 ot->name = "Scroller Activate";
2224 ot->description = "Scroll view by mouse click and drag";
2225 ot->idname = "VIEW2D_OT_scroller_activate";
2226
2227 /* flags */
2228 ot->flag = OPTYPE_BLOCKING;
2229
2230 /* API callbacks. */
2231 ot->invoke = scroller_activate_invoke;
2232 ot->modal = scroller_activate_modal;
2233 ot->cancel = scroller_activate_cancel;
2234
2235 ot->poll = scroller_activate_poll;
2236}
2237
2239
2240/* -------------------------------------------------------------------- */
2243
2245{
2246 const uiStyle *style = UI_style_get();
2247 ARegion *region = CTX_wm_region(C);
2248 View2D *v2d = &region->v2d;
2249 const int snap_test = ED_region_snap_size_test(region);
2250
2251 region->category_scroll = 0;
2252
2253 /* zoom 1.0 */
2254 const int winx = float(BLI_rcti_size_x(&v2d->mask) + 1);
2255 const int winy = float(BLI_rcti_size_y(&v2d->mask) + 1);
2256
2257 v2d->cur.xmax = v2d->cur.xmin + winx;
2258 v2d->cur.ymax = v2d->cur.ymin + winy;
2259
2260 /* align */
2261 if (v2d->align) {
2262 /* posx and negx flags are mutually exclusive, so watch out */
2263 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
2264 v2d->cur.xmax = 0.0f;
2265 v2d->cur.xmin = -winx * style->panelzoom;
2266 }
2267 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
2268 v2d->cur.xmax = winx * style->panelzoom;
2269 v2d->cur.xmin = 0.0f;
2270 }
2271
2272 /* - posx and negx flags are mutually exclusive, so watch out */
2273 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
2274 v2d->cur.ymax = 0.0f;
2275 v2d->cur.ymin = -winy * style->panelzoom;
2276 }
2277 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
2278 v2d->cur.ymax = winy * style->panelzoom;
2279 v2d->cur.ymin = 0.0f;
2280 }
2281 }
2282
2283 /* Inform v2d about changes after this operation. */
2285
2286 if (ED_region_snap_size_apply(region, snap_test)) {
2287 ScrArea *area = CTX_wm_area(C);
2288 ED_area_tag_redraw(area);
2290 }
2291
2292 /* request updates to be done... */
2293 ED_region_tag_redraw(region);
2295
2297
2298 return OPERATOR_FINISHED;
2299}
2300
2302{
2303 /* identifiers */
2304 ot->name = "Reset View";
2305 ot->description = "Reset the view";
2306 ot->idname = "VIEW2D_OT_reset";
2307
2308 /* API callbacks. */
2309 ot->exec = reset_exec;
2310 ot->poll = view2d_poll;
2311}
2312
2314
2315/* -------------------------------------------------------------------- */
2318
2345
2347{
2348 WM_keymap_ensure(keyconf, "View2D", SPACE_EMPTY, RGN_TYPE_WINDOW);
2349}
2350
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float max_ff(float a, float b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE bool is_zero_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
void BLI_rcti_union(struct rcti *rct_a, const struct rcti *rct_b)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
void BLI_rctf_union(struct rctf *rct_a, const struct rctf *rct_b)
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:189
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:185
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
void BLI_rctf_interp(struct rctf *rect, const struct rctf *rect_a, const struct rctf *rect_b, float fac)
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
bool BLI_rctf_compare(const struct rctf *rect_a, const struct rctf *rect_b, float limit)
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:65
#define ELEM(...)
#define IS_EQF(a, b)
#define IN_RANGE_INCL(a, b, c)
@ RGN_TYPE_WINDOW
@ SPACE_ACTION
@ SPACE_SEQ
@ SPACE_EMPTY
@ SPACE_GRAPH
#define UI_SCALE_FAC
@ USER_ZOOM_INVERT
@ USER_ZOOM_TO_MOUSEPOS
@ USER_ZOOM_HORIZ
#define NDOF_PIXELS_PER_SECOND
@ USER_ZOOM_SCALE
@ USER_ZOOM_CONTINUE
@ V2D_SCROLL_V_ACTIVE
@ V2D_SCROLL_H_ACTIVE
@ V2D_SCROLL_HORIZONTAL_FULLR
@ V2D_SCROLL_VERTICAL_FULLR
@ V2D_SCROLL_VERTICAL_HANDLES
@ V2D_SCROLL_HORIZONTAL_HANDLES
@ V2D_IS_NAVIGATING
@ V2D_IS_INIT
@ V2D_ZOOM_IGNORE_KEEPOFS
@ V2D_SNAP_TO_PAGESIZE_Y
@ V2D_LOCKOFS_X
@ V2D_LOCKOFS_Y
@ V2D_KEEPOFS_Y
@ V2D_KEEPOFS_X
@ V2D_ALIGN_NO_NEG_X
@ V2D_ALIGN_NO_NEG_Y
@ V2D_ALIGN_NO_POS_Y
@ V2D_ALIGN_NO_POS_X
@ V2D_LIMITZOOM
@ V2D_LOCKZOOM_X
@ V2D_KEEPASPECT
@ V2D_LOCKZOOM_Y
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:714
int ED_region_snap_size_test(const ARegion *region)
Definition area.cc:4221
bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2])
Definition area_query.cc:88
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:659
bool ED_region_snap_size_apply(ARegion *region, int snap_flag)
Definition area.cc:4234
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
Read Guarded memory(de)allocation.
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
const uiStyle * UI_style_get()
void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
Definition view2d.cc:865
#define V2D_LOCK_COPY
Definition UI_view2d.hh:85
void UI_view2d_edge_pan_operator_init(bContext *C, View2DEdgePanData *vpd, wmOperator *op)
char char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2]) ATTR_NONNULL(1
void UI_view2d_curRect_changed(const bContext *C, View2D *v2d)
Definition view2d.cc:833
void UI_view2d_edge_pan_operator_properties(wmOperatorType *ot)
#define V2D_SCROLL_HANDLE_SIZE_HOTSPOT
Definition UI_view2d.hh:71
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1667
void void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event)
void UI_view2d_zoom_cache_reset()
Definition view2d.cc:1041
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1674
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#define NC_SCREEN
Definition WM_types.hh:374
#define NA_EDITED
Definition WM_types.hh:581
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_LOCK_BYPASS
Definition WM_types.hh:205
@ OPTYPE_GRAB_CURSOR_XY
Definition WM_types.hh:188
#define U
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define fabsf(x)
#define abs
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
#define MEM_SAFE_FREE(v)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
VecBase< float, 3 > float3
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define FLT_MAX
Definition stdcycles.h:14
float minzoom
struct SmoothView2DStore * sms
short scroll_ui
short keepzoom
struct wmTimer * smooth_timer
short keepofs
float maxzoom
float xmax
float xmin
float ymax
float ymin
int ymin
int xmin
float panelzoom
bool do_category_scroll
bScreen * screen
Definition view2d_ops.cc:86
ARegion * region
Definition view2d_ops.cc:90
ScrArea * area
Definition view2d_ops.cc:88
double edge_pan_last_time
double edge_pan_start_time_x
double edge_pan_start_time_y
wmTimer * timer
double timer_lastdraw
ARegion * region
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int xy[2]
Definition WM_types.hh:758
int mval[2]
Definition WM_types.hh:760
int prev_xy[2]
Definition WM_types.hh:817
void * customdata
Definition WM_types.hh:804
struct PointerRNA * ptr
double time_duration
Definition WM_types.hh:965
struct wmEvent * eventstate
i
Definition text_draw.cc:230
float view2d_page_size_y(const View2D &v2d)
Definition view2d.cc:77
void view2d_scrollers_calc(View2D *v2d, const rcti *mask_custom, View2DScrollers *r_scrollers)
Definition view2d.cc:1390
bool view2d_edge_pan_poll(bContext *C)
@ SCROLLHANDLE_BAR
@ SCROLLHANDLE_MIN_OUTSIDE
@ SCROLLHANDLE_MAX
@ SCROLLHANDLE_MIN
@ SCROLLHANDLE_MAX_OUTSIDE
static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
static wmOperatorStatus view_scrolldown_exec(bContext *C, wmOperator *op)
static wmOperatorStatus scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void VIEW2D_OT_scroll_left(wmOperatorType *ot)
static wmOperatorStatus view_zoomin_exec(bContext *C, wmOperator *op)
static void scroller_activate_apply(bContext *C, wmOperator *op)
void UI_view2d_smooth_view(const bContext *C, ARegion *region, const rctf *cur, const int smooth_viewtx)
static bool view_zoom_poll(bContext *C)
static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
void ED_operatortypes_view2d()
static void view_edge_pan_cancel(bContext *, wmOperator *op)
static wmOperatorStatus view_scrollright_exec(bContext *C, wmOperator *op)
static wmOperatorStatus view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void VIEW2D_OT_scroll_down(wmOperatorType *ot)
static wmOperatorStatus view_zoomout_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void view_zoomstep_apply(bContext *C, wmOperator *op)
static void VIEW2D_OT_pan(wmOperatorType *ot)
static short scrollbar_zone_get(int mouse, int sh_min, int sh_max)
static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const float facx, const float facy)
static wmOperatorStatus view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void view_zoomstep_exit(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_out(wmOperatorType *ot)
static wmOperatorStatus view_pan_exec(bContext *C, wmOperator *op)
static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dy)
static void scroller_activate_exit(bContext *C, wmOperator *op)
static wmOperatorStatus view_borderzoom_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
static void view_pan_exit(wmOperator *op)
static void VIEW2D_OT_zoom(wmOperatorType *ot)
static bool view2d_poll(bContext *C)
Definition view2d_ops.cc:40
static void view_zoomdrag_exit(bContext *C, wmOperator *op)
static void VIEW2D_OT_zoom_in(wmOperatorType *ot)
static wmOperatorStatus view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus view_zoomout_exec(bContext *C, wmOperator *op)
static void view_zoomdrag_apply(bContext *C, wmOperator *op)
static wmOperatorStatus view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void view_pan_cancel(bContext *, wmOperator *op)
static void scroller_activate_cancel(bContext *C, wmOperator *op)
static float view2d_scroll_delta_y_snap_page_size(const View2D &v2d, const float delta_y)
Definition view2d_ops.cc:52
void ED_keymap_view2d(wmKeyConfig *keyconf)
static wmOperatorStatus view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
static void view_pan_init(bContext *C, wmOperator *op)
static wmOperatorStatus view_scrollup_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_scroll_right(wmOperatorType *ot)
static wmOperatorStatus reset_exec(bContext *C, wmOperator *)
static wmOperatorStatus view_zoomdrag_exec(bContext *C, wmOperator *op)
static void view_pan_apply(bContext *C, wmOperator *op)
static bool view_pan_poll(bContext *C)
static wmOperatorStatus view_scrollleft_exec(bContext *C, wmOperator *op)
static void VIEW2D_OT_edge_pan(wmOperatorType *ot)
static void view_zoomdrag_cancel(bContext *C, wmOperator *op)
static void VIEW2D_OT_reset(wmOperatorType *ot)
static wmOperatorStatus view2d_smoothview_invoke(bContext *C, wmOperator *, const wmEvent *event)
static bool scroller_activate_poll(bContext *C)
static void VIEW2D_OT_smoothview(wmOperatorType *ot)
static wmOperatorStatus scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void scroller_activate_init(bContext *C, wmOperator *op, const wmEvent *event, const char in_scroller)
static void view_zoomdrag_init(bContext *C, wmOperator *op)
static wmOperatorStatus view_zoomin_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:52
@ WM_CURSOR_NS_SCROLL
Definition wm_cursors.hh:53
@ WM_CURSOR_EW_SCROLL
Definition wm_cursors.hh:54
int WM_event_absolute_delta_y(const wmEvent *event)
int WM_event_absolute_delta_x(const wmEvent *event)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_event_add_mousemove(wmWindow *win)
@ MOUSEPAN
@ TIMER
@ MOUSEZOOM
@ TIMER1
@ MOUSEMOVE
@ LEFTMOUSE
@ NDOF_MOTION
@ MIDDLEMOUSE
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:893
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_border_to_rctf(wmOperator *op, rctf *r_rect)
void WM_operator_properties_use_cursor_init(wmOperatorType *ot)
void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
int WM_operator_smooth_viewtx_get(const wmOperator *op)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
wmWindow * WM_window_find_under_cursor(wmWindow *win, const int event_xy[2], int r_event_xy_other[2])
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)