From 76f0edce08dec541c8f266971608dbdb6891d890 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sat, 17 Oct 2026 23:44:41 +0700 Subject: [PATCH] #766: port the engine test, a section and click-elsewhere UI case, and the control-protocol note --- crates/engine/src/frame_label_tests.rs | 24 +++++++++++++++++++++++- crates/ui-egui/src/frame_label_tests.rs | 24 +++++++++++++++++++++++- docs/control-protocol.md | 3 ++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/crates/engine/src/frame_label_tests.rs b/crates/engine/src/frame_label_tests.rs index 8e9ede7b..3da9522f 100644 --- a/crates/engine/src/frame_label_tests.rs +++ b/crates/engine/src/frame_label_tests.rs @@ -1,6 +1,7 @@ //! Frame name labels as pointer targets: a press on a label (its area as the canvas drew it, //! turned with a rotated frame) selects the frame and drags it in one undo step, hovering it -//! highlights the frame, and labels of locked, hidden, deleted or other pages' frames do nothing. +//! highlights the frame, a double-click asks the UI to rename it there, and labels of locked, +//! hidden, deleted or other pages' frames do nothing. use figcraft_doc::NodeId; use figcraft_geom::Point; @@ -90,3 +91,24 @@ fn labels_of_frames_that_cant_be_picked_do_nothing() { let flat = [Point::new(1.0, 1.0); 4]; assert_eq!(label_at(s.doc().unwrap(), page, &[(f, nan), (f, flat)], Point::new(1.0, 1.0)), None); } + +#[test] +fn double_clicking_a_label_asks_to_rename_its_frame() { + let (mut s, f, q) = setup(); + let c = center(&q); + let before = s.doc().unwrap().world_bounds(f).unwrap(); + let undo = s.ds().unwrap().history.undo.len(); + for kind in [PointerKind::Down, PointerKind::Up, PointerKind::Down, PointerKind::DoubleClick] { + s.pointer(&ev(kind, c)).unwrap(); + } + assert_eq!(s.selection(), vec![f]); + let asked = s.ui_requests.iter().find(|r| r.kind == "canvas.renameLabel").map(|r| id_of(&r.data["id"])); + assert_eq!(asked, Some(Some(f)), "the UI renames the frame at its label"); + assert_eq!(s.doc().unwrap().world_bounds(f), Some(before), "not moved"); + assert_eq!(s.ds().unwrap().history.undo.len(), undo, "nothing to undo yet"); + // Other tools ignore the label. + s.ui_requests.clear(); + s.set_tool(crate::tools::ToolId::Hand); + s.pointer(&ev(PointerKind::DoubleClick, c)).unwrap(); + assert!(s.ui_requests.iter().all(|r| r.kind != "canvas.renameLabel")); +} diff --git a/crates/ui-egui/src/frame_label_tests.rs b/crates/ui-egui/src/frame_label_tests.rs index dbb981bd..687669e8 100644 --- a/crates/ui-egui/src/frame_label_tests.rs +++ b/crates/ui-egui/src/frame_label_tests.rs @@ -1,6 +1,6 @@ //! Frame name labels on the canvas, driven through egui headless: an upright frame's label sits //! just above its top-left corner, a rotated frame's runs along its turned top edge, and pressing -//! either label selects and drags its frame. +//! either label selects and drags its frame; double-clicking one renames the frame or section there. use egui::{Event, Pos2, pos2, vec2}; use figcraft_doc::NodeId; @@ -134,3 +134,25 @@ fn escape_leaves_a_label_rename_with_the_old_name() { assert_eq!(name(&u, b), "Turned"); assert!(u.app.ui.rename_label.is_none()); } + +#[test] +fn a_sections_label_renames_it_and_a_click_elsewhere_commits() { + let mut s = Session::new(); + let a = id(s.execute("node.create", &json!({"type": "section", "x": 0, "y": 0, "width": 300, "height": 200, "name": "Area"})).unwrap()); + s.execute("select.none", &json!({})).unwrap(); + let mut u = Ui::new(s); + u.frame(vec![]); + let undo = u.app.session.ds().unwrap().history.undo.len(); + double_click_label(&mut u, a); + assert_eq!(u.app.ui.rename_label.map(|r| r.id), Some(a), "a field over the section's label"); + assert_eq!(u.app.session.selection(), vec![a]); + u.frame(vec![Event::Text("Zone".into())]); + let at = mid(&label(&u, a)); + let away = pos2(at.x + 200.0, at.y + 500.0); + u.button(away, true); + u.button(away, false); + u.frame(vec![]); + assert_eq!(name(&u, a), "Zone", "a click on the canvas commits"); + assert!(u.app.ui.rename_label.is_none()); + assert_eq!(u.app.session.ds().unwrap().history.undo.len(), undo + 1, "one undo step"); +} diff --git a/docs/control-protocol.md b/docs/control-protocol.md index 3a06874e..0dc52a1e 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -198,7 +198,8 @@ A mouse gesture with the current tool. `kind`: `down`, `move`, `up`, `click`, `d (`x`, `y`) or screen pixels (`sx`, `sy`). `pressure` (0–1) is pen pressure (the Pencil records it); a drag's pressure goes from `pressure` to `pressureTo`. Returns `{selection, tool}`. With the Move and Scale tools, a press on a frame's or section's name label (as the canvas last drew it, along a rotated -frame's top edge) selects and drags that frame. +frame's top edge) selects and drags that frame; a double-click there renames the frame in place in +the desktop app (the name field opens over the label). ```json {"id":4,"method":"engine.execute","params":{"command":"tool.set","params":{"tool":"ellipse"}}}