1use crate::{widget::WidgetId, window::WindowId};
2
3use super::*;
4
5#[derive(Clone)]
9pub struct WidgetPath {
10 window_id: WindowId,
11 path: Arc<Vec<WidgetId>>,
12}
13impl PartialEq for WidgetPath {
14 fn eq(&self, other: &Self) -> bool {
16 self.window_id == other.window_id && self.path == other.path
17 }
18}
19impl Eq for WidgetPath {}
20impl PartialEq<InteractionPath> for WidgetPath {
21 fn eq(&self, other: &InteractionPath) -> bool {
22 other == self
23 }
24}
25impl fmt::Debug for WidgetPath {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 if f.alternate() {
28 f.debug_struct("WidgetPath")
29 .field("window_id", &self.window_id)
30 .field("path", &self.path)
31 .finish_non_exhaustive()
32 } else {
33 write!(f, "{self}")
34 }
35 }
36}
37impl fmt::Display for WidgetPath {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(f, "{}//", self.window_id)?;
40 for w in self.ancestors() {
41 write!(f, "{w}/")?;
42 }
43 write!(f, "{}", self.widget_id())
44 }
45}
46impl WidgetPath {
47 pub fn new(window_id: WindowId, path: Arc<Vec<WidgetId>>) -> WidgetPath {
49 WidgetPath { window_id, path }
50 }
51
52 pub fn into_parts(self) -> (WindowId, Arc<Vec<WidgetId>>) {
54 (self.window_id, self.path)
55 }
56
57 pub fn window_id(&self) -> WindowId {
59 self.window_id
60 }
61
62 pub fn ancestors(&self) -> &[WidgetId] {
64 &self.path[..self.path.len() - 1]
65 }
66
67 pub fn widget_id(&self) -> WidgetId {
69 self.path[self.path.len() - 1]
70 }
71
72 pub fn parent_id(&self) -> Option<WidgetId> {
74 self.ancestors().iter().copied().next_back()
75 }
76
77 pub fn widgets_path(&self) -> &[WidgetId] {
79 &self.path[..]
80 }
81
82 pub fn contains(&self, widget_id: WidgetId) -> bool {
84 self.path.iter().any(move |&w| w == widget_id)
85 }
86
87 pub fn ancestor_path(&self, ancestor_id: WidgetId) -> Option<Cow<WidgetPath>> {
89 self.path.iter().position(|&id| id == ancestor_id).map(|i| {
90 if i == self.path.len() - 1 {
91 Cow::Borrowed(self)
92 } else {
93 Cow::Owned(WidgetPath {
94 window_id: self.window_id,
95 path: self.path[..i].to_vec().into(),
96 })
97 }
98 })
99 }
100
101 pub fn shared_ancestor<'a>(&'a self, other: &'a WidgetPath) -> Option<Cow<'a, WidgetPath>> {
103 if self.window_id == other.window_id {
104 if let Some(i) = self.path.iter().zip(other.path.iter()).position(|(a, b)| a != b) {
105 if i == 0 {
106 None
107 } else {
108 let path = self.path[..i].to_vec().into();
109 Some(Cow::Owned(WidgetPath {
110 window_id: self.window_id,
111 path,
112 }))
113 }
114 } else if self.path.len() <= other.path.len() {
115 Some(Cow::Borrowed(self))
116 } else {
117 Some(Cow::Borrowed(other))
118 }
119 } else {
120 None
121 }
122 }
123
124 pub fn root_path(&self) -> Cow<WidgetPath> {
126 if self.path.len() == 1 {
127 Cow::Borrowed(self)
128 } else {
129 Cow::Owned(WidgetPath {
130 window_id: self.window_id,
131 path: Arc::new(vec![self.path[0]]),
132 })
133 }
134 }
135
136 pub fn sub_path(&self, widget_id: WidgetId) -> Option<Cow<WidgetPath>> {
138 if self.widget_id() == widget_id {
139 Some(Cow::Borrowed(self))
140 } else {
141 let i = self.path.iter().position(|&id| id == widget_id)?;
142 let path = Self::new(self.window_id, Arc::new(self.path[..=i].to_vec()));
143 Some(Cow::Owned(path))
144 }
145 }
146}
147
148#[derive(Clone)]
150pub struct InteractionPath {
151 path: WidgetPath,
152 blocked: usize,
153 disabled: usize,
154}
155impl PartialEq for InteractionPath {
156 fn eq(&self, other: &Self) -> bool {
158 self.as_path() == other.as_path() && self.blocked == other.blocked && self.disabled == other.disabled
159 }
160}
161impl Eq for InteractionPath {}
162impl PartialEq<WidgetPath> for InteractionPath {
163 fn eq(&self, other: &WidgetPath) -> bool {
165 self.as_path() == other
166 }
167}
168impl fmt::Debug for InteractionPath {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 if f.alternate() {
171 f.debug_struct("InteractionPath")
172 .field("window_id", &self.window_id)
173 .field("path", &self.path)
174 .field("blocked", &self.blocked_index())
175 .field("disabled", &self.disabled_index())
176 .finish_non_exhaustive()
177 } else {
178 write!(f, "{self}")
179 }
180 }
181}
182impl fmt::Display for InteractionPath {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 write!(f, "{}//", self.window_id)?;
185 let mut sep = "";
186 for (w, i) in self.zip() {
187 write!(f, "{sep}{w}{{{i:?}}}")?;
188 sep = "/";
189 }
190 Ok(())
191 }
192}
193impl InteractionPath {
194 pub(super) fn new_internal(path: WidgetPath, blocked: usize, disabled: usize) -> Self {
195 Self { path, blocked, disabled }
196 }
197
198 pub fn new<P: IntoIterator<Item = (WidgetId, Interactivity)>>(window_id: WindowId, path: P) -> InteractionPath {
200 let iter = path.into_iter();
201 let mut path = Vec::with_capacity(iter.size_hint().0);
202 let mut blocked = None;
203 let mut disabled = None;
204 for (i, (w, interactivity)) in iter.enumerate() {
205 path.push(w);
206 if blocked.is_none() && interactivity.contains(Interactivity::BLOCKED) {
207 blocked = Some(i);
208 }
209 if disabled.is_none() && interactivity.contains(Interactivity::DISABLED) {
210 disabled = Some(i);
211 }
212 }
213 let len = path.len();
214 InteractionPath {
215 path: WidgetPath::new(window_id, path.into()),
216 blocked: blocked.unwrap_or(len),
217 disabled: disabled.unwrap_or(len),
218 }
219 }
220
221 pub fn new_enabled(window_id: WindowId, path: Arc<Vec<WidgetId>>) -> InteractionPath {
223 let path = WidgetPath::new(window_id, path);
224 Self::from_enabled(path)
225 }
226
227 pub fn from_enabled(path: WidgetPath) -> InteractionPath {
229 let len = path.path.len();
230 InteractionPath {
231 path,
232 blocked: len,
233 disabled: len,
234 }
235 }
236
237 pub fn as_path(&self) -> &WidgetPath {
239 &self.path
240 }
241
242 pub fn blocked_index(&self) -> Option<usize> {
246 if self.blocked < self.path.path.len() {
247 Some(self.blocked)
248 } else {
249 None
250 }
251 }
252 pub fn disabled_index(&self) -> Option<usize> {
256 if self.disabled < self.path.path.len() {
257 Some(self.disabled)
258 } else {
259 None
260 }
261 }
262
263 pub fn interaction_path(&self) -> impl DoubleEndedIterator<Item = Interactivity> + ExactSizeIterator {
265 struct InteractivityIter {
266 range: ops::Range<usize>,
267 blocked: usize,
268 disabled: usize,
269 }
270
271 impl InteractivityIter {
272 fn interactivity(&self, i: usize) -> Interactivity {
273 let mut interactivity = Interactivity::ENABLED;
274 if self.blocked <= i {
275 interactivity |= Interactivity::BLOCKED;
276 }
277 if self.disabled <= i {
278 interactivity |= Interactivity::DISABLED;
279 }
280 interactivity
281 }
282 }
283 impl Iterator for InteractivityIter {
284 type Item = Interactivity;
285
286 fn next(&mut self) -> Option<Self::Item> {
287 self.range.next().map(|i| self.interactivity(i))
288 }
289
290 fn size_hint(&self) -> (usize, Option<usize>) {
291 (self.range.len(), Some(self.range.len()))
292 }
293 }
294 impl ExactSizeIterator for InteractivityIter {}
295 impl DoubleEndedIterator for InteractivityIter {
296 fn next_back(&mut self) -> Option<Self::Item> {
297 self.range.next_back().map(|i| self.interactivity(i))
298 }
299 }
300
301 InteractivityIter {
302 range: 0..self.path.path.len(),
303 blocked: self.blocked,
304 disabled: self.disabled,
305 }
306 }
307
308 pub fn interactivity_of(&self, widget_id: WidgetId) -> Option<Interactivity> {
310 self.path.widgets_path().iter().position(|&w| w == widget_id).map(|i| {
311 let mut interactivity = Interactivity::ENABLED;
312 if self.blocked <= i {
313 interactivity |= Interactivity::BLOCKED;
314 }
315 if self.disabled <= i {
316 interactivity |= Interactivity::DISABLED;
317 }
318 interactivity
319 })
320 }
321
322 pub fn interactivity(&self) -> Interactivity {
324 let mut interactivity = Interactivity::ENABLED;
325 let len = self.path.path.len();
326 if self.blocked < len {
327 interactivity |= Interactivity::BLOCKED;
328 }
329 if self.disabled < len {
330 interactivity |= Interactivity::DISABLED;
331 }
332 interactivity
333 }
334
335 pub fn zip(&self) -> impl DoubleEndedIterator<Item = (WidgetId, Interactivity)> + ExactSizeIterator + '_ {
337 self.path.widgets_path().iter().copied().zip(self.interaction_path())
338 }
339
340 pub fn unblocked(self) -> Option<InteractionPath> {
345 if self.blocked < self.path.path.len() {
346 if self.blocked == 0 {
347 return None;
348 }
349 Some(InteractionPath {
350 path: WidgetPath {
351 window_id: self.path.window_id,
352 path: self.path.path[..self.blocked].to_vec().into(),
353 },
354 blocked: self.blocked,
355 disabled: self.disabled,
356 })
357 } else {
358 Some(self)
359 }
360 }
361
362 pub fn enabled(self) -> Option<WidgetPath> {
366 let enabled_end = self.blocked.min(self.disabled);
367
368 if enabled_end < self.path.path.len() {
369 if enabled_end == 0 {
370 return None;
371 }
372 Some(WidgetPath {
373 window_id: self.path.window_id,
374 path: self.path.path[..enabled_end].to_vec().into(),
375 })
376 } else {
377 Some(self.path)
378 }
379 }
380
381 pub fn ancestor_path(&self, ancestor_id: WidgetId) -> Option<Cow<InteractionPath>> {
383 self.widgets_path().iter().position(|&id| id == ancestor_id).map(|i| {
384 if i == self.path.path.len() - 1 {
385 Cow::Borrowed(self)
386 } else {
387 Cow::Owned(InteractionPath {
388 path: WidgetPath {
389 window_id: self.window_id,
390 path: self.path.path[..=i].to_vec().into(),
391 },
392 blocked: self.blocked,
393 disabled: self.disabled,
394 })
395 }
396 })
397 }
398
399 pub fn shared_ancestor<'a>(&'a self, other: &'a InteractionPath) -> Option<Cow<'a, InteractionPath>> {
401 if self.window_id == other.window_id {
402 if let Some(i) = self.zip().zip(other.zip()).position(|(a, b)| a != b) {
403 if i == 0 {
404 None
405 } else {
406 let path = self.path.path[..i].to_vec().into();
407 Some(Cow::Owned(InteractionPath {
408 path: WidgetPath {
409 window_id: self.window_id,
410 path,
411 },
412 blocked: self.blocked,
413 disabled: self.disabled,
414 }))
415 }
416 } else if self.path.path.len() <= other.path.path.len() {
417 Some(Cow::Borrowed(self))
418 } else {
419 Some(Cow::Borrowed(other))
420 }
421 } else {
422 None
423 }
424 }
425
426 pub fn root_path(&self) -> Cow<InteractionPath> {
428 if self.path.path.len() == 1 {
429 Cow::Borrowed(self)
430 } else {
431 Cow::Owned(InteractionPath {
432 path: WidgetPath {
433 window_id: self.window_id,
434 path: Arc::new(vec![self.path.path[0]]),
435 },
436 blocked: self.blocked,
437 disabled: self.disabled,
438 })
439 }
440 }
441
442 pub fn sub_path(&self, widget_id: WidgetId) -> Option<Cow<InteractionPath>> {
444 if widget_id == self.widget_id() {
445 Some(Cow::Borrowed(self))
446 } else {
447 let path = self.path.sub_path(widget_id)?;
448 Some(Cow::Owned(Self {
449 path: path.into_owned(),
450 blocked: self.blocked,
451 disabled: self.disabled,
452 }))
453 }
454 }
455}
456impl ops::Deref for InteractionPath {
457 type Target = WidgetPath;
458
459 fn deref(&self) -> &Self::Target {
460 &self.path
461 }
462}
463impl From<InteractionPath> for WidgetPath {
464 fn from(p: InteractionPath) -> Self {
465 p.path
466 }
467}