mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 13:00:16 +00:00
Transform fields: Tab moves from a field to the next field, one press each
A numeric field's label is a scrub zone laid over the label (a drag scrubs the field, a click focuses it). It was registered with `Sense::click_and_drag()`, which is focusable, so every field had a second, invisible tab stop: Tab went from X to W's label zone and only then to W. The zone is now `CLICK | DRAG`, not focusable, and the W/H Constrain Proportions link — a button between W and H in the Control bar — is no longer a tab stop either, so Tab goes from W straight to H. The preference stays reachable from Preferences › General. Test: `one_tab_moves_from_a_field_to_the_next` (Transform panel, Properties panel's Transform section and Control bar).
This commit is contained in:
committed by
bflatastic
parent
6173bd404f
commit
892c0659e5
@@ -296,3 +296,53 @@ fn the_link_is_a_preference_agents_can_read_and_set() {
|
||||
run(&mut app, "object.setBounds", json!({"height": 100, "reference": 4, "proportional": true}));
|
||||
assert_eq!(size(&app), (200.0, 100.0));
|
||||
}
|
||||
|
||||
/// One Tab moves from a field to the next field: neither a field's label (its scrub zone) nor the
|
||||
/// W/H link between two fields is a tab stop of its own. In the Transform panel and the Properties
|
||||
/// panel the fields run X, W, Y, H (a grid, two to a row); in the Control bar X, Y, W, H.
|
||||
#[test]
|
||||
fn one_tab_moves_from_a_field_to_the_next() {
|
||||
// From X (the selection's centre, 60 pt) the next field is W in the panels and Y in the bar.
|
||||
for (name, draw, next) in [
|
||||
("Transform panel", transform::show as Draw, "width"),
|
||||
("Properties", properties::transform_section as Draw, "width"),
|
||||
("Control bar", crate::chrome::control_bar as Draw, "y"),
|
||||
] {
|
||||
let mut app = rect_app();
|
||||
tab_then_type(&mut app, draw, "60 pt", "200");
|
||||
let st = app.session.active().unwrap();
|
||||
let b = st.doc.bounds_of(&st.selection.objects, false).unwrap();
|
||||
match next {
|
||||
"width" => assert_eq!(size(&app), (200.0, 50.0), "{name}: Tab went from X to W"),
|
||||
_ => assert_eq!((b.width().round(), b.y0.round()), (100.0, 175.0), "{name}: Tab went from X to Y"),
|
||||
}
|
||||
}
|
||||
// In the Control bar the W/H link sits between W and H: Tab goes from W straight to H.
|
||||
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");
|
||||
}
|
||||
|
||||
/// Type `value` into the field of what `draw` shows that shows `field` after one Tab, and press
|
||||
/// Enter. The field the Tab moved to takes the value; the one pressed keeps its own.
|
||||
fn tab_then_type(app: &mut VectorcraftApp, draw: Draw, field: &str, value: &str) {
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
let mut time = 0.0;
|
||||
let mut frame = |app: &mut VectorcraftApp, events: Vec<Event>| {
|
||||
time += 1.0;
|
||||
frame_raw(&ctx, app, egui::RawInput { events, time: Some(time), ..Default::default() }, draw)
|
||||
};
|
||||
frame(app, vec![]);
|
||||
let shown = frame(app, vec![]);
|
||||
let at = shown.iter().filter(|(s, _)| s == field).map(|(_, r)| r.center()).next().unwrap_or_else(|| panic!("no {field} field in {shown:?}"));
|
||||
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 };
|
||||
for events in [vec![Event::PointerMoved(at), button(at, true), button(at, false)], vec![], vec![tab], vec![]] {
|
||||
frame(app, events);
|
||||
}
|
||||
let enter = Event::Key { key: Key::Enter, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE };
|
||||
for events in [vec![Event::Text(value.into())], vec![enter], vec![]] {
|
||||
frame(app, events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,11 +484,14 @@ pub fn link_width(ui: &Ui) -> f32 {
|
||||
pub const CONSTRAIN_LINK_SIZE: f32 = 22.0;
|
||||
|
||||
/// The link between W and H (Transform panel, Properties panel, Control bar): one toggle, the
|
||||
/// `constrainProportions` preference, which the size fields pass on as `proportional`.
|
||||
/// `constrainProportions` preference, which the size fields pass on as `proportional`. It is not a
|
||||
/// tab stop, so Tab goes from W straight to H (it is the same preference as Preferences › General
|
||||
/// › Constrain Width and Height Proportions for the keyboard).
|
||||
pub fn constrain_link(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let on = app.session.prefs.constrain_proportions;
|
||||
let tip = tl!("Constrain Width and Height Proportions");
|
||||
if widgets::icon_button(ui, if on { "link" } else { "link-2-off" }, tip, on, CONSTRAIN_LINK_SIZE).clicked() {
|
||||
let (_, resp) = ui.allocate_exact_size(egui::Vec2::splat(CONSTRAIN_LINK_SIZE), egui::Sense::CLICK);
|
||||
if widgets::paint_icon_button(ui, resp, if on { "link" } else { "link-2-off" }, tip, on).clicked() {
|
||||
set_pref(app, "constrainProportions", !on);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,10 +168,11 @@ pub(crate) fn field(ui: &Ui, field: Id, rect: Rect, text: &str, value: Option<f6
|
||||
}
|
||||
// On top of the label, which may sense clicks itself (selectable text). A link senses drags
|
||||
// too and keeps its clicks (and its pointing hand while hovered): a widget laid over it would
|
||||
// take them.
|
||||
// take them. The zone is not a tab stop of its own (`Sense::click_and_drag` would be): Tab
|
||||
// moves from a field to the next field, not onto the label before it.
|
||||
let resp = match label.link {
|
||||
Some(link) => ui.interact(label.rect, link, Sense::drag()),
|
||||
None => ui.interact(label.rect, field.with("scrub-label"), Sense::click_and_drag()),
|
||||
None => ui.interact(label.rect, field.with("scrub-label"), Sense::CLICK | Sense::DRAG),
|
||||
};
|
||||
drag(ui, &resp, field, text, value, decimals, label.link.is_some()).map(|v| v.max(*bounds.start()).min(*bounds.end()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user