From 28ff392b190d0cff9fe0752dd781f6ffaaef2ad4 Mon Sep 17 00:00:00 2001 From: John Kiril Swenson Date: Thu, 24 Jul 2025 20:12:03 +0200 Subject: [PATCH] Fix: VSE: Copy to selected would interfere with dual handles After #139075, since dual handle translation always selects two strips at a time, any later handle movement on one of the strips would affect both, which is unintuitive and likely not what the user intended. We can fix this for now by checking for strips that directly border one another and removing them from the `copy_to` list. But in the future, if we change selection logic so that strip and handle selection states are mutually incompatible, we could also fix this by making sure that dual handle tweaking only selects the handles and not the strips themselves. Pull Request: https://projects.blender.org/blender/blender/pulls/142916 --- .../space_sequencer/sequencer_select.cc | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.cc b/source/blender/editors/space_sequencer/sequencer_select.cc index 2910dc5a272..ff1756c7128 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.cc +++ b/source/blender/editors/space_sequencer/sequencer_select.cc @@ -855,8 +855,9 @@ static void sequencer_select_connected_strips(const StripSelection &selection) } } -static void sequencer_copy_handles_to_selected_strips(const StripSelection &selection, - const VectorSet prev_selection) +static void sequencer_copy_handles_to_selected_strips(const Scene *scene, + const StripSelection &selection, + VectorSet copy_to) { /* TODO(john): Dual handle propagation is not supported for now due to its complexity, * but once we simplify selection assumptions in 5.0 we can add support for it. */ @@ -865,9 +866,33 @@ static void sequencer_copy_handles_to_selected_strips(const StripSelection &sele } Strip *source = selection.strip1; - /* For left or right handle selection only, simply copy selection state. */ - /* NOTE that this must be `ALLSEL` since `prev_selection` was deselected earlier. */ - for (Strip *strip : prev_selection) { + /* Test for neighboring strips in the `copy_to` list. If any border one another, remove them, + * since we don't want to mess with dual handles. */ + blender::VectorSet test(copy_to); + test.add(source); + for (Strip *strip_a : test) { + for (Strip *strip_b : test) { + if (strip_a == strip_b || strip_a->channel != strip_b->channel) { + continue; + } + + /* Don't copy left handle over to a `strip_b` that has `strip_a` directly on its left. */ + if ((source->flag & SEQ_LEFTSEL) && (seq::time_right_handle_frame_get(scene, strip_a) == + seq::time_left_handle_frame_get(scene, strip_b))) + { + copy_to.remove(strip_b); + } + /* Don't copy right handle over to a `strip_a` that has `strip_b` directly on its right. */ + if ((source->flag & SEQ_RIGHTSEL) && (seq::time_right_handle_frame_get(scene, strip_a) == + seq::time_left_handle_frame_get(scene, strip_b))) + { + copy_to.remove(strip_a); + } + } + } + + for (Strip *strip : copy_to) { + /* NOTE that this can be `ALLSEL` since `prev_selection` was deselected earlier. */ strip->flag &= ~STRIP_ALLSEL; strip->flag |= source->flag & STRIP_ALLSEL; } @@ -1306,7 +1331,7 @@ wmOperatorStatus sequencer_select_exec(bContext *C, wmOperator *op) if (copy_handles_to_sel) { copy_to = seq::query_selected_strips(seq::active_seqbase_get(scene->ed)); copy_to.remove(selection.strip1); - copy_to.remove_if([](Strip *strip) { return strip->type == STRIP_TYPE_EFFECT; }); + copy_to.remove_if([](Strip *strip) { return (strip->type & STRIP_TYPE_EFFECT); }); } bool changed = false; @@ -1339,7 +1364,7 @@ wmOperatorStatus sequencer_select_exec(bContext *C, wmOperator *op) } if (copy_handles_to_sel) { - sequencer_copy_handles_to_selected_strips(selection, copy_to); + sequencer_copy_handles_to_selected_strips(scene, selection, copy_to); } if (!ignore_connections) {