iced_accessibility/
id.rs

1//! Widget and Window IDs.
2
3use std::borrow;
4use std::hash::Hash;
5use std::sync::atomic::{self, AtomicU64};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub enum A11yId {
9    Window(u64),
10    Widget(Id),
11}
12
13// impl A11yId {
14//     pub fn new_widget() -> Self {
15//         Self::Widget(Id::unique())
16//     }
17
18//     pub fn new_window() -> Self {
19//         Self::Window(window_node_id())
20//     }
21// }
22
23impl From<u64> for A11yId {
24    fn from(id: u64) -> Self {
25        Self::Window(id)
26    }
27}
28
29impl From<Id> for A11yId {
30    fn from(id: Id) -> Self {
31        assert!(!matches!(id.0, Internal::Set(_)));
32        Self::Widget(id)
33    }
34}
35
36impl IdEq for A11yId {
37    fn eq(&self, other: &Self) -> bool {
38        match (self, other) {
39            (A11yId::Widget(self_), A11yId::Widget(other)) => {
40                IdEq::eq(self_, other)
41            }
42            _ => self == other,
43        }
44    }
45}
46
47impl From<accesskit::NodeId> for A11yId {
48    fn from(value: accesskit::NodeId) -> Self {
49        let val = u64::from(value.0);
50        if val > u32::MAX as u64 {
51            Self::Window(value.0)
52        } else {
53            Self::Widget(Id::from(val as u64))
54        }
55    }
56}
57
58impl From<A11yId> for accesskit::NodeId {
59    fn from(value: A11yId) -> Self {
60        let node_id = match value {
61            A11yId::Window(id) => id,
62            A11yId::Widget(id) => id.into(),
63        };
64        accesskit::NodeId(node_id)
65    }
66}
67
68static NEXT_ID: AtomicU64 = AtomicU64::new(1);
69static NEXT_WINDOW_ID: AtomicU64 = AtomicU64::new(1);
70
71/// The identifier of a generic widget.
72#[derive(Debug, Clone, PartialEq, Eq, Hash)]
73pub struct Id(pub Internal);
74
75impl Id {
76    /// Creates a custom [`Id`].
77    pub fn new(id: impl Into<borrow::Cow<'static, str>>) -> Self {
78        Self(Internal::Custom(Self::next(), id.into()))
79    }
80
81    /// resets the id counter
82    pub fn reset() {
83        NEXT_ID.store(1, atomic::Ordering::Relaxed);
84    }
85
86    fn next() -> u64 {
87        NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)
88    }
89
90    /// Creates a unique [`Id`].
91    ///
92    /// This function produces a different [`Id`] every time it is called.
93    pub fn unique() -> Self {
94        let id = Self::next();
95
96        Self(Internal::Unique(id))
97    }
98}
99
100impl IdEq for Id {
101    fn eq(&self, other: &Self) -> bool {
102        IdEq::eq(&self.0, &other.0)
103    }
104}
105// Not meant to be used directly
106impl From<u64> for Id {
107    fn from(value: u64) -> Self {
108        Self(Internal::Unique(value))
109    }
110}
111
112// Not meant to be used directly
113impl From<Id> for u64 {
114    fn from(val: Id) -> u64 {
115        match &val.0 {
116            Internal::Unique(id) => *id,
117            Internal::Custom(id, _) => *id,
118            // this is a set id, which is not a valid id and will not ever be converted to a NonZeroU128
119            // so we panic
120            Internal::Set(_) => {
121                panic!("Cannot convert a set id to a NonZeroU128")
122            }
123        }
124    }
125}
126
127impl std::fmt::Display for Id {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        match &self.0 {
130            Internal::Unique(_) => write!(f, "Undefined"),
131            Internal::Custom(_, id) => write!(f, "{}", id.to_string()),
132            Internal::Set(_) => write!(f, "Set"),
133        }
134    }
135}
136
137// XXX WIndow IDs are made unique by adding u32::MAX to them
138/// get window node id that won't conflict with other node ids for the duration of the program
139pub fn window_node_id() -> u64 {
140    u32::MAX as u64
141        + NEXT_WINDOW_ID.fetch_add(1, atomic::Ordering::Relaxed) as u64
142}
143
144// TODO refactor to make panic impossible?
145#[derive(Debug, Clone, Eq)]
146/// Internal representation of an [`Id`].
147pub enum Internal {
148    /// a unique id
149    Unique(u64),
150    /// a custom id, which is equal to any [`Id`] with a matching number or string
151    Custom(u64, borrow::Cow<'static, str>),
152    /// XXX Do not use this as an id for an accessibility node, it will panic!
153    /// XXX Only meant to be used for widgets that have multiple accessibility nodes, each with a
154    /// unique or custom id
155    /// an Id Set, which is equal to any [`Id`] with a matching number or string
156    Set(Vec<Self>),
157}
158
159impl PartialEq for Internal {
160    fn eq(&self, other: &Self) -> bool {
161        match (self, other) {
162            (Self::Unique(l0), Self::Unique(r0)) => l0 == r0,
163            (Self::Custom(_, l1), Self::Custom(_, r1)) => l1 == r1,
164            (Self::Set(l0), Self::Set(r0)) => l0 == r0,
165            _ => false,
166        }
167    }
168}
169
170/// Similar to PartialEq, but only intended for use when comparing Ids
171pub trait IdEq {
172    fn eq(&self, other: &Self) -> bool;
173}
174
175impl IdEq for Internal {
176    fn eq(&self, other: &Self) -> bool {
177        match (self, other) {
178            (Self::Unique(l0), Self::Unique(r0)) => l0 == r0,
179            (Self::Custom(l0, l1), Self::Custom(r0, r1)) => {
180                l0 == r0 || l1 == r1
181            }
182            // allow custom ids to be equal to unique ids
183            (Self::Unique(l0), Self::Custom(r0, _))
184            | (Self::Custom(l0, _), Self::Unique(r0)) => l0 == r0,
185            (Self::Set(l0), Self::Set(r0)) => l0 == r0,
186            // allow set ids to just be equal to any of their members
187            (Self::Set(l0), r) | (r, Self::Set(l0)) => {
188                l0.iter().any(|l| l == r)
189            }
190        }
191    }
192}
193
194impl Hash for Internal {
195    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
196        match self {
197            Self::Unique(id) => id.hash(state),
198            Self::Custom(name, _) => name.hash(state),
199            Self::Set(ids) => ids.hash(state),
200        }
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::Id;
207
208    #[test]
209    fn unique_generates_different_ids() {
210        let a = Id::unique();
211        let b = Id::unique();
212
213        assert_ne!(a, b);
214    }
215}