From d1e69239a75fc740abfdb574396a8065939d6091 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 4 Nov 2026 05:00:46 -0800 Subject: [PATCH] Accessibility: a set value can reach a field's number past the rail The range screen readers get for a dialog slider takes in a field's number past the rail's end, and the set-value action now accepts that range too. --- crates/ui-egui/src/dialogs/tests.rs | 27 +++++++++++++++++++++++++-- crates/ui-egui/src/widgets/color.rs | 10 ++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 9b6365ac87..37a027e730 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -2018,9 +2018,10 @@ fn the_blob_brush_tip_preview_is_a_named_slider_with_arrow_keys() { /// Screen readers can set the Blob Brush tip preview's Angle to a number: the slider offers the /// set-value action, a fraction rounds to the whole degrees the field shows, a number past -180° to /// 180° sets the nearer end, the last request in a frame wins, and a value that is not a finite -/// number leaves the Angle as it is. +/// number leaves the Angle as it is. On a field past its rail's end, a set value can go as far as +/// the field's number. #[test] -fn screen_readers_set_the_blob_brush_angle() { +fn screen_readers_set_a_dialog_slider_number() { use egui::accesskit::{Action, ActionData, ActionRequest, TreeId}; let mut app = app(); app.run("tool.options", json!({"tool": "blobBrush"})).unwrap(); @@ -2055,6 +2056,28 @@ fn screen_readers_set_the_blob_brush_angle() { assert_eq!(angle(&app), -90.0, "not a number"); f.run(&mut app, vec![set(30.0), set(f64::INFINITY)]); assert_eq!(angle(&app), -90.0, "the last request is not a finite number"); + // A field past its rail's end extends the range screen readers get, and a set value can go + // that far: Transform Each's Horizontal Scale at 300% on a rail that ends at 200%. + app.ui.dialog = None; + app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 50})).unwrap(); + crate::menus::invoke(&mut app, "object.transformEach", json!({})); + app.ui.dialog.as_mut().expect("Transform Each is open").fields.insert("scaleH".into(), json!(300)); + let scale = |app: &VectorcraftApp| app.ui.dialog.as_ref().expect("Transform Each is open").f64("scaleH", f64::NAN); + f.run(&mut app, vec![]); + let out = f.run(&mut app, vec![]); + let (node, ..) = slider_node(&out, "Horizontal:"); + let set = |value: f64| { + egui::Event::AccessKitActionRequest(ActionRequest { + action: Action::SetValue, + target_node: node, + target_tree: TreeId::ROOT, + data: Some(ActionData::NumericValue(value)), + }) + }; + f.run(&mut app, vec![set(250.0)]); + 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"); } #[test] diff --git a/crates/ui-egui/src/widgets/color.rs b/crates/ui-egui/src/widgets/color.rs index 747de05cb1..b982d1707f 100644 --- a/crates/ui-egui/src/widgets/color.rs +++ b/crates/ui-egui/src/widgets/color.rs @@ -123,8 +123,9 @@ pub fn color_slider_valued( /// and ↓ lower it to the next whole number, Shift with an arrow, Page Up and Page Down add or take /// ten, and Home and End set the start and the end of `shown`'s range. Screen readers' increment /// and decrement actions step the number as → and ← do, and their set-value action sets it within -/// the range, rounded to [`SliderValue::decimals`] (a value that is not a finite number leaves it -/// as it is). A number past the range steps from the range's nearer end, and no step goes past +/// the range they get (the range, extended to a number past its end), rounded to +/// [`SliderValue::decimals`] (a value that is not a finite number leaves it as it is). A number +/// past the range steps from the range's nearer end, and no step goes past /// either end. With `shown` `None` (a blank field), or /// in a disabled `ui`, the keys and actions leave the rail as it is. Returns the new value from 0 /// to 1 that a click or a drag gives, its phase, and the number in `shown`'s units that a key or @@ -247,7 +248,7 @@ fn rail_step(ui: &Ui, id: egui::Id, focused: bool, s: &SliderValue) -> Option Option