mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-14 04:10:55 +00:00
M8: a double-click on a layer's icon zooms to it, as in Figma
Measured in Figma's web app on 2026-11-12: a double-click on a layer's icon in the Layers panel zooms the canvas to the layer and starts no rename; a double-click on the name renames. The row now checks whether the double-click fell within the rect it paints the icon in, and if so selects the layer and runs view.zoomSelection. A double-click elsewhere on the row is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Model-Effort: Opus 5.5 drove; Fable 5.2 queued; Sonnet 5.5 measured and wrote at medium
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
0d6ce1124c
commit
9a29a3cbb7
@@ -841,3 +841,46 @@ fn search_fields_keep_their_text_in_the_middle() {
|
||||
let off = off_centre(&mut u, "Search assets");
|
||||
assert!(off < 1.0, "Search assets is {off} points off the middle");
|
||||
}
|
||||
|
||||
/// Measured in Figma's web app, 2026-11-12: a double-click on a layer's icon zooms the canvas to
|
||||
/// the layer and starts no rename (a double-click on the name renames, as before).
|
||||
#[test]
|
||||
fn a_double_click_on_a_layers_icon_zooms_to_it_instead_of_renaming() {
|
||||
let mut s = Session::new();
|
||||
rect(&mut s, "near", 0.0, None);
|
||||
let far = rect(&mut s, "far", 3000.0, None);
|
||||
s.execute("select.none", &json!({})).unwrap();
|
||||
let mut u = Ui::new(s);
|
||||
u.frame(vec![]);
|
||||
let y = u.row_at(far).y;
|
||||
// The row's left edge: the first point from the left at which the pointer is over it. At depth
|
||||
// 0 the icon's centre is 8 (margin) + 4 + 22 points further right.
|
||||
let mut left = None;
|
||||
for x in 0..150 {
|
||||
let p = Pos2::new(x as f32, y);
|
||||
u.app.session.ds_mut().unwrap().hover = None;
|
||||
u.frame(vec![Event::PointerMoved(p)]);
|
||||
if u.app.session.ds().unwrap().hover == Some(far) {
|
||||
left = Some(x as f32);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let icon = Pos2::new(left.expect("the row has a left edge") + 34.0, y);
|
||||
let view = |u: &Ui| {
|
||||
let v = &u.app.session.ds().unwrap().view;
|
||||
(v.cx, v.cy, v.zoom)
|
||||
};
|
||||
let before = view(&u);
|
||||
for _ in 0..2 {
|
||||
u.button(icon, true);
|
||||
u.button(icon, false);
|
||||
}
|
||||
u.frame(vec![]);
|
||||
assert_eq!(u.app.ui.rename, None, "a double-click on the icon renames nothing");
|
||||
assert_eq!(u.app.session.selection(), vec![far], "the layer is selected");
|
||||
let after = view(&u);
|
||||
assert_ne!(after, before, "the view moved");
|
||||
u.app.session.execute("view.set", &json!({"cx": before.0, "cy": before.1, "zoom": before.2})).unwrap();
|
||||
u.app.session.execute("view.zoomSelection", &json!({})).unwrap();
|
||||
assert_eq!(after, view(&u), "the view frames the layer, as view.zoomSelection does");
|
||||
}
|
||||
|
||||
@@ -1239,7 +1239,8 @@ fn layers(app: &mut FigApp, ui: &mut Ui) {
|
||||
let icon = node_icon(&doc, row.id);
|
||||
let shown = n.visible && !row.dimmed;
|
||||
let icon_col = row_color(&t, is_comp || icon == Icon::Slot, shown, t.icon, t.text_tertiary);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(pos2(x0 + 22.0, r.center().y), vec2(14.0, 14.0)), icon, icon_col);
|
||||
let icon_rect = Rect::from_center_size(pos2(x0 + 22.0, r.center().y), vec2(14.0, 14.0));
|
||||
icons::paint(ui.painter(), icon_rect, icon, icon_col);
|
||||
let name_x = x0 + 36.0;
|
||||
if app.ui.rename.is_some_and(|rn| rn.id == row.id) {
|
||||
if let Some(new) = rename_field(app, ui, row.id, row.depth, &n.name, r) {
|
||||
@@ -1260,8 +1261,17 @@ fn layers(app: &mut FigApp, ui: &mut Ui) {
|
||||
if resp.clicked() {
|
||||
clicked = Some((row.id, ui.input(|i| i.modifiers)));
|
||||
}
|
||||
if resp.double_clicked() && !crate::menus::dev_mode_refuses(app, "object.rename") {
|
||||
app.ui.rename = Some(Rename { id: row.id, start: true });
|
||||
if resp.double_clicked() {
|
||||
// On the icon it zooms to the layer and renames nothing (measured in Figma's web
|
||||
// app, 2026-11-12); elsewhere on the row it renames.
|
||||
if resp.interact_pointer_pos().is_some_and(|p| icon_rect.contains(p)) {
|
||||
if sel.as_slice() != [row.id] {
|
||||
app.run("select.set", json!({"ids": [row.id]}));
|
||||
}
|
||||
app.run("view.zoomSelection", json!({}));
|
||||
} else if !crate::menus::dev_mode_refuses(app, "object.rename") {
|
||||
app.ui.rename = Some(Rename { id: row.id, start: true });
|
||||
}
|
||||
}
|
||||
if resp.secondary_clicked() {
|
||||
if !sel.contains(&row.id) {
|
||||
|
||||
+3
-2
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user