Merge pull request #301 from theironchef/fix/layers-alt-click-others
Layers: Alt-clicking an eye or lock toggles the other layers
This commit is contained in:
commit
de779cc820
|
|
@ -116,7 +116,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist
|
|||
- **Temporary tools:** Cmd with any other tool drags with the selection tool used last (Selection, Direct Selection or Group Selection). Held Space pans and Cmd+Space zooms (Cmd+Alt+Space out) whatever the tool.
|
||||
- **Panel drag and drop:** art dragged off the canvas onto the Symbols panel becomes a symbol, and a symbol dragged onto the canvas places an instance where it is dropped.
|
||||
- **Tool double-clicks:** double-clicking a tool's button opens its options: Hand fits the artboard in the window, Zoom shows 100 %, Rotate, Scale, Reflect and Shear open their Object › Transform dialogs, Gradient, Eyedropper, Blend and the Liquify tools their panels or options.
|
||||
- **Layers panel:** every layer, sublayer, group and object is a clickable row at any depth: a click highlights it and makes its layer current (new art goes there), Shift-click a range, Ctrl/Cmd-click toggles. Sublayers are no longer treated as objects: clicks, marquees, Select All and the Select menu take the art inside them, and each sublayer selects in its own colour. Selection squares on rows with selected art (click selects a row's art, Shift adds or removes; dragging the square moves the selection to another row, Alt copies), the eye and lock on every row with drag-down-the-column (one undo step), Ctrl/Cmd-click an eye for per-layer Outline, triangles with Alt to open or close everything inside, drag and drop rows above, below or into layers and groups with a drop line (Alt copies, the trash deletes), inline rename, Delete key on highlighted rows. Buttons and the full panel menu: New Layer/Sublayer (Alt: options, Ctrl: on top), Duplicate, Delete, Options for Selection, clipping mask, isolation, Locate Object, Merge Selected, Flatten Artwork, Collect in New Layer, Release to Layers (Sequence and Build), Reverse Order, Template, Hide/Outline/Lock Others and Show/Preview/Unlock All, Paste Remembers Layers, Panel Options (row size, thumbnails per kind, Show Layers Only). Layer Options: name, colour (presets or custom), Template, Lock, Show, Print, Preview (outline drawing and clicking) and Dim Images to %. Every operation is a command (`layer.move`, `layer.highlight`, `layer.merge`…) with validation against cycles and invalid nesting.
|
||||
- **Layers panel:** every layer, sublayer, group and object is a clickable row at any depth: a click highlights it and makes its layer current (new art goes there), Shift-click a range, Ctrl/Cmd-click toggles. Sublayers are no longer treated as objects: clicks, marquees, Select All and the Select menu take the art inside them, and each sublayer selects in its own colour. Selection squares on rows with selected art (click selects a row's art, Shift adds or removes; dragging the square moves the selection to another row, Alt copies), the eye and lock on every row with drag-down-the-column (one undo step), Ctrl/Cmd-click an eye for per-layer Outline, Alt-click an eye or lock to hide or lock the other layers (again: show or unlock them all), triangles with Alt to open or close everything inside, drag and drop rows above, below or into layers and groups with a drop line (Alt copies, the trash deletes), inline rename, Delete key on highlighted rows. Buttons and the full panel menu: New Layer/Sublayer (Alt: options, Ctrl: on top), Duplicate, Delete, Options for Selection, clipping mask, isolation, Locate Object, Merge Selected, Flatten Artwork, Collect in New Layer, Release to Layers (Sequence and Build), Reverse Order, Template, Hide/Outline/Lock Others and Show/Preview/Unlock All, Paste Remembers Layers, Panel Options (row size, thumbnails per kind, Show Layers Only). Layer Options: name, colour (presets or custom), Template, Lock, Show, Print, Preview (outline drawing and clicking) and Dim Images to %. Every operation is a command (`layer.move`, `layer.highlight`, `layer.merge`…) with validation against cycles and invalid nesting.
|
||||
- **Drawing aids:** Smart Guides and snapping, guides dragged out of the rulers, and Draw Normal / Behind / Inside modes.
|
||||
- **Views:** the status bar's artboard navigator (first, previous, next, last; `view.goToArtboard`) goes to an artboard and fits it, and Fit Artboard in Window and Actual Size show the navigator's artboard.
|
||||
- **Geometry and effects:**
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
//! another row moves the selected art there (Alt copies it).
|
||||
//! - The eye and lock columns toggle a row (`layer.setProps`); dragging down a column sets every
|
||||
//! row it passes to the same state, in one undo step. Ctrl/Cmd-click an eye switches the layer
|
||||
//! between Preview and Outline.
|
||||
//! between Preview and Outline. Alt-click an eye hides the other layers (`layer.hideOthers`), or
|
||||
//! shows every layer when they are hidden (`layer.showAll`); Alt-click a lock does the same with
|
||||
//! `layer.lockOthers` and `layer.unlockAll`.
|
||||
//! - Dragging rows moves them above, below or into a layer or group (`layer.move`, a drop line or
|
||||
//! a box shows where); Alt copies them; dropped on the trash they are deleted.
|
||||
//! - The triangle opens or closes a row; Alt-click does the same to everything inside it.
|
||||
|
|
@ -543,6 +545,21 @@ fn row(ui: &mut Ui, view: &View, n: &Node, depth: usize, clip_path: bool, expand
|
|||
}
|
||||
}
|
||||
|
||||
/// Alt-clicking row `n`'s eye (`eye`) or lock: Hide Others or Lock Others, every top-level layer
|
||||
/// but the one holding the row; when they all are hidden (locked) already, Show All Layers (Unlock
|
||||
/// All Layers). Each is one undo step.
|
||||
fn others_action(doc: &Document, n: &Node, eye: bool) -> (String, Value) {
|
||||
let own = doc.layer_of(n.id);
|
||||
let mut others = doc.layers.iter().filter(|l| Some(l.id) != own);
|
||||
let cmd = match (eye, if eye { others.any(|l| l.visible) } else { others.any(|l| !l.locked) }) {
|
||||
(true, true) => "layer.hideOthers",
|
||||
(true, false) => "layer.showAll",
|
||||
(false, true) => "layer.lockOthers",
|
||||
(false, false) => "layer.unlockAll",
|
||||
};
|
||||
(cmd.into(), json!({ "ids": [n.id.0] }))
|
||||
}
|
||||
|
||||
/// The eye and lock columns of row `n` at `r`.
|
||||
fn eye_and_lock(ui: &mut Ui, view: &View, n: &Node, r: egui::Rect, out: &mut Out) {
|
||||
let t = &view.t;
|
||||
|
|
@ -574,14 +591,18 @@ fn eye_and_lock(ui: &mut Ui, view: &View, n: &Node, r: egui::Rect, out: &mut Out
|
|||
}
|
||||
let m = ui.input(|i| i.modifiers);
|
||||
if er.clicked() {
|
||||
if m.command && n.is_layer() {
|
||||
if m.alt {
|
||||
out.actions.push(others_action(view.doc, n, true));
|
||||
} else if m.command && n.is_layer() {
|
||||
// Ctrl/Cmd-click: Preview ↔ Outline for this layer.
|
||||
out.actions.push(("layer.setProps".into(), json!({"ids": [n.id.0], "preview": !preview})));
|
||||
} else {
|
||||
out.actions.push(("layer.setProps".into(), json!({"ids": [n.id.0], "visible": !n.visible})));
|
||||
}
|
||||
}
|
||||
if lr.clicked() {
|
||||
if lr.clicked() && m.alt {
|
||||
out.actions.push(others_action(view.doc, n, false));
|
||||
} else if lr.clicked() {
|
||||
out.actions.push(("layer.setProps".into(), json!({"ids": [n.id.0], "locked": !n.locked})));
|
||||
}
|
||||
// Dragging down a column gives every row it passes this row's new state.
|
||||
|
|
@ -993,6 +1014,55 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_clicking_an_eye_or_lock_toggles_the_other_layers() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
let run = |app: &mut VectorcraftApp, id: &str, p: serde_json::Value| app.session.execute(id, &p).unwrap();
|
||||
run(&mut app, "file.new", json!({"width": 200, "height": 200}));
|
||||
run(&mut app, "layer.new", json!({}));
|
||||
run(&mut app, "layer.new", json!({}));
|
||||
let ctx = egui::Context::default();
|
||||
// Rows top first: Layers 3, 2, 1.
|
||||
let (circles, _) = frame(&mut app, &ctx, vec![], false);
|
||||
assert_eq!(circles.len(), 3);
|
||||
let state =
|
||||
|app: &VectorcraftApp| -> Vec<(bool, bool)> { app.session.active().unwrap().doc.layers.iter().map(|l| (l.visible, l.locked)).collect() };
|
||||
let click = |app: &mut VectorcraftApp, at: egui::Pos2, alt: bool| {
|
||||
frame(app, &ctx, vec![egui::Event::PointerMoved(at)], alt);
|
||||
frame(app, &ctx, vec![button(at, true)], alt);
|
||||
frame(app, &ctx, vec![button(at, false)], alt);
|
||||
};
|
||||
let (eye, lock) = (egui::pos2(12.0, circles[1].y), egui::pos2(37.0, circles[1].y));
|
||||
let undo = |app: &VectorcraftApp| app.session.active().unwrap().history.undo.len();
|
||||
let before = undo(&app);
|
||||
// Alt-click Layer 2's eye: Layers 1 and 3 hide in one step; again, they show.
|
||||
click(&mut app, eye, true);
|
||||
assert_eq!(state(&app), [(false, false), (true, false), (false, false)]);
|
||||
assert_eq!(undo(&app), before + 1);
|
||||
click(&mut app, eye, true);
|
||||
assert_eq!(state(&app), [(true, false); 3]);
|
||||
// Alt-click its lock: the others lock; again, every layer unlocks.
|
||||
click(&mut app, lock, true);
|
||||
assert_eq!(state(&app), [(true, true), (true, false), (true, true)]);
|
||||
assert_eq!(app.session.active().unwrap().history.undo.last().map(|u| u.label.as_str()), Some("Lock Others"));
|
||||
// A plain click still toggles only that layer.
|
||||
click(&mut app, eye, false);
|
||||
assert_eq!(state(&app), [(true, true), (false, false), (true, true)]);
|
||||
click(&mut app, lock, true);
|
||||
assert_eq!(state(&app), [(true, false), (false, false), (true, false)]);
|
||||
// A sublayer's row stands for its top-level layer: the other layers hide.
|
||||
click(&mut app, eye, false);
|
||||
let layer2 = app.session.active().unwrap().doc.layers[1].id.0;
|
||||
let sub = run(&mut app, "layer.newSublayer", json!({"parent": layer2}));
|
||||
// Rows: Layer 3, Layer 2, its sublayer, Layer 1.
|
||||
let (circles, _) = frame(&mut app, &ctx, vec![], false);
|
||||
assert_eq!(circles.len(), 4);
|
||||
click(&mut app, egui::pos2(12.0, circles[2].y), true);
|
||||
assert_eq!(state(&app), [(false, false), (true, false), (false, false)]);
|
||||
let doc = &app.session.active().unwrap().doc;
|
||||
assert!(doc.node(NodeId(sub["id"].as_u64().unwrap())).unwrap().visible);
|
||||
}
|
||||
|
||||
/// A document with two rectangles on one layer → (app, layer, [bottom, top]).
|
||||
fn two_rects() -> (VectorcraftApp, NodeId, [NodeId; 2]) {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
|
|
|
|||
|
|
@ -752,7 +752,8 @@ and `layerRows`, the rows highlighted in the panel.
|
|||
templates stay), `layer.collectInNew {ids?}`, `layer.releaseToLayers {id?, build?}` and
|
||||
`layer.releaseToLayersBuild` (each object of a layer or group in a sublayer of its own; Build adds up copies),
|
||||
`layer.reverse {ids?}`, `layer.template {ids?, on?}`, `layer.hideOthers` / `layer.showAll`,
|
||||
`layer.outlineOthers` / `layer.previewAll`, `layer.lockOthers` / `layer.unlockAll`, `layer.pasteRemembersLayers`,
|
||||
`layer.outlineOthers` / `layer.previewAll`, `layer.lockOthers` / `layer.unlockAll` (Alt-clicking an eye or a lock
|
||||
runs Hide or Lock Others for the row's layer, or Show or Unlock All when the others already are), `layer.pasteRemembersLayers`,
|
||||
`object.isolate {id}` / `object.exitIsolation`.
|
||||
- **Dialogs and panel state** (control channel and the app's MCP): `ui.layerOptions {ids?}` and `ui.newLayer
|
||||
{sublayer?}` open Layer Options (dialog `layerOptions`: name, color, template, locked, visible, printable, preview,
|
||||
|
|
|
|||
Loading…
Reference in New Issue