use crate::Id;
use crate::dir::Direction;
use crate::event::Key;
#[allow(unused)] use crate::event::{Event, EventState};
use crate::geom::Offset;
use crate::layout::GridCellInfo;
#[allow(unused)]
use crate::messages::{DecrementStep, IncrementStep, SetValueF64};
use crate::text::CursorRange;
#[allow(unused)] use crate::{Layout, Tile};
#[non_exhaustive]
pub enum Role<'a> {
None,
Unknown,
Label(&'a str),
AccessLabel(&'a str, Key),
Button,
CheckBox(bool),
RadioButton(bool),
Tab,
TabPage,
Border,
ScrollRegion {
offset: Offset,
max_offset: Offset,
},
ScrollBar {
direction: Direction,
value: i32,
max_value: i32,
},
Indicator,
Image,
Canvas,
TextLabel {
text: &'a str,
cursor: usize,
sel_index: usize,
},
TextInput {
text: &'a str,
multi_line: bool,
cursor: CursorRange,
},
Grip,
Slider {
min: f64,
max: f64,
step: f64,
value: f64,
direction: Direction,
},
SpinButton {
min: f64,
max: f64,
step: f64,
value: f64,
},
ProgressBar {
fraction: f32,
direction: Direction,
},
OptionList {
len: Option<usize>,
direction: Direction,
},
OptionListItem {
index: Option<usize>,
selected: Option<bool>,
},
Grid {
columns: Option<usize>,
rows: Option<usize>,
},
GridCell {
info: Option<GridCellInfo>,
selected: Option<bool>,
},
MenuBar,
Menu {
expanded: bool,
},
ComboBox {
active: usize,
text: &'a str,
expanded: bool,
},
Splitter,
Window,
TitleBar,
}
pub enum TextOrSource<'a> {
Borrowed(&'a str),
Owned(String),
Source(Id),
}
impl<'a> From<&'a str> for TextOrSource<'a> {
#[inline]
fn from(text: &'a str) -> Self {
Self::Borrowed(text)
}
}
impl From<String> for TextOrSource<'static> {
#[inline]
fn from(text: String) -> Self {
Self::Owned(text)
}
}
impl<'a> From<&'a String> for TextOrSource<'a> {
#[inline]
fn from(text: &'a String) -> Self {
Self::Borrowed(text)
}
}
impl From<Id> for TextOrSource<'static> {
#[inline]
fn from(id: Id) -> Self {
Self::Source(id)
}
}
#[cfg(feature = "accesskit")]
impl<'a> Role<'a> {
pub(crate) fn as_accesskit_role(&self) -> accesskit::Role {
use accesskit::Role as R;
match self {
Role::None => R::GenericContainer,
Role::Unknown | Role::Grip => R::Unknown,
Role::Label(_) | Role::AccessLabel(_, _) | Role::TextLabel { .. } => R::Label,
Role::Button => R::Button,
Role::CheckBox(_) => R::CheckBox,
Role::RadioButton(_) => R::RadioButton,
Role::Tab => R::Tab,
Role::TabPage => R::TabPanel,
Role::ScrollRegion { .. } => R::ScrollView,
Role::ScrollBar { .. } => R::ScrollBar,
Role::Indicator => R::Unknown,
Role::Image => R::Image,
Role::Canvas => R::Canvas,
Role::TextInput {
multi_line: false, ..
} => R::TextInput,
Role::TextInput {
multi_line: true, ..
} => R::MultilineTextInput,
Role::Slider { .. } => R::Slider,
Role::SpinButton { .. } => R::SpinButton,
Role::ProgressBar { .. } => R::ProgressIndicator,
Role::Border => R::Unknown,
Role::OptionList { .. } => R::ListBox,
Role::OptionListItem { .. } => R::ListBoxOption,
Role::Grid { .. } => R::Grid,
Role::GridCell { .. } => R::Cell,
Role::MenuBar => R::MenuBar,
Role::Menu { .. } => R::Menu,
Role::ComboBox { .. } => R::ComboBox,
Role::Splitter => R::Splitter,
Role::Window => R::Window,
Role::TitleBar => R::TitleBar,
}
}
pub(crate) fn as_accesskit_node(&self, tile: &dyn Tile) -> accesskit::Node {
use crate::cast::Cast;
use accesskit::Action;
let mut node = accesskit::Node::new(self.as_accesskit_role());
node.set_bounds(tile.rect().cast());
if tile.navigable() {
node.add_action(Action::Focus);
}
match *self {
Role::None | Role::Unknown | Role::Border | Role::Grip | Role::Splitter => (),
Role::Button | Role::Tab => {
node.add_action(Action::Click);
}
Role::TabPage => (),
Role::Indicator | Role::Image | Role::Canvas => (),
Role::MenuBar | Role::Window | Role::TitleBar => (),
Role::Label(text) | Role::TextLabel { text, .. } => node.set_value(text),
Role::TextInput { text, .. } => {
node.add_action(Action::SetValue);
node.add_action(Action::ReplaceSelectedText);
node.set_value(text)
}
Role::AccessLabel(text, ref key) => {
node.set_value(text);
if let Some(text) = key.to_text() {
node.set_access_key(text);
}
}
Role::CheckBox(state) | Role::RadioButton(state) => {
node.add_action(Action::Click);
node.set_toggled(state.into());
}
Role::ScrollRegion { offset, max_offset } => {
crate::accesskit::apply_scroll_props_to_node(offset, max_offset, &mut node);
}
Role::ScrollBar {
direction,
value,
max_value,
} => {
node.set_orientation(direction.into());
node.set_numeric_value(value.cast());
node.set_min_numeric_value(0.0);
node.set_max_numeric_value(max_value.cast());
}
Role::Slider {
min,
max,
step,
value,
..
}
| Role::SpinButton {
min,
max,
step,
value,
} => {
node.add_action(Action::SetValue);
node.add_action(Action::Increment);
node.add_action(Action::Decrement);
if min.is_finite() {
node.set_min_numeric_value(min);
}
if max.is_finite() {
node.set_max_numeric_value(max);
}
if step.is_finite() {
node.set_numeric_value_step(step);
}
node.set_numeric_value(value);
if let Role::Slider { direction, .. } = self {
node.set_orientation((*direction).into());
}
}
Role::ProgressBar {
fraction,
direction,
} => {
node.set_max_numeric_value(1.0);
node.set_numeric_value(fraction.cast());
node.set_orientation(direction.into());
}
Role::OptionList { len, direction } => {
if let Some(len) = len {
node.set_size_of_set(len);
}
node.set_orientation(direction.into());
}
Role::OptionListItem { index, selected } => {
if let Some(index) = index {
node.set_position_in_set(index);
}
if let Some(state) = selected {
node.set_selected(state);
}
}
Role::Grid { columns, rows } => {
if let Some(cols) = columns {
node.set_column_count(cols);
}
if let Some(rows) = rows {
node.set_row_count(rows);
}
}
Role::GridCell { info, selected } => {
if let Some(info) = info {
node.set_column_index(info.col.cast());
if info.last_col > info.col {
node.set_column_span((info.last_col + 1 - info.col).cast());
}
node.set_row_index(info.row.cast());
if info.last_row > info.row {
node.set_row_span((info.last_row + 1 - info.row).cast());
}
}
if let Some(state) = selected {
node.set_selected(state);
}
}
Role::ComboBox { expanded, .. } | Role::Menu { expanded } => {
node.add_action(Action::Expand);
node.add_action(Action::Collapse);
node.set_expanded(expanded);
}
}
node
}
}
pub trait RoleCx {
fn set_label_impl(&mut self, label: TextOrSource<'_>);
}
pub trait RoleCxExt: RoleCx {
fn set_label<'a>(&mut self, label: impl Into<TextOrSource<'a>>) {
self.set_label_impl(label.into());
}
}
impl<C: RoleCx + ?Sized> RoleCxExt for C {}