Blender V5.0
view2d_edge_pan.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_context.hh"
10
11#include "BLI_math_base.h"
12#include "BLI_rect.h"
13#include "BLI_time.h"
14
15#include "ED_screen.hh"
16
17#include "RNA_access.hh"
18#include "RNA_define.hh"
19
20#include "UI_view2d.hh"
21
22#include "WM_api.hh"
23#include "WM_types.hh"
24
25#include "view2d_intern.hh"
26
27/* -------------------------------------------------------------------- */
30
32{
33 ARegion *region = CTX_wm_region(C);
34
35 /* Check if there's a region in context to work with. */
36 if (region == nullptr) {
37 return false;
38 }
39
40 View2D *v2d = &region->v2d;
41
42 /* Check that 2d-view can pan. */
43 if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) {
44 return false;
45 }
46
47 /* View can pan. */
48 return true;
49}
50
53 float inside_pad,
54 float outside_pad,
55 float speed_ramp,
56 float max_speed,
57 float delay,
58 float zoom_influence)
59{
60 if (!view2d_edge_pan_poll(C)) {
61 return;
62 }
63
64 /* Set pointers to owners. */
65 vpd->screen = CTX_wm_screen(C);
66 vpd->area = CTX_wm_area(C);
67 vpd->region = CTX_wm_region(C);
68 vpd->v2d = &vpd->region->v2d;
70
71 BLI_assert(speed_ramp > 0.0f);
72 vpd->inside_pad = inside_pad;
73 vpd->outside_pad = outside_pad;
74 vpd->speed_ramp = speed_ramp;
75 vpd->max_speed = max_speed;
76 vpd->delay = delay;
77 vpd->zoom_influence = zoom_influence;
78
79 vpd->enabled = false;
80
81 /* Calculate translation factor, based on size of view. */
82 const float winx = float(BLI_rcti_size_x(&vpd->region->winrct) + 1);
83 const float winy = float(BLI_rcti_size_y(&vpd->region->winrct) + 1);
84 vpd->facx = BLI_rctf_size_x(&vpd->v2d->cur) / winx;
85 vpd->facy = BLI_rctf_size_y(&vpd->v2d->cur) / winy;
86
88}
89
91 View2DEdgePanData *vpd, float xmin, float xmax, float ymin, float ymax)
92{
93 BLI_rctf_init(&vpd->limit, xmin, xmax, ymin, ymax);
94}
95
103
109 int pan_dir_x,
110 int pan_dir_y,
111 const double current_time)
112{
113 if (pan_dir_x == 0) {
114 vpd->edge_pan_start_time_x = 0.0;
115 }
116 else if (vpd->edge_pan_start_time_x == 0.0) {
117 vpd->edge_pan_start_time_x = current_time;
118 }
119 if (pan_dir_y == 0) {
120 vpd->edge_pan_start_time_y = 0.0;
121 }
122 else if (vpd->edge_pan_start_time_y == 0.0) {
123 vpd->edge_pan_start_time_y = current_time;
124 }
125}
126
133static float smootherstep(const float domain_max, float x)
134{
135 x = clamp_f(x / domain_max, 0.0, 1.0);
136 return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
137}
138
140 int event_loc,
141 bool x_dir,
142 const double current_time)
143{
144 ARegion *region = vpd->region;
145
146 /* Find the distance from the start of the drag zone. */
147 const int pad = vpd->inside_pad * U.widget_unit;
148 const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + pad;
149 const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - pad;
150 int distance = 0.0;
151 if (event_loc > max) {
152 distance = event_loc - max;
153 }
154 else if (event_loc < min) {
155 distance = min - event_loc;
156 }
157 else {
158 BLI_assert_msg(0, "Calculating speed outside of pan zones");
159 return 0.0f;
160 }
161 float distance_factor = distance / (vpd->speed_ramp * U.widget_unit);
162 CLAMP(distance_factor, 0.0f, 1.0f);
163
164 /* Apply a fade in to the speed based on a start time delay. */
165 const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y;
166 const float delay_factor = vpd->delay > 0.01f ?
167 smootherstep(vpd->delay, float(current_time - start_time)) :
168 1.0f;
169
170 /* Zoom factor increases speed when zooming in and decreases speed when zooming out. */
171 const float zoomx = float(BLI_rcti_size_x(&region->winrct) + 1) /
172 BLI_rctf_size_x(&region->v2d.cur);
173 const float zoom_factor = 1.0f + std::clamp(vpd->zoom_influence, 0.0f, 1.0f) * (zoomx - 1.0f);
174
175 return distance_factor * delay_factor * zoom_factor * vpd->max_speed * U.widget_unit *
177}
178
179static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, float dy)
180{
181 View2D *v2d = vpd->v2d;
182 if (!v2d) {
183 return;
184 }
185
186 /* Calculate amount to move view by. */
187 dx *= vpd->facx;
188 dy *= vpd->facy;
189
190 /* Only move view on an axis if change is allowed. */
191 if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) {
192 v2d->cur.xmin += dx;
193 v2d->cur.xmax += dx;
194 }
195 if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) {
196 v2d->cur.ymin += dy;
197 v2d->cur.ymax += dy;
198 }
199
200 if (dx != 0.0f || dy != 0.0f) {
201 /* Inform v2d about changes after this operation. */
203
204 /* Don't rebuild full tree in outliner, since we're just changing our view. */
206
207 /* Request updates to be done. */
209
210 UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY);
211 }
212}
213
215{
216 ARegion *region = vpd->region;
217
218 rcti inside_rect, outside_rect;
219 inside_rect = region->winrct;
220 outside_rect = region->winrct;
221 BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit);
222 BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit);
223
224 /* Check if we can actually start the edge pan (e.g. adding nodes outside the view will start
225 * disabled). */
226 if (BLI_rcti_isect_pt_v(&inside_rect, xy)) {
227 /* We are inside once, can start. */
228 vpd->enabled = true;
229 }
230
231 const rctf *cur = &vpd->v2d->cur;
232 const rctf *limit = &vpd->limit;
233
234 int pan_dir_x = 0;
235 int pan_dir_y = 0;
236 if (vpd->enabled && ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy))) {
237 /* Find whether the mouse is beyond X and Y edges. */
238 if (xy[0] > inside_rect.xmax && cur->xmax < limit->xmax) {
239 pan_dir_x = 1;
240 }
241 else if (xy[0] < inside_rect.xmin && cur->xmin > limit->xmin) {
242 pan_dir_x = -1;
243 }
244 if (xy[1] > inside_rect.ymax && cur->ymax < limit->ymax) {
245 pan_dir_y = 1;
246 }
247 else if (xy[1] < inside_rect.ymin && cur->ymin > limit->ymin) {
248 pan_dir_y = -1;
249 }
250 }
251
252 const double current_time = BLI_time_now_seconds();
253 edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time);
254
255 /* Calculate the delta since the last time the operator was called. */
256 const float dtime = float(current_time - vpd->edge_pan_last_time);
257 float dx = 0.0f, dy = 0.0f;
258 if (pan_dir_x != 0) {
259 const float speed = view2d_edge_pan_speed(vpd, xy[0], true, current_time);
260 dx = dtime * speed * float(pan_dir_x);
261 }
262 if (pan_dir_y != 0) {
263 const float speed = view2d_edge_pan_speed(vpd, xy[1], false, current_time);
264 dy = dtime * speed * float(pan_dir_y);
265 }
266 vpd->edge_pan_last_time = current_time;
267
268 /* Pan, clamping inside the regions total bounds. */
269 edge_pan_apply_delta(C, vpd, dx, dy);
270}
271
273{
274 /* Only mouse-move events matter here, ignore others. */
275 if (event->type != MOUSEMOVE) {
276 return;
277 }
278
279 UI_view2d_edge_pan_apply(C, vpd, event->xy);
280}
281
283{
284 View2D *v2d = vpd->v2d;
285 if (!v2d) {
286 return;
287 }
288
289 v2d->cur = vpd->initial_rect;
290
291 /* Inform v2d about changes after this operation. */
293
294 /* Don't rebuild full tree in outliner, since we're just changing our view. */
296
297 /* Request updates to be done. */
299
300 UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY);
301}
302
304{
305 /* Default values for edge panning operators. */
307 /*inside_pad*/ 1.0f,
308 /*outside_pad*/ 0.0f,
309 /*speed_ramp*/ 1.0f,
310 /*max_speed*/ 500.0f,
311 /*delay*/ 1.0f,
312 /*zoom_influence*/ 0.0f);
313}
314
316 float inside_pad,
317 float outside_pad,
318 float speed_ramp,
319 float max_speed,
320 float delay,
321 float zoom_influence)
322{
324 ot->srna,
325 "inside_padding",
326 inside_pad,
327 0.0f,
328 100.0f,
329 "Inside Padding",
330 "Inside distance in UI units from the edge of the region within which to start panning",
331 0.0f,
332 100.0f);
334 ot->srna,
335 "outside_padding",
336 outside_pad,
337 0.0f,
338 100.0f,
339 "Outside Padding",
340 "Outside distance in UI units from the edge of the region at which to stop panning",
341 0.0f,
342 100.0f);
343 RNA_def_float(ot->srna,
344 "speed_ramp",
345 speed_ramp,
346 0.0f,
347 100.0f,
348 "Speed Ramp",
349 "Width of the zone in UI units where speed increases with distance from the edge",
350 0.0f,
351 100.0f);
352 RNA_def_float(ot->srna,
353 "max_speed",
354 max_speed,
355 0.0f,
356 10000.0f,
357 "Max Speed",
358 "Maximum speed in UI units per second",
359 0.0f,
360 10000.0f);
361 RNA_def_float(ot->srna,
362 "delay",
363 delay,
364 0.0f,
365 10.0f,
366 "Delay",
367 "Delay in seconds before maximum speed is reached",
368 0.0f,
369 10.0f);
370 RNA_def_float(ot->srna,
371 "zoom_influence",
372 zoom_influence,
373 0.0f,
374 1.0f,
375 "Zoom Influence",
376 "Influence of the zoom factor on scroll speed",
377 0.0f,
378 1.0f);
379}
380
382{
384 vpd,
385 RNA_float_get(op->ptr, "inside_padding"),
386 RNA_float_get(op->ptr, "outside_padding"),
387 RNA_float_get(op->ptr, "speed_ramp"),
388 RNA_float_get(op->ptr, "max_speed"),
389 RNA_float_get(op->ptr, "delay"),
390 RNA_float_get(op->ptr, "zoom_influence"));
391}
392
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE float clamp_f(float value, float min, float max)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y)
Definition rct.cc:629
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.cc:404
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
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define CLAMP(a, b, c)
#define UI_SCALE_FAC
@ V2D_LOCKOFS_X
@ V2D_LOCKOFS_Y
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:638
#define C
Definition RandGen.cpp:29
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_curRect_changed(const bContext *C, View2D *v2d)
Definition view2d.cc:833
int pad[32 - sizeof(int)]
#define U
nullptr float
float distance(VecOp< float, D >, VecOp< float, D >) RET
float RNA_float_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)
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
double edge_pan_last_time
Definition UI_view2d.hh:548
double edge_pan_start_time_x
Definition UI_view2d.hh:549
struct rctf limit
Definition UI_view2d.hh:516
double edge_pan_start_time_y
Definition UI_view2d.hh:549
short keepofs
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
wmEventType type
Definition WM_types.hh:757
int xy[2]
Definition WM_types.hh:761
struct PointerRNA * ptr
max
Definition text_draw.cc:251
static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, float dy)
static float view2d_edge_pan_speed(View2DEdgePanData *vpd, int event_loc, bool x_dir, const double current_time)
static void edge_pan_manage_delay_timers(View2DEdgePanData *vpd, int pan_dir_x, int pan_dir_y, const double current_time)
void UI_view2d_edge_pan_operator_init(bContext *C, View2DEdgePanData *vpd, wmOperator *op)
void UI_view2d_edge_pan_set_limits(View2DEdgePanData *vpd, float xmin, float xmax, float ymin, float ymax)
void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd)
void UI_view2d_edge_pan_operator_properties_ex(wmOperatorType *ot, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
void UI_view2d_edge_pan_init(bContext *C, View2DEdgePanData *vpd, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[2])
void UI_view2d_edge_pan_operator_properties(wmOperatorType *ot)
static float smootherstep(const float domain_max, float x)
void UI_view2d_edge_pan_reset(View2DEdgePanData *vpd)
void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event)
bool view2d_edge_pan_poll(bContext *C)
int xy[2]
Definition wm_draw.cc:178
void WM_event_add_mousemove(wmWindow *win)
@ MOUSEMOVE
wmOperatorType * ot
Definition wm_files.cc:4237