From 9c879301b4b7099b17cfcb709ab2f7a7f1ad06fe Mon Sep 17 00:00:00 2001 From: FractalEncrypt <59615752+FractalEncrypt@users.noreply.github.com> Date: Fri, 6 Nov 2026 11:37:11 -0500 Subject: [PATCH] Transform fields: the Artboard Control bar's link comes after its fields The Artboard tool's own Control bar drew the W/H Constrain Proportions link between W and H, so with that tool Tab stopped on it before H, as the object Control bar did before the last commit. It reserves the link's place and makes the button after the fields too, and the link now toggles with Space or Enter when it has the keyboard, as a dialog's button does. Tests: `one_tab_moves_from_a_field_to_the_next` covers the Artboard Control bar (W to H in one press), and `the_control_bar_link_toggles_by_click` Tabs from H to the link and toggles it with Enter. --- crates/ui-egui/src/panels/artboards.rs | 10 ++++++++-- crates/ui-egui/src/panels/tests_constrain.rs | 16 ++++++++++++++++ crates/ui-egui/src/panels/transform.rs | 7 +++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/panels/artboards.rs b/crates/ui-egui/src/panels/artboards.rs index deff9d872..54a90ddc7 100644 --- a/crates/ui-egui/src/panels/artboards.rs +++ b/crates/ui-egui/src/panels/artboards.rs @@ -100,10 +100,12 @@ pub(crate) fn control_bar(app: &mut VectorcraftApp, ui: &mut Ui) -> bool { super::transform::reference_locator(ui, refi); let link = super::transform::proportional(app, ui); let rp = vectorcraft_geom::reference_point(r, refi); + // The W/H link sits between W and H, but is made after H so Tab goes from W straight to H and + // finds the link after it, as in the object Control bar. + let mut link_room = None; for (k, lbl, v) in [("x", "X:", rp.x - origin.x), ("y", "Y:", rp.y - origin.y), ("width", "W:", r.width()), ("height", "H:", r.height())] { - // The W/H link sits between W and H. if k == "height" { - super::transform::constrain_link(app, ui); + link_room = Some(super::transform::constrain_link_room(ui)); } widgets::field_label(ui, egui::RichText::new(tl!(lbl)).size(12.0).color(t.text_dim)); let set = if matches!(k, "width" | "height") { @@ -121,6 +123,10 @@ pub(crate) fn control_bar(app: &mut VectorcraftApp, ui: &mut Ui) -> bool { super::transform::set_artboard(app, i, refi, xywh, link); } } + if let Some(room) = link_room { + let id = ui.scope_id().with("cb-ab-constrain-link"); + super::transform::constrain_link_at(app, ui, ui.interact(room, id, egui::Sense::click())); + } true } diff --git a/crates/ui-egui/src/panels/tests_constrain.rs b/crates/ui-egui/src/panels/tests_constrain.rs index ea9ceec27..b5e8ed7f7 100644 --- a/crates/ui-egui/src/panels/tests_constrain.rs +++ b/crates/ui-egui/src/panels/tests_constrain.rs @@ -322,6 +322,12 @@ fn one_tab_moves_from_a_field_to_the_next() { let mut app = rect_app(); tab_then_type(&mut app, crate::chrome::control_bar, "100 pt", "200"); assert_eq!(size(&app), (100.0, 200.0), "Control bar: Tab went from W to H"); + // The Artboard tool's Control bar puts the link after its fields too: Tab goes from W to H. + let mut app = rect_app(); + app.select_tool("artboard"); + tab_then_type(&mut app, crate::chrome::control_bar, "400 pt", "200"); + let r = app.session.doc().unwrap().doc.artboards[0].rect; + assert_eq!((r.width().round(), r.height().round()), (400.0, 200.0), "Artboard Control bar: Tab went from W to H"); // Shift+Tab goes back one field, one press too: from W to X in the panels. for (name, draw) in [("Transform panel", transform::show as Draw), ("Properties", properties::transform_section as Draw)] { let mut app = rect_app(); @@ -356,6 +362,16 @@ fn the_control_bar_link_toggles_by_click() { frame(&mut app, events); } assert!(app.session.prefs.constrain_proportions, "the click toggled the link"); + // Tab from H finds the link, and Space or Enter toggles it, as a dialog's button takes them. + let h = at("50 pt").center(); + let button = |pos, pressed| Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE }; + let tab = Event::Key { key: Key::Tab, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE }; + let enter = Event::Key { key: Key::Enter, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE }; + for events in [vec![Event::PointerMoved(h), button(h, true), button(h, false)], vec![], vec![tab], vec![], vec![enter], vec![]] { + frame(&mut app, events); + } + assert!(!app.session.prefs.constrain_proportions, "Enter on the focused link toggled it"); + assert_eq!(size(&app), (100.0, 50.0), "and the fields kept their values"); } /// Type `value` into the field of what `draw` shows that shows `field` after one Tab, and press diff --git a/crates/ui-egui/src/panels/transform.rs b/crates/ui-egui/src/panels/transform.rs index 815b5c4a8..ee3ce335c 100644 --- a/crates/ui-egui/src/panels/transform.rs +++ b/crates/ui-egui/src/panels/transform.rs @@ -492,11 +492,14 @@ pub fn constrain_link(app: &mut VectorcraftApp, ui: &mut Ui) { } /// [`constrain_link`]'s toggle, made over `at`: the Control bar keeps the link's place between W -/// and H but makes it after H, so Tab goes from W straight to H and finds the link after it. +/// and H but makes it after H, so Tab goes from W straight to H and finds the link after it. With +/// the keyboard on it, Space or Enter toggles it, as a dialog's button takes them. pub fn constrain_link_at(app: &mut VectorcraftApp, ui: &mut Ui, at: egui::Response) { let on = app.session.prefs.constrain_proportions; let tip = tl!("Constrain Width and Height Proportions"); - if widgets::paint_icon_button(ui, at, if on { "link" } else { "link-2-off" }, tip, on).clicked() { + let keyed = at.has_focus() + && ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Space) | i.consume_key(egui::Modifiers::NONE, egui::Key::Enter)); + if widgets::paint_icon_button(ui, at, if on { "link" } else { "link-2-off" }, tip, on).clicked() || keyed { set_pref(app, "constrainProportions", !on); } }