From 412b5b3b3f4ba2d0320deedb57831f4c7abc8904 Mon Sep 17 00:00:00 2001 From: Ramon Klauck Date: Tue, 30 Sep 2025 18:12:12 +0200 Subject: [PATCH] Fix: VSE: Propagate split to connected strips by default Currently, attempting to split connected strips that are not both selected means that only one of the strips are affected. This violates the "connected" principle, so this PR makes the split operator propagate the split to connected strips by default. Holding alt returns old behavior (ignores connections), similar to selection logic. Pull Request: https://projects.blender.org/blender/blender/pulls/146380 --- .../keyconfig/keymap_data/blender_default.py | 8 ++++++ .../editors/space_sequencer/sequencer_edit.cc | 22 +++++++++++++--- .../makesrna/intern/rna_sequencer_api.cc | 21 +++++++++++++--- source/blender/sequencer/SEQ_edit.hh | 1 + source/blender/sequencer/intern/strip_edit.cc | 25 ++++++++----------- .../sequencer/intern/strip_transform.cc | 2 ++ 6 files changed, 57 insertions(+), 22 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index e403f68ec05..fa50c713376 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -8628,6 +8628,14 @@ def km_sequencer_tool_blade(_params): ("use_cursor_position", True), ("ignore_selection", True), ]}), + ("sequencer.split", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, + {"properties": [ + ("type", 'SOFT'), + ("side", 'NO_CHANGE'), + ("use_cursor_position", True), + ("ignore_selection", True), + ("ignore_connections", True), + ]}), ]}, ) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.cc b/source/blender/editors/space_sequencer/sequencer_edit.cc index 09d09a48a93..098dcc4141f 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.cc +++ b/source/blender/editors/space_sequencer/sequencer_edit.cc @@ -1757,6 +1757,7 @@ static wmOperatorStatus sequencer_split_exec(bContext *C, wmOperator *op) const seq::eSplitMethod method = seq::eSplitMethod(RNA_enum_get(op->ptr, "type")); const int split_side = sequence_split_side_for_exec_get(op); const bool ignore_selection = RNA_boolean_get(op->ptr, "ignore_selection"); + const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections"); seq::prefetch_stop(scene); @@ -1767,9 +1768,14 @@ static wmOperatorStatus sequencer_split_exec(bContext *C, wmOperator *op) if (ignore_selection || strip->flag & SELECT) { const char *error_msg = nullptr; - if (seq::edit_strip_split( - bmain, scene, ed->current_strips(), strip, split_frame, method, &error_msg) != - nullptr) + if (seq::edit_strip_split(bmain, + scene, + ed->current_strips(), + strip, + split_frame, + method, + ignore_connections, + &error_msg) != nullptr) { changed = true; } @@ -1881,6 +1887,10 @@ static void sequencer_split_ui(bContext * /*C*/, wmOperator *op) if (RNA_boolean_get(op->ptr, "use_cursor_position")) { layout->prop(op->ptr, "channel", UI_ITEM_NONE, std::nullopt, ICON_NONE); } + + layout->separator(); + + layout->prop(op->ptr, "ignore_connections", UI_ITEM_NONE, std::nullopt, ICON_NONE); } void SEQUENCER_OT_split(wmOperatorType *ot) @@ -1948,6 +1958,12 @@ void SEQUENCER_OT_split(wmOperatorType *ot) "Make cut even if strip is not selected preserving selection state after cut"); RNA_def_property_flag(prop, PROP_HIDDEN); + + RNA_def_boolean(ot->srna, + "ignore_connections", + false, + "Ignore Connections", + "Don't propagate split to connected strips"); } /** \} */ diff --git a/source/blender/makesrna/intern/rna_sequencer_api.cc b/source/blender/makesrna/intern/rna_sequencer_api.cc index efbfa11a42e..2aaba7f5c1a 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.cc +++ b/source/blender/makesrna/intern/rna_sequencer_api.cc @@ -88,15 +88,26 @@ static void rna_Strips_move_strip_to_meta( WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } -static Strip *rna_Strip_split( - ID *id, Strip *strip, Main *bmain, ReportList *reports, int frame, int split_method) +static Strip *rna_Strip_split(ID *id, + Strip *strip, + Main *bmain, + ReportList *reports, + int frame, + int split_method, + bool ignore_connections) { Scene *scene = (Scene *)id; ListBase *seqbase = blender::seq::get_seqbase_by_strip(scene, strip); const char *error_msg = nullptr; - Strip *strip_split = blender::seq::edit_strip_split( - bmain, scene, seqbase, strip, frame, blender::seq::eSplitMethod(split_method), &error_msg); + Strip *strip_split = blender::seq::edit_strip_split(bmain, + scene, + seqbase, + strip, + frame, + blender::seq::eSplitMethod(split_method), + ignore_connections, + &error_msg); if (error_msg != nullptr) { BKE_report(reports, RPT_ERROR, error_msg); } @@ -754,6 +765,8 @@ void RNA_api_strip(StructRNA *srna) RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); parm = RNA_def_enum(func, "split_method", strip_split_method_items, 0, "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean( + func, "ignore_connections", false, "", "Don't propagate split to connected strips"); /* Return type. */ parm = RNA_def_pointer(func, "sequence", "Strip", "", "Right side Strip"); RNA_def_function_return(func, parm); diff --git a/source/blender/sequencer/SEQ_edit.hh b/source/blender/sequencer/SEQ_edit.hh index 1d5921a8521..ea71d66c09f 100644 --- a/source/blender/sequencer/SEQ_edit.hh +++ b/source/blender/sequencer/SEQ_edit.hh @@ -73,6 +73,7 @@ Strip *edit_strip_split(Main *bmain, Strip *strip, int timeline_frame, eSplitMethod method, + bool ignore_connections, const char **r_error); /** * Find gap after initial_frame and move strips on right side to close the gap diff --git a/source/blender/sequencer/intern/strip_edit.cc b/source/blender/sequencer/intern/strip_edit.cc index 0eeee93f4b8..f368ac533db 100644 --- a/source/blender/sequencer/intern/strip_edit.cc +++ b/source/blender/sequencer/intern/strip_edit.cc @@ -409,6 +409,7 @@ Strip *edit_strip_split(Main *bmain, Strip *strip, const int timeline_frame, const eSplitMethod method, + const bool ignore_connections, const char **r_error) { if (!seq_edit_split_intersect_check(scene, strip, timeline_frame)) { @@ -418,21 +419,11 @@ Strip *edit_strip_split(Main *bmain, /* Whole strip effect chain must be duplicated in order to preserve relationships. */ blender::VectorSet strips; strips.add(strip); - iterator_set_expand(scene, seqbase, strips, query_strip_effect_chain); - - /* All connected strips (that are selected and at the cut frame) must also be duplicated. */ - blender::VectorSet strips_old(strips); - for (Strip *strip : strips_old) { - blender::VectorSet connections = connected_strips_get(strip); - connections.remove_if([&](Strip *connection) { - return !(connection->flag & SELECT) || - !seq_edit_split_intersect_check(scene, connection, timeline_frame); - }); - strips.add_multiple(connections.as_span()); - } - - /* In case connected strips had effects, duplicate those too: */ - iterator_set_expand(scene, seqbase, strips, query_strip_effect_chain); + iterator_set_expand(scene, + seqbase, + strips, + ignore_connections ? query_strip_effect_chain : + query_strip_connected_and_effect_chain); if (!seq_edit_split_operation_permitted_check(scene, strips, timeline_frame, r_error)) { return nullptr; @@ -448,6 +439,10 @@ Strip *edit_strip_split(Main *bmain, BLI_remlink(seqbase, strip_iter); BLI_addtail(&left_strips, strip_iter); + if (ignore_connections) { + seq::disconnect(strip_iter); + } + /* Duplicate curves from backup, so they can be renamed along with split strips. */ animation_duplicate_backup_to_scene(scene, strip_iter, &animation_backup); } diff --git a/source/blender/sequencer/intern/strip_transform.cc b/source/blender/sequencer/intern/strip_transform.cc index 114a14fa0d2..a107860972e 100644 --- a/source/blender/sequencer/intern/strip_transform.cc +++ b/source/blender/sequencer/intern/strip_transform.cc @@ -410,6 +410,7 @@ static void strip_transform_handle_overwrite_split(Scene *scene, target, time_left_handle_frame_get(scene, transformed), SPLIT_SOFT, + true, nullptr); edit_strip_split(bmain, scene, @@ -417,6 +418,7 @@ static void strip_transform_handle_overwrite_split(Scene *scene, split_strip, time_right_handle_frame_get(scene, transformed), SPLIT_SOFT, + true, nullptr); edit_flag_for_removal(scene, seqbasep, split_strip); edit_remove_flagged_strips(scene, seqbasep);