emilk / emilk/egui

Add `WidgetType::Tab`

Open
#8,474 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
30.6k
Forks
2.1k
Avg merge
1d 9h
Merged PRs (30d)
72

Description

**Is your feature request related to a problem? Please describe.**
There is no suitable `WidgetType` for tabs while I was implementing AccessKit support for egui_dock. AccessKit provides [`accesskit::Role::Tab`](https://docs.rs/accesskit/latest/accesskit/enum.Role.html#variant.Tab) and other tab-related widgets that will be described by assistive technologies accurately.

**Describe the solution you'd like**
Add a `WidgetType::Tab` variant mapping to `accesskit::Role::Tab`, with `WidgetInfo::selected` triggering set_selected instead of set_toggled for it. Possibly also some way to mark a container `Ui` as `Role::TabList` or `Role::TabPanel`.

**Describe alternatives you've considered**

Currently I had to override the role after `widget_info` using `accesskit_node_builder`:
```rust

response.widget_info(|| WidgetInfo::selected(WidgetType::Button, enabled, selected, title));
ui.ctx().accesskit_node_builder(response.id, |node| {
node.set_role(accesskit::Role::Tab);
node.set_selected(selected);
node.clear_toggled();
});
```

Also tried using other existing `WidgetTypes` like selectable label, but assistive technologies will interpret it as a selectable label instead of a tab, which can be confusing.

**Additional context**
See https://github.com/anhosh/egui_dock/pull/345.

**Edit:** the following has been opened to #8557.

During that PR, I also found out that there is no way to describe the direction (horizontal/vertical) on `WidgetType::ResizeHandle`, and all splitters are interpreted as "horizontal" in VoiceOver, and also window resizing doesn't work using VoiceOver (it always reports the window resizing handle as a horizontal splitter stuck at 0%). The PR works around this by

```rust
let ak_steps = ui.input(|input| {
input.num_accesskit_action_requests(response.id, accesskit::Action::Increment) as f32
- input.num_accesskit_action_requests(response.id, accesskit::Action::Decrement) as f32
});
let ak_set_value = ui.input(|input| {
input
.accesskit_action_requests(response.id, accesskit::Action::SetValue)
.find_map(|request| match request.data {
Some(accesskit::ActionData::NumericValue(value)) => Some(value as f32),
_ => None,
})
});
let mut delta = arrow_key_offset.unwrap_or(response.drag_delta());
delta.dim_point += ak_steps * style.separator.arrow_key_step_distance;
if let Some(value) = ak_set_value {
delta.dim_point += (value.clamp(0.0, 1.0) - split.fraction) * rect.dim_size();
}
apply_separator_delta(split, SeparatorAxis::sep_axis, delta, &style.separator);

if response.double_clicked() {
split.fraction = 0.5;
}

// Allow assistive technologies to adjust the separator fraction.
let fraction = split.fraction;
let step = style.separator.arrow_key_step_distance / rect.dim_size().max(1.0);
ui.ctx().accesskit_node_builder(response.id, |node| {
node.set_orientation(accesskit::Orientation::ak_orientation);
node.set_numeric_value(fraction.into());
node.set_min_numeric_value(0.0);
node.set_max_numeric_value(1.0);
node.set_numeric_value_step(step.into());
node.add_action(accesskit::Action::Increment);
node.add_action(accesskit::Action::Decrement);
node.add_action(accesskit::Action::SetValue);
});
```

Contributor guide

Open the contributing guide

Research direction

Locate the WidgetType definition and the implementation of WidgetInfo::selected, then trace the existing AccessKit role and state mapping. Use the egui_dock pull request context to verify the tab behavior; done means tabs map to accesskit::Role::Tab and selected tabs use set_selected rather than set_toggled, with any container-role support resolved.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
accessibility
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.