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