From 07e598c6365113c6dae8dcb24fbd03339c9d280c Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 4 Nov 2026 05:35:28 -0800 Subject: [PATCH] Accessibility: a set value in another unit keeps a number past the rail A dialog slider whose field shows another unit, such as Transform Each's Move in millimeters, converted the number back to points and held it to the rail's range, so a set value past the rail that the field holds came back as the rail's end. --- crates/ui-egui/src/dialogs/form.rs | 5 +++-- crates/ui-egui/src/dialogs/tests.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index faad934c2..333c9de1b 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -813,8 +813,9 @@ pub(super) fn slider_rail_valued( let units = shown.as_ref().map(|s| s.range.clone()); let (dragged, _, keyed) = crate::widgets::color_slider_stepped(ui, ("dlg-slider", key), name, shown, ((v - min) / (max - min)) as f32, SLIDER_WIDTH, track); - let keyed = - keyed.zip(units).map(|(k, u)| if u == range { k } else { (min + (k - u.start()) / (u.end() - u.start()) * (max - min)).clamp(min, max) }); + // Not clamped to the rail: a screen reader's set value can reach a number past it that the + // field holds. + let keyed = keyed.zip(units).map(|(k, u)| if u == range { k } else { min + (k - u.start()) / (u.end() - u.start()) * (max - min) }); if let Some(n) = dragged.map(|x| (min + x as f64 * (max - min)).round()).or(keyed).filter(|n| *n != held) { d.fields.insert(key.into(), json!(n)); } diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 37a027e73..b52328dd1 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -2078,6 +2078,23 @@ fn screen_readers_set_a_dialog_slider_number() { assert_eq!(scale(&app), 250.0, "within the range screen readers get"); f.run(&mut app, vec![set(400.0)]); assert_eq!(scale(&app), 250.0, "past 250%, the range's end now"); + // The same for a Move slider in millimeters, whose rail ends at 1000 pt: with the field at + // 1200 pt, 400 mm sets about 1134 pt. + app.ui.dialog = None; + app.run("document.setUnits", json!({"units": "millimeters"})).unwrap(); + crate::menus::invoke(&mut app, "object.transformEach", json!({})); + app.ui.dialog.as_mut().expect("Transform Each is open").fields.insert("moveH".into(), json!(1200)); + let mut f = Frames::new(); + f.run(&mut app, vec![]); + let out = f.run(&mut app, vec![]); + let nodes = &out.platform_output.accesskit_update.as_ref().expect("an accessibility tree").nodes; + let mut horizontal = nodes.iter().filter(|(_, n)| n.role() == egui::accesskit::Role::Slider && n.label() == Some("Horizontal:")); + let (node, _) = horizontal.nth(1).expect("the Move slider named Horizontal:"); + let request = + ActionRequest { action: Action::SetValue, target_node: *node, target_tree: TreeId::ROOT, data: Some(ActionData::NumericValue(400.0)) }; + f.run(&mut app, vec![egui::Event::AccessKitActionRequest(request)]); + let moved = app.ui.dialog.as_ref().expect("Transform Each is open").f64("moveH", f64::NAN); + assert!((moved - 400.0 * 72.0 / 25.4).abs() < 1e-6, "{moved} pt"); } #[test]