mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 13:47:21 -06:00
Accessibility: Tab no longer stops on a dialog's backdrop; tests check a reachable control
The backdrop behind a modal dialog took clicks with Sense::click(), which egui makes focusable, so Tab stopped on it. It now senses clicks without taking the keyboard focus. The Tab tests now check that Tab reaches a known control in each region instead of only that some widget took the focus: the title bar's three caption buttons, a control inside each dialog, a Tools panel button and a floating strip's Put back in the toolbar button, and the isolation bar's back button. The dialog test also checks that Tab skips the backdrop.
This commit is contained in:
@@ -995,10 +995,12 @@ fn tab_skips_the_isolation_bars_background() {
|
||||
};
|
||||
frame(vec![]);
|
||||
let bar = egui::Rect::from_min_size(screen.min, egui::vec2(screen.width(), 24.0));
|
||||
let bar = ctx.viewport(|vp| vp.prev_pass.widgets.layers().flat_map(|(_, w)| w.iter()).find(|w| w.rect == bar).map(|w| w.id));
|
||||
let bar = bar.expect("the isolation bar");
|
||||
let back = egui::Rect::from_min_size(bar.min + egui::vec2(6.0, 3.0), egui::vec2(18.0, 18.0));
|
||||
let widget =
|
||||
|rect: egui::Rect| ctx.viewport(|vp| vp.prev_pass.widgets.layers().flat_map(|(_, w)| w.iter()).find(|w| w.rect == rect).map(|w| w.id));
|
||||
let (bar, back) = (widget(bar).expect("the isolation bar"), widget(back).expect("the back button"));
|
||||
let stops = crate::widgets::accessibility_tests::tab_stops(&ctx, 10, frame);
|
||||
assert!(!stops.is_empty(), "Tab reaches the back button and the breadcrumbs");
|
||||
assert!(stops.contains(&back), "Tab reaches the back button: {stops:?}");
|
||||
assert!(!stops.contains(&bar), "{stops:?}");
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ pub(crate) fn show_at<R>(ctx: &egui::Context, title: &str, id: Id, initial: Vec2
|
||||
let dim = backdrop();
|
||||
egui::Area::new(dim.id).order(dim.order).fixed_pos(egui::Pos2::ZERO).show(ctx, |ui| {
|
||||
// Modal, but the canvas isn't dimmed so previews stay readable (as in the reference app).
|
||||
ui.allocate_rect(ctx.content_rect(), egui::Sense::click());
|
||||
// Not focusable (`Sense::click()` is): Tab goes through the dialog's controls, not the backdrop.
|
||||
ui.allocate_rect(ctx.content_rect(), egui::Sense::CLICK);
|
||||
});
|
||||
let shown = egui::Window::new(title)
|
||||
.id(id)
|
||||
|
||||
@@ -133,10 +133,17 @@ fn tab_skips_the_band_that_moves_a_dialog() {
|
||||
settle(&ctx, &mut app);
|
||||
let band = modal::handle(id);
|
||||
assert!(ctx.read_response(band).is_some(), "{name}: the band is drawn");
|
||||
let window = ctx.memory(|m| m.area_rect(id)).unwrap_or_else(|| panic!("{name}: the window is drawn"));
|
||||
let stops = crate::widgets::accessibility_tests::tab_stops(&ctx, 30, |events| {
|
||||
frame(&ctx, &mut app, events);
|
||||
});
|
||||
assert!(!stops.is_empty(), "{name}: Tab reaches the dialog's controls");
|
||||
let inside = |s: &egui::Id| ctx.read_response(*s).is_some_and(|r| r.rect.is_positive() && window.contains_rect(r.rect));
|
||||
assert!(stops.iter().any(inside), "{name}: Tab reaches a control in the dialog: {stops:?}");
|
||||
let backdrop: Vec<egui::Id> = ctx.viewport(|vp| {
|
||||
vp.prev_pass.widgets.layers().filter(|(l, _)| **l == modal::backdrop()).flat_map(|(_, w)| w.iter()).map(|w| w.id).collect()
|
||||
});
|
||||
assert!(!backdrop.is_empty(), "{name}: the backdrop is drawn");
|
||||
assert!(!stops.iter().any(|id| backdrop.contains(id)), "{name}: Tab stops on the backdrop: {stops:?}");
|
||||
assert!(!stops.contains(&band), "{name}: {stops:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +403,9 @@ mod tests {
|
||||
let stops = crate::widgets::accessibility_tests::tab_stops(&ctx, 40, |events| {
|
||||
w.frame(events);
|
||||
});
|
||||
assert!(!stops.is_empty(), "Tab reaches the bar's controls");
|
||||
for c in CAPTIONS {
|
||||
assert!(stops.contains(&caption_id(c)), "Tab reaches the {c:?} button: {stops:?}");
|
||||
}
|
||||
let regions: Vec<Id> = std::iter::once(Id::unique("titlebar-drag")).chain((0..8usize).map(|i| Id::unique(("titlebar-resize", i)))).collect();
|
||||
assert!(!stops.iter().any(|id| regions.contains(id)), "{stops:?}");
|
||||
}
|
||||
|
||||
@@ -1030,8 +1030,15 @@ fn tab_skips_the_grip_and_the_tear_off_bars() {
|
||||
.map(|w| w.id)
|
||||
});
|
||||
let grip = grip.expect("the Tools panel's grip");
|
||||
// The Tools panel's tool buttons: 36 by 29 pt.
|
||||
let buttons: Vec<egui::Id> = ctx.viewport(|vp| {
|
||||
vp.prev_pass.widgets.layers().flat_map(|(_, w)| w.iter()).filter(|w| w.rect.size() == vec2(36.0, PITCH - 1.0)).map(|w| w.id).collect()
|
||||
});
|
||||
assert!(!buttons.is_empty(), "the Tools panel's buttons are drawn");
|
||||
let stops = crate::widgets::accessibility_tests::tab_stops(&ctx, 120, frame);
|
||||
assert!(!stops.is_empty(), "Tab reaches the tool buttons");
|
||||
assert!(stops.iter().any(|id| buttons.contains(id)), "Tab reaches the Tools panel's buttons: {stops:?}");
|
||||
let close = egui::Id::unique(("tool-flyout-close", "polygon"));
|
||||
assert!(stops.contains(&close), "Tab reaches the strip's Put back in the toolbar button: {stops:?}");
|
||||
for (name, id) in [("the grip", grip), ("the flyout's bar", bar_id("rectangle")), ("the strip's bar", bar_id("polygon"))] {
|
||||
assert!(!stops.contains(&id), "Tab stops on {name}: {stops:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user