From 96f5bb9f05eb0338643a4be98885a78d7410a3d3 Mon Sep 17 00:00:00 2001 From: John Kiril Swenson Date: Mon, 25 Aug 2025 20:36:01 +0200 Subject: [PATCH] VSE: Slip toolbar tool This PR adds a slip tool in the toolbar with its own custom icon for applying slips using the mouse. This is useful for e.g. tablets where a keyboard is not handy and a button would be best to activate the operator. There is also a custom cursor that appears when hovering over valid, slippable strips (and a "stop" icon when the strip cannot be slipped). Alt may be used in order to ignore slipping connected strips when using the tool, similar to selection logic. The slip tool only performs its sole function of slipping and as such does not change the selection state. In the future, we can also add "slide" functionality to the same tool, giving it multiple functions. Pull Request: https://projects.blender.org/blender/blender/pulls/143513 --- intern/ghost/GHOST_Types.h | 1 + intern/ghost/intern/GHOST_SystemWayland.cc | 2 + release/datafiles/cursors/cursor_slip.svg | 271 ++++++++++++++++++ .../datafiles/icons/ops.sequencer.slip.dat | 3 + release/datafiles/icons_blend/toolbar.blend | 4 +- .../keyconfig/keymap_data/blender_default.py | 23 +- .../keymap_data/industry_compatible_data.py | 1 + .../startup/bl_ui/space_toolsystem_toolbar.py | 13 + .../blender/editors/datafiles/CMakeLists.txt | 2 + .../editors/space_sequencer/sequencer_edit.cc | 39 ++- .../space_sequencer/space_sequencer.cc | 17 +- .../windowmanager/intern/wm_cursors.cc | 3 + source/blender/windowmanager/wm_cursors.hh | 1 + 13 files changed, 366 insertions(+), 14 deletions(-) create mode 100644 release/datafiles/cursors/cursor_slip.svg create mode 100644 release/datafiles/icons/ops.sequencer.slip.dat diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 0db26735d65..39e34c0f42c 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -428,6 +428,7 @@ typedef enum { GHOST_kStandardCursorHandClosed, GHOST_kStandardCursorHandPoint, GHOST_kStandardCursorBlade, + GHOST_kStandardCursorSlip, GHOST_kStandardCursorCustom, #define GHOST_kStandardCursorNumCursors (int(GHOST_kStandardCursorCustom) + 1) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cc b/intern/ghost/intern/GHOST_SystemWayland.cc index 0969a20a3e2..215eed8f8d1 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cc +++ b/intern/ghost/intern/GHOST_SystemWayland.cc @@ -2893,6 +2893,8 @@ static std::optional gwl_seat_cursor_find_wl_sh return std::nullopt; case GHOST_kStandardCursorBlade: return std::nullopt; + case GHOST_kStandardCursorSlip: + return std::nullopt; case GHOST_kStandardCursorCustom: return std::nullopt; } diff --git a/release/datafiles/cursors/cursor_slip.svg b/release/datafiles/cursors/cursor_slip.svg new file mode 100644 index 00000000000..5a07c1c1f03 --- /dev/null +++ b/release/datafiles/cursors/cursor_slip.svg @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/release/datafiles/icons/ops.sequencer.slip.dat b/release/datafiles/icons/ops.sequencer.slip.dat new file mode 100644 index 00000000000..8232941b716 --- /dev/null +++ b/release/datafiles/icons/ops.sequencer.slip.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c729938f088f015b66fae7dfde3e0a2f18dee4c790b74354e1f4e372bdc03441 +size 3050 diff --git a/release/datafiles/icons_blend/toolbar.blend b/release/datafiles/icons_blend/toolbar.blend index 9ac838d971c..fd281f70717 100644 --- a/release/datafiles/icons_blend/toolbar.blend +++ b/release/datafiles/icons_blend/toolbar.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c40c877c54afd91d6fc4d5999d8d511eef69ed553c8d6a7fe9ac15a66ad7e4b3 -size 2666476 +oid sha256:77cb80cb0db21bfebd39b19541a57e2bcc0cf4089ce1acae8037f650d086bfd2 +size 2714741 diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 9c4a64c72b1..2ff1575832a 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3101,7 +3101,7 @@ def km_sequencer(params): op_menu("SEQUENCER_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}), op_menu("SEQUENCER_MT_change", {"type": 'C', "value": 'PRESS'}), op_menu_pie("SEQUENCER_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}), - ("sequencer.slip", {"type": 'S', "value": 'PRESS'}, None), + ("sequencer.slip", {"type": 'S', "value": 'PRESS'}, {"properties": [("use_cursor_position", False)]}), ("wm.context_set_int", {"type": 'O', "value": 'PRESS'}, {"properties": [("data_path", "scene.sequence_editor.overlay_frame"), ("value", 0)]}), ("transform.seq_slide", {"type": 'G', "value": 'PRESS'}, @@ -8514,6 +8514,26 @@ def km_sequencer_tool_blade(_params): ) +def km_sequencer_tool_slip(_params): + return ( + "Sequencer Tool: Slip", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("sequencer.slip", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": [ + ("slip_keyframes", True), + ("use_cursor_position", True), + ]}), + ("sequencer.slip", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, + {"properties": [ + ("slip_keyframes", True), + ("use_cursor_position", True), + ("ignore_connections", True), + ]}), + ]}, + ) + + # ------------------------------------------------------------------------------ # Tool System (Sequencer, Preview) @@ -8850,6 +8870,7 @@ def generate_keymaps(params=None): km_3d_view_tool_paint_grease_pencil_trim(params), km_3d_view_tool_edit_grease_pencil_texture_gradient(params), km_sequencer_tool_blade(params), + km_sequencer_tool_slip(params), km_sequencer_preview_tool_generic_cursor(params), km_sequencer_preview_tool_sample(params), km_sequencer_preview_tool_move(params), diff --git a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index beacf6a4490..08bff5411ea 100644 --- a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1887,6 +1887,7 @@ def km_sequencer(params): # Tools op_tool_cycle("builtin.select_box", {"type": 'Q', "value": 'PRESS'}), op_tool_cycle("builtin.blade", {"type": 'B', "value": 'PRESS'}), + op_tool_cycle("builtin.slip", {"type": 'S', "value": 'PRESS'}), ]) return keymap diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index ecbb5ff0603..8e4ce562343 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -3158,6 +3158,18 @@ class _defs_sequencer_generic: options={'KEYMAP_FALLBACK'}, ) + @ToolDef.from_fn + def slip(): + return dict( + idname="builtin.slip", + label="Slip", + description=( + "Shift underlying strip content without affecting handles" + ), + icon="ops.sequencer.slip", + keymap="Sequencer Tool: Slip", + ) + @ToolDef.from_fn def sample(): return dict( @@ -3960,6 +3972,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): 'SEQUENCER': [ _defs_sequencer_select.box_timeline, _defs_sequencer_generic.blade, + _defs_sequencer_generic.slip ], 'SEQUENCER_PREVIEW': [ _defs_sequencer_select.box_timeline, diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index f4176dda8de..cfd6616acff 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -152,6 +152,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.sculpt.polyline_trim ops.sequencer.blade ops.sequencer.retime + ops.sequencer.slip ops.transform.bone_envelope ops.transform.bone_size ops.transform.edge_slide @@ -965,6 +966,7 @@ if(WITH_BLENDER) cursor_pointer cursor_right_handle cursor_s_arrow + cursor_slip cursor_stop cursor_swap_area cursor_text_edit diff --git a/source/blender/editors/space_sequencer/sequencer_edit.cc b/source/blender/editors/space_sequencer/sequencer_edit.cc index 0c80896ffbd..685c94fc798 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.cc +++ b/source/blender/editors/space_sequencer/sequencer_edit.cc @@ -766,12 +766,31 @@ static void slip_update_header(const Scene *scene, ED_area_status_text(area, msg); } -static SlipData *slip_data_init(const Scene *scene) +static SlipData *slip_data_init(bContext *C, const wmOperator *op, const wmEvent *event) { - Editing *ed = seq::editing_get(scene); + const Scene *scene = CTX_data_scene(C); + const Editing *ed = seq::editing_get(scene); + const View2D *v2d = UI_view2d_fromcontext(C); + SlipData *data = MEM_new("slipdata"); - VectorSet strips = seq::query_selected_strips(ed->current_strips()); + VectorSet strips; + if (RNA_boolean_get(op->ptr, "use_cursor_position") && event) { + Strip *strip = strip_under_mouse_get(scene, v2d, event->mval); + if (strip) { + strips.add(strip); + } + if (!RNA_boolean_get(op->ptr, "ignore_connections")) { + VectorSet connections = seq::connected_strips_get(strip); + for (Strip *connection : connections) { + strips.add(connection); + } + } + } + else { + strips = seq::query_selected_strips(ed->current_strips()); + } + ListBase *channels = seq::channels_displayed_get(seq::editing_get(scene)); strips.remove_if([&](Strip *strip) { return (seq::transform_single_image_check(strip) || seq::transform_is_locked(channels, strip)); @@ -818,7 +837,7 @@ static wmOperatorStatus sequencer_slip_invoke(bContext *C, wmOperator *op, const ScrArea *area = CTX_wm_area(C); View2D *v2d = UI_view2d_fromcontext(C); - SlipData *data = slip_data_init(scene); + SlipData *data = slip_data_init(C, op, event); if (data == nullptr) { return OPERATOR_CANCELLED; } @@ -958,7 +977,7 @@ static wmOperatorStatus sequencer_slip_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_sequencer_scene(C); - SlipData *data = slip_data_init(scene); + SlipData *data = slip_data_init(C, op, nullptr); if (data == nullptr) { return OPERATOR_CANCELLED; } @@ -1110,6 +1129,16 @@ void SEQUENCER_OT_slip(wmOperatorType *ot) false, "Slip Keyframes", "Move the keyframes alongside the media"); + RNA_def_boolean(ot->srna, + "use_cursor_position", + false, + "Use Cursor Position", + "Slip strips under mouse cursor instead of all selected strips"); + RNA_def_boolean(ot->srna, + "ignore_connections", + false, + "Ignore Connections", + "Do not slip connected strips if using cursor position"); } /** \} */ diff --git a/source/blender/editors/space_sequencer/space_sequencer.cc b/source/blender/editors/space_sequencer/space_sequencer.cc index c8c1dc3020e..e3689396241 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.cc +++ b/source/blender/editors/space_sequencer/space_sequencer.cc @@ -673,9 +673,7 @@ static void sequencer_main_cursor(wmWindow *win, ScrArea *area, ARegion *region) int wmcursor = WM_CURSOR_DEFAULT; const bToolRef *tref = area->runtime.tool; - if (tref == nullptr || - !(STRPREFIX(tref->idname, "builtin.select") || STREQ(tref->idname, "builtin.blade"))) - { + if (tref == nullptr) { WM_cursor_set(win, wmcursor); return; } @@ -707,12 +705,19 @@ static void sequencer_main_cursor(wmWindow *win, ScrArea *area, ARegion *region) } const Editing *ed = seq::editing_get(scene); - if (STREQ(tref->idname, "builtin.blade")) { + if (STREQ(tref->idname, "builtin.blade") || STREQ(tref->idname, "builtin.slip")) { int mval[2] = {int(mouse_co_region[0]), int(mouse_co_region[1])}; Strip *strip = strip_under_mouse_get(scene, v2d, mval); - ListBase *channels = seq::channels_displayed_get(ed); if (strip != nullptr) { - wmcursor = seq::transform_is_locked(channels, strip) ? WM_CURSOR_STOP : WM_CURSOR_BLADE; + ListBase *channels = seq::channels_displayed_get(ed); + const bool locked = seq::transform_is_locked(channels, strip); + if (STREQ(tref->idname, "builtin.blade")) { + wmcursor = locked ? WM_CURSOR_STOP : WM_CURSOR_BLADE; + } + else if (STREQ(tref->idname, "builtin.slip")) { + wmcursor = (locked || seq::transform_single_image_check(strip)) ? WM_CURSOR_STOP : + WM_CURSOR_SLIP; + } } WM_cursor_set(win, wmcursor); return; diff --git a/source/blender/windowmanager/intern/wm_cursors.cc b/source/blender/windowmanager/intern/wm_cursors.cc index 68292e95034..34dcdb1d2c2 100644 --- a/source/blender/windowmanager/intern/wm_cursors.cc +++ b/source/blender/windowmanager/intern/wm_cursors.cc @@ -153,6 +153,8 @@ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) return GHOST_kStandardCursorBothHandles; case WM_CURSOR_BLADE: return GHOST_kStandardCursorBlade; + case WM_CURSOR_SLIP: + return GHOST_kStandardCursorSlip; default: return GHOST_kStandardCursorCustom; } @@ -1017,5 +1019,6 @@ void wm_init_cursor_data() wm_add_cursor(WM_CURSOR_BOTH_HANDLES, datatoc_cursor_both_handles_svg, {0.5f, 0.5f}); wm_add_cursor(WM_CURSOR_RIGHT_HANDLE, datatoc_cursor_right_handle_svg, {0.5f, 0.5f}); wm_add_cursor(WM_CURSOR_LEFT_HANDLE, datatoc_cursor_left_handle_svg, {0.5f, 0.5f}); + wm_add_cursor(WM_CURSOR_SLIP, datatoc_cursor_slip_svg, {0.5f, 0.5f}); #endif /* !WITH_HEADLESS */ } diff --git a/source/blender/windowmanager/wm_cursors.hh b/source/blender/windowmanager/wm_cursors.hh index a085bae9ffb..b8d0864e01f 100644 --- a/source/blender/windowmanager/wm_cursors.hh +++ b/source/blender/windowmanager/wm_cursors.hh @@ -64,6 +64,7 @@ enum WMCursorType { WM_CURSOR_LEFT_HANDLE, WM_CURSOR_RIGHT_HANDLE, WM_CURSOR_BOTH_HANDLES, + WM_CURSOR_SLIP, /* --- ALWAYS LAST ----- */ WM_CURSOR_NUM,