diff options
-rw-r--r-- | source/blender/editors/include/ED_node.h | 5 | ||||
-rw-r--r-- | source/blender/editors/include/UI_view2d.h | 75 | ||||
-rw-r--r-- | source/blender/editors/interface/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/interface/view2d_edge_pan.c | 345 | ||||
-rw-r--r-- | source/blender/editors/interface/view2d_ops.c | 160 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_intern.h | 4 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_relationships.cc | 11 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_dragdrop.c | 2 | ||||
-rw-r--r-- | source/blender/editors/transform/transform.h | 3 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_convert_node.c | 66 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_ops.c | 5 |
11 files changed, 514 insertions, 163 deletions
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 67a50b83bd6..ba65840dc99 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -49,6 +49,11 @@ typedef enum { } NodeBorder; #define NODE_GRID_STEPS 5 +#define NODE_EDGE_PAN_INSIDE_PAD 2 +#define NODE_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for node panning, use whole screen. */ +#define NODE_EDGE_PAN_SPEED_RAMP 1 +#define NODE_EDGE_PAN_MAX_SPEED 40 /* In UI units per second, slower than default. */ +#define NODE_EDGE_PAN_DELAY 1.0f /* space_node.c */ diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 64f881052a1..999f42efe65 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -105,8 +105,12 @@ struct ScrArea; struct bContext; struct bScreen; struct rctf; +struct rcti; +struct wmEvent; struct wmGizmoGroupType; struct wmKeyConfig; +struct wmOperator; +struct wmOperatorType; typedef struct View2DScrollers View2DScrollers; @@ -287,6 +291,77 @@ void UI_view2d_smooth_view(struct bContext *C, /* Caller passes in own idname. */ void VIEW2D_GGT_navigate_impl(struct wmGizmoGroupType *gzgt, const char *idname); +/* Edge pan */ + +/** + * Custom-data for view panning operators. + */ +typedef struct View2DEdgePanData { + /** Screen where view pan was initiated. */ + struct bScreen *screen; + /** Area where view pan was initiated. */ + struct ScrArea *area; + /** Region where view pan was initiated. */ + struct ARegion *region; + /** View2d we're operating in. */ + struct View2D *v2d; + + /** Inside distance in UI units from the edge of the region within which to start panning. */ + float inside_pad; + /** Outside distance in UI units from the edge of the region at which to stop panning. */ + float outside_pad; + /** + * Width of the zone in UI units where speed increases with distance from the edge. + * At the end of this zone max speed is reached. + */ + float speed_ramp; + /** Maximum speed in UI units per second. */ + float max_speed; + /** Delay in seconds before maximum speed is reached. */ + float delay; + + /** Amount to move view relative to zoom. */ + float facx, facy; + + /* Timers. */ + double edge_pan_last_time; + double edge_pan_start_time_x, edge_pan_start_time_y; +} View2DEdgePanData; + +bool UI_view2d_edge_pan_poll(struct bContext *C); + +void UI_view2d_edge_pan_init(struct bContext *C, + struct View2DEdgePanData *vpd, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay); + +void UI_view2d_edge_pan_reset(struct View2DEdgePanData *vpd); + +/* Apply transform to view (i.e. adjust 'cur' rect). */ +void UI_view2d_edge_pan_apply(struct bContext *C, struct View2DEdgePanData *vpd, int x, int y); + +/* Apply transform to view using mouse events. */ +void UI_view2d_edge_pan_apply_event(struct bContext *C, + struct View2DEdgePanData *vpd, + const struct wmEvent *event); + +void UI_view2d_edge_pan_operator_properties(struct wmOperatorType *ot); + +void UI_view2d_edge_pan_operator_properties_ex(struct wmOperatorType *ot, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay); + +/* Initialize panning data with operator settings. */ +void UI_view2d_edge_pan_operator_init(struct bContext *C, + struct View2DEdgePanData *vpd, + struct wmOperator *op); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 421019bebb8..5011a50ed73 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -75,6 +75,7 @@ set(SRC resources.c view2d.c view2d_draw.c + view2d_edge_pan.c view2d_gizmo_navigate.c view2d_ops.c diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c new file mode 100644 index 00000000000..38364a687fd --- /dev/null +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -0,0 +1,345 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup spnode + */ + +#include "BKE_context.h" + +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" +#include "UI_view2d.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* -------------------------------------------------------------------- */ +/** \name Edge Pan Operator Utilties + * \{ */ + +bool UI_view2d_edge_pan_poll(bContext *C) +{ + ARegion *region = CTX_wm_region(C); + + /* Check if there's a region in context to work with. */ + if (region == NULL) { + return false; + } + + View2D *v2d = ®ion->v2d; + + /* Check that 2d-view can pan. */ + if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) { + return false; + } + + /* View can pan. */ + return true; +} + +void UI_view2d_edge_pan_init(bContext *C, + View2DEdgePanData *vpd, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay) +{ + if (!UI_view2d_edge_pan_poll(C)) { + return; + } + + /* Set pointers to owners. */ + vpd->screen = CTX_wm_screen(C); + vpd->area = CTX_wm_area(C); + vpd->region = CTX_wm_region(C); + vpd->v2d = &vpd->region->v2d; + + BLI_assert(speed_ramp > 0.0f); + vpd->inside_pad = inside_pad; + vpd->outside_pad = outside_pad; + vpd->speed_ramp = speed_ramp; + vpd->max_speed = max_speed; + vpd->delay = delay; + + /* Calculate translation factor, based on size of view. */ + const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1); + const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1); + vpd->facx = (BLI_rctf_size_x(&vpd->v2d->cur)) / winx; + vpd->facy = (BLI_rctf_size_y(&vpd->v2d->cur)) / winy; + + UI_view2d_edge_pan_reset(vpd); +} + +void UI_view2d_edge_pan_reset(View2DEdgePanData *vpd) +{ + vpd->edge_pan_start_time_x = 0.0; + vpd->edge_pan_start_time_y = 0.0; + vpd->edge_pan_last_time = PIL_check_seconds_timer(); +} + +/** + * Reset the edge pan timers if the mouse isn't in the scroll zone and + * start the timers when the mouse enters a scroll zone. + */ +static void edge_pan_manage_delay_timers(View2DEdgePanData *vpd, + int pan_dir_x, + int pan_dir_y, + const double current_time) +{ + if (pan_dir_x == 0) { + vpd->edge_pan_start_time_x = 0.0; + } + else if (vpd->edge_pan_start_time_x == 0.0) { + vpd->edge_pan_start_time_x = current_time; + } + if (pan_dir_y == 0) { + vpd->edge_pan_start_time_y = 0.0; + } + else if (vpd->edge_pan_start_time_y == 0.0) { + vpd->edge_pan_start_time_y = current_time; + } +} + +/** + * Used to calculate a "fade in" factor for edge panning to make the interaction feel smooth + * and more purposeful. + * + * \note Assumes a domain_min of 0.0f. + */ +static float smootherstep(const float domain_max, float x) +{ + x = clamp_f(x / domain_max, 0.0, 1.0); + return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); +} + +static float edge_pan_speed(View2DEdgePanData *vpd, + int event_loc, + bool x_dir, + const double current_time) +{ + ARegion *region = vpd->region; + + /* Find the distance from the start of the drag zone. */ + const int pad = vpd->inside_pad * U.widget_unit; + const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + pad; + const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - pad; + int distance = 0.0; + if (event_loc > max) { + distance = event_loc - max; + } + else if (event_loc < min) { + distance = min - event_loc; + } + else { + BLI_assert(!"Calculating speed outside of pan zones"); + return 0.0f; + } + float distance_factor = distance / (vpd->speed_ramp * U.widget_unit); + CLAMP(distance_factor, 0.0f, 1.0f); + + /* Apply a fade in to the speed based on a start time delay. */ + const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; + const float delay_factor = smootherstep(vpd->delay, (float)(current_time - start_time)); + + return distance_factor * delay_factor * vpd->max_speed * U.widget_unit * (float)U.dpi_fac; +} + +static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, float dy) +{ + View2D *v2d = vpd->v2d; + if (!v2d) { + return; + } + + /* Calculate amount to move view by. */ + dx *= vpd->facx; + dy *= vpd->facy; + + /* Only move view on an axis if change is allowed. */ + if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) { + v2d->cur.xmin += dx; + v2d->cur.xmax += dx; + } + if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) { + v2d->cur.ymin += dy; + v2d->cur.ymax += dy; + } + + /* Inform v2d about changes after this operation. */ + UI_view2d_curRect_changed(C, v2d); + + /* Don't rebuild full tree in outliner, since we're just changing our view. */ + ED_region_tag_redraw_no_rebuild(vpd->region); + + /* Request updates to be done. */ + WM_event_add_mousemove(CTX_wm_window(C)); + + UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY); +} + +void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) +{ + ARegion *region = vpd->region; + + rcti inside_rect, outside_rect; + inside_rect = region->winrct; + outside_rect = region->winrct; + BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit); + BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit); + + int pan_dir_x = 0; + int pan_dir_y = 0; + if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt(&outside_rect, x, y)) { + /* Find whether the mouse is beyond X and Y edges. */ + if (x > inside_rect.xmax) { + pan_dir_x = 1; + } + else if (x < inside_rect.xmin) { + pan_dir_x = -1; + } + if (y > inside_rect.ymax) { + pan_dir_y = 1; + } + else if (y < inside_rect.ymin) { + pan_dir_y = -1; + } + } + + const double current_time = PIL_check_seconds_timer(); + edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time); + + /* Calculate the delta since the last time the operator was called. */ + const float dtime = (float)(current_time - vpd->edge_pan_last_time); + float dx = 0.0f, dy = 0.0f; + if (pan_dir_x != 0) { + const float speed = edge_pan_speed(vpd, x, true, current_time); + dx = dtime * speed * (float)pan_dir_x; + } + if (pan_dir_y != 0) { + const float speed = edge_pan_speed(vpd, y, false, current_time); + dy = dtime * speed * (float)pan_dir_y; + } + vpd->edge_pan_last_time = current_time; + + /* Pan, clamping inside the regions's total bounds. */ + edge_pan_apply_delta(C, vpd, dx, dy); +} + +void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event) +{ + /* Only mousemove events matter here, ignore others. */ + if (event->type != MOUSEMOVE) { + return; + } + + UI_view2d_edge_pan_apply(C, vpd, event->x, event->y); +} + +void UI_view2d_edge_pan_operator_properties(wmOperatorType *ot) +{ + /* Default values for edge panning operators. */ + UI_view2d_edge_pan_operator_properties_ex(ot, + /*inside_pad*/ 1.0f, + /*outside_pad*/ 0.0f, + /*speed_ramp*/ 1.0f, + /*max_speed*/ 500.0f, + /*delay*/ 1.0f); +} + +void UI_view2d_edge_pan_operator_properties_ex(struct wmOperatorType *ot, + float inside_pad, + float outside_pad, + float speed_ramp, + float max_speed, + float delay) +{ + RNA_def_float( + ot->srna, + "inside_padding", + inside_pad, + 0.0f, + 100.0f, + "Inside Padding", + "Inside distance in UI units from the edge of the region within which to start panning", + 0.0f, + 100.0f); + RNA_def_float( + ot->srna, + "outside_padding", + outside_pad, + 0.0f, + 100.0f, + "Outside Padding", + "Outside distance in UI units from the edge of the region at which to stop panning", + 0.0f, + 100.0f); + RNA_def_float(ot->srna, + "speed_ramp", + speed_ramp, + 0.0f, + 100.0f, + "Speed Ramp", + "Width of the zone in UI units where speed increases with distance from the edge", + 0.0f, + 100.0f); + RNA_def_float(ot->srna, + "max_speed", + max_speed, + 0.0f, + 10000.0f, + "Max Speed", + "Maximum speed in UI units per second", + 0.0f, + 10000.0f); + RNA_def_float(ot->srna, + "delay", + delay, + 0.0f, + 10.0f, + "Delay", + "Delay in seconds before maximum speed is reached", + 0.0f, + 10.0f); +} + +void UI_view2d_edge_pan_operator_init(bContext *C, View2DEdgePanData *vpd, wmOperator *op) +{ + UI_view2d_edge_pan_init(C, + vpd, + RNA_float_get(op->ptr, "inside_padding"), + RNA_float_get(op->ptr, "outside_padding"), + RNA_float_get(op->ptr, "speed_ramp"), + RNA_float_get(op->ptr, "max_speed"), + RNA_float_get(op->ptr, "delay")); +} + +/** \} */ diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 40c510af7e5..7ad28cd6069 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -341,162 +341,37 @@ static void VIEW2D_OT_pan(wmOperatorType *ot) * passes through. * \{ */ -/** Distance from the edge of the region within which to start panning. */ -#define EDGE_PAN_REGION_PAD (U.widget_unit) -/** Speed factor in pixels per second per pixel of distance from edge pan zone beginning. */ -#define EDGE_PAN_SPEED_PER_PIXEL (25.0f * (float)U.dpi_fac) -/** Delay before drag panning in seconds. */ -#define EDGE_PAN_DELAY 1.0f - /* set up modal operator and relevant settings */ static int view_edge_pan_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - /* Set up customdata. */ - view_pan_init(C, op); - - v2dViewPanData *vpd = op->customdata; - - vpd->edge_pan_start_time_x = 0.0; - vpd->edge_pan_start_time_y = 0.0; - vpd->edge_pan_last_time = PIL_check_seconds_timer(); + op->customdata = MEM_callocN(sizeof(View2DEdgePanData), "View2DEdgePanData"); + View2DEdgePanData *vpd = op->customdata; + UI_view2d_edge_pan_operator_init(C, vpd, op); WM_event_add_modal_handler(C, op); return (OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH); } -/** - * Reset the edge pan timers if the mouse isn't in the scroll zone and - * start the timers when the mouse enters a scroll zone. - */ -static void edge_pan_manage_delay_timers(v2dViewPanData *vpd, - int pan_dir_x, - int pan_dir_y, - const double current_time) -{ - if (pan_dir_x == 0) { - vpd->edge_pan_start_time_x = 0.0; - } - else if (vpd->edge_pan_start_time_x == 0.0) { - vpd->edge_pan_start_time_x = current_time; - } - if (pan_dir_y == 0) { - vpd->edge_pan_start_time_y = 0.0; - } - else if (vpd->edge_pan_start_time_y == 0.0) { - vpd->edge_pan_start_time_y = current_time; - } -} - -/** - * Used to calculate a "fade in" factor for edge panning to make the interaction feel smooth - * and more purposeful. - * - * \note Assumes a domain_min of 0.0f. - */ -static float smootherstep(const float domain_max, float x) -{ - x = clamp_f(x / domain_max, 0.0, 1.0); - return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); -} - -static float edge_pan_speed(v2dViewPanData *vpd, - int event_loc, - bool x_dir, - const double current_time) -{ - ARegion *region = vpd->region; - - /* Find the distance from the start of the drag zone. */ - const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD; - const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD; - int distance = 0.0; - if (event_loc > max) { - distance = event_loc - max; - } - else if (event_loc < min) { - distance = min - event_loc; - } - else { - BLI_assert(!"Calculating speed outside of pan zones"); - return 0.0f; - } - - /* Apply a fade in to the speed based on a start time delay. */ - const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; - const float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time)); - - return distance * EDGE_PAN_SPEED_PER_PIXEL * delay_factor; -} - static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) { - v2dViewPanData *vpd = op->customdata; - ARegion *region = vpd->region; + View2DEdgePanData *vpd = op->customdata; if (event->val == KM_RELEASE || event->type == EVT_ESCKEY) { - view_pan_exit(op); + MEM_SAFE_FREE(op->customdata); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); } - /* Only mousemove events matter here, ignore others. */ - if (event->type != MOUSEMOVE) { - return OPERATOR_PASS_THROUGH; - } + + UI_view2d_edge_pan_apply_event(C, vpd, event); /* This operator is supposed to run together with some drag action. * On successful handling, always pass events on to other handlers. */ - const int success_retval = OPERATOR_PASS_THROUGH; - - const int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X; - rcti padding_rect; - if (outside_padding != 0) { - padding_rect = region->winrct; - BLI_rcti_pad(&padding_rect, outside_padding, outside_padding); - } - - int pan_dir_x = 0; - int pan_dir_y = 0; - if ((outside_padding == 0) || BLI_rcti_isect_pt(&padding_rect, event->x, event->y)) { - /* Find whether the mouse is beyond X and Y edges. */ - if (event->x > region->winrct.xmax - EDGE_PAN_REGION_PAD) { - pan_dir_x = 1; - } - else if (event->x < region->winrct.xmin + EDGE_PAN_REGION_PAD) { - pan_dir_x = -1; - } - if (event->y > region->winrct.ymax - EDGE_PAN_REGION_PAD) { - pan_dir_y = 1; - } - else if (event->y < region->winrct.ymin + EDGE_PAN_REGION_PAD) { - pan_dir_y = -1; - } - } - - const double current_time = PIL_check_seconds_timer(); - edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time); - - /* Calculate the delta since the last time the operator was called. */ - const float dtime = (float)(current_time - vpd->edge_pan_last_time); - float dx = 0.0f, dy = 0.0f; - if (pan_dir_x != 0) { - const float speed = edge_pan_speed(vpd, event->x, true, current_time); - dx = dtime * speed * (float)pan_dir_x; - } - if (pan_dir_y != 0) { - const float speed = edge_pan_speed(vpd, event->y, false, current_time); - dy = dtime * speed * (float)pan_dir_y; - } - vpd->edge_pan_last_time = current_time; - - /* Pan, clamping inside the regions's total bounds. */ - view_pan_apply_ex(C, vpd, dx, dy); - - return success_retval; + return OPERATOR_PASS_THROUGH; } static void view_edge_pan_cancel(bContext *UNUSED(C), wmOperator *op) { - view_pan_exit(op); + MEM_SAFE_FREE(op->customdata); } static void VIEW2D_OT_edge_pan(wmOperatorType *ot) @@ -510,26 +385,13 @@ static void VIEW2D_OT_edge_pan(wmOperatorType *ot) ot->invoke = view_edge_pan_invoke; ot->modal = view_edge_pan_modal; ot->cancel = view_edge_pan_cancel; - ot->poll = view_pan_poll; + ot->poll = UI_view2d_edge_pan_poll; /* operator is modal */ ot->flag = OPTYPE_INTERNAL; - RNA_def_int(ot->srna, - "outside_padding", - 0, - 0, - 100, - "Outside Padding", - "Padding around the region in UI units within which panning is activated (0 to " - "disable boundary)", - 0, - 100); + UI_view2d_edge_pan_operator_properties(ot); } -#undef EDGE_PAN_REGION_PAD -#undef EDGE_PAN_SPEED_PER_PIXEL -#undef EDGE_PAN_DELAY - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 2fcc59cde0b..8dfc43333e3 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -25,6 +25,7 @@ #include "BKE_node.h" #include "UI_interface.h" +#include "UI_view2d.h" #include <stddef.h> /* for size_t */ /* internal exports only */ @@ -64,6 +65,9 @@ typedef struct bNodeLinkDrag { /** Temporarily stores the last hovered socket for multi-input socket operator. * Store it to recalculate sorting after it is no longer hovered. */ struct bNode *last_node_hovered_while_dragging_a_link; + + /* Data for edge panning */ + View2DEdgePanData pan_data; } bNodeLinkDrag; typedef struct SpaceNode_Runtime { diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 57dc0b6fef2..a1b8e4e3395 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -973,6 +973,8 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) ARegion *region = CTX_wm_region(C); float cursor[2]; + UI_view2d_edge_pan_apply_event(C, &nldrag->pan_data, event); + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); switch (event->type) { @@ -1130,6 +1132,8 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach); if (nldrag) { + UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op); + op->customdata = nldrag; BLI_addtail(&snode->runtime->linkdrag, nldrag); @@ -1193,6 +1197,13 @@ void NODE_OT_link(wmOperatorType *ot) UI_PRECISION_FLOAT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); RNA_def_property_flag(prop, PROP_HIDDEN); + + UI_view2d_edge_pan_operator_properties_ex(ot, + NODE_EDGE_PAN_INSIDE_PAD, + NODE_EDGE_PAN_OUTSIDE_PAD, + NODE_EDGE_PAN_SPEED_RAMP, + NODE_EDGE_PAN_MAX_SPEED, + NODE_EDGE_PAN_DELAY); } /** \} */ diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 7d889eed612..8021b45ac77 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -1365,7 +1365,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true); PointerRNA op_ptr; WM_operator_properties_create_ptr(&op_ptr, ot); - RNA_int_set(&op_ptr, "outside_padding", OUTLINER_DRAG_SCOLL_OUTSIDE_PAD); + RNA_float_set(&op_ptr, "outside_padding", OUTLINER_DRAG_SCOLL_OUTSIDE_PAD); WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr); WM_operator_properties_free(&op_ptr); } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f0ced665679..2b26ef3b3e4 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -149,6 +149,9 @@ typedef enum { T_AUTOMERGE = 1 << 20, /** Runs auto-merge & splits. */ T_AUTOSPLIT = 1 << 21, + + /** No cursor wrapping on region bounds */ + T_NO_CURSOR_WRAP = 1 << 23, } eTFlag; /** #TransInfo.modifiers */ diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index 12c4d0816ae..9d2d3713bf0 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -27,6 +27,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_rect.h" #include "BKE_context.h" #include "BKE_node.h" @@ -35,6 +36,7 @@ #include "ED_node.h" #include "UI_interface.h" +#include "UI_view2d.h" #include "transform.h" #include "transform_convert.h" @@ -44,6 +46,12 @@ /** \name Node Transform Creation * \{ */ +typedef struct NodeTransCustomData { + /* Initial rect of the view2d, used for computing offset during edge panning */ + rctf initial_v2d_cur; + View2DEdgePanData edge_pan; +} NodeTransCustomData; + /* transcribe given node into TransData2D for Transforming */ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const float dpi_fac) { @@ -107,6 +115,24 @@ void createTransNodeData(TransInfo *t) const float dpi_fac = UI_DPI_FAC; SpaceNode *snode = t->area->spacedata.first; + if (t->mode == TFM_TRANSLATION) { + /* Disable cursor wrapping in the node editor for edge pan */ + t->flag |= T_NO_CURSOR_WRAP; + } + + /* Custom data to enable edge panning during the node transform */ + NodeTransCustomData *customdata = MEM_callocN(sizeof(*customdata), __func__); + UI_view2d_edge_pan_init(t->context, + &customdata->edge_pan, + NODE_EDGE_PAN_INSIDE_PAD, + NODE_EDGE_PAN_OUTSIDE_PAD, + NODE_EDGE_PAN_SPEED_RAMP, + NODE_EDGE_PAN_MAX_SPEED, + NODE_EDGE_PAN_DELAY); + customdata->initial_v2d_cur = t->region->v2d.cur; + t->custom.type.data = customdata; + t->custom.type.use_free = true; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); tc->data_len = 0; @@ -150,6 +176,19 @@ void flushTransNodes(TransInfo *t) { const float dpi_fac = UI_DPI_FAC; + NodeTransCustomData *customdata = (NodeTransCustomData *)t->custom.type.data; + + if (t->mode == TFM_TRANSLATION) { + /* Edge panning functions expect window coordinates, mval is relative to region */ + const float x = t->region->winrct.xmin + t->mval[0]; + const float y = t->region->winrct.ymin + t->mval[1]; + UI_view2d_edge_pan_apply(t->context, &customdata->edge_pan, x, y); + } + + /* Initial and current view2D rects for additional transform due to view panning and zooming */ + const rctf *rect_src = &customdata->initial_v2d_cur; + const rctf *rect_dst = &t->region->v2d.cur; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { applyGridAbsolute(t); @@ -159,23 +198,28 @@ void flushTransNodes(TransInfo *t) TransData2D *td2d = &tc->data_2d[i]; bNode *node = td->extra; - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + float loc[2]; + copy_v2_v2(loc, td2d->loc); + + /* additional offset due to change in view2D rect */ + BLI_rctf_transform_pt_v(rect_dst, rect_src, loc, loc); + #ifdef USE_NODE_CENTER - float locx = (td2d->loc[0] - (BLI_rctf_size_x(&node->totr)) * +0.5f) / dpi_fac; - float locy = (td2d->loc[1] - (BLI_rctf_size_y(&node->totr)) * -0.5f) / dpi_fac; -#else - float locx = td2d->loc[0] / dpi_fac; - float locy = td2d->loc[1] / dpi_fac; + loc[0] -= 0.5f * BLI_rctf_size_x(&node->totr); + loc[1] += 0.5f * BLI_rctf_size_y(&node->totr); #endif + /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + loc[0] /= dpi_fac; + loc[1] /= dpi_fac; + /* account for parents (nested nodes) */ if (node->parent) { - nodeFromView(node->parent, locx, locy, &node->locx, &node->locy); - } - else { - node->locx = locx; - node->locy = locy; + nodeFromView(node->parent, loc[0], loc[1], &loc[0], &loc[1]); } + + node->locx = loc[0]; + node->locy = loc[1]; } /* handle intersection with noodles */ diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 9b5f15a2574..d7bcf8dcf90 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -519,10 +519,11 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* add temp handler */ WM_event_add_modal_handler(C, op); - op->flag |= OP_IS_MODAL_GRAB_CURSOR; /* XXX maybe we want this with the gizmo only? */ - /* Use when modal input has some transformation to begin with. */ TransInfo *t = op->customdata; + if ((t->flag & T_NO_CURSOR_WRAP) == 0) { + op->flag |= OP_IS_MODAL_GRAB_CURSOR; /* XXX maybe we want this with the gizmo only? */ + } if (UNLIKELY(!is_zero_v4(t->values_modal_offset))) { transformApply(C, t); } |