Sequencer: improvements to preview selection

- Support toggle/deselect/deselect_all options
  (matching 3D viewport object selection).
- Support legacy selection behavior.
- Support selecting by the center in preview views (holding Ctrl).
This commit is contained in:
Campbell Barton 2021-10-06 17:27:52 +11:00
parent 82f0e4948c
commit 68dc970219
Notes: blender-bot 2023-02-13 11:53:00 +01:00
Referenced by commit 4ab8212e1a, Fix errors in 68dc970219
Referenced by commit 85267ec1ae, Correct error in 68dc970219
2 changed files with 144 additions and 44 deletions

View File

@ -2767,21 +2767,11 @@ def km_sequencer(params):
for i in range(10)
)
),
*_template_sequencer_select(
*_template_sequencer_timeline_select(
type=params.select_mouse,
value=params.select_mouse_value_fallback,
legacy=params.legacy,
),
("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "alt": True},
{"properties": [("linked_handle", True)]}),
("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "alt": True},
{"properties": [("extend", True), ("linked_handle", True)]}),
("sequencer.select",
{"type": params.select_mouse, "value": 'PRESS' if params.legacy else 'CLICK', "ctrl": True},
{"properties": [("side_of_frame", True), ("linked_time", True)]}),
("sequencer.select",
{"type": params.select_mouse, "value": 'PRESS' if params.legacy else 'CLICK', "ctrl": True, "shift": True},
{"properties": [("side_of_frame", True), ("linked_time", True), ("extend", True)]}),
("sequencer.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("sequencer.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("sequencer.select_linked_pick", {"type": 'L', "value": 'PRESS'}, None),
@ -2831,7 +2821,7 @@ def km_sequencerpreview(params):
items.extend([
# Selection.
*_template_sequencer_select(
*_template_sequencer_preview_select(
type=params.select_mouse,
value=params.select_mouse_value_fallback,
legacy=params.legacy,
@ -4646,32 +4636,58 @@ def _template_uv_select_for_fallback(params, fallback):
return []
def _template_sequencer_select(*, type, value, legacy):
# FIXME.
legacy = True
def _template_sequencer_generic_select(*, type, value, legacy):
return [(
"sequencer.select",
{"type": type, "value": value, **{m: True for m in mods}},
{"properties": [(c, True) for c in props]},
) for props, mods in (
(("deselect_all",) if not legacy else (), ()),
(("extend",), ("shift",)),
(("toggle",), ("shift",)),
)]
def _template_sequencer_timeline_select(*, type, value, legacy):
return _template_sequencer_generic_select(
type=type, value=value, legacy=legacy,
) + [(
"sequencer.select",
{"type": type, "value": value, **{m: True for m in mods}},
{"properties": [(c, True) for c in props]},
) for props, mods in (
(("center",), ("ctrl",)),
# TODO:
# (("center", "object"), ("ctrl",)),
# (("enumerate",), ("alt",)),
# (("toggle", "center"), ("shift", "ctrl")),
(("toggle", "center"), ("shift", "ctrl")),
# (("center", "enumerate"), ("ctrl", "alt")),
# (("toggle", "enumerate"), ("shift", "alt")),
# (("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")),
)]
def _template_sequencer_preview_select(*, type, value, legacy):
return _template_sequencer_generic_select(
type=type, value=value, legacy=legacy,
) + [(
"sequencer.select",
{"type": type, "value": value, **{m: True for m in mods}},
{"properties": [(c, True) for c in props]},
) for props, mods in (
(("linked_handle",), ("alt",)),
(("linked_handle", "extend"), ("shift", "alt",)),
(("side_of_frame", "linked_time"), ("ctrl",)),
(("side_of_frame", "linked_time", "extend"), ("ctrl", "shift")),
)]
def _template_sequencer_select_for_fallback(params, fallback):
if (not fallback) and params.use_fallback_tool_rmb:
# Needed so we have immediate select+tweak when the default select tool is active.
return _template_sequencer_select(
return _template_sequencer_generic_select(
type=params.select_mouse,
value=params.select_mouse_value,
preview=False,
legacy=params.legacy,
)
return []
@ -7452,10 +7468,10 @@ def km_sequencer_editor_tool_select(params, *, fallback):
{"items": [
# TODO: Use 2D cursor for preview region (currently `sequencer.sample`).
*([] if fallback else
_template_items_tool_select(params, "sequencer.select", "sequencer.sample", extend="extend")
_template_items_tool_select(params, "sequencer.select", "sequencer.sample", extend="toggle")
),
*([] if (not params.use_fallback_tool_rmb) else _template_sequencer_select(
type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)),
*([] if (not params.use_fallback_tool_rmb) else _template_sequencer_generic_select(
type=params.select_mouse, value=params.select_mouse_value, preview=False, legacy=params.legacy)),
*_template_items_change_frame(params),
]},

View File

@ -637,7 +637,9 @@ static void sequencer_select_linked_handle(const bContext *C,
/* Check if click happened on image which belongs to strip. If multiple strips are found, loop
* through them in order. */
static Sequence *seq_select_seq_from_preview(const bContext *C, const int mval[2])
static Sequence *seq_select_seq_from_preview(const bContext *C,
const int mval[2],
const bool center)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
@ -649,10 +651,53 @@ static Sequence *seq_select_seq_from_preview(const bContext *C, const int mval[2
UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]);
SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown);
/* Allow strips this far from the closest center to be included.
* This allows cycling over center points which are near enough
* to overlapping from the users perspective. */
const float center_threshold_cycle_px = 5.0f;
const float center_dist_sq_eps = square_f(center_threshold_cycle_px * U.pixelsize);
const float center_scale_px[2] = {
UI_view2d_scale_get_x(v2d),
UI_view2d_scale_get_y(v2d),
};
float center_co_best[2] = {0.0f};
if (center) {
Sequence *seq_best = NULL;
float center_dist_sq_best = 0.0f;
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
float co[2];
SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co);
sub_v2_v2(co, mouseco_view);
const float center_dist_sq_test = len_squared_v2(co);
if ((seq_best == NULL) || (center_dist_sq_test < center_dist_sq_best)) {
seq_best = seq;
center_dist_sq_best = center_dist_sq_test;
copy_v2_v2(center_co_best, co);
}
}
}
ListBase strips_ordered = {NULL};
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
if (seq_point_image_isect(scene, seq, mouseco_view)) {
bool isect = false;
if (center) {
/* Detect overlapping center points (scaled by the zoom level). */
float co[2];
SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co);
sub_v2_v2(co, center_co_best);
mul_v2_v2(co, center_scale_px);
isect = len_squared_v2(co) <= center_dist_sq_eps;
}
else {
isect = seq_point_image_isect(scene, seq, mouseco_view);
}
if (isect) {
BLI_remlink(seqbase, seq);
BLI_addtail(&strips_ordered, seq);
}
@ -686,14 +731,15 @@ static bool element_already_selected(const Sequence *seq, const int handle_click
static void sequencer_select_strip_impl(const Editing *ed,
Sequence *seq,
const int handle_clicked,
const bool extend)
const bool extend,
const bool deselect,
const bool toggle)
{
/* Deselect strip. */
if (extend && (seq->flag & SELECT) && ed->act_seq == seq) {
const bool is_active = (ed->act_seq == seq);
/* Exception for active strip handles. */
if ((handle_clicked != SEQ_SIDE_NONE) && (seq->flag & SELECT) && is_active && toggle) {
switch (handle_clicked) {
case SEQ_SIDE_NONE:
seq->flag &= ~SEQ_ALLSEL;
break;
case SEQ_SIDE_LEFT:
seq->flag ^= SEQ_LEFTSEL;
break;
@ -701,8 +747,28 @@ static void sequencer_select_strip_impl(const Editing *ed,
seq->flag ^= SEQ_RIGHTSEL;
break;
}
return;
}
else { /* Select strip. */
/* Select strip. */
/* Match object selection behavior. */
int action = -1;
if (extend) {
action = 1;
}
else if (deselect) {
action = 0;
}
else {
if ((seq->flag & SELECT) == 0 || is_active) {
action = 1;
}
else if (toggle) {
action = 0;
}
}
if (action == 1) {
seq->flag |= SELECT;
if (handle_clicked == SEQ_SIDE_LEFT) {
seq->flag |= SEQ_LEFTSEL;
@ -711,6 +777,9 @@ static void sequencer_select_strip_impl(const Editing *ed,
seq->flag |= SEQ_RIGHTSEL;
}
}
else if (action == 0) {
seq->flag &= ~SEQ_ALLSEL;
}
}
static int sequencer_select_exec(bContext *C, wmOperator *op)
@ -718,12 +787,17 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
View2D *v2d = UI_view2d_fromcontext(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
const bool extend = RNA_boolean_get(op->ptr, "extend");
if (ed == NULL) {
return OPERATOR_CANCELLED;
}
bool extend = RNA_boolean_get(op->ptr, "extend");
bool deselect = RNA_boolean_get(op->ptr, "deselect");
bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
bool toggle = RNA_boolean_get(op->ptr, "toggle");
bool center = RNA_boolean_get(op->ptr, "center");
int mval[2];
mval[0] = RNA_int_get(op->ptr, "mouse_x");
mval[1] = RNA_int_get(op->ptr, "mouse_y");
@ -732,7 +806,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
int handle_clicked = SEQ_SIDE_NONE;
Sequence *seq = NULL;
if (region->regiontype == RGN_TYPE_PREVIEW) {
seq = seq_select_seq_from_preview(C, mval);
seq = seq_select_seq_from_preview(C, mval, center);
}
else {
seq = find_nearest_seq(scene, v2d, &handle_clicked, mval);
@ -744,7 +818,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
if (!extend) {
ED_sequencer_deselect_all(scene);
}
sequencer_select_strip_impl(ed, seq, handle_clicked, extend);
sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle);
select_linked_time(ed->seqbasep, seq);
sequencer_select_do_updates(C, scene);
sequencer_select_set_active(scene, seq);
@ -776,29 +850,32 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
/* Clicking on already selected element falls on modal operation.
* All strips are deselected on mouse button release unless extend mode is used. */
if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !extend) {
if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !toggle) {
return OPERATOR_RUNNING_MODAL;
}
int ret_value = OPERATOR_CANCELLED;
bool changed = false;
if (!extend) {
/* Deselect everything */
if (deselect_all || (seq && ((extend == false && deselect == false && toggle == false)))) {
ED_sequencer_deselect_all(scene);
ret_value = OPERATOR_FINISHED;
changed = true;
}
/* Nothing to select, but strips could be deselected. */
if (!seq) {
sequencer_select_do_updates(C, scene);
return ret_value;
if (changed) {
sequencer_select_do_updates(C, scene);
}
return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
/* Do actual selection. */
sequencer_select_strip_impl(ed, seq, handle_clicked, extend);
ret_value = OPERATOR_FINISHED;
sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle);
sequencer_select_do_updates(C, scene);
sequencer_select_set_active(scene, seq);
return ret_value;
return OPERATOR_FINISHED;
}
static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@ -832,7 +909,14 @@ void SEQUENCER_OT_select(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_generic_select(ot);
prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
WM_operator_properties_mouse_select(ot);
prop = RNA_def_boolean(
ot->srna,
"center",
0,
"Center",
"Use the object center when selecting, in edit mode used to extend object selection");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna,