Add accessibility to `ProgressBar` and `Spinner` (#4139)

- Introduces `WidgetType::ProgressIndicator` and maps it to the
corresponding AccessKit role.
- A `Spinner` is now exposed as a widget indicating a progress for which
a completion state is not known.
- On the other hand, a `ProgressBar` reports a completion state and can
possibly be labeled. Note that a label is not used if not explicitly
asked by the user, as it would be redundant information. Assistive
technologies prefer the numerical value so they can, for instance, emit
beeps of which the frequency rise as the completion state increase. I
had to call `floor` on the progression as it seems all the ATs I tested
would round the value, hence reporting something different than what is
displayed on the label.
This commit is contained in:
Arnold Loubriat 2024-03-08 09:54:21 +01:00 committed by GitHub
parent a1d5145c16
commit 76411b5d74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 17 additions and 1 deletions

View File

@ -633,6 +633,7 @@ impl WidgetInfo {
WidgetType::ColorButton => "color button",
WidgetType::ImageButton => "image button",
WidgetType::CollapsingHeader => "collapsing header",
WidgetType::ProgressIndicator => "progress indicator",
WidgetType::Label | WidgetType::Other => "",
};

View File

@ -643,6 +643,8 @@ pub enum WidgetType {
CollapsingHeader,
ProgressIndicator,
/// If you cannot fit any of the above slots.
///
/// If this is something you think should be added, file an issue.

View File

@ -783,6 +783,7 @@ impl Response {
WidgetType::Slider => Role::Slider,
WidgetType::DragValue => Role::SpinButton,
WidgetType::ColorButton => Role::ColorWell,
WidgetType::ProgressIndicator => Role::ProgressIndicator,
WidgetType::Other => Role::Unknown,
});
if let Some(label) = info.label {

View File

@ -113,6 +113,17 @@ impl Widget for ProgressBar {
let (outer_rect, response) =
ui.allocate_exact_size(vec2(desired_width, height), Sense::hover());
response.widget_info(|| {
let mut info = if let Some(ProgressBarText::Custom(text)) = &text {
WidgetInfo::labeled(WidgetType::ProgressIndicator, text.text())
} else {
WidgetInfo::new(WidgetType::ProgressIndicator)
};
info.value = Some((progress as f64 * 100.0).floor());
info
});
if ui.is_rect_visible(response.rect) {
if animate {
ui.ctx().request_repaint();

View File

@ -1,6 +1,6 @@
use epaint::{emath::lerp, vec2, Color32, Pos2, Rect, Shape, Stroke};
use crate::{Response, Sense, Ui, Widget};
use crate::{Response, Sense, Ui, Widget, WidgetInfo, WidgetType};
/// A spinner widget used to indicate loading.
///
@ -66,6 +66,7 @@ impl Widget for Spinner {
.size
.unwrap_or_else(|| ui.style().spacing.interact_size.y);
let (rect, response) = ui.allocate_exact_size(vec2(size, size), Sense::hover());
response.widget_info(|| WidgetInfo::new(WidgetType::ProgressIndicator));
self.paint_at(ui, rect);
response