iced_accessibility/
id.rs
1use 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
13impl 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
73pub struct Id(pub Internal);
74
75impl Id {
76 pub fn new(id: impl Into<borrow::Cow<'static, str>>) -> Self {
78 Self(Internal::Custom(Self::next(), id.into()))
79 }
80
81 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 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}
105impl From<u64> for Id {
107 fn from(value: u64) -> Self {
108 Self(Internal::Unique(value))
109 }
110}
111
112impl 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 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
137pub 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#[derive(Debug, Clone, Eq)]
146pub enum Internal {
148 Unique(u64),
150 Custom(u64, borrow::Cow<'static, str>),
152 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
170pub 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 (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 (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}