#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Role {
#[default]
None,
Label,
Heading,
Button,
Link,
CheckBox,
Switch,
RadioButton,
Slider,
TextInput,
Image,
Tab,
ListItem,
ProgressBar,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Toggled {
#[default]
None,
False,
True,
Mixed,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct SemanticsProperties {
pub role: Role,
pub label: Option<String>,
pub value: Option<String>,
pub toggled: Toggled,
pub clickable: bool,
pub disabled: bool,
pub range: Option<(f32, f32, f32)>,
}
impl SemanticsProperties {
pub fn new(role: Role) -> Self {
Self {
role,
..Default::default()
}
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn value(mut self, value: impl Into<String>) -> Self {
self.value = Some(value.into());
self
}
pub fn toggled(mut self, on: bool) -> Self {
self.toggled = if on { Toggled::True } else { Toggled::False };
self
}
pub fn maybe_toggled(mut self, on: Option<bool>) -> Self {
self.toggled = match on {
Some(true) => Toggled::True,
Some(false) => Toggled::False,
None => Toggled::Mixed,
};
self
}
pub fn clickable(mut self) -> Self {
self.clickable = true;
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn range(mut self, min: f32, value: f32, max: f32) -> Self {
self.range = Some((min, value, max));
self
}
pub fn is_meaningful(&self) -> bool {
self.role != Role::None || self.label.is_some()
}
#[must_use]
pub fn over(self, under: &Self) -> Self {
Self {
role: if self.role != Role::None {
self.role
} else {
under.role
},
label: match (self.label, under.label.clone()) {
(Some(mine), Some(theirs)) => Some(format!("{mine}\n{theirs}")),
(mine, theirs) => mine.or(theirs),
},
value: self.value.or_else(|| under.value.clone()),
toggled: if self.toggled != Toggled::None {
self.toggled
} else {
under.toggled
},
clickable: self.clickable || under.clickable,
disabled: self.disabled || under.disabled,
range: self.range.or(under.range),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builders_compose() {
let s = SemanticsProperties::new(Role::CheckBox)
.label("Notifications")
.toggled(true);
assert_eq!(s.role, Role::CheckBox);
assert_eq!(s.label.as_deref(), Some("Notifications"));
assert_eq!(s.toggled, Toggled::True);
assert!(s.is_meaningful());
}
#[test]
fn empty_is_not_meaningful() {
assert!(!SemanticsProperties::default().is_meaningful());
assert!(SemanticsProperties::default().label("x").is_meaningful());
}
}