Merge pull request #1470 from watchakorn-18k/fix/context-menu-frame-label

fix(ui): right-click on a frame's name label picks that frame
This commit is contained in:
BFlat
2026-10-29 09:37:58 +07:00
committed by GitHub
2 changed files with 54 additions and 6 deletions
+11 -6
View File
@@ -247,16 +247,21 @@ fn send(app: &mut FigApp, kind: PointerKind, pos: Pos2, m: &egui::Modifiers) {
}
/// Right-click at screen `pos`: select what's under the pointer (`deep`: the nested layer), then
/// open the context menu there.
/// open the context menu there. A frame's name label counts as the frame, as on a press, so a
/// label drawn over its section picks the frame, not the section behind it. A layer that is
/// already selected keeps the whole selection.
pub fn open_context_menu(app: &mut FigApp, pos: Pos2, deep: bool) {
let p = app.to_page(pos);
if let Ok(ds) = app.session.ds() {
let (doc, page, sel, zoom) = (ds.doc.clone(), ds.page, ds.selection.clone(), ds.view.zoom.max(1e-6));
if let Some(leaf) = app.session.hit(&doc, page, p, 2.0 / zoom) {
let target = figcraft_doc::geometry::select_target(&doc, leaf, &sel, deep);
if !sel.contains(&target) {
app.session.set_selection(vec![target]);
}
// A frame's name label is the frame, as on a press, even where it sits over a section.
let label = figcraft_engine::tools::label_at(&doc, page, &app.session.tool.labels, p);
let hit = app.session.hit(&doc, page, p, 2.0 / zoom);
let target = label.or_else(|| hit.map(|leaf| figcraft_doc::geometry::select_target(&doc, leaf, &sel, deep)));
if let Some(target) = target
&& !sel.contains(&target)
{
app.session.set_selection(vec![target]);
}
}
app.ui.context_menu = Some((pos, app.session.selection()));
+43
View File
@@ -84,3 +84,46 @@ fn copying_a_link_with_nothing_selected_copies_nothing() {
assert!(copied.lock().unwrap().is_empty(), "no link to node 0");
assert!(u.app.ui.toast.is_none());
}
/// With a section selected, a right-click on either frame inside it links that frame, not the
/// section that was selected before.
#[test]
fn copying_a_link_on_a_frame_inside_a_selected_section_links_that_frame() {
let mut s = Session::new();
let sec = id(&s.execute("node.create", &json!({"type": "section", "x": 0, "y": 0, "width": 800, "height": 400, "name": "Flow"})).unwrap());
let a = id(&s.execute("frame.create", &json!({"x": 20, "y": 60, "width": 300, "height": 200, "name": "main", "parent": sec})).unwrap());
let b = id(&s.execute("frame.create", &json!({"x": 400, "y": 60, "width": 300, "height": 200, "name": "dropdown", "parent": sec})).unwrap());
s.set_selection(vec![sec]);
let copied = Arc::new(Mutex::new(Vec::new()));
let mut u = Ui::new(s);
u.app.services = Box::new(Copied(copied.clone()));
u.frame(vec![]);
for (frame, x) in [(a, 170.0), (b, 550.0)] {
let at = u.app.to_screen(x, 160.0);
crate::canvas::open_context_menu(&mut u.app, at, false);
crate::menus::invoke(&mut u.app, "edit.copyLink");
let last = copied.lock().unwrap().last().cloned();
assert_eq!(last, Some(format!("figcraft://node/{}", frame.0)), "the frame at x={x}");
}
}
/// A frame inside a section has its label above it, inside the section: a right-click there picks
/// the frame whose label it is, not the section behind the label.
#[test]
fn copying_a_link_from_a_frames_name_label_links_that_frame() {
let mut s = Session::new();
let sec = id(&s.execute("node.create", &json!({"type": "section", "x": 0, "y": 0, "width": 800, "height": 400, "name": "Flow"})).unwrap());
let a = id(&s.execute("frame.create", &json!({"x": 20, "y": 80, "width": 300, "height": 200, "name": "main", "parent": sec})).unwrap());
s.set_selection(vec![sec]);
let copied = Arc::new(Mutex::new(Vec::new()));
let mut u = Ui::new(s);
u.app.services = Box::new(Copied(copied.clone()));
u.frame(vec![]);
let quad = u.app.session.tool.labels.iter().find(|(i, _)| *i == a).map(|(_, q)| *q).expect("the frame has a label");
let (cx, cy) = (quad.iter().map(|p| p.x).sum::<f64>() / 4.0, quad.iter().map(|p| p.y).sum::<f64>() / 4.0);
let at = u.app.to_screen(cx, cy);
crate::canvas::open_context_menu(&mut u.app, at, false);
assert_eq!(u.app.session.selection(), vec![a], "the label's frame is selected");
crate::menus::invoke(&mut u.app, "edit.copyLink");
assert_eq!(copied.lock().unwrap().last().cloned(), Some(format!("figcraft://node/{}", a.0)));
}