1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
3 | |
4 | // cSpell: ignore xffff |
5 | |
6 | //! This module contains the ItemTree and code that helps navigating it |
7 | |
8 | use crate::accessibility::{ |
9 | AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction, |
10 | }; |
11 | use crate::items::{AccessibleRole, ItemRef, ItemVTable}; |
12 | use crate::layout::{LayoutInfo, Orientation}; |
13 | use crate::lengths::{ItemTransform, LogicalPoint, LogicalRect}; |
14 | use crate::slice::Slice; |
15 | use crate::window::WindowAdapterRc; |
16 | use crate::SharedString; |
17 | use alloc::vec::Vec; |
18 | use core::ops::ControlFlow; |
19 | use core::pin::Pin; |
20 | use vtable::*; |
21 | |
22 | #[repr (C)] |
23 | #[derive (Debug, Clone, Copy)] |
24 | /// A range of indices |
25 | pub struct IndexRange { |
26 | /// Start index |
27 | pub start: usize, |
28 | /// Index one past the last index |
29 | pub end: usize, |
30 | } |
31 | |
32 | impl From<core::ops::Range<usize>> for IndexRange { |
33 | fn from(r: core::ops::Range<usize>) -> Self { |
34 | Self { start: r.start, end: r.end } |
35 | } |
36 | } |
37 | impl From<IndexRange> for core::ops::Range<usize> { |
38 | fn from(r: IndexRange) -> Self { |
39 | Self { start: r.start, end: r.end } |
40 | } |
41 | } |
42 | |
43 | /// A ItemTree is representing an unit that is allocated together |
44 | #[vtable ] |
45 | #[repr (C)] |
46 | pub struct ItemTreeVTable { |
47 | /// Visit the children of the item at index `index`. |
48 | /// Note that the root item is at index 0, so passing 0 would visit the item under root (the children of root). |
49 | /// If you want to visit the root item, you need to pass -1 as an index. |
50 | pub visit_children_item: extern "C" fn( |
51 | core::pin::Pin<VRef<ItemTreeVTable>>, |
52 | index: isize, |
53 | order: TraversalOrder, |
54 | visitor: VRefMut<ItemVisitorVTable>, |
55 | ) -> VisitChildrenResult, |
56 | |
57 | /// Return a reference to an item using the given index |
58 | pub get_item_ref: extern "C" fn( |
59 | core::pin::Pin<VRef<ItemTreeVTable>>, |
60 | index: u32, |
61 | ) -> core::pin::Pin<VRef<ItemVTable>>, |
62 | |
63 | /// Return the range of indices below the dynamic `ItemTreeNode` at `index` |
64 | pub get_subtree_range: |
65 | extern "C" fn(core::pin::Pin<VRef<ItemTreeVTable>>, index: u32) -> IndexRange, |
66 | |
67 | /// Return the `ItemTreeRc` at `subindex` below the dynamic `ItemTreeNode` at `index` |
68 | pub get_subtree: extern "C" fn( |
69 | core::pin::Pin<VRef<ItemTreeVTable>>, |
70 | index: u32, |
71 | subindex: usize, |
72 | result: &mut vtable::VWeak<ItemTreeVTable, Dyn>, |
73 | ), |
74 | |
75 | /// Return the item tree that is defined by this `ItemTree`. |
76 | /// The return value is an item weak because it can be null if there is no parent. |
77 | /// And the return value is passed by &mut because ItemWeak has a destructor |
78 | pub get_item_tree: extern "C" fn(core::pin::Pin<VRef<ItemTreeVTable>>) -> Slice<ItemTreeNode>, |
79 | |
80 | /// Return the node this ItemTree is a part of in the parent ItemTree. |
81 | /// |
82 | /// The return value is an item weak because it can be null if there is no parent. |
83 | /// And the return value is passed by &mut because ItemWeak has a destructor |
84 | /// Note that the returned value will typically point to a repeater node, which is |
85 | /// strictly speaking not an Item at all! |
86 | pub parent_node: extern "C" fn(core::pin::Pin<VRef<ItemTreeVTable>>, result: &mut ItemWeak), |
87 | |
88 | /// This embeds this ItemTree into the item tree of another ItemTree |
89 | /// |
90 | /// Returns `true` if this ItemTree was embedded into the `parent` |
91 | /// at `parent_item_tree_index`. |
92 | pub embed_component: extern "C" fn( |
93 | core::pin::Pin<VRef<ItemTreeVTable>>, |
94 | parent: &VWeak<ItemTreeVTable>, |
95 | parent_item_tree_index: u32, |
96 | ) -> bool, |
97 | |
98 | /// Return the index of the current subtree or usize::MAX if this is not a subtree |
99 | pub subtree_index: extern "C" fn(core::pin::Pin<VRef<ItemTreeVTable>>) -> usize, |
100 | |
101 | /// Returns the layout info for the root of the ItemTree |
102 | pub layout_info: extern "C" fn(core::pin::Pin<VRef<ItemTreeVTable>>, Orientation) -> LayoutInfo, |
103 | |
104 | /// Returns the item's geometry (relative to its parent item) |
105 | pub item_geometry: |
106 | extern "C" fn(core::pin::Pin<VRef<ItemTreeVTable>>, item_index: u32) -> LogicalRect, |
107 | |
108 | /// Returns the accessible role for a given item |
109 | pub accessible_role: |
110 | extern "C" fn(core::pin::Pin<VRef<ItemTreeVTable>>, item_index: u32) -> AccessibleRole, |
111 | |
112 | /// Returns the accessible property via the `result`. Returns true if such a property exists. |
113 | pub accessible_string_property: extern "C" fn( |
114 | core::pin::Pin<VRef<ItemTreeVTable>>, |
115 | item_index: u32, |
116 | what: AccessibleStringProperty, |
117 | result: &mut SharedString, |
118 | ) -> bool, |
119 | |
120 | /// Executes an accessibility action. |
121 | pub accessibility_action: extern "C" fn( |
122 | core::pin::Pin<VRef<ItemTreeVTable>>, |
123 | item_index: u32, |
124 | action: &AccessibilityAction, |
125 | ), |
126 | |
127 | /// Returns the supported accessibility actions. |
128 | pub supported_accessibility_actions: extern "C" fn( |
129 | core::pin::Pin<VRef<ItemTreeVTable>>, |
130 | item_index: u32, |
131 | ) -> SupportedAccessibilityAction, |
132 | |
133 | /// Add the `ElementName::id` entries of the given item |
134 | pub item_element_infos: extern "C" fn( |
135 | core::pin::Pin<VRef<ItemTreeVTable>>, |
136 | item_index: u32, |
137 | result: &mut SharedString, |
138 | ) -> bool, |
139 | |
140 | /// Returns a Window, creating a fresh one if `do_create` is true. |
141 | pub window_adapter: extern "C" fn( |
142 | core::pin::Pin<VRef<ItemTreeVTable>>, |
143 | do_create: bool, |
144 | result: &mut Option<WindowAdapterRc>, |
145 | ), |
146 | |
147 | /// in-place destructor (for VRc) |
148 | pub drop_in_place: unsafe fn(VRefMut<ItemTreeVTable>) -> vtable::Layout, |
149 | |
150 | /// dealloc function (for VRc) |
151 | pub dealloc: unsafe fn(&ItemTreeVTable, ptr: *mut u8, layout: vtable::Layout), |
152 | } |
153 | |
154 | #[cfg (test)] |
155 | pub(crate) use ItemTreeVTable_static; |
156 | |
157 | /// Alias for `vtable::VRef<ItemTreeVTable>` which represent a pointer to a `dyn ItemTree` with |
158 | /// the associated vtable |
159 | pub type ItemTreeRef<'a> = vtable::VRef<'a, ItemTreeVTable>; |
160 | |
161 | /// Type alias to the commonly used `Pin<VRef<ItemTreeVTable>>>` |
162 | pub type ItemTreeRefPin<'a> = core::pin::Pin<ItemTreeRef<'a>>; |
163 | |
164 | /// Type alias to the commonly used VRc<ItemTreeVTable, Dyn>> |
165 | pub type ItemTreeRc = vtable::VRc<ItemTreeVTable, Dyn>; |
166 | /// Type alias to the commonly used VWeak<ItemTreeVTable, Dyn>> |
167 | pub type ItemTreeWeak = vtable::VWeak<ItemTreeVTable, Dyn>; |
168 | |
169 | /// Call init() on the ItemVTable for each item of the ItemTree. |
170 | pub fn register_item_tree(item_tree_rc: &ItemTreeRc, window_adapter: Option<WindowAdapterRc>) { |
171 | let c: Pin> = vtable::VRc::borrow_pin(this:item_tree_rc); |
172 | let item_tree = c.as_ref().get_item_tree(); |
173 | item_tree.iter().enumerate().for_each(|(tree_index: usize, node: ItemTreeNode)| { |
174 | let tree_index: u32 = tree_index as u32; |
175 | if let ItemTreeNode::Item { .. } = &node { |
176 | let item: ItemRc = ItemRc::new(item_tree_rc.clone(), tree_index); |
177 | c.as_ref().get_item_ref(tree_index).as_ref().init(&item); |
178 | } |
179 | }); |
180 | if let Some(adapter: &dyn WindowAdapterInternal) = window_adapter.as_ref().and_then(|a: &Rc| a.internal(crate::InternalToken)) { |
181 | adapter.register_item_tree(); |
182 | } |
183 | } |
184 | |
185 | /// Free the backend graphics resources allocated by the ItemTree's items. |
186 | pub fn unregister_item_tree<Base>( |
187 | base: core::pin::Pin<&Base>, |
188 | item_tree: ItemTreeRef, |
189 | item_array: &[vtable::VOffset<Base, ItemVTable, vtable::AllowPin>], |
190 | window_adapter: &WindowAdapterRc, |
191 | ) { |
192 | window_adapter.renderer().free_graphics_resources( |
193 | item_tree, |
194 | &mut item_array.iter().map(|item| item.apply_pin(base)), |
195 | ).expect(msg:"Fatal error encountered when freeing graphics resources while destroying Slint component" ); |
196 | if let Some(w: &dyn WindowAdapterInternal) = window_adapter.internal(crate::InternalToken) { |
197 | w.unregister_item_tree(_component:item_tree, &mut item_array.iter().map(|item: &VOffset| item.apply_pin(base))); |
198 | } |
199 | } |
200 | |
201 | fn find_sibling_outside_repeater( |
202 | component: &ItemTreeRc, |
203 | comp_ref_pin: Pin<VRef<ItemTreeVTable>>, |
204 | index: u32, |
205 | sibling_step: &dyn Fn(&crate::item_tree::ItemTreeNodeArray, u32) -> Option<u32>, |
206 | subtree_child: &dyn Fn(usize, usize) -> usize, |
207 | ) -> Option<ItemRc> { |
208 | assert_ne!(index, 0); |
209 | |
210 | let item_tree: ItemTreeNodeArray<'_> = crate::item_tree::ItemTreeNodeArray::new(&comp_ref_pin); |
211 | |
212 | let mut current_sibling: u32 = index; |
213 | loop { |
214 | current_sibling = sibling_step(&item_tree, current_sibling)?; |
215 | |
216 | if let Some(node: ItemRc) = step_into_node( |
217 | component, |
218 | &comp_ref_pin, |
219 | node_index:current_sibling, |
220 | &item_tree, |
221 | subtree_child, |
222 | &core::convert::identity, |
223 | ) { |
224 | return Some(node); |
225 | } |
226 | } |
227 | } |
228 | |
229 | fn step_into_node( |
230 | component: &ItemTreeRc, |
231 | comp_ref_pin: &Pin<VRef<ItemTreeVTable>>, |
232 | node_index: u32, |
233 | item_tree: &crate::item_tree::ItemTreeNodeArray, |
234 | subtree_child: &dyn Fn(usize, usize) -> usize, |
235 | wrap_around: &dyn Fn(ItemRc) -> ItemRc, |
236 | ) -> Option<ItemRc> { |
237 | match item_tree.get(node_index).expect(msg:"Invalid index passed to item tree" ) { |
238 | crate::item_tree::ItemTreeNode::Item { .. } => { |
239 | Some(ItemRc::new(item_tree:component.clone(), node_index)) |
240 | } |
241 | crate::item_tree::ItemTreeNode::DynamicTree { index: &u32, .. } => { |
242 | let range = comp_ref_pin.as_ref().get_subtree_range(*index); |
243 | let component_index: usize = subtree_child(range.start, range.end); |
244 | let mut child_instance = Default::default(); |
245 | comp_ref_pin.as_ref().get_subtree(*index, component_index, &mut child_instance); |
246 | child_instance |
247 | .upgrade() |
248 | .map(|child_instance: VRc| wrap_around(ItemRc::new(item_tree:child_instance, index:0))) |
249 | } |
250 | } |
251 | } |
252 | |
253 | /// A ItemRc is holding a reference to a ItemTree containing the item, and the index of this item |
254 | #[repr (C)] |
255 | #[derive (Clone, Debug)] |
256 | pub struct ItemRc { |
257 | item_tree: vtable::VRc<ItemTreeVTable>, |
258 | index: u32, |
259 | } |
260 | |
261 | impl ItemRc { |
262 | /// Create an ItemRc from a ItemTree and an index |
263 | pub fn new(item_tree: vtable::VRc<ItemTreeVTable>, index: u32) -> Self { |
264 | Self { item_tree, index } |
265 | } |
266 | |
267 | pub fn is_root_item_of(&self, item_tree: &VRc<ItemTreeVTable>) -> bool { |
268 | self.index == 0 && VRc::ptr_eq(&self.item_tree, item_tree) |
269 | } |
270 | |
271 | /// Return a `Pin<ItemRef<'a>>` |
272 | pub fn borrow<'a>(&'a self) -> Pin<ItemRef<'a>> { |
273 | #![allow (unsafe_code)] |
274 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
275 | let result = comp_ref_pin.as_ref().get_item_ref(self.index); |
276 | // Safety: we can expand the lifetime of the ItemRef because we know it lives for at least the |
277 | // lifetime of the ItemTree, which is 'a. Pin::as_ref removes the lifetime, but we can just put it back. |
278 | unsafe { core::mem::transmute::<Pin<ItemRef<'_>>, Pin<ItemRef<'a>>>(result) } |
279 | } |
280 | |
281 | /// Returns a `VRcMapped` of this item, to conveniently access specialized item API. |
282 | pub fn downcast<T: HasStaticVTable<ItemVTable>>(&self) -> Option<VRcMapped<ItemTreeVTable, T>> { |
283 | #![allow (unsafe_code)] |
284 | let item = self.borrow(); |
285 | ItemRef::downcast_pin::<T>(item)?; |
286 | |
287 | Some(vtable::VRc::map_dyn(self.item_tree.clone(), |comp_ref_pin| { |
288 | let result = comp_ref_pin.as_ref().get_item_ref(self.index); |
289 | // Safety: we can expand the lifetime of the ItemRef because we know it lives for at least the |
290 | // lifetime of the ItemTree, which is 'a. Pin::as_ref removes the lifetime, but we can just put it back. |
291 | let item = |
292 | unsafe { core::mem::transmute::<Pin<ItemRef<'_>>, Pin<ItemRef<'_>>>(result) }; |
293 | ItemRef::downcast_pin::<T>(item).unwrap() |
294 | })) |
295 | } |
296 | |
297 | pub fn downgrade(&self) -> ItemWeak { |
298 | ItemWeak { item_tree: VRc::downgrade(&self.item_tree), index: self.index } |
299 | } |
300 | |
301 | /// Return the parent Item in the item tree. |
302 | /// |
303 | /// If the item is a the root on its Window or PopupWindow, then the parent is None. |
304 | pub fn parent_item(&self) -> Option<ItemRc> { |
305 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
306 | let item_tree = crate::item_tree::ItemTreeNodeArray::new(&comp_ref_pin); |
307 | |
308 | if let Some(parent_index) = item_tree.parent(self.index) { |
309 | return Some(ItemRc::new(self.item_tree.clone(), parent_index)); |
310 | } |
311 | |
312 | let mut r = ItemWeak::default(); |
313 | comp_ref_pin.as_ref().parent_node(&mut r); |
314 | let parent = r.upgrade()?; |
315 | let comp_ref_pin = vtable::VRc::borrow_pin(&parent.item_tree); |
316 | let item_tree_array = crate::item_tree::ItemTreeNodeArray::new(&comp_ref_pin); |
317 | if let Some(ItemTreeNode::DynamicTree { parent_index, .. }) = |
318 | item_tree_array.get(parent.index()) |
319 | { |
320 | // parent_node returns the repeater node, go up one more level! |
321 | Some(ItemRc::new(parent.item_tree.clone(), *parent_index)) |
322 | } else { |
323 | // the Item was most likely a PopupWindow and we don't want to return the item for the purpose of this call |
324 | // (eg, focus/geometry/...) |
325 | None |
326 | } |
327 | } |
328 | |
329 | /// Returns true if this item is visible from the root of the item tree. Note that this will return |
330 | /// false for `Clip` elements with the `clip` property evaluating to true. |
331 | pub fn is_visible(&self) -> bool { |
332 | let (clip, geometry) = self.absolute_clip_rect_and_geometry(); |
333 | let clip = clip.to_box2d(); |
334 | let geometry = geometry.to_box2d(); |
335 | !clip.is_empty() |
336 | && clip.max.x >= geometry.min.x |
337 | && clip.max.y >= geometry.min.y |
338 | && clip.min.x <= geometry.max.x |
339 | && clip.min.y <= geometry.max.y |
340 | } |
341 | |
342 | /// Returns the clip rect that applies to this item (in window coordinates) as well as the |
343 | /// item's (unclipped) geometry (also in window coordinates). |
344 | fn absolute_clip_rect_and_geometry(&self) -> (LogicalRect, LogicalRect) { |
345 | let (mut clip, parent_geometry) = self.parent_item().map_or_else( |
346 | || { |
347 | ( |
348 | LogicalRect::from_size((crate::Coord::MAX, crate::Coord::MAX).into()), |
349 | Default::default(), |
350 | ) |
351 | }, |
352 | |parent| parent.absolute_clip_rect_and_geometry(), |
353 | ); |
354 | |
355 | let geometry = self.geometry().translate(parent_geometry.origin.to_vector()); |
356 | |
357 | let item = self.borrow(); |
358 | if item.as_ref().clips_children() { |
359 | clip = geometry.intersection(&clip).unwrap_or_default(); |
360 | } |
361 | |
362 | (clip, geometry) |
363 | } |
364 | |
365 | pub fn is_accessible(&self) -> bool { |
366 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
367 | let item_tree = crate::item_tree::ItemTreeNodeArray::new(&comp_ref_pin); |
368 | |
369 | if let Some(n) = &item_tree.get(self.index) { |
370 | match n { |
371 | ItemTreeNode::Item { is_accessible, .. } => *is_accessible, |
372 | ItemTreeNode::DynamicTree { .. } => false, |
373 | } |
374 | } else { |
375 | false |
376 | } |
377 | } |
378 | |
379 | pub fn accessible_role(&self) -> crate::items::AccessibleRole { |
380 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
381 | comp_ref_pin.as_ref().accessible_role(self.index) |
382 | } |
383 | |
384 | pub fn accessible_string_property( |
385 | &self, |
386 | what: crate::accessibility::AccessibleStringProperty, |
387 | ) -> Option<SharedString> { |
388 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
389 | let mut result = Default::default(); |
390 | let ok = comp_ref_pin.as_ref().accessible_string_property(self.index, what, &mut result); |
391 | ok.then_some(result) |
392 | } |
393 | |
394 | pub fn accessible_action(&self, action: &crate::accessibility::AccessibilityAction) { |
395 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
396 | comp_ref_pin.as_ref().accessibility_action(self.index, action); |
397 | } |
398 | |
399 | pub fn supported_accessibility_actions(&self) -> SupportedAccessibilityAction { |
400 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
401 | comp_ref_pin.as_ref().supported_accessibility_actions(self.index) |
402 | } |
403 | |
404 | pub fn element_count(&self) -> Option<usize> { |
405 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
406 | let mut result = SharedString::new(); |
407 | comp_ref_pin |
408 | .as_ref() |
409 | .item_element_infos(self.index, &mut result) |
410 | .then(|| result.as_str().split("/" ).count()) |
411 | } |
412 | |
413 | pub fn element_type_names_and_ids( |
414 | &self, |
415 | element_index: usize, |
416 | ) -> Option<Vec<(SharedString, SharedString)>> { |
417 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
418 | let mut result = SharedString::new(); |
419 | comp_ref_pin.as_ref().item_element_infos(self.index, &mut result).then(|| { |
420 | result |
421 | .as_str() |
422 | .split("/" ) |
423 | .nth(element_index) |
424 | .unwrap() |
425 | .split(";" ) |
426 | .map(|encoded_elem_info| { |
427 | let mut decoder = encoded_elem_info.split(',' ); |
428 | let type_name = decoder.next().unwrap().into(); |
429 | let id = decoder.next().map(Into::into).unwrap_or_default(); |
430 | (type_name, id) |
431 | }) |
432 | .collect() |
433 | }) |
434 | } |
435 | |
436 | pub fn geometry(&self) -> LogicalRect { |
437 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
438 | comp_ref_pin.as_ref().item_geometry(self.index) |
439 | } |
440 | |
441 | pub fn bounding_rect( |
442 | &self, |
443 | geometry: &LogicalRect, |
444 | window_adapter: &WindowAdapterRc, |
445 | ) -> LogicalRect { |
446 | self.borrow().as_ref().bounding_rect(window_adapter, self, *geometry) |
447 | } |
448 | |
449 | /// Returns an absolute position of `p` in the parent item coordinate system |
450 | /// (does not add this item's x and y) |
451 | pub fn map_to_window(&self, p: LogicalPoint) -> LogicalPoint { |
452 | let mut current = self.clone(); |
453 | let mut result = p; |
454 | while let Some(parent) = current.parent_item() { |
455 | let geometry = parent.geometry(); |
456 | result += geometry.origin.to_vector(); |
457 | current = parent.clone(); |
458 | } |
459 | result |
460 | } |
461 | |
462 | /// Returns an absolute position of `p` in the `ItemTree`'s coordinate system |
463 | /// (does not add this item's x and y) |
464 | pub fn map_to_item_tree( |
465 | &self, |
466 | p: LogicalPoint, |
467 | item_tree: &vtable::VRc<ItemTreeVTable>, |
468 | ) -> LogicalPoint { |
469 | let mut current = self.clone(); |
470 | let mut result = p; |
471 | if current.is_root_item_of(item_tree) { |
472 | return result; |
473 | } |
474 | while let Some(parent) = current.parent_item() { |
475 | if parent.is_root_item_of(item_tree) { |
476 | break; |
477 | } |
478 | let geometry = parent.geometry(); |
479 | result += geometry.origin.to_vector(); |
480 | current = parent.clone(); |
481 | } |
482 | result |
483 | } |
484 | |
485 | /// Return the index of the item within the ItemTree |
486 | pub fn index(&self) -> u32 { |
487 | self.index |
488 | } |
489 | /// Returns a reference to the ItemTree holding this item |
490 | pub fn item_tree(&self) -> &vtable::VRc<ItemTreeVTable> { |
491 | &self.item_tree |
492 | } |
493 | |
494 | fn find_child( |
495 | &self, |
496 | child_access: &dyn Fn(&crate::item_tree::ItemTreeNodeArray, u32) -> Option<u32>, |
497 | child_step: &dyn Fn(&crate::item_tree::ItemTreeNodeArray, u32) -> Option<u32>, |
498 | subtree_child: &dyn Fn(usize, usize) -> usize, |
499 | ) -> Option<Self> { |
500 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
501 | let item_tree = crate::item_tree::ItemTreeNodeArray::new(&comp_ref_pin); |
502 | |
503 | let mut current_child_index = child_access(&item_tree, self.index())?; |
504 | loop { |
505 | if let Some(item) = step_into_node( |
506 | self.item_tree(), |
507 | &comp_ref_pin, |
508 | current_child_index, |
509 | &item_tree, |
510 | subtree_child, |
511 | &core::convert::identity, |
512 | ) { |
513 | return Some(item); |
514 | } |
515 | current_child_index = child_step(&item_tree, current_child_index)?; |
516 | } |
517 | } |
518 | |
519 | /// The first child Item of this Item |
520 | pub fn first_child(&self) -> Option<Self> { |
521 | self.find_child( |
522 | &|item_tree, index| item_tree.first_child(index), |
523 | &|item_tree, index| item_tree.next_sibling(index), |
524 | &|start, _| start, |
525 | ) |
526 | } |
527 | |
528 | /// The last child Item of this Item |
529 | pub fn last_child(&self) -> Option<Self> { |
530 | self.find_child( |
531 | &|item_tree, index| item_tree.last_child(index), |
532 | &|item_tree, index| item_tree.previous_sibling(index), |
533 | &|_, end| end.wrapping_sub(1), |
534 | ) |
535 | } |
536 | |
537 | fn find_sibling( |
538 | &self, |
539 | sibling_step: &dyn Fn(&crate::item_tree::ItemTreeNodeArray, u32) -> Option<u32>, |
540 | subtree_step: &dyn Fn(usize) -> usize, |
541 | subtree_child: &dyn Fn(usize, usize) -> usize, |
542 | ) -> Option<Self> { |
543 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
544 | if self.index == 0 { |
545 | let mut parent_item = Default::default(); |
546 | comp_ref_pin.as_ref().parent_node(&mut parent_item); |
547 | let current_component_subtree_index = comp_ref_pin.as_ref().subtree_index(); |
548 | if let Some(parent_item) = parent_item.upgrade() { |
549 | let parent = parent_item.item_tree(); |
550 | let parent_ref_pin = vtable::VRc::borrow_pin(parent); |
551 | let parent_item_index = parent_item.index(); |
552 | let parent_item_tree = crate::item_tree::ItemTreeNodeArray::new(&parent_ref_pin); |
553 | |
554 | let subtree_index = match parent_item_tree.get(parent_item_index)? { |
555 | crate::item_tree::ItemTreeNode::Item { .. } => { |
556 | // Popups can trigger this case! |
557 | return None; |
558 | } |
559 | crate::item_tree::ItemTreeNode::DynamicTree { index, .. } => *index, |
560 | }; |
561 | |
562 | let next_subtree_index = subtree_step(current_component_subtree_index); |
563 | |
564 | // Get next subtree from repeater! |
565 | let mut next_subtree_instance = Default::default(); |
566 | parent_ref_pin.as_ref().get_subtree( |
567 | subtree_index, |
568 | next_subtree_index, |
569 | &mut next_subtree_instance, |
570 | ); |
571 | if let Some(next_subtree_instance) = next_subtree_instance.upgrade() { |
572 | return Some(ItemRc::new(next_subtree_instance, 0)); |
573 | } |
574 | |
575 | // We need to leave the repeater: |
576 | find_sibling_outside_repeater( |
577 | parent, |
578 | parent_ref_pin, |
579 | parent_item_index, |
580 | sibling_step, |
581 | subtree_child, |
582 | ) |
583 | } else { |
584 | None // At root if the item tree |
585 | } |
586 | } else { |
587 | find_sibling_outside_repeater( |
588 | self.item_tree(), |
589 | comp_ref_pin, |
590 | self.index(), |
591 | sibling_step, |
592 | subtree_child, |
593 | ) |
594 | } |
595 | } |
596 | |
597 | /// The previous sibling of this Item |
598 | pub fn previous_sibling(&self) -> Option<Self> { |
599 | self.find_sibling( |
600 | &|item_tree, index| item_tree.previous_sibling(index), |
601 | &|index| index.wrapping_sub(1), |
602 | &|_, end| end.wrapping_sub(1), |
603 | ) |
604 | } |
605 | |
606 | /// The next sibling of this Item |
607 | pub fn next_sibling(&self) -> Option<Self> { |
608 | self.find_sibling( |
609 | &|item_tree, index| item_tree.next_sibling(index), |
610 | &|index| index.saturating_add(1), |
611 | &|start, _| start, |
612 | ) |
613 | } |
614 | |
615 | fn move_focus( |
616 | &self, |
617 | focus_step: &dyn Fn(&crate::item_tree::ItemTreeNodeArray, u32) -> Option<u32>, |
618 | subtree_step: &dyn Fn(ItemRc) -> Option<ItemRc>, |
619 | subtree_child: &dyn Fn(usize, usize) -> usize, |
620 | step_in: &dyn Fn(ItemRc) -> ItemRc, |
621 | step_out: &dyn Fn(&crate::item_tree::ItemTreeNodeArray, u32) -> Option<u32>, |
622 | ) -> Self { |
623 | let mut component = self.item_tree().clone(); |
624 | let mut comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
625 | let mut item_tree = crate::item_tree::ItemTreeNodeArray::new(&comp_ref_pin); |
626 | |
627 | let mut to_focus = self.index(); |
628 | |
629 | 'in_tree: loop { |
630 | if let Some(next) = focus_step(&item_tree, to_focus) { |
631 | if let Some(item) = step_into_node( |
632 | &component, |
633 | &comp_ref_pin, |
634 | next, |
635 | &item_tree, |
636 | subtree_child, |
637 | step_in, |
638 | ) { |
639 | return item; |
640 | } |
641 | to_focus = next; |
642 | // Loop: We stepped into an empty repeater! |
643 | } else { |
644 | // Step out of this component: |
645 | let mut root = ItemRc::new(component, 0); |
646 | if let Some(item) = subtree_step(root.clone()) { |
647 | // Next component inside same repeater |
648 | return step_in(item); |
649 | } |
650 | |
651 | // Step out of the repeater |
652 | let root_component = root.item_tree(); |
653 | let root_comp_ref = vtable::VRc::borrow_pin(root_component); |
654 | let mut parent_node = Default::default(); |
655 | root_comp_ref.as_ref().parent_node(&mut parent_node); |
656 | |
657 | while let Some(parent) = parent_node.upgrade() { |
658 | // .. not at the root of the item tree: |
659 | component = parent.item_tree().clone(); |
660 | comp_ref_pin = vtable::VRc::borrow_pin(&component); |
661 | item_tree = crate::item_tree::ItemTreeNodeArray::new(&comp_ref_pin); |
662 | |
663 | let index = parent.index(); |
664 | |
665 | if !matches!(item_tree.get(index), Some(ItemTreeNode::DynamicTree { .. })) { |
666 | // That was not a repeater (eg, a popup window) |
667 | break; |
668 | } |
669 | |
670 | if let Some(next) = step_out(&item_tree, index) { |
671 | if let Some(item) = step_into_node( |
672 | parent.item_tree(), |
673 | &comp_ref_pin, |
674 | next, |
675 | &item_tree, |
676 | subtree_child, |
677 | step_in, |
678 | ) { |
679 | // Step into a dynamic node |
680 | return item; |
681 | } else { |
682 | // The dynamic node was empty, proceed in normal tree |
683 | to_focus = parent.index(); |
684 | continue 'in_tree; // Find a node in the current (parent!) tree |
685 | } |
686 | } |
687 | |
688 | root = ItemRc::new(component.clone(), 0); |
689 | if let Some(item) = subtree_step(root.clone()) { |
690 | return step_in(item); |
691 | } |
692 | |
693 | // Go up one more level: |
694 | let root_component = root.item_tree(); |
695 | let root_comp_ref = vtable::VRc::borrow_pin(root_component); |
696 | parent_node = Default::default(); |
697 | root_comp_ref.as_ref().parent_node(&mut parent_node); |
698 | } |
699 | |
700 | // Loop around after hitting the root node: |
701 | return step_in(root); |
702 | } |
703 | } |
704 | } |
705 | |
706 | /// Move tab focus to the previous item: |
707 | pub fn previous_focus_item(&self) -> Self { |
708 | self.move_focus( |
709 | &|item_tree, index| { |
710 | crate::item_focus::default_previous_in_local_focus_chain(index, item_tree) |
711 | }, |
712 | &|root| root.previous_sibling(), |
713 | &|_, end| end.wrapping_sub(1), |
714 | &|root| { |
715 | let mut current = root; |
716 | loop { |
717 | if let Some(next) = current.last_child() { |
718 | current = next; |
719 | } else { |
720 | return current; |
721 | } |
722 | } |
723 | }, |
724 | &|item_tree, index| item_tree.parent(index), |
725 | ) |
726 | } |
727 | |
728 | /// Move tab focus to the next item: |
729 | pub fn next_focus_item(&self) -> Self { |
730 | self.move_focus( |
731 | &|item_tree, index| { |
732 | crate::item_focus::default_next_in_local_focus_chain(index, item_tree) |
733 | }, |
734 | &|root| root.next_sibling(), |
735 | &|start, _| start, |
736 | &core::convert::identity, |
737 | &|item_tree, index| crate::item_focus::step_out_of_node(index, item_tree), |
738 | ) |
739 | } |
740 | |
741 | pub fn window_adapter(&self) -> Option<WindowAdapterRc> { |
742 | let comp_ref_pin = vtable::VRc::borrow_pin(&self.item_tree); |
743 | let mut result = None; |
744 | comp_ref_pin.as_ref().window_adapter(false, &mut result); |
745 | result |
746 | } |
747 | |
748 | /// Visit the children of this element and call the visitor to each of them, until the visitor returns [`ControlFlow::Break`]. |
749 | /// When the visitor breaks, the function returns the value. If it doesn't break, the function returns None. |
750 | fn visit_descendants_impl<R>( |
751 | &self, |
752 | visitor: &mut impl FnMut(&ItemRc) -> ControlFlow<R>, |
753 | ) -> Option<R> { |
754 | let mut result = None; |
755 | |
756 | let mut actual_visitor = |item_tree: &ItemTreeRc, |
757 | index: u32, |
758 | _item_pin: core::pin::Pin<ItemRef>| |
759 | -> VisitChildrenResult { |
760 | let item_rc = ItemRc::new(item_tree.clone(), index); |
761 | |
762 | match visitor(&item_rc) { |
763 | ControlFlow::Continue(_) => { |
764 | if let Some(x) = item_rc.visit_descendants_impl(visitor) { |
765 | result = Some(x); |
766 | return VisitChildrenResult::abort(index, 0); |
767 | } |
768 | } |
769 | ControlFlow::Break(x) => { |
770 | result = Some(x); |
771 | return VisitChildrenResult::abort(index, 0); |
772 | } |
773 | } |
774 | |
775 | VisitChildrenResult::CONTINUE |
776 | }; |
777 | vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor); |
778 | |
779 | VRc::borrow_pin(self.item_tree()).as_ref().visit_children_item( |
780 | self.index() as isize, |
781 | TraversalOrder::BackToFront, |
782 | actual_visitor, |
783 | ); |
784 | |
785 | result |
786 | } |
787 | |
788 | /// Visit the children of this element and call the visitor to each of them, until the visitor returns [`ControlFlow::Break`]. |
789 | /// When the visitor breaks, the function returns the value. If it doesn't break, the function returns None. |
790 | pub fn visit_descendants<R>( |
791 | &self, |
792 | mut visitor: impl FnMut(&ItemRc) -> ControlFlow<R>, |
793 | ) -> Option<R> { |
794 | self.visit_descendants_impl(&mut visitor) |
795 | } |
796 | |
797 | /// Returns the transform to apply to children to map them into the local coordinate space of this item. |
798 | /// Typically this is None, but rotation for example may return Some. |
799 | pub fn children_transform(&self) -> Option<ItemTransform> { |
800 | self.downcast::<crate::items::Rotate>().map(|rotate_item| { |
801 | let origin = euclid::Vector2D::<f32, crate::lengths::LogicalPx>::from_lengths( |
802 | rotate_item.as_pin_ref().rotation_origin_x().cast(), |
803 | rotate_item.as_pin_ref().rotation_origin_y().cast(), |
804 | ); |
805 | ItemTransform::translation(-origin.x, -origin.y) |
806 | .cast() |
807 | .then_rotate(euclid::Angle { |
808 | radians: rotate_item.as_pin_ref().rotation_angle().to_radians(), |
809 | }) |
810 | .then_translate(origin) |
811 | }) |
812 | } |
813 | } |
814 | |
815 | impl PartialEq for ItemRc { |
816 | fn eq(&self, other: &Self) -> bool { |
817 | VRc::ptr_eq(&self.item_tree, &other.item_tree) && self.index == other.index |
818 | } |
819 | } |
820 | |
821 | impl Eq for ItemRc {} |
822 | |
823 | /// A Weak reference to an item that can be constructed from an ItemRc. |
824 | #[derive (Clone, Default)] |
825 | #[repr (C)] |
826 | pub struct ItemWeak { |
827 | item_tree: crate::item_tree::ItemTreeWeak, |
828 | index: u32, |
829 | } |
830 | |
831 | impl ItemWeak { |
832 | pub fn upgrade(&self) -> Option<ItemRc> { |
833 | self.item_tree.upgrade().map(|c: VRc| ItemRc::new(item_tree:c, self.index)) |
834 | } |
835 | } |
836 | |
837 | impl PartialEq for ItemWeak { |
838 | fn eq(&self, other: &Self) -> bool { |
839 | VWeak::ptr_eq(&self.item_tree, &other.item_tree) && self.index == other.index |
840 | } |
841 | } |
842 | |
843 | impl Eq for ItemWeak {} |
844 | |
845 | #[repr (u8)] |
846 | #[derive (Debug, Copy, Clone, Eq, PartialEq)] |
847 | pub enum TraversalOrder { |
848 | BackToFront, |
849 | FrontToBack, |
850 | } |
851 | |
852 | /// The return value of the ItemTree::visit_children_item function |
853 | /// |
854 | /// Represents something like `enum { Continue, Aborted{aborted_at_item: isize} }`. |
855 | /// But this is just wrapping a int because it is easier to use ffi with isize than |
856 | /// complex enum. |
857 | /// |
858 | /// -1 means the visitor will continue |
859 | /// otherwise this is the index of the item that aborted the visit. |
860 | #[repr (transparent)] |
861 | #[derive (Copy, Clone, Eq, PartialEq)] |
862 | pub struct VisitChildrenResult(u64); |
863 | impl VisitChildrenResult { |
864 | /// The result used for a visitor that want to continue the visit |
865 | pub const CONTINUE: Self = Self(u64::MAX); |
866 | |
867 | /// Returns a result that means that the visitor must stop, and convey the item that caused the abort |
868 | pub fn abort(item_index: u32, index_within_repeater: usize) -> Self { |
869 | assert!(index_within_repeater < u32::MAX as usize); |
870 | Self(item_index as u64 | (index_within_repeater as u64) << 32) |
871 | } |
872 | /// True if the visitor wants to abort the visit |
873 | pub fn has_aborted(&self) -> bool { |
874 | self.0 != Self::CONTINUE.0 |
875 | } |
876 | pub fn aborted_index(&self) -> Option<usize> { |
877 | if self.0 != Self::CONTINUE.0 { |
878 | Some((self.0 & 0xffff_ffff) as usize) |
879 | } else { |
880 | None |
881 | } |
882 | } |
883 | pub fn aborted_indexes(&self) -> Option<(usize, usize)> { |
884 | if self.0 != Self::CONTINUE.0 { |
885 | Some(((self.0 & 0xffff_ffff) as usize, (self.0 >> 32) as usize)) |
886 | } else { |
887 | None |
888 | } |
889 | } |
890 | } |
891 | impl core::fmt::Debug for VisitChildrenResult { |
892 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
893 | if self.0 == Self::CONTINUE.0 { |
894 | write!(f, "CONTINUE" ) |
895 | } else { |
896 | write!(f, "( {}, {})" , (self.0 & 0xffff_ffff) as usize, (self.0 >> 32) as usize) |
897 | } |
898 | } |
899 | } |
900 | |
901 | /// The item tree is an array of ItemTreeNode representing a static tree of items |
902 | /// within a ItemTree. |
903 | #[repr (u8)] |
904 | #[derive (Debug)] |
905 | pub enum ItemTreeNode { |
906 | /// Static item |
907 | Item { |
908 | /// True when the item has accessibility properties attached |
909 | is_accessible: bool, |
910 | |
911 | /// number of children |
912 | children_count: u32, |
913 | |
914 | /// index of the first children within the item tree |
915 | children_index: u32, |
916 | |
917 | /// The index of the parent item (not valid for the root) |
918 | parent_index: u32, |
919 | |
920 | /// The index in the extra item_array |
921 | item_array_index: u32, |
922 | }, |
923 | /// A placeholder for many instance of item in their own ItemTree which |
924 | /// are instantiated according to a model. |
925 | DynamicTree { |
926 | /// the index which is passed in the visit_dynamic callback. |
927 | index: u32, |
928 | |
929 | /// The index of the parent item (not valid for the root) |
930 | parent_index: u32, |
931 | }, |
932 | } |
933 | |
934 | impl ItemTreeNode { |
935 | pub fn parent_index(&self) -> u32 { |
936 | match self { |
937 | ItemTreeNode::Item { parent_index: &u32, .. } => *parent_index, |
938 | ItemTreeNode::DynamicTree { parent_index: &u32, .. } => *parent_index, |
939 | } |
940 | } |
941 | } |
942 | |
943 | /// The `ItemTreeNodeArray` provides tree walking code for the physical ItemTree stored in |
944 | /// a `ItemTree` without stitching any inter-ItemTree links together! |
945 | pub struct ItemTreeNodeArray<'a> { |
946 | node_array: &'a [ItemTreeNode], |
947 | } |
948 | |
949 | impl<'a> ItemTreeNodeArray<'a> { |
950 | /// Create a new `ItemTree` from its raw data. |
951 | pub fn new(comp_ref_pin: &'a Pin<VRef<'a, ItemTreeVTable>>) -> Self { |
952 | Self { node_array: comp_ref_pin.as_ref().get_item_tree().as_slice() } |
953 | } |
954 | |
955 | /// Get a ItemTreeNode |
956 | pub fn get(&self, index: u32) -> Option<&ItemTreeNode> { |
957 | self.node_array.get(index as usize) |
958 | } |
959 | |
960 | /// Get the parent of a node, returns `None` if this is the root node of this item tree. |
961 | pub fn parent(&self, index: u32) -> Option<u32> { |
962 | let index = index as usize; |
963 | (index < self.node_array.len() && index != 0).then(|| self.node_array[index].parent_index()) |
964 | } |
965 | |
966 | /// Returns the next sibling or `None` if this is the last sibling. |
967 | pub fn next_sibling(&self, index: u32) -> Option<u32> { |
968 | if let Some(parent_index) = self.parent(index) { |
969 | match self.node_array[parent_index as usize] { |
970 | ItemTreeNode::Item { children_index, children_count, .. } => { |
971 | (index < (children_count + children_index - 1)).then_some(index + 1) |
972 | } |
973 | ItemTreeNode::DynamicTree { .. } => { |
974 | unreachable!("Parent in same item tree is a repeater." ) |
975 | } |
976 | } |
977 | } else { |
978 | None // No parent, so we have no siblings either:-) |
979 | } |
980 | } |
981 | |
982 | /// Returns the previous sibling or `None` if this is the first sibling. |
983 | pub fn previous_sibling(&self, index: u32) -> Option<u32> { |
984 | if let Some(parent_index) = self.parent(index) { |
985 | match self.node_array[parent_index as usize] { |
986 | ItemTreeNode::Item { children_index, .. } => { |
987 | (index > children_index).then_some(index - 1) |
988 | } |
989 | ItemTreeNode::DynamicTree { .. } => { |
990 | unreachable!("Parent in same item tree is a repeater." ) |
991 | } |
992 | } |
993 | } else { |
994 | None // No parent, so we have no siblings either:-) |
995 | } |
996 | } |
997 | |
998 | /// Returns the first child or `None` if this are no children or the `index` |
999 | /// points to a `DynamicTree`. |
1000 | pub fn first_child(&self, index: u32) -> Option<u32> { |
1001 | match self.node_array.get(index as usize)? { |
1002 | ItemTreeNode::Item { children_index, children_count, .. } => { |
1003 | (*children_count != 0).then_some(*children_index as _) |
1004 | } |
1005 | ItemTreeNode::DynamicTree { .. } => None, |
1006 | } |
1007 | } |
1008 | |
1009 | /// Returns the last child or `None` if this are no children or the `index` |
1010 | /// points to an `DynamicTree`. |
1011 | pub fn last_child(&self, index: u32) -> Option<u32> { |
1012 | match self.node_array.get(index as usize)? { |
1013 | ItemTreeNode::Item { children_index, children_count, .. } => { |
1014 | if *children_count != 0 { |
1015 | Some(*children_index + *children_count - 1) |
1016 | } else { |
1017 | None |
1018 | } |
1019 | } |
1020 | ItemTreeNode::DynamicTree { .. } => None, |
1021 | } |
1022 | } |
1023 | |
1024 | /// Returns the number of nodes in the `ItemTreeNodeArray` |
1025 | pub fn node_count(&self) -> usize { |
1026 | self.node_array.len() |
1027 | } |
1028 | } |
1029 | |
1030 | impl<'a> From<&'a [ItemTreeNode]> for ItemTreeNodeArray<'a> { |
1031 | fn from(item_tree: &'a [ItemTreeNode]) -> Self { |
1032 | Self { node_array: item_tree } |
1033 | } |
1034 | } |
1035 | |
1036 | #[repr (C)] |
1037 | #[vtable ] |
1038 | /// Object to be passed in visit_item_children method of the ItemTree. |
1039 | pub struct ItemVisitorVTable { |
1040 | /// Called for each child of the visited item |
1041 | /// |
1042 | /// The `item_tree` parameter is the ItemTree in which the item live which might not be the same |
1043 | /// as the parent's ItemTree. |
1044 | /// `index` is to be used again in the visit_item_children function of the ItemTree (the one passed as parameter) |
1045 | /// and `item` is a reference to the item itself |
1046 | visit_item: fn( |
1047 | VRefMut<ItemVisitorVTable>, |
1048 | item_tree: &VRc<ItemTreeVTable, vtable::Dyn>, |
1049 | index: u32, |
1050 | item: Pin<VRef<ItemVTable>>, |
1051 | ) -> VisitChildrenResult, |
1052 | /// Destructor |
1053 | drop: fn(VRefMut<ItemVisitorVTable>), |
1054 | } |
1055 | |
1056 | /// Type alias to `vtable::VRefMut<ItemVisitorVTable>` |
1057 | pub type ItemVisitorRefMut<'a> = vtable::VRefMut<'a, ItemVisitorVTable>; |
1058 | |
1059 | impl<T: FnMut(&ItemTreeRc, u32, Pin<ItemRef>) -> VisitChildrenResult> ItemVisitor for T { |
1060 | fn visit_item( |
1061 | &mut self, |
1062 | item_tree: &ItemTreeRc, |
1063 | index: u32, |
1064 | item: Pin<ItemRef>, |
1065 | ) -> VisitChildrenResult { |
1066 | self(item_tree, index, item) |
1067 | } |
1068 | } |
1069 | pub enum ItemVisitorResult<State> { |
1070 | Continue(State), |
1071 | Abort, |
1072 | } |
1073 | |
1074 | /// Visit each items recursively |
1075 | /// |
1076 | /// The state parameter returned by the visitor is passed to each child. |
1077 | /// |
1078 | /// Returns the index of the item that cancelled, or -1 if nobody cancelled |
1079 | pub fn visit_items<State>( |
1080 | item_tree: &ItemTreeRc, |
1081 | order: TraversalOrder, |
1082 | mut visitor: impl FnMut(&ItemTreeRc, Pin<ItemRef>, u32, &State) -> ItemVisitorResult<State>, |
1083 | state: State, |
1084 | ) -> VisitChildrenResult { |
1085 | visit_internal(item_tree, order, &mut visitor, index:-1, &state) |
1086 | } |
1087 | |
1088 | fn visit_internal<State>( |
1089 | item_tree: &ItemTreeRc, |
1090 | order: TraversalOrder, |
1091 | visitor: &mut impl FnMut(&ItemTreeRc, Pin<ItemRef>, u32, &State) -> ItemVisitorResult<State>, |
1092 | index: isize, |
1093 | state: &State, |
1094 | ) -> VisitChildrenResult { |
1095 | let mut actual_visitor: impl FnMut(&VRc, …) -> … = |
1096 | |item_tree: &ItemTreeRc, index: u32, item: Pin<ItemRef>| -> VisitChildrenResult { |
1097 | match visitor(item_tree, item, index, state) { |
1098 | ItemVisitorResult::Continue(state: State) => { |
1099 | visit_internal(item_tree, order, visitor, index as isize, &state) |
1100 | } |
1101 | |
1102 | ItemVisitorResult::Abort => VisitChildrenResult::abort(index, index_within_repeater:0), |
1103 | } |
1104 | }; |
1105 | vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor); |
1106 | VRc::borrow_pin(this:item_tree).as_ref().visit_children_item(index, order, actual_visitor) |
1107 | } |
1108 | |
1109 | /// Visit the children within an array of ItemTreeNode |
1110 | /// |
1111 | /// The dynamic visitor is called for the dynamic nodes, its signature is |
1112 | /// `fn(base: &Base, visitor: vtable::VRefMut<ItemVisitorVTable>, dyn_index: usize)` |
1113 | /// |
1114 | /// FIXME: the design of this use lots of indirection and stack frame in recursive functions |
1115 | /// Need to check if the compiler is able to optimize away some of it. |
1116 | /// Possibly we should generate code that directly call the visitor instead |
1117 | pub fn visit_item_tree<Base>( |
1118 | base: Pin<&Base>, |
1119 | item_tree: &ItemTreeRc, |
1120 | item_tree_array: &[ItemTreeNode], |
1121 | index: isize, |
1122 | order: TraversalOrder, |
1123 | mut visitor: vtable::VRefMut<ItemVisitorVTable>, |
1124 | visit_dynamic: impl Fn( |
1125 | Pin<&Base>, |
1126 | TraversalOrder, |
1127 | vtable::VRefMut<ItemVisitorVTable>, |
1128 | u32, |
1129 | ) -> VisitChildrenResult, |
1130 | ) -> VisitChildrenResult { |
1131 | let mut visit_at_index = |idx: u32| -> VisitChildrenResult { |
1132 | match &item_tree_array[idx as usize] { |
1133 | ItemTreeNode::Item { .. } => { |
1134 | let item = crate::items::ItemRc::new(item_tree.clone(), idx); |
1135 | visitor.visit_item(item_tree, idx, item.borrow()) |
1136 | } |
1137 | ItemTreeNode::DynamicTree { index, .. } => { |
1138 | if let Some(sub_idx) = |
1139 | visit_dynamic(base, order, visitor.borrow_mut(), *index).aborted_index() |
1140 | { |
1141 | VisitChildrenResult::abort(idx, sub_idx) |
1142 | } else { |
1143 | VisitChildrenResult::CONTINUE |
1144 | } |
1145 | } |
1146 | } |
1147 | }; |
1148 | if index == -1 { |
1149 | visit_at_index(0) |
1150 | } else { |
1151 | match &item_tree_array[index as usize] { |
1152 | ItemTreeNode::Item { children_index, children_count, .. } => { |
1153 | for c in 0..*children_count { |
1154 | let idx = match order { |
1155 | TraversalOrder::BackToFront => *children_index + c, |
1156 | TraversalOrder::FrontToBack => *children_index + *children_count - c - 1, |
1157 | }; |
1158 | let maybe_abort_index = visit_at_index(idx); |
1159 | if maybe_abort_index.has_aborted() { |
1160 | return maybe_abort_index; |
1161 | } |
1162 | } |
1163 | } |
1164 | ItemTreeNode::DynamicTree { .. } => panic!("should not be called with dynamic items" ), |
1165 | }; |
1166 | VisitChildrenResult::CONTINUE |
1167 | } |
1168 | } |
1169 | |
1170 | #[cfg (feature = "ffi" )] |
1171 | pub(crate) mod ffi { |
1172 | #![allow (unsafe_code)] |
1173 | |
1174 | use super::*; |
1175 | use core::ffi::c_void; |
1176 | |
1177 | /// Call init() on the ItemVTable of each item in the item array. |
1178 | #[no_mangle ] |
1179 | pub unsafe extern "C" fn slint_register_item_tree( |
1180 | item_tree_rc: &ItemTreeRc, |
1181 | window_handle: *const crate::window::ffi::WindowAdapterRcOpaque, |
1182 | ) { |
1183 | let window_adapter = (window_handle as *const WindowAdapterRc).as_ref().cloned(); |
1184 | super::register_item_tree(item_tree_rc, window_adapter) |
1185 | } |
1186 | |
1187 | /// Free the backend graphics resources allocated in the item array. |
1188 | #[no_mangle ] |
1189 | pub unsafe extern "C" fn slint_unregister_item_tree( |
1190 | component: ItemTreeRefPin, |
1191 | item_array: Slice<vtable::VOffset<u8, ItemVTable, vtable::AllowPin>>, |
1192 | window_handle: *const crate::window::ffi::WindowAdapterRcOpaque, |
1193 | ) { |
1194 | let window_adapter = &*(window_handle as *const WindowAdapterRc); |
1195 | super::unregister_item_tree( |
1196 | core::pin::Pin::new_unchecked(&*(component.as_ptr() as *const u8)), |
1197 | core::pin::Pin::into_inner(component), |
1198 | item_array.as_slice(), |
1199 | window_adapter, |
1200 | ) |
1201 | } |
1202 | |
1203 | /// Expose `crate::item_tree::visit_item_tree` to C++ |
1204 | /// |
1205 | /// Safety: Assume a correct implementation of the item_tree array |
1206 | #[no_mangle ] |
1207 | pub unsafe extern "C" fn slint_visit_item_tree( |
1208 | item_tree: &ItemTreeRc, |
1209 | item_tree_array: Slice<ItemTreeNode>, |
1210 | index: isize, |
1211 | order: TraversalOrder, |
1212 | visitor: VRefMut<ItemVisitorVTable>, |
1213 | visit_dynamic: extern "C" fn( |
1214 | base: *const c_void, |
1215 | order: TraversalOrder, |
1216 | visitor: vtable::VRefMut<ItemVisitorVTable>, |
1217 | dyn_index: u32, |
1218 | ) -> VisitChildrenResult, |
1219 | ) -> VisitChildrenResult { |
1220 | crate::item_tree::visit_item_tree( |
1221 | VRc::as_pin_ref(item_tree), |
1222 | item_tree, |
1223 | item_tree_array.as_slice(), |
1224 | index, |
1225 | order, |
1226 | visitor, |
1227 | |a, b, c, d| visit_dynamic(a.get_ref() as *const vtable::Dyn as *const c_void, b, c, d), |
1228 | ) |
1229 | } |
1230 | } |
1231 | |
1232 | #[cfg (test)] |
1233 | mod tests { |
1234 | use super::*; |
1235 | use std::vec; |
1236 | |
1237 | struct TestItemTree { |
1238 | parent_component: Option<ItemTreeRc>, |
1239 | item_tree: Vec<ItemTreeNode>, |
1240 | subtrees: std::cell::RefCell<Vec<Vec<vtable::VRc<ItemTreeVTable, TestItemTree>>>>, |
1241 | subtree_index: usize, |
1242 | } |
1243 | |
1244 | impl ItemTree for TestItemTree { |
1245 | fn visit_children_item( |
1246 | self: core::pin::Pin<&Self>, |
1247 | _1: isize, |
1248 | _2: crate::item_tree::TraversalOrder, |
1249 | _3: vtable::VRefMut<crate::item_tree::ItemVisitorVTable>, |
1250 | ) -> crate::item_tree::VisitChildrenResult { |
1251 | unimplemented!("Not needed for this test" ) |
1252 | } |
1253 | |
1254 | fn get_item_ref( |
1255 | self: core::pin::Pin<&Self>, |
1256 | _1: u32, |
1257 | ) -> core::pin::Pin<vtable::VRef<super::ItemVTable>> { |
1258 | unimplemented!("Not needed for this test" ) |
1259 | } |
1260 | |
1261 | fn get_item_tree(self: core::pin::Pin<&Self>) -> Slice<ItemTreeNode> { |
1262 | Slice::from_slice(&self.get_ref().item_tree) |
1263 | } |
1264 | |
1265 | fn parent_node(self: core::pin::Pin<&Self>, result: &mut ItemWeak) { |
1266 | if let Some(parent_item) = self.parent_component.clone() { |
1267 | *result = |
1268 | ItemRc::new(parent_item.clone(), self.item_tree[0].parent_index()).downgrade(); |
1269 | } |
1270 | } |
1271 | |
1272 | fn embed_component( |
1273 | self: core::pin::Pin<&Self>, |
1274 | _parent_component: &ItemTreeWeak, |
1275 | _item_tree_index: u32, |
1276 | ) -> bool { |
1277 | false |
1278 | } |
1279 | |
1280 | fn layout_info(self: core::pin::Pin<&Self>, _1: Orientation) -> LayoutInfo { |
1281 | unimplemented!("Not needed for this test" ) |
1282 | } |
1283 | |
1284 | fn subtree_index(self: core::pin::Pin<&Self>) -> usize { |
1285 | self.subtree_index |
1286 | } |
1287 | |
1288 | fn get_subtree_range(self: core::pin::Pin<&Self>, subtree_index: u32) -> IndexRange { |
1289 | (0..self.subtrees.borrow()[subtree_index as usize].len()).into() |
1290 | } |
1291 | |
1292 | fn get_subtree( |
1293 | self: core::pin::Pin<&Self>, |
1294 | subtree_index: u32, |
1295 | component_index: usize, |
1296 | result: &mut ItemTreeWeak, |
1297 | ) { |
1298 | if let Some(vrc) = self.subtrees.borrow()[subtree_index as usize].get(component_index) { |
1299 | *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(vrc.clone())) |
1300 | } |
1301 | } |
1302 | |
1303 | fn accessible_role(self: Pin<&Self>, _: u32) -> AccessibleRole { |
1304 | unimplemented!("Not needed for this test" ) |
1305 | } |
1306 | |
1307 | fn accessible_string_property( |
1308 | self: Pin<&Self>, |
1309 | _: u32, |
1310 | _: AccessibleStringProperty, |
1311 | _: &mut SharedString, |
1312 | ) -> bool { |
1313 | false |
1314 | } |
1315 | |
1316 | fn item_element_infos(self: Pin<&Self>, _: u32, _: &mut SharedString) -> bool { |
1317 | false |
1318 | } |
1319 | |
1320 | fn window_adapter( |
1321 | self: Pin<&Self>, |
1322 | _do_create: bool, |
1323 | _result: &mut Option<WindowAdapterRc>, |
1324 | ) { |
1325 | unimplemented!("Not needed for this test" ) |
1326 | } |
1327 | |
1328 | fn item_geometry(self: Pin<&Self>, _: u32) -> LogicalRect { |
1329 | unimplemented!("Not needed for this test" ) |
1330 | } |
1331 | |
1332 | fn accessibility_action(self: core::pin::Pin<&Self>, _: u32, _: &AccessibilityAction) { |
1333 | unimplemented!("Not needed for this test" ) |
1334 | } |
1335 | |
1336 | fn supported_accessibility_actions( |
1337 | self: core::pin::Pin<&Self>, |
1338 | _: u32, |
1339 | ) -> SupportedAccessibilityAction { |
1340 | unimplemented!("Not needed for this test" ) |
1341 | } |
1342 | } |
1343 | |
1344 | crate::item_tree::ItemTreeVTable_static!(static TEST_COMPONENT_VT for TestItemTree); |
1345 | |
1346 | fn create_one_node_component() -> VRc<ItemTreeVTable, vtable::Dyn> { |
1347 | let component = VRc::new(TestItemTree { |
1348 | parent_component: None, |
1349 | item_tree: vec![ItemTreeNode::Item { |
1350 | is_accessible: false, |
1351 | children_count: 0, |
1352 | children_index: 1, |
1353 | parent_index: 0, |
1354 | item_array_index: 0, |
1355 | }], |
1356 | subtrees: std::cell::RefCell::new(vec![]), |
1357 | subtree_index: usize::MAX, |
1358 | }); |
1359 | VRc::into_dyn(component) |
1360 | } |
1361 | |
1362 | #[test ] |
1363 | fn test_tree_traversal_one_node_structure() { |
1364 | let component = create_one_node_component(); |
1365 | |
1366 | let item = ItemRc::new(component.clone(), 0); |
1367 | |
1368 | assert!(item.first_child().is_none()); |
1369 | assert!(item.last_child().is_none()); |
1370 | assert!(item.previous_sibling().is_none()); |
1371 | assert!(item.next_sibling().is_none()); |
1372 | } |
1373 | |
1374 | #[test ] |
1375 | fn test_tree_traversal_one_node_forward_focus() { |
1376 | let component = create_one_node_component(); |
1377 | |
1378 | let item = ItemRc::new(component.clone(), 0); |
1379 | |
1380 | // Wrap the focus around: |
1381 | assert_eq!(item.next_focus_item(), item); |
1382 | } |
1383 | |
1384 | #[test ] |
1385 | fn test_tree_traversal_one_node_backward_focus() { |
1386 | let component = create_one_node_component(); |
1387 | |
1388 | let item = ItemRc::new(component.clone(), 0); |
1389 | |
1390 | // Wrap the focus around: |
1391 | assert_eq!(item.previous_focus_item(), item); |
1392 | } |
1393 | |
1394 | fn create_children_nodes() -> VRc<ItemTreeVTable, vtable::Dyn> { |
1395 | let component = VRc::new(TestItemTree { |
1396 | parent_component: None, |
1397 | item_tree: vec![ |
1398 | ItemTreeNode::Item { |
1399 | is_accessible: false, |
1400 | children_count: 3, |
1401 | children_index: 1, |
1402 | parent_index: 0, |
1403 | item_array_index: 0, |
1404 | }, |
1405 | ItemTreeNode::Item { |
1406 | is_accessible: false, |
1407 | children_count: 0, |
1408 | children_index: 4, |
1409 | parent_index: 0, |
1410 | item_array_index: 1, |
1411 | }, |
1412 | ItemTreeNode::Item { |
1413 | is_accessible: false, |
1414 | children_count: 0, |
1415 | children_index: 4, |
1416 | parent_index: 0, |
1417 | item_array_index: 2, |
1418 | }, |
1419 | ItemTreeNode::Item { |
1420 | is_accessible: false, |
1421 | children_count: 0, |
1422 | children_index: 4, |
1423 | parent_index: 0, |
1424 | item_array_index: 3, |
1425 | }, |
1426 | ], |
1427 | subtrees: std::cell::RefCell::new(vec![]), |
1428 | subtree_index: usize::MAX, |
1429 | }); |
1430 | VRc::into_dyn(component) |
1431 | } |
1432 | |
1433 | #[test ] |
1434 | fn test_tree_traversal_children_nodes_structure() { |
1435 | let component = create_children_nodes(); |
1436 | |
1437 | // Examine root node: |
1438 | let item = ItemRc::new(component.clone(), 0); |
1439 | assert!(item.previous_sibling().is_none()); |
1440 | assert!(item.next_sibling().is_none()); |
1441 | |
1442 | let fc = item.first_child().unwrap(); |
1443 | assert_eq!(fc.index(), 1); |
1444 | assert!(VRc::ptr_eq(fc.item_tree(), item.item_tree())); |
1445 | |
1446 | let fcn = fc.next_sibling().unwrap(); |
1447 | assert_eq!(fcn.index(), 2); |
1448 | |
1449 | let lc = item.last_child().unwrap(); |
1450 | assert_eq!(lc.index(), 3); |
1451 | assert!(VRc::ptr_eq(lc.item_tree(), item.item_tree())); |
1452 | |
1453 | let lcp = lc.previous_sibling().unwrap(); |
1454 | assert!(VRc::ptr_eq(lcp.item_tree(), item.item_tree())); |
1455 | assert_eq!(lcp.index(), 2); |
1456 | |
1457 | // Examine first child: |
1458 | assert!(fc.first_child().is_none()); |
1459 | assert!(fc.last_child().is_none()); |
1460 | assert!(fc.previous_sibling().is_none()); |
1461 | assert_eq!(fc.parent_item().unwrap(), item); |
1462 | |
1463 | // Examine item between first and last child: |
1464 | assert_eq!(fcn, lcp); |
1465 | assert_eq!(lcp.parent_item().unwrap(), item); |
1466 | assert_eq!(fcn.previous_sibling().unwrap(), fc); |
1467 | assert_eq!(fcn.next_sibling().unwrap(), lc); |
1468 | |
1469 | // Examine last child: |
1470 | assert!(lc.first_child().is_none()); |
1471 | assert!(lc.last_child().is_none()); |
1472 | assert!(lc.next_sibling().is_none()); |
1473 | assert_eq!(lc.parent_item().unwrap(), item); |
1474 | } |
1475 | |
1476 | #[test ] |
1477 | fn test_tree_traversal_children_nodes_forward_focus() { |
1478 | let component = create_children_nodes(); |
1479 | |
1480 | let item = ItemRc::new(component.clone(), 0); |
1481 | let fc = item.first_child().unwrap(); |
1482 | let fcn = fc.next_sibling().unwrap(); |
1483 | let lc = item.last_child().unwrap(); |
1484 | |
1485 | let mut cursor = item.clone(); |
1486 | |
1487 | cursor = cursor.next_focus_item(); |
1488 | assert_eq!(cursor, fc); |
1489 | |
1490 | cursor = cursor.next_focus_item(); |
1491 | assert_eq!(cursor, fcn); |
1492 | |
1493 | cursor = cursor.next_focus_item(); |
1494 | assert_eq!(cursor, lc); |
1495 | |
1496 | cursor = cursor.next_focus_item(); |
1497 | assert_eq!(cursor, item); |
1498 | } |
1499 | |
1500 | #[test ] |
1501 | fn test_tree_traversal_children_nodes_backward_focus() { |
1502 | let component = create_children_nodes(); |
1503 | |
1504 | let item = ItemRc::new(component.clone(), 0); |
1505 | let fc = item.first_child().unwrap(); |
1506 | let fcn = fc.next_sibling().unwrap(); |
1507 | let lc = item.last_child().unwrap(); |
1508 | |
1509 | let mut cursor = item.clone(); |
1510 | |
1511 | cursor = cursor.previous_focus_item(); |
1512 | assert_eq!(cursor, lc); |
1513 | |
1514 | cursor = cursor.previous_focus_item(); |
1515 | assert_eq!(cursor, fcn); |
1516 | |
1517 | cursor = cursor.previous_focus_item(); |
1518 | assert_eq!(cursor, fc); |
1519 | |
1520 | cursor = cursor.previous_focus_item(); |
1521 | assert_eq!(cursor, item); |
1522 | } |
1523 | |
1524 | fn create_empty_subtree() -> VRc<ItemTreeVTable, vtable::Dyn> { |
1525 | let component = vtable::VRc::new(TestItemTree { |
1526 | parent_component: None, |
1527 | item_tree: vec![ |
1528 | ItemTreeNode::Item { |
1529 | is_accessible: false, |
1530 | children_count: 1, |
1531 | children_index: 1, |
1532 | parent_index: 0, |
1533 | item_array_index: 0, |
1534 | }, |
1535 | ItemTreeNode::DynamicTree { index: 0, parent_index: 0 }, |
1536 | ], |
1537 | subtrees: std::cell::RefCell::new(vec![vec![]]), |
1538 | subtree_index: usize::MAX, |
1539 | }); |
1540 | vtable::VRc::into_dyn(component) |
1541 | } |
1542 | |
1543 | #[test ] |
1544 | fn test_tree_traversal_empty_subtree_structure() { |
1545 | let component = create_empty_subtree(); |
1546 | |
1547 | // Examine root node: |
1548 | let item = ItemRc::new(component.clone(), 0); |
1549 | assert!(item.previous_sibling().is_none()); |
1550 | assert!(item.next_sibling().is_none()); |
1551 | assert!(item.first_child().is_none()); |
1552 | assert!(item.last_child().is_none()); |
1553 | |
1554 | // Wrap the focus around: |
1555 | assert!(item.previous_focus_item() == item); |
1556 | assert!(item.next_focus_item() == item); |
1557 | } |
1558 | |
1559 | #[test ] |
1560 | fn test_tree_traversal_empty_subtree_forward_focus() { |
1561 | let component = create_empty_subtree(); |
1562 | |
1563 | // Examine root node: |
1564 | let item = ItemRc::new(component.clone(), 0); |
1565 | |
1566 | assert!(item.next_focus_item() == item); |
1567 | } |
1568 | |
1569 | #[test ] |
1570 | fn test_tree_traversal_empty_subtree_backward_focus() { |
1571 | let component = create_empty_subtree(); |
1572 | |
1573 | // Examine root node: |
1574 | let item = ItemRc::new(component.clone(), 0); |
1575 | |
1576 | assert!(item.previous_focus_item() == item); |
1577 | } |
1578 | |
1579 | fn create_item_subtree_item() -> VRc<ItemTreeVTable, vtable::Dyn> { |
1580 | let component = VRc::new(TestItemTree { |
1581 | parent_component: None, |
1582 | item_tree: vec![ |
1583 | ItemTreeNode::Item { |
1584 | is_accessible: false, |
1585 | children_count: 3, |
1586 | children_index: 1, |
1587 | parent_index: 0, |
1588 | item_array_index: 0, |
1589 | }, |
1590 | ItemTreeNode::Item { |
1591 | is_accessible: false, |
1592 | children_count: 0, |
1593 | children_index: 4, |
1594 | parent_index: 0, |
1595 | item_array_index: 0, |
1596 | }, |
1597 | ItemTreeNode::DynamicTree { index: 0, parent_index: 0 }, |
1598 | ItemTreeNode::Item { |
1599 | is_accessible: false, |
1600 | children_count: 0, |
1601 | children_index: 4, |
1602 | parent_index: 0, |
1603 | item_array_index: 0, |
1604 | }, |
1605 | ], |
1606 | subtrees: std::cell::RefCell::new(vec![]), |
1607 | subtree_index: usize::MAX, |
1608 | }); |
1609 | |
1610 | component.as_pin_ref().subtrees.replace(vec![vec![VRc::new(TestItemTree { |
1611 | parent_component: Some(VRc::into_dyn(component.clone())), |
1612 | item_tree: vec![ItemTreeNode::Item { |
1613 | is_accessible: false, |
1614 | children_count: 0, |
1615 | children_index: 1, |
1616 | parent_index: 2, |
1617 | item_array_index: 0, |
1618 | }], |
1619 | subtrees: std::cell::RefCell::new(vec![]), |
1620 | subtree_index: 0, |
1621 | })]]); |
1622 | |
1623 | VRc::into_dyn(component) |
1624 | } |
1625 | |
1626 | #[test ] |
1627 | fn test_tree_traversal_item_subtree_item_structure() { |
1628 | let component = create_item_subtree_item(); |
1629 | |
1630 | // Examine root node: |
1631 | let item = ItemRc::new(component.clone(), 0); |
1632 | assert!(item.previous_sibling().is_none()); |
1633 | assert!(item.next_sibling().is_none()); |
1634 | |
1635 | let fc = item.first_child().unwrap(); |
1636 | assert!(VRc::ptr_eq(fc.item_tree(), item.item_tree())); |
1637 | assert_eq!(fc.index(), 1); |
1638 | |
1639 | let lc = item.last_child().unwrap(); |
1640 | assert!(VRc::ptr_eq(lc.item_tree(), item.item_tree())); |
1641 | assert_eq!(lc.index(), 3); |
1642 | |
1643 | let fcn = fc.next_sibling().unwrap(); |
1644 | let lcp = lc.previous_sibling().unwrap(); |
1645 | |
1646 | assert_eq!(fcn, lcp); |
1647 | assert!(!VRc::ptr_eq(fcn.item_tree(), item.item_tree())); |
1648 | |
1649 | let last = fcn.next_sibling().unwrap(); |
1650 | assert_eq!(last, lc); |
1651 | |
1652 | let first = lcp.previous_sibling().unwrap(); |
1653 | assert_eq!(first, fc); |
1654 | } |
1655 | |
1656 | #[test ] |
1657 | fn test_tree_traversal_item_subtree_item_forward_focus() { |
1658 | let component = create_item_subtree_item(); |
1659 | |
1660 | let item = ItemRc::new(component.clone(), 0); |
1661 | let fc = item.first_child().unwrap(); |
1662 | let lc = item.last_child().unwrap(); |
1663 | let fcn = fc.next_sibling().unwrap(); |
1664 | |
1665 | let mut cursor = item.clone(); |
1666 | |
1667 | cursor = cursor.next_focus_item(); |
1668 | assert_eq!(cursor, fc); |
1669 | |
1670 | cursor = cursor.next_focus_item(); |
1671 | assert_eq!(cursor, fcn); |
1672 | |
1673 | cursor = cursor.next_focus_item(); |
1674 | assert_eq!(cursor, lc); |
1675 | |
1676 | cursor = cursor.next_focus_item(); |
1677 | assert_eq!(cursor, item); |
1678 | } |
1679 | |
1680 | #[test ] |
1681 | fn test_tree_traversal_item_subtree_item_backward_focus() { |
1682 | let component = create_item_subtree_item(); |
1683 | |
1684 | let item = ItemRc::new(component.clone(), 0); |
1685 | let fc = item.first_child().unwrap(); |
1686 | let lc = item.last_child().unwrap(); |
1687 | let fcn = fc.next_sibling().unwrap(); |
1688 | |
1689 | let mut cursor = item.clone(); |
1690 | |
1691 | cursor = cursor.previous_focus_item(); |
1692 | assert_eq!(cursor, lc); |
1693 | |
1694 | cursor = cursor.previous_focus_item(); |
1695 | assert_eq!(cursor, fcn); |
1696 | |
1697 | cursor = cursor.previous_focus_item(); |
1698 | assert_eq!(cursor, fc); |
1699 | |
1700 | cursor = cursor.previous_focus_item(); |
1701 | assert_eq!(cursor, item); |
1702 | } |
1703 | |
1704 | fn create_nested_subtrees() -> VRc<ItemTreeVTable, vtable::Dyn> { |
1705 | let component = VRc::new(TestItemTree { |
1706 | parent_component: None, |
1707 | item_tree: vec![ |
1708 | ItemTreeNode::Item { |
1709 | is_accessible: false, |
1710 | children_count: 3, |
1711 | children_index: 1, |
1712 | parent_index: 0, |
1713 | item_array_index: 0, |
1714 | }, |
1715 | ItemTreeNode::Item { |
1716 | is_accessible: false, |
1717 | children_count: 0, |
1718 | children_index: 4, |
1719 | parent_index: 0, |
1720 | item_array_index: 0, |
1721 | }, |
1722 | ItemTreeNode::DynamicTree { index: 0, parent_index: 0 }, |
1723 | ItemTreeNode::Item { |
1724 | is_accessible: false, |
1725 | children_count: 0, |
1726 | children_index: 4, |
1727 | parent_index: 0, |
1728 | item_array_index: 0, |
1729 | }, |
1730 | ], |
1731 | subtrees: std::cell::RefCell::new(vec![]), |
1732 | subtree_index: usize::MAX, |
1733 | }); |
1734 | |
1735 | let sub_component1 = VRc::new(TestItemTree { |
1736 | parent_component: Some(VRc::into_dyn(component.clone())), |
1737 | item_tree: vec![ |
1738 | ItemTreeNode::Item { |
1739 | is_accessible: false, |
1740 | children_count: 1, |
1741 | children_index: 1, |
1742 | parent_index: 2, |
1743 | item_array_index: 0, |
1744 | }, |
1745 | ItemTreeNode::DynamicTree { index: 0, parent_index: 0 }, |
1746 | ], |
1747 | subtrees: std::cell::RefCell::new(vec![]), |
1748 | subtree_index: usize::MAX, |
1749 | }); |
1750 | let sub_component2 = VRc::new(TestItemTree { |
1751 | parent_component: Some(VRc::into_dyn(sub_component1.clone())), |
1752 | item_tree: vec![ |
1753 | ItemTreeNode::Item { |
1754 | is_accessible: false, |
1755 | children_count: 1, |
1756 | children_index: 1, |
1757 | parent_index: 1, |
1758 | item_array_index: 0, |
1759 | }, |
1760 | ItemTreeNode::Item { |
1761 | is_accessible: false, |
1762 | children_count: 0, |
1763 | children_index: 2, |
1764 | parent_index: 0, |
1765 | item_array_index: 0, |
1766 | }, |
1767 | ], |
1768 | subtrees: std::cell::RefCell::new(vec![]), |
1769 | subtree_index: usize::MAX, |
1770 | }); |
1771 | |
1772 | sub_component1.as_pin_ref().subtrees.replace(vec![vec![sub_component2]]); |
1773 | component.as_pin_ref().subtrees.replace(vec![vec![sub_component1]]); |
1774 | |
1775 | VRc::into_dyn(component) |
1776 | } |
1777 | |
1778 | #[test ] |
1779 | fn test_tree_traversal_nested_subtrees_structure() { |
1780 | let component = create_nested_subtrees(); |
1781 | |
1782 | // Examine root node: |
1783 | let item = ItemRc::new(component.clone(), 0); |
1784 | assert!(item.previous_sibling().is_none()); |
1785 | assert!(item.next_sibling().is_none()); |
1786 | |
1787 | let fc = item.first_child().unwrap(); |
1788 | assert!(VRc::ptr_eq(fc.item_tree(), item.item_tree())); |
1789 | assert_eq!(fc.index(), 1); |
1790 | |
1791 | let lc = item.last_child().unwrap(); |
1792 | assert!(VRc::ptr_eq(lc.item_tree(), item.item_tree())); |
1793 | assert_eq!(lc.index(), 3); |
1794 | |
1795 | let fcn = fc.next_sibling().unwrap(); |
1796 | let lcp = lc.previous_sibling().unwrap(); |
1797 | |
1798 | assert_eq!(fcn, lcp); |
1799 | assert!(!VRc::ptr_eq(fcn.item_tree(), item.item_tree())); |
1800 | |
1801 | let last = fcn.next_sibling().unwrap(); |
1802 | assert_eq!(last, lc); |
1803 | |
1804 | let first = lcp.previous_sibling().unwrap(); |
1805 | assert_eq!(first, fc); |
1806 | |
1807 | // Nested component: |
1808 | let nested_root = fcn.first_child().unwrap(); |
1809 | assert_eq!(nested_root, fcn.last_child().unwrap()); |
1810 | assert!(nested_root.next_sibling().is_none()); |
1811 | assert!(nested_root.previous_sibling().is_none()); |
1812 | assert!(!VRc::ptr_eq(nested_root.item_tree(), item.item_tree())); |
1813 | assert!(!VRc::ptr_eq(nested_root.item_tree(), fcn.item_tree())); |
1814 | |
1815 | let nested_child = nested_root.first_child().unwrap(); |
1816 | assert_eq!(nested_child, nested_root.last_child().unwrap()); |
1817 | assert!(VRc::ptr_eq(nested_root.item_tree(), nested_child.item_tree())); |
1818 | } |
1819 | |
1820 | #[test ] |
1821 | fn test_tree_traversal_nested_subtrees_forward_focus() { |
1822 | let component = create_nested_subtrees(); |
1823 | |
1824 | // Examine root node: |
1825 | let item = ItemRc::new(component.clone(), 0); |
1826 | let fc = item.first_child().unwrap(); |
1827 | let fcn = fc.next_sibling().unwrap(); |
1828 | let lc = item.last_child().unwrap(); |
1829 | let nested_root = fcn.first_child().unwrap(); |
1830 | let nested_child = nested_root.first_child().unwrap(); |
1831 | |
1832 | // Focus traversal: |
1833 | let mut cursor = item.clone(); |
1834 | |
1835 | cursor = cursor.next_focus_item(); |
1836 | assert_eq!(cursor, fc); |
1837 | |
1838 | cursor = cursor.next_focus_item(); |
1839 | assert_eq!(cursor, fcn); |
1840 | |
1841 | cursor = cursor.next_focus_item(); |
1842 | assert_eq!(cursor, nested_root); |
1843 | |
1844 | cursor = cursor.next_focus_item(); |
1845 | assert_eq!(cursor, nested_child); |
1846 | |
1847 | cursor = cursor.next_focus_item(); |
1848 | assert_eq!(cursor, lc); |
1849 | |
1850 | cursor = cursor.next_focus_item(); |
1851 | assert_eq!(cursor, item); |
1852 | } |
1853 | |
1854 | #[test ] |
1855 | fn test_tree_traversal_nested_subtrees_backward_focus() { |
1856 | let component = create_nested_subtrees(); |
1857 | |
1858 | // Examine root node: |
1859 | let item = ItemRc::new(component.clone(), 0); |
1860 | let fc = item.first_child().unwrap(); |
1861 | let fcn = fc.next_sibling().unwrap(); |
1862 | let lc = item.last_child().unwrap(); |
1863 | let nested_root = fcn.first_child().unwrap(); |
1864 | let nested_child = nested_root.first_child().unwrap(); |
1865 | |
1866 | // Focus traversal: |
1867 | let mut cursor = item.clone(); |
1868 | |
1869 | cursor = cursor.previous_focus_item(); |
1870 | assert_eq!(cursor, lc); |
1871 | |
1872 | cursor = cursor.previous_focus_item(); |
1873 | assert_eq!(cursor, nested_child); |
1874 | |
1875 | cursor = cursor.previous_focus_item(); |
1876 | assert_eq!(cursor, nested_root); |
1877 | |
1878 | cursor = cursor.previous_focus_item(); |
1879 | assert_eq!(cursor, fcn); |
1880 | |
1881 | cursor = cursor.previous_focus_item(); |
1882 | assert_eq!(cursor, fc); |
1883 | |
1884 | cursor = cursor.previous_focus_item(); |
1885 | assert_eq!(cursor, item); |
1886 | } |
1887 | |
1888 | fn create_subtrees_item() -> VRc<ItemTreeVTable, vtable::Dyn> { |
1889 | let component = VRc::new(TestItemTree { |
1890 | parent_component: None, |
1891 | item_tree: vec![ |
1892 | ItemTreeNode::Item { |
1893 | is_accessible: false, |
1894 | children_count: 2, |
1895 | children_index: 1, |
1896 | parent_index: 0, |
1897 | item_array_index: 0, |
1898 | }, |
1899 | ItemTreeNode::DynamicTree { index: 0, parent_index: 0 }, |
1900 | ItemTreeNode::Item { |
1901 | is_accessible: false, |
1902 | children_count: 0, |
1903 | children_index: 4, |
1904 | parent_index: 0, |
1905 | item_array_index: 0, |
1906 | }, |
1907 | ], |
1908 | subtrees: std::cell::RefCell::new(vec![]), |
1909 | subtree_index: usize::MAX, |
1910 | }); |
1911 | |
1912 | component.as_pin_ref().subtrees.replace(vec![vec![ |
1913 | VRc::new(TestItemTree { |
1914 | parent_component: Some(VRc::into_dyn(component.clone())), |
1915 | item_tree: vec![ItemTreeNode::Item { |
1916 | is_accessible: false, |
1917 | children_count: 0, |
1918 | children_index: 1, |
1919 | parent_index: 1, |
1920 | item_array_index: 0, |
1921 | }], |
1922 | subtrees: std::cell::RefCell::new(vec![]), |
1923 | subtree_index: 0, |
1924 | }), |
1925 | VRc::new(TestItemTree { |
1926 | parent_component: Some(VRc::into_dyn(component.clone())), |
1927 | item_tree: vec![ItemTreeNode::Item { |
1928 | is_accessible: false, |
1929 | children_count: 0, |
1930 | children_index: 1, |
1931 | parent_index: 1, |
1932 | item_array_index: 0, |
1933 | }], |
1934 | subtrees: std::cell::RefCell::new(vec![]), |
1935 | subtree_index: 1, |
1936 | }), |
1937 | VRc::new(TestItemTree { |
1938 | parent_component: Some(VRc::into_dyn(component.clone())), |
1939 | item_tree: vec![ItemTreeNode::Item { |
1940 | is_accessible: false, |
1941 | children_count: 0, |
1942 | children_index: 1, |
1943 | parent_index: 1, |
1944 | item_array_index: 0, |
1945 | }], |
1946 | subtrees: std::cell::RefCell::new(vec![]), |
1947 | subtree_index: 2, |
1948 | }), |
1949 | ]]); |
1950 | |
1951 | VRc::into_dyn(component) |
1952 | } |
1953 | |
1954 | #[test ] |
1955 | fn test_tree_traversal_subtrees_item_structure() { |
1956 | let component = create_subtrees_item(); |
1957 | |
1958 | // Examine root node: |
1959 | let item = ItemRc::new(component.clone(), 0); |
1960 | assert!(item.previous_sibling().is_none()); |
1961 | assert!(item.next_sibling().is_none()); |
1962 | |
1963 | let sub1 = item.first_child().unwrap(); |
1964 | assert_eq!(sub1.index(), 0); |
1965 | assert!(!VRc::ptr_eq(sub1.item_tree(), item.item_tree())); |
1966 | |
1967 | // assert!(sub1.previous_sibling().is_none()); |
1968 | |
1969 | let sub2 = sub1.next_sibling().unwrap(); |
1970 | assert_eq!(sub2.index(), 0); |
1971 | assert!(!VRc::ptr_eq(sub1.item_tree(), sub2.item_tree())); |
1972 | assert!(!VRc::ptr_eq(item.item_tree(), sub2.item_tree())); |
1973 | |
1974 | assert!(sub2.previous_sibling() == Some(sub1.clone())); |
1975 | |
1976 | let sub3 = sub2.next_sibling().unwrap(); |
1977 | assert_eq!(sub3.index(), 0); |
1978 | assert!(!VRc::ptr_eq(sub1.item_tree(), sub2.item_tree())); |
1979 | assert!(!VRc::ptr_eq(sub2.item_tree(), sub3.item_tree())); |
1980 | assert!(!VRc::ptr_eq(item.item_tree(), sub3.item_tree())); |
1981 | |
1982 | assert_eq!(sub3.previous_sibling().unwrap(), sub2.clone()); |
1983 | } |
1984 | |
1985 | #[test ] |
1986 | fn test_component_item_tree_root_only() { |
1987 | let nodes = vec![ItemTreeNode::Item { |
1988 | is_accessible: false, |
1989 | children_count: 0, |
1990 | children_index: 1, |
1991 | parent_index: 0, |
1992 | item_array_index: 0, |
1993 | }]; |
1994 | |
1995 | let tree: ItemTreeNodeArray = (nodes.as_slice()).into(); |
1996 | |
1997 | assert_eq!(tree.first_child(0), None); |
1998 | assert_eq!(tree.last_child(0), None); |
1999 | assert_eq!(tree.previous_sibling(0), None); |
2000 | assert_eq!(tree.next_sibling(0), None); |
2001 | assert_eq!(tree.parent(0), None); |
2002 | } |
2003 | |
2004 | #[test ] |
2005 | fn test_component_item_tree_one_child() { |
2006 | let nodes = vec![ |
2007 | ItemTreeNode::Item { |
2008 | is_accessible: false, |
2009 | children_count: 1, |
2010 | children_index: 1, |
2011 | parent_index: 0, |
2012 | item_array_index: 0, |
2013 | }, |
2014 | ItemTreeNode::Item { |
2015 | is_accessible: false, |
2016 | children_count: 0, |
2017 | children_index: 2, |
2018 | parent_index: 0, |
2019 | item_array_index: 0, |
2020 | }, |
2021 | ]; |
2022 | |
2023 | let tree: ItemTreeNodeArray = (nodes.as_slice()).into(); |
2024 | |
2025 | assert_eq!(tree.first_child(0), Some(1)); |
2026 | assert_eq!(tree.last_child(0), Some(1)); |
2027 | assert_eq!(tree.previous_sibling(0), None); |
2028 | assert_eq!(tree.next_sibling(0), None); |
2029 | assert_eq!(tree.parent(0), None); |
2030 | assert_eq!(tree.previous_sibling(1), None); |
2031 | assert_eq!(tree.next_sibling(1), None); |
2032 | assert_eq!(tree.parent(1), Some(0)); |
2033 | } |
2034 | |
2035 | #[test ] |
2036 | fn test_component_item_tree_tree_children() { |
2037 | let nodes = vec![ |
2038 | ItemTreeNode::Item { |
2039 | is_accessible: false, |
2040 | children_count: 3, |
2041 | children_index: 1, |
2042 | parent_index: 0, |
2043 | item_array_index: 0, |
2044 | }, |
2045 | ItemTreeNode::Item { |
2046 | is_accessible: false, |
2047 | children_count: 0, |
2048 | children_index: 4, |
2049 | parent_index: 0, |
2050 | item_array_index: 0, |
2051 | }, |
2052 | ItemTreeNode::Item { |
2053 | is_accessible: false, |
2054 | children_count: 0, |
2055 | children_index: 4, |
2056 | parent_index: 0, |
2057 | item_array_index: 0, |
2058 | }, |
2059 | ItemTreeNode::Item { |
2060 | is_accessible: false, |
2061 | children_count: 0, |
2062 | children_index: 4, |
2063 | parent_index: 0, |
2064 | item_array_index: 0, |
2065 | }, |
2066 | ]; |
2067 | |
2068 | let tree: ItemTreeNodeArray = (nodes.as_slice()).into(); |
2069 | |
2070 | assert_eq!(tree.first_child(0), Some(1)); |
2071 | assert_eq!(tree.last_child(0), Some(3)); |
2072 | assert_eq!(tree.previous_sibling(0), None); |
2073 | assert_eq!(tree.next_sibling(0), None); |
2074 | assert_eq!(tree.parent(0), None); |
2075 | |
2076 | assert_eq!(tree.previous_sibling(1), None); |
2077 | assert_eq!(tree.next_sibling(1), Some(2)); |
2078 | assert_eq!(tree.parent(1), Some(0)); |
2079 | |
2080 | assert_eq!(tree.previous_sibling(2), Some(1)); |
2081 | assert_eq!(tree.next_sibling(2), Some(3)); |
2082 | assert_eq!(tree.parent(2), Some(0)); |
2083 | |
2084 | assert_eq!(tree.previous_sibling(3), Some(2)); |
2085 | assert_eq!(tree.next_sibling(3), None); |
2086 | assert_eq!(tree.parent(3), Some(0)); |
2087 | } |
2088 | } |
2089 | |