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); } }