mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-12 17:01:09 +00:00
Selection: Escape clears the selection instead of stepping out to the parent, as in Figma
Measured in Figma: with a layer inside a group inside a frame selected (from the canvas, from the Layers panel, or after Tab), one Escape selects nothing; FigCraft selected the parent first. Escape still leaves text and vector editing, ends gestures and the pen first. Three tests updated to the measured rule (selection_behaviour, devfocus, proto_editor); the rest of the suites pass. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at low
This commit is contained in:
1 parent
3635c23070
commit
a285fd293b
5 files changed
+16
-32
No files matched your search
@@ -1,4 +1,4 @@
|
||||
use super::{Document, Gesture, Node, NodeId, Overlay, Point, PointerEvent, PointerKind, Result, Session, ShapeHandle, ToolId, json, textedit};
|
||||
use super::{Document, Gesture, NodeId, Overlay, Point, PointerEvent, PointerKind, Result, Session, ShapeHandle, ToolId, json, textedit};
|
||||
|
||||
impl Session {
|
||||
/// Keys the current mode handles before menu shortcuts (vector edit mode's tools and point
|
||||
@@ -146,21 +146,9 @@ impl Session {
|
||||
self.ds_mut()?.focus = None;
|
||||
return Ok(());
|
||||
}
|
||||
// Escape selects the parent, then nothing.
|
||||
let doc = self.doc()?;
|
||||
let parents: Vec<NodeId> =
|
||||
sel.iter().filter_map(|i| doc.node(*i).and_then(|n| n.parent)).filter(|p| !doc.node(*p).is_some_and(Node::is_page)).collect();
|
||||
if !parents.is_empty() {
|
||||
let mut uniq = Vec::new();
|
||||
for p in parents {
|
||||
if !uniq.contains(&p) {
|
||||
uniq.push(p);
|
||||
}
|
||||
}
|
||||
self.set_selection(uniq);
|
||||
} else {
|
||||
self.set_selection(vec![]);
|
||||
}
|
||||
// Escape clears the selection whatever its nesting (measured in Figma: it never selects the
|
||||
// parent).
|
||||
self.set_selection(vec![]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -84,12 +84,12 @@ fn only_the_focused_frame_is_hit_and_escape_leaves() {
|
||||
click(&mut s, 75.0, 75.0);
|
||||
assert_eq!(s.selection(), vec![in_a], "the layer on top of it isn't hit either");
|
||||
assert_ne!(s.selection(), vec![over]);
|
||||
// Escape: selection up to the frame, then out of the focus view.
|
||||
// Escape: the selection goes, then out of the focus view.
|
||||
s.escape().unwrap();
|
||||
assert_eq!(s.selection(), vec![a]);
|
||||
assert!(s.selection().is_empty(), "Escape selects nothing, not the parent frame");
|
||||
assert_eq!(focused(&s), Some(a));
|
||||
s.escape().unwrap();
|
||||
assert_eq!(focused(&s), None, "Escape with the frame selected leaves");
|
||||
assert_eq!(focused(&s), None, "Escape with nothing selected leaves");
|
||||
click(&mut s, 350.0, 100.0);
|
||||
assert_eq!(s.selection(), vec![b], "everything is hit again");
|
||||
}
|
||||
|
||||
@@ -271,14 +271,12 @@ fn keyboard_selection() {
|
||||
ex(&mut s, cmd, json!({}));
|
||||
assert_eq!(sorted(s.selection()), sorted(after), "{why}");
|
||||
}
|
||||
// Esc steps out to the parent, then deselects.
|
||||
// Esc clears the selection at once, whatever the nesting (measured in Figma).
|
||||
ex(&mut s, "select.set", json!({"ids": [k.a]}));
|
||||
s.escape().unwrap();
|
||||
assert_eq!(s.selection(), vec![k.g], "Esc selects the parent");
|
||||
assert_eq!(s.selection(), Vec::<NodeId>::new(), "Esc from a nested layer selects nothing, not the parent");
|
||||
s.escape().unwrap();
|
||||
assert_eq!(s.selection(), vec![k.f]);
|
||||
s.escape().unwrap();
|
||||
assert_eq!(s.selection(), Vec::<NodeId>::new(), "Esc on a top-level layer deselects");
|
||||
assert_eq!(s.selection(), Vec::<NodeId>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -328,8 +328,8 @@ fn edit_delete_from_the_menu_removes_the_open_interaction_not_the_layer() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_closes_prototype_editor_without_deselecting_layer() {
|
||||
let (s, a, _b, btn) = setup();
|
||||
fn escape_closes_prototype_editor_and_the_next_deselects() {
|
||||
let (s, _a, _b, btn) = setup();
|
||||
let mut u = Ui::new(s);
|
||||
u.app.ui.right_tab = crate::RightTab::Prototype;
|
||||
u.run("select.set", json!({"ids": [btn]}));
|
||||
@@ -344,13 +344,9 @@ fn escape_closes_prototype_editor_without_deselecting_layer() {
|
||||
assert_eq!(crate::proto_panel::editor(&u.ctx), None);
|
||||
assert_eq!(u.app.session.selection(), vec![btn], "layer remains selected");
|
||||
|
||||
// Second Escape steps out to parent frame.
|
||||
// Second Escape clears the selection, not stepping out to the parent frame.
|
||||
u.key(pos2(100.0, 100.0), egui::Key::Escape);
|
||||
assert_eq!(u.app.session.selection(), vec![a], "steps out to parent frame");
|
||||
|
||||
// Third Escape deselects all.
|
||||
u.key(pos2(100.0, 100.0), egui::Key::Escape);
|
||||
assert!(u.app.session.selection().is_empty(), "selection cleared on third escape");
|
||||
assert!(u.app.session.selection().is_empty(), "the second Escape deselects");
|
||||
}
|
||||
|
||||
/// After a click in the Pages list, Delete that removes an interaction ends the list's hold on the
|
||||
|
||||
@@ -59,6 +59,7 @@ interaction feel not yet compared.
|
||||
| Click, Shift-click, marquee, Shift-marquee toggle | done | Shift-drag on a selected layer moves the selection, kept to one axis (#498); a marquee selects what it touches; from a top-level frame's background, the layers inside it; locked and hidden layers are skipped (`selection_behaviour_tests`) |
|
||||
| Deep select (Cmd/Ctrl-click, Cmd/Ctrl-marquee) | done | |
|
||||
| Select layer menu (every layer under the click) | done | |
|
||||
| Escape with layers selected | done | clears the selection at once, whatever the nesting; it never selects the parent. Measured in the Figma web app (2026-11-08) with a rectangle in a group in a frame, selected from the canvas (a double-click into the group), from the Layers panel, and after Tab moved the selection to its sibling: one Escape selects nothing, a second leaves nothing selected. Escape still leaves text and vector editing, ends gestures and the pen first, and ends the focus view once nothing or the focused frame is selected (`selection_behaviour_tests`, `devfocus_tests`) |
|
||||
| Enter / Shift+Enter (children / parent), Tab / Shift+Tab (siblings) | done | Tab renames the next layer while renaming |
|
||||
| Resize handles: Shift keeps proportions, Alt from centre, constraints-aware | done | Alt shrinks too; past an edge flips (several layers mirror); groups and mixed rotations scale each layer along its own axes, keeping rotation and flip (no skew: children turned off a right angle are approximate); Cmd/Ctrl ignores a frame's constraints (`transform_behaviour_tests`) |
|
||||
| Rotate just outside a corner; Shift in 15° steps | done | rotated handle box when layers share a rotation |
|
||||
@@ -186,6 +187,7 @@ in the table. `crates/ui-egui/src/shortcut_keys_tests.rs` drives the keys a look
|
||||
## Revision history
|
||||
|
||||
| Date | Change | Summary |
|
||||
| 2026-11-08 | patch | Selection: Escape with layers selected clears the selection instead of stepping out to the parent first. Measured in the Figma web app with a rectangle in a group in a frame, selected from the canvas, from the Layers panel and after Tab: one Escape selects nothing. Editing, gestures and the pen still end first |
|
||||
|---|---|---|
|
||||
| 2026-11-02 | patch | Shortcuts: a Cmd/Ctrl-click on a padding field (the horizontal or vertical pair, or a side with Individual padding open) makes the value typed go to all four sides, in one undo step (Guide to auto layout, Keyboard shortcut guide); a plain click still edits that axis or side alone. A documented gesture row: 134 documented, 133 bound. Test: `padding_individual_tests` |
|
||||
| 2026-11-01 | patch | Number fields: dragging the field itself with Alt held scrubs it ("You can also hover the input field itself and hold down the modifier key: Mac: ⌥ Option Windows: Alt", Adjust alignment, rotation, position, and dimensions), with the resize cursor and the label's rates (Alt is the enabler there, so it isn't the fine rate); a click with Alt doesn't open the editor. Found by a documentation audit of the ui-parity rows. Test: `field_tests::alt_held_over_a_field_scrubs_it`. Counts unchanged |
|
||||
|
||||
Reference in new issue
Block a user