Forms · NumberInput label: the name assistive technology reads, drawn nowhere

This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 12:34:19 -04:00
1 parent 47caebdd36
commit 718617939e
3 files changed
+22

No files matched your search

+1
View File
@@ -262,3 +262,4 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- 2026-10-04: chapter 44, Rendering, holds `RenderSurface` alone. gpui at 1a28cff paints `RenderImage`s everywhere, `CVPixelBuffer`s (NV12) on macOS's Metal renderer alone, and shares no device or shader hook, so shader, camera and GPU views are the host's, feeding the surface, and zero-copy or Metal, OpenGL, Vulkan and Direct3D surfaces are blocked (`tasks/ch44.md`). CI tests the crate and builds the gallery on Linux, Windows and macOS, arm64 and x86_64.
- 2026-10-05: an overlay its owner removes without calling `Close` still hands focus back: `take_focus` records each holder and what it took focus from, `give_back` forgets it, and when the focused element leaves the tree the root scope walks back past every gone holder to the focus before them all, else takes it; nested overlays removed together return to what the outer one took focus from. Before, a dialog whose action set its owner's state to closed left focus on the root, and keys reached nothing until a click.
- 2026-10-05: `Select::label` and `TextInput::label` name the control for assistive technology without drawing anything; a text input with neither label nor placeholder had no name, and a select read only its value, so a field named by a description list beside it was anonymous in the AX tree.
- 2026-10-05: `NumberInput::label` names the field for assistive technology without drawing it, through `TextInput::set_label` on the field's inner input, which lives across frames; a number field named only by a description list beside it was anonymous in the AX tree.
+12
View File
@@ -224,6 +224,7 @@ pub struct NumberInput {
on_change: Option<OnChange>,
on_commit: Option<OnChange>,
scrub_label: Option<SharedString>,
label: SharedString,
}
impl NumberInput {
@@ -241,9 +242,16 @@ impl NumberInput {
on_change: None,
on_commit: None,
scrub_label: None,
label: SharedString::default(),
}
}
/// The name assistive technology reads, drawn nowhere.
pub fn label(mut self, text: impl Into<SharedString>) -> Self {
self.label = text.into();
self
}
pub fn range(mut self, min: f64, max: f64) -> Self {
assert!(min <= max, "number range {min}..{max} is empty");
self.min = min;
@@ -316,6 +324,10 @@ impl RenderOnce for NumberInput {
numeric.limits = (min, max, step, self.precision);
});
let input = state.read(cx).input.clone();
if input.read(cx).label_text() != &self.label {
let label = self.label.clone();
input.update(cx, |input, cx| input.set_label(label, cx));
}
let shown = format::number(value, self.precision, Separators::EN);
let editing = input.read(cx).focus().is_focused(window);
if !editing && input.read(cx).text() != shown {
+9
View File
@@ -204,6 +204,10 @@ impl TextInput {
&self.placeholder
}
pub fn label_text(&self) -> &SharedString {
&self.label
}
pub fn selection(&self) -> Range<usize> {
self.selection.clone()
}
@@ -221,6 +225,11 @@ impl TextInput {
cx.notify();
}
pub fn set_label(&mut self, text: impl Into<SharedString>, cx: &mut Context<Self>) {
self.label = text.into();
cx.notify();
}
pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
self.masked = masked;
cx.notify();