Skip to main content

tessera_components/list_item/
selection.rs

1//! Controlled selection behavior for list rows.
2//!
3//! ## Usage
4//!
5//! Present action rows, exclusive choices, and independently checked items.
6
7use tessera_ui::{
8    AccessibilityActionHandler, AccessibilityNode, Callback, CallbackWith, SemanticsModifierNode,
9    accesskit::{Role, Toggled},
10};
11
12/// Selection behavior and accessibility role of a list row.
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
14pub enum ListItemSelectionMode {
15    /// Activate a general list action through `on_click`.
16    #[default]
17    Action,
18    /// Request selection of a radio-style item. The owner manages exclusivity.
19    Single,
20    /// Request toggling of an independently checked item.
21    Multiple,
22}
23
24impl ListItemSelectionMode {
25    pub(super) fn role(self) -> Role {
26        match self {
27            Self::Action => Role::ListItem,
28            Self::Single => Role::RadioButton,
29            Self::Multiple => Role::CheckBox,
30        }
31    }
32}
33
34#[derive(Clone, PartialEq)]
35pub(super) struct ListItemActivation {
36    pub mode: ListItemSelectionMode,
37    pub selected: bool,
38    pub on_click: Option<Callback>,
39    pub on_selection_change: Option<CallbackWith<bool>>,
40}
41
42impl ListItemActivation {
43    pub fn has_handler(&self) -> bool {
44        self.on_click.is_some()
45            || (self.mode != ListItemSelectionMode::Action && self.on_selection_change.is_some())
46    }
47
48    pub fn activate(self) {
49        if self.mode != ListItemSelectionMode::Action
50            && let Some(callback) = self.on_selection_change
51        {
52            callback.call(self.mode == ListItemSelectionMode::Single || !self.selected);
53        } else if let Some(callback) = self.on_click {
54            callback.call();
55        }
56    }
57}
58
59#[derive(Clone)]
60pub(super) struct ListItemSelectionSemantics {
61    pub mode: ListItemSelectionMode,
62    pub selected: Option<bool>,
63}
64
65impl SemanticsModifierNode for ListItemSelectionSemantics {
66    fn apply(&self, node: &mut AccessibilityNode, _: &mut Option<AccessibilityActionHandler>) {
67        let selected = self.selected.unwrap_or(false);
68        match self.mode {
69            ListItemSelectionMode::Action => {
70                if self.selected.is_some() {
71                    node.selected = self.selected;
72                }
73                node.toggled = None;
74            }
75            ListItemSelectionMode::Single => {
76                node.selected = Some(selected);
77                node.toggled = Some(if selected {
78                    Toggled::True
79                } else {
80                    Toggled::False
81                });
82            }
83            ListItemSelectionMode::Multiple => {
84                node.selected = None;
85                node.toggled = Some(if selected {
86                    Toggled::True
87                } else {
88                    Toggled::False
89                });
90            }
91        }
92    }
93}