fret_ui_kit/primitives/
toggle_group.rs1use std::sync::Arc;
10
11use fret_core::SemanticsRole;
12use fret_runtime::Model;
13use fret_ui::element::PressableA11y;
14use fret_ui::{ElementContext, UiHost};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ToggleGroupKind {
19 Single,
20 Multiple,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
25pub enum ToggleGroupOrientation {
26 #[default]
27 Horizontal,
28 Vertical,
29}
30
31pub fn toggle_group_item_a11y_multiple(label: Arc<str>, pressed: bool) -> PressableA11y {
37 PressableA11y {
38 role: Some(SemanticsRole::Button),
39 label: Some(label),
40 pressed_state: Some(if pressed {
41 fret_core::SemanticsPressedState::True
42 } else {
43 fret_core::SemanticsPressedState::False
44 }),
45 ..Default::default()
46 }
47}
48
49pub fn toggle_group_item_a11y_single(label: Arc<str>, checked: bool) -> PressableA11y {
51 PressableA11y {
52 role: Some(SemanticsRole::RadioButton),
53 label: Some(label),
54 checked: Some(checked),
55 ..Default::default()
56 }
57}
58
59pub fn toggle_group_item_a11y(label: Arc<str>, pressed: bool) -> PressableA11y {
61 toggle_group_item_a11y_multiple(label, pressed)
62}
63
64pub fn toggle_group_use_single_model<H: UiHost>(
67 cx: &mut ElementContext<'_, H>,
68 controlled: Option<Model<Option<Arc<str>>>>,
69 default_value: impl FnOnce() -> Option<Arc<str>>,
70) -> crate::primitives::controllable_state::ControllableModel<Option<Arc<str>>> {
71 crate::primitives::controllable_state::use_controllable_model(cx, controlled, default_value)
72}
73
74pub fn toggle_group_use_multiple_model<H: UiHost>(
77 cx: &mut ElementContext<'_, H>,
78 controlled: Option<Model<Vec<Arc<str>>>>,
79 default_value: impl FnOnce() -> Vec<Arc<str>>,
80) -> crate::primitives::controllable_state::ControllableModel<Vec<Arc<str>>> {
81 crate::primitives::controllable_state::use_controllable_model(cx, controlled, default_value)
82}
83
84pub fn tab_stop_index_single(
87 values: &[Arc<str>],
88 selected: Option<&str>,
89 disabled: &[bool],
90) -> Option<usize> {
91 if let Some(selected) = selected {
92 if let Some(active) = crate::headless::roving_focus::active_index_from_str_keys(
93 values,
94 Some(selected),
95 disabled,
96 ) {
97 return Some(active);
98 }
99 }
100 crate::headless::roving_focus::first_enabled(disabled)
101}
102
103pub fn tab_stop_index_multiple(
106 values: &[Arc<str>],
107 selected: &[Arc<str>],
108 disabled: &[bool],
109) -> Option<usize> {
110 let first_selected_enabled = values.iter().enumerate().find_map(|(idx, v)| {
111 let enabled = !disabled.get(idx).copied().unwrap_or(true);
112 let on = selected.iter().any(|s| s.as_ref() == v.as_ref());
113 (enabled && on).then_some(idx)
114 });
115 first_selected_enabled.or_else(|| crate::headless::roving_focus::first_enabled(disabled))
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn toggle_group_item_a11y_single_uses_radio_role_and_checked() {
124 let a11y = toggle_group_item_a11y_single(Arc::from("A"), true);
125 assert_eq!(a11y.role, Some(SemanticsRole::RadioButton));
126 assert_eq!(a11y.checked, Some(true));
127 assert!(!a11y.selected);
128 }
129
130 #[test]
131 fn toggle_group_item_a11y_multiple_uses_button_role_and_pressed_state() {
132 let a11y = toggle_group_item_a11y_multiple(Arc::from("A"), true);
133 assert_eq!(a11y.role, Some(SemanticsRole::Button));
134 assert_eq!(
135 a11y.pressed_state,
136 Some(fret_core::SemanticsPressedState::True)
137 );
138 assert_eq!(a11y.checked, None);
139 }
140}