Merge pull request #913 from storytold/fix/901-shift-click-target

Fix #901: Shift-click a target circle in the Layers panel to add or remove it
This commit is contained in:
BFlat 2026-10-10 13:15:31 +07:00 committed by GitHub
commit 809c4266a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 28 additions and 3 deletions

View File

@ -128,6 +128,20 @@ impl Selection {
}
self.target = Some(id);
}
/// Shift-clicking `id`'s target circle: it (a layer: its art) joins the selection, or leaves it
/// when it is all selected already. Several objects are then selected, none targeted.
pub fn toggle_target(&mut self, doc: &Document, id: NodeId) {
let ids = match doc.node(id) {
Some(n) if n.is_layer() => n.layer_art(true),
_ => vec![id],
};
if ids.iter().all(|i| self.contains(*i)) {
ids.iter().for_each(|i| self.remove(*i));
} else {
ids.into_iter().for_each(|i| self.add(i));
}
self.target = None;
}
/// What appearance and transparency edits act on: the targeted object, group or layer, else
/// the selected objects.
pub fn subjects(&self) -> &[NodeId] {

View File

@ -163,7 +163,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Target",
[],
None,
"{id} target a layer, group or object as clicking its target circle in the Layers panel does: a layer gets its visible, unlocked art (its sublayers' too) selected and becomes the current layer, and appearance.*, effect.*, transparency.* and the opacity-mask commands without `ids` then act on the layer itself; anything else is selected. Any other selection change ends the targeting (`document.inspect` → target) → {id, selected: [..]}",
"{id, add?: bool (Shift-click: the item joins the selection, or leaves it when all selected; nothing is targeted)} target a layer, group or object as clicking its target circle in the Layers panel does: a layer gets its visible, unlocked art (its sublayers' too) selected and becomes the current layer, and appearance.*, effect.*, transparency.* and the opacity-mask commands without `ids` then act on the layer itself; anything else is selected. Any other selection change ends the targeting (`document.inspect` → target) → {id, selected: [..]}",
has_doc,
target
),
@ -362,7 +362,11 @@ fn target(s: &mut Session, p: &Value) -> Result<Value> {
}
let layer = n.is_layer();
let rows = st.layer_rows.clone();
s.select(|d, sel| sel.set_target(d, id))?;
if p.get("add").and_then(Value::as_bool).unwrap_or(false) {
s.select(|d, sel| sel.toggle_target(d, id))?;
} else {
s.select(|d, sel| sel.set_target(d, id))?;
}
let st = s.doc_mut()?;
// Targeting doesn't change which rows are highlighted.
st.layer_rows = rows;

View File

@ -664,7 +664,7 @@ fn row(ui: &mut Ui, view: &View, item: &Row, out: &mut Out) {
}
let m = ui.input(|i| i.modifiers);
if tresp.clicked() {
out.actions.push(("layer.target".into(), json!({"id": n.id.0})));
out.actions.push(("layer.target".into(), json!({"id": n.id.0, "add": m.shift})));
} else if sq_resp.clicked() {
out.actions.push(("layer.selectAll".into(), json!({"id": n.id.0, "add": m.shift})));
} else if resp.clicked() {
@ -1239,6 +1239,13 @@ mod tests {
}
let st = app.session.active().unwrap();
assert_eq!((st.selection.target, st.selection.objects.clone()), (Some(a), vec![a]));
// Shift-clicking another object's circle adds it to the selection (#901), and again takes
// it out; nothing is targeted meanwhile.
click_with(&mut app, &ctx, c[1], egui::Modifiers::SHIFT);
let st = app.session.active().unwrap();
assert_eq!((st.selection.target, st.selection.objects.clone()), (None, vec![a, b]));
click_with(&mut app, &ctx, c[1], egui::Modifiers::SHIFT);
assert_eq!(app.session.active().unwrap().selection.objects, vec![a]);
}
#[test]