1 | // Copyright 2021 The AccessKit Authors. All rights reserved. |
2 | // Licensed under the Apache License, Version 2.0 (found in |
3 | // the LICENSE-APACHE file) or the MIT license (found in |
4 | // the LICENSE-MIT file), at your option. |
5 | |
6 | use accesskit::{FrozenNode as NodeData, NodeId, Tree as TreeData, TreeUpdate}; |
7 | use alloc::{format, string::String, sync::Arc, vec, vec::Vec}; |
8 | use hashbrown::{HashMap, HashSet}; |
9 | use immutable_chunkmap::map::MapM as ChunkMap; |
10 | |
11 | use crate::node::{Node, NodeState, ParentAndIndex}; |
12 | |
13 | #[derive (Clone)] |
14 | pub struct State { |
15 | pub(crate) nodes: ChunkMap<NodeId, NodeState>, |
16 | pub(crate) data: TreeData, |
17 | pub(crate) focus: NodeId, |
18 | is_host_focused: bool, |
19 | } |
20 | |
21 | #[derive (Default)] |
22 | struct InternalChanges { |
23 | added_node_ids: HashSet<NodeId>, |
24 | updated_node_ids: HashSet<NodeId>, |
25 | removed_node_ids: HashSet<NodeId>, |
26 | } |
27 | |
28 | impl State { |
29 | fn validate_global(&self) { |
30 | if self.nodes.get_key(&self.data.root).is_none() { |
31 | panic!("Root id # {} is not in the node list" , self.data.root.0); |
32 | } |
33 | if self.nodes.get_key(&self.focus).is_none() { |
34 | panic!("Focused id # {} is not in the node list" , self.focus.0); |
35 | } |
36 | } |
37 | |
38 | fn update( |
39 | &mut self, |
40 | update: TreeUpdate, |
41 | is_host_focused: bool, |
42 | mut changes: Option<&mut InternalChanges>, |
43 | ) { |
44 | let mut orphans = HashSet::new(); |
45 | |
46 | if let Some(tree) = update.tree { |
47 | if tree.root != self.data.root { |
48 | orphans.insert(self.data.root); |
49 | } |
50 | self.data = tree; |
51 | } |
52 | |
53 | let root = self.data.root; |
54 | let mut pending_nodes: HashMap<NodeId, _> = HashMap::new(); |
55 | let mut pending_children = HashMap::new(); |
56 | |
57 | fn add_node( |
58 | nodes: &mut ChunkMap<NodeId, NodeState>, |
59 | changes: &mut Option<&mut InternalChanges>, |
60 | parent_and_index: Option<ParentAndIndex>, |
61 | id: NodeId, |
62 | data: NodeData, |
63 | ) { |
64 | let state = NodeState { |
65 | parent_and_index, |
66 | data: Arc::new(data), |
67 | }; |
68 | nodes.insert_cow(id, state); |
69 | if let Some(changes) = changes { |
70 | changes.added_node_ids.insert(id); |
71 | } |
72 | } |
73 | |
74 | for (node_id, node_data) in update.nodes { |
75 | let node_data = NodeData::from(node_data); |
76 | |
77 | orphans.remove(&node_id); |
78 | |
79 | let mut seen_child_ids = HashSet::with_capacity(node_data.children().len()); |
80 | for (child_index, child_id) in node_data.children().iter().enumerate() { |
81 | if seen_child_ids.contains(child_id) { |
82 | panic!( |
83 | "Node # {} of TreeUpdate includes duplicate child # {};" , |
84 | node_id.0, child_id.0 |
85 | ); |
86 | } |
87 | orphans.remove(child_id); |
88 | let parent_and_index = ParentAndIndex(node_id, child_index); |
89 | if let Some(child_state) = self.nodes.get_mut_cow(child_id) { |
90 | if child_state.parent_and_index != Some(parent_and_index) { |
91 | child_state.parent_and_index = Some(parent_and_index); |
92 | } |
93 | } else if let Some(child_data) = pending_nodes.remove(child_id) { |
94 | add_node( |
95 | &mut self.nodes, |
96 | &mut changes, |
97 | Some(parent_and_index), |
98 | *child_id, |
99 | child_data, |
100 | ); |
101 | } else { |
102 | pending_children.insert(*child_id, parent_and_index); |
103 | } |
104 | seen_child_ids.insert(*child_id); |
105 | } |
106 | |
107 | if let Some(node_state) = self.nodes.get_mut_cow(&node_id) { |
108 | if node_id == root { |
109 | node_state.parent_and_index = None; |
110 | } |
111 | for child_id in node_state.data.children().iter() { |
112 | if !seen_child_ids.contains(child_id) { |
113 | orphans.insert(*child_id); |
114 | } |
115 | } |
116 | if *node_state.data != node_data { |
117 | node_state.data = Arc::new(node_data); |
118 | if let Some(changes) = &mut changes { |
119 | changes.updated_node_ids.insert(node_id); |
120 | } |
121 | } |
122 | } else if let Some(parent_and_index) = pending_children.remove(&node_id) { |
123 | add_node( |
124 | &mut self.nodes, |
125 | &mut changes, |
126 | Some(parent_and_index), |
127 | node_id, |
128 | node_data, |
129 | ); |
130 | } else if node_id == root { |
131 | add_node(&mut self.nodes, &mut changes, None, node_id, node_data); |
132 | } else { |
133 | pending_nodes.insert(node_id, node_data); |
134 | } |
135 | } |
136 | |
137 | if !pending_nodes.is_empty() { |
138 | panic!("TreeUpdate includes {} nodes which are neither in the current tree nor a child of another node from the update: {}" , pending_nodes.len(), short_node_list(pending_nodes.keys())); |
139 | } |
140 | if !pending_children.is_empty() { |
141 | panic!("TreeUpdate's nodes include {} children ids which are neither in the current tree nor the id of another node from the update: {}" , pending_children.len(), short_node_list(pending_children.keys())); |
142 | } |
143 | |
144 | self.focus = update.focus; |
145 | self.is_host_focused = is_host_focused; |
146 | |
147 | if !orphans.is_empty() { |
148 | let mut to_remove = Vec::new(); |
149 | |
150 | fn traverse_orphan( |
151 | nodes: &ChunkMap<NodeId, NodeState>, |
152 | to_remove: &mut Vec<NodeId>, |
153 | id: NodeId, |
154 | ) { |
155 | to_remove.push(id); |
156 | let node = nodes.get(&id).unwrap(); |
157 | for child_id in node.data.children().iter() { |
158 | traverse_orphan(nodes, to_remove, *child_id); |
159 | } |
160 | } |
161 | |
162 | for id in orphans { |
163 | traverse_orphan(&self.nodes, &mut to_remove, id); |
164 | } |
165 | |
166 | for id in to_remove { |
167 | if self.nodes.remove_cow(&id).is_some() { |
168 | if let Some(changes) = &mut changes { |
169 | changes.removed_node_ids.insert(id); |
170 | } |
171 | } |
172 | } |
173 | } |
174 | |
175 | self.validate_global(); |
176 | } |
177 | |
178 | fn update_host_focus_state( |
179 | &mut self, |
180 | is_host_focused: bool, |
181 | changes: Option<&mut InternalChanges>, |
182 | ) { |
183 | let update = TreeUpdate { |
184 | nodes: vec![], |
185 | tree: None, |
186 | focus: self.focus, |
187 | }; |
188 | self.update(update, is_host_focused, changes); |
189 | } |
190 | |
191 | pub fn has_node(&self, id: NodeId) -> bool { |
192 | self.nodes.get(&id).is_some() |
193 | } |
194 | |
195 | pub fn node_by_id(&self, id: NodeId) -> Option<Node<'_>> { |
196 | self.nodes.get(&id).map(|node_state| Node { |
197 | tree_state: self, |
198 | id, |
199 | state: node_state, |
200 | }) |
201 | } |
202 | |
203 | pub fn root_id(&self) -> NodeId { |
204 | self.data.root |
205 | } |
206 | |
207 | pub fn root(&self) -> Node<'_> { |
208 | self.node_by_id(self.root_id()).unwrap() |
209 | } |
210 | |
211 | pub fn is_host_focused(&self) -> bool { |
212 | self.is_host_focused |
213 | } |
214 | |
215 | pub fn focus_id(&self) -> Option<NodeId> { |
216 | self.is_host_focused.then_some(self.focus) |
217 | } |
218 | |
219 | pub fn focus(&self) -> Option<Node<'_>> { |
220 | self.focus_id().map(|id| self.node_by_id(id).unwrap()) |
221 | } |
222 | |
223 | pub fn app_name(&self) -> Option<String> { |
224 | self.data.app_name.clone() |
225 | } |
226 | |
227 | pub fn toolkit_name(&self) -> Option<String> { |
228 | self.data.toolkit_name.clone() |
229 | } |
230 | |
231 | pub fn toolkit_version(&self) -> Option<String> { |
232 | self.data.toolkit_version.clone() |
233 | } |
234 | } |
235 | |
236 | pub trait ChangeHandler { |
237 | fn node_added(&mut self, node: &Node); |
238 | fn node_updated(&mut self, old_node: &Node, new_node: &Node); |
239 | fn focus_moved(&mut self, old_node: Option<&Node>, new_node: Option<&Node>); |
240 | fn node_removed(&mut self, node: &Node); |
241 | } |
242 | |
243 | pub struct Tree { |
244 | state: State, |
245 | } |
246 | |
247 | impl Tree { |
248 | pub fn new(mut initial_state: TreeUpdate, is_host_focused: bool) -> Self { |
249 | let Some(tree) = initial_state.tree.take() else { |
250 | panic!("Tried to initialize the accessibility tree without a root tree. TreeUpdate::tree must be Some." ); |
251 | }; |
252 | let mut state = State { |
253 | nodes: ChunkMap::new(), |
254 | data: tree, |
255 | focus: initial_state.focus, |
256 | is_host_focused, |
257 | }; |
258 | state.update(initial_state, is_host_focused, None); |
259 | Self { state } |
260 | } |
261 | |
262 | pub fn update(&mut self, update: TreeUpdate) { |
263 | self.state.update(update, self.state.is_host_focused, None); |
264 | } |
265 | |
266 | pub fn update_and_process_changes( |
267 | &mut self, |
268 | update: TreeUpdate, |
269 | handler: &mut impl ChangeHandler, |
270 | ) { |
271 | let mut changes = InternalChanges::default(); |
272 | let old_state = self.state.clone(); |
273 | self.state |
274 | .update(update, self.state.is_host_focused, Some(&mut changes)); |
275 | self.process_changes(old_state, changes, handler); |
276 | } |
277 | |
278 | pub fn update_host_focus_state(&mut self, is_host_focused: bool) { |
279 | self.state.update_host_focus_state(is_host_focused, None); |
280 | } |
281 | |
282 | pub fn update_host_focus_state_and_process_changes( |
283 | &mut self, |
284 | is_host_focused: bool, |
285 | handler: &mut impl ChangeHandler, |
286 | ) { |
287 | let mut changes = InternalChanges::default(); |
288 | let old_state = self.state.clone(); |
289 | self.state |
290 | .update_host_focus_state(is_host_focused, Some(&mut changes)); |
291 | self.process_changes(old_state, changes, handler); |
292 | } |
293 | |
294 | fn process_changes( |
295 | &self, |
296 | old_state: State, |
297 | changes: InternalChanges, |
298 | handler: &mut impl ChangeHandler, |
299 | ) { |
300 | for id in &changes.added_node_ids { |
301 | let node = self.state.node_by_id(*id).unwrap(); |
302 | handler.node_added(&node); |
303 | } |
304 | for id in &changes.updated_node_ids { |
305 | let old_node = old_state.node_by_id(*id).unwrap(); |
306 | let new_node = self.state.node_by_id(*id).unwrap(); |
307 | handler.node_updated(&old_node, &new_node); |
308 | } |
309 | if old_state.focus_id() != self.state.focus_id() { |
310 | let old_node = old_state.focus(); |
311 | if let Some(old_node) = &old_node { |
312 | let id = old_node.id(); |
313 | if !changes.updated_node_ids.contains(&id) |
314 | && !changes.removed_node_ids.contains(&id) |
315 | { |
316 | if let Some(old_node_new_version) = self.state.node_by_id(id) { |
317 | handler.node_updated(old_node, &old_node_new_version); |
318 | } |
319 | } |
320 | } |
321 | let new_node = self.state.focus(); |
322 | if let Some(new_node) = &new_node { |
323 | let id = new_node.id(); |
324 | if !changes.added_node_ids.contains(&id) && !changes.updated_node_ids.contains(&id) |
325 | { |
326 | if let Some(new_node_old_version) = old_state.node_by_id(id) { |
327 | handler.node_updated(&new_node_old_version, new_node); |
328 | } |
329 | } |
330 | } |
331 | handler.focus_moved(old_node.as_ref(), new_node.as_ref()); |
332 | } |
333 | for id in &changes.removed_node_ids { |
334 | let node = old_state.node_by_id(*id).unwrap(); |
335 | handler.node_removed(&node); |
336 | } |
337 | } |
338 | |
339 | pub fn state(&self) -> &State { |
340 | &self.state |
341 | } |
342 | } |
343 | |
344 | fn short_node_list<'a>(nodes: impl ExactSizeIterator<Item = &'a NodeId>) -> String { |
345 | if nodes.len() > 10 { |
346 | format!( |
347 | "[ {} ...]" , |
348 | nodes |
349 | .take(10) |
350 | .map(|id| format!("# {}" , id.0)) |
351 | .collect::<Vec<_>>() |
352 | .join(", " ), |
353 | ) |
354 | } else { |
355 | format!( |
356 | "[ {}]" , |
357 | nodes |
358 | .map(|id| format!("# {}" , id.0)) |
359 | .collect::<Vec<_>>() |
360 | .join(", " ), |
361 | ) |
362 | } |
363 | } |
364 | |
365 | #[cfg (test)] |
366 | mod tests { |
367 | use accesskit::{Node, NodeId, Role, Tree, TreeUpdate}; |
368 | use alloc::vec; |
369 | |
370 | #[test ] |
371 | fn init_tree_with_root_node() { |
372 | let update = TreeUpdate { |
373 | nodes: vec![(NodeId(0), Node::new(Role::Window))], |
374 | tree: Some(Tree::new(NodeId(0))), |
375 | focus: NodeId(0), |
376 | }; |
377 | let tree = super::Tree::new(update, false); |
378 | assert_eq!(NodeId(0), tree.state().root().id()); |
379 | assert_eq!(Role::Window, tree.state().root().role()); |
380 | assert!(tree.state().root().parent().is_none()); |
381 | } |
382 | |
383 | #[test ] |
384 | fn root_node_has_children() { |
385 | let update = TreeUpdate { |
386 | nodes: vec![ |
387 | (NodeId(0), { |
388 | let mut node = Node::new(Role::Window); |
389 | node.set_children(vec![NodeId(1), NodeId(2)]); |
390 | node |
391 | }), |
392 | (NodeId(1), Node::new(Role::Button)), |
393 | (NodeId(2), Node::new(Role::Button)), |
394 | ], |
395 | tree: Some(Tree::new(NodeId(0))), |
396 | focus: NodeId(0), |
397 | }; |
398 | let tree = super::Tree::new(update, false); |
399 | let state = tree.state(); |
400 | assert_eq!( |
401 | NodeId(0), |
402 | state.node_by_id(NodeId(1)).unwrap().parent().unwrap().id() |
403 | ); |
404 | assert_eq!( |
405 | NodeId(0), |
406 | state.node_by_id(NodeId(2)).unwrap().parent().unwrap().id() |
407 | ); |
408 | assert_eq!(2, state.root().children().count()); |
409 | } |
410 | |
411 | #[test ] |
412 | fn add_child_to_root_node() { |
413 | let root_node = Node::new(Role::Window); |
414 | let first_update = TreeUpdate { |
415 | nodes: vec![(NodeId(0), root_node.clone())], |
416 | tree: Some(Tree::new(NodeId(0))), |
417 | focus: NodeId(0), |
418 | }; |
419 | let mut tree = super::Tree::new(first_update, false); |
420 | assert_eq!(0, tree.state().root().children().count()); |
421 | let second_update = TreeUpdate { |
422 | nodes: vec![ |
423 | (NodeId(0), { |
424 | let mut node = root_node; |
425 | node.push_child(NodeId(1)); |
426 | node |
427 | }), |
428 | (NodeId(1), Node::new(Role::RootWebArea)), |
429 | ], |
430 | tree: None, |
431 | focus: NodeId(0), |
432 | }; |
433 | struct Handler { |
434 | got_new_child_node: bool, |
435 | got_updated_root_node: bool, |
436 | } |
437 | fn unexpected_change() { |
438 | panic!("expected only new child node and updated root node" ); |
439 | } |
440 | impl super::ChangeHandler for Handler { |
441 | fn node_added(&mut self, node: &crate::Node) { |
442 | if node.id() == NodeId(1) { |
443 | self.got_new_child_node = true; |
444 | return; |
445 | } |
446 | unexpected_change(); |
447 | } |
448 | fn node_updated(&mut self, old_node: &crate::Node, new_node: &crate::Node) { |
449 | if new_node.id() == NodeId(0) |
450 | && old_node.data().children().is_empty() |
451 | && new_node.data().children() == [NodeId(1)] |
452 | { |
453 | self.got_updated_root_node = true; |
454 | return; |
455 | } |
456 | unexpected_change(); |
457 | } |
458 | fn focus_moved( |
459 | &mut self, |
460 | _old_node: Option<&crate::Node>, |
461 | _new_node: Option<&crate::Node>, |
462 | ) { |
463 | unexpected_change(); |
464 | } |
465 | fn node_removed(&mut self, _node: &crate::Node) { |
466 | unexpected_change(); |
467 | } |
468 | } |
469 | let mut handler = Handler { |
470 | got_new_child_node: false, |
471 | got_updated_root_node: false, |
472 | }; |
473 | tree.update_and_process_changes(second_update, &mut handler); |
474 | assert!(handler.got_new_child_node); |
475 | assert!(handler.got_updated_root_node); |
476 | let state = tree.state(); |
477 | assert_eq!(1, state.root().children().count()); |
478 | assert_eq!(NodeId(1), state.root().children().next().unwrap().id()); |
479 | assert_eq!( |
480 | NodeId(0), |
481 | state.node_by_id(NodeId(1)).unwrap().parent().unwrap().id() |
482 | ); |
483 | } |
484 | |
485 | #[test ] |
486 | fn remove_child_from_root_node() { |
487 | let root_node = Node::new(Role::Window); |
488 | let first_update = TreeUpdate { |
489 | nodes: vec![ |
490 | (NodeId(0), { |
491 | let mut node = root_node.clone(); |
492 | node.push_child(NodeId(1)); |
493 | node |
494 | }), |
495 | (NodeId(1), Node::new(Role::RootWebArea)), |
496 | ], |
497 | tree: Some(Tree::new(NodeId(0))), |
498 | focus: NodeId(0), |
499 | }; |
500 | let mut tree = super::Tree::new(first_update, false); |
501 | assert_eq!(1, tree.state().root().children().count()); |
502 | let second_update = TreeUpdate { |
503 | nodes: vec![(NodeId(0), root_node)], |
504 | tree: None, |
505 | focus: NodeId(0), |
506 | }; |
507 | struct Handler { |
508 | got_updated_root_node: bool, |
509 | got_removed_child_node: bool, |
510 | } |
511 | fn unexpected_change() { |
512 | panic!("expected only removed child node and updated root node" ); |
513 | } |
514 | impl super::ChangeHandler for Handler { |
515 | fn node_added(&mut self, _node: &crate::Node) { |
516 | unexpected_change(); |
517 | } |
518 | fn node_updated(&mut self, old_node: &crate::Node, new_node: &crate::Node) { |
519 | if new_node.id() == NodeId(0) |
520 | && old_node.data().children() == [NodeId(1)] |
521 | && new_node.data().children().is_empty() |
522 | { |
523 | self.got_updated_root_node = true; |
524 | return; |
525 | } |
526 | unexpected_change(); |
527 | } |
528 | fn focus_moved( |
529 | &mut self, |
530 | _old_node: Option<&crate::Node>, |
531 | _new_node: Option<&crate::Node>, |
532 | ) { |
533 | unexpected_change(); |
534 | } |
535 | fn node_removed(&mut self, node: &crate::Node) { |
536 | if node.id() == NodeId(1) { |
537 | self.got_removed_child_node = true; |
538 | return; |
539 | } |
540 | unexpected_change(); |
541 | } |
542 | } |
543 | let mut handler = Handler { |
544 | got_updated_root_node: false, |
545 | got_removed_child_node: false, |
546 | }; |
547 | tree.update_and_process_changes(second_update, &mut handler); |
548 | assert!(handler.got_updated_root_node); |
549 | assert!(handler.got_removed_child_node); |
550 | assert_eq!(0, tree.state().root().children().count()); |
551 | assert!(tree.state().node_by_id(NodeId(1)).is_none()); |
552 | } |
553 | |
554 | #[test ] |
555 | fn move_focus_between_siblings() { |
556 | let first_update = TreeUpdate { |
557 | nodes: vec![ |
558 | (NodeId(0), { |
559 | let mut node = Node::new(Role::Window); |
560 | node.set_children(vec![NodeId(1), NodeId(2)]); |
561 | node |
562 | }), |
563 | (NodeId(1), Node::new(Role::Button)), |
564 | (NodeId(2), Node::new(Role::Button)), |
565 | ], |
566 | tree: Some(Tree::new(NodeId(0))), |
567 | focus: NodeId(1), |
568 | }; |
569 | let mut tree = super::Tree::new(first_update, true); |
570 | assert!(tree.state().node_by_id(NodeId(1)).unwrap().is_focused()); |
571 | let second_update = TreeUpdate { |
572 | nodes: vec![], |
573 | tree: None, |
574 | focus: NodeId(2), |
575 | }; |
576 | struct Handler { |
577 | got_old_focus_node_update: bool, |
578 | got_new_focus_node_update: bool, |
579 | got_focus_change: bool, |
580 | } |
581 | fn unexpected_change() { |
582 | panic!("expected only focus change" ); |
583 | } |
584 | impl super::ChangeHandler for Handler { |
585 | fn node_added(&mut self, _node: &crate::Node) { |
586 | unexpected_change(); |
587 | } |
588 | fn node_updated(&mut self, old_node: &crate::Node, new_node: &crate::Node) { |
589 | if old_node.id() == NodeId(1) |
590 | && new_node.id() == NodeId(1) |
591 | && old_node.is_focused() |
592 | && !new_node.is_focused() |
593 | { |
594 | self.got_old_focus_node_update = true; |
595 | return; |
596 | } |
597 | if old_node.id() == NodeId(2) |
598 | && new_node.id() == NodeId(2) |
599 | && !old_node.is_focused() |
600 | && new_node.is_focused() |
601 | { |
602 | self.got_new_focus_node_update = true; |
603 | return; |
604 | } |
605 | unexpected_change(); |
606 | } |
607 | fn focus_moved( |
608 | &mut self, |
609 | old_node: Option<&crate::Node>, |
610 | new_node: Option<&crate::Node>, |
611 | ) { |
612 | if let (Some(old_node), Some(new_node)) = (old_node, new_node) { |
613 | if old_node.id() == NodeId(1) && new_node.id() == NodeId(2) { |
614 | self.got_focus_change = true; |
615 | return; |
616 | } |
617 | } |
618 | unexpected_change(); |
619 | } |
620 | fn node_removed(&mut self, _node: &crate::Node) { |
621 | unexpected_change(); |
622 | } |
623 | } |
624 | let mut handler = Handler { |
625 | got_old_focus_node_update: false, |
626 | got_new_focus_node_update: false, |
627 | got_focus_change: false, |
628 | }; |
629 | tree.update_and_process_changes(second_update, &mut handler); |
630 | assert!(handler.got_old_focus_node_update); |
631 | assert!(handler.got_new_focus_node_update); |
632 | assert!(handler.got_focus_change); |
633 | assert!(tree.state().node_by_id(NodeId(2)).unwrap().is_focused()); |
634 | assert!(!tree.state().node_by_id(NodeId(1)).unwrap().is_focused()); |
635 | } |
636 | |
637 | #[test ] |
638 | fn update_node() { |
639 | let child_node = Node::new(Role::Button); |
640 | let first_update = TreeUpdate { |
641 | nodes: vec![ |
642 | (NodeId(0), { |
643 | let mut node = Node::new(Role::Window); |
644 | node.set_children(vec![NodeId(1)]); |
645 | node |
646 | }), |
647 | (NodeId(1), { |
648 | let mut node = child_node.clone(); |
649 | node.set_label("foo" ); |
650 | node |
651 | }), |
652 | ], |
653 | tree: Some(Tree::new(NodeId(0))), |
654 | focus: NodeId(0), |
655 | }; |
656 | let mut tree = super::Tree::new(first_update, false); |
657 | assert_eq!( |
658 | Some("foo" .into()), |
659 | tree.state().node_by_id(NodeId(1)).unwrap().label() |
660 | ); |
661 | let second_update = TreeUpdate { |
662 | nodes: vec![(NodeId(1), { |
663 | let mut node = child_node; |
664 | node.set_label("bar" ); |
665 | node |
666 | })], |
667 | tree: None, |
668 | focus: NodeId(0), |
669 | }; |
670 | struct Handler { |
671 | got_updated_child_node: bool, |
672 | } |
673 | fn unexpected_change() { |
674 | panic!("expected only updated child node" ); |
675 | } |
676 | impl super::ChangeHandler for Handler { |
677 | fn node_added(&mut self, _node: &crate::Node) { |
678 | unexpected_change(); |
679 | } |
680 | fn node_updated(&mut self, old_node: &crate::Node, new_node: &crate::Node) { |
681 | if new_node.id() == NodeId(1) |
682 | && old_node.label() == Some("foo" .into()) |
683 | && new_node.label() == Some("bar" .into()) |
684 | { |
685 | self.got_updated_child_node = true; |
686 | return; |
687 | } |
688 | unexpected_change(); |
689 | } |
690 | fn focus_moved( |
691 | &mut self, |
692 | _old_node: Option<&crate::Node>, |
693 | _new_node: Option<&crate::Node>, |
694 | ) { |
695 | unexpected_change(); |
696 | } |
697 | fn node_removed(&mut self, _node: &crate::Node) { |
698 | unexpected_change(); |
699 | } |
700 | } |
701 | let mut handler = Handler { |
702 | got_updated_child_node: false, |
703 | }; |
704 | tree.update_and_process_changes(second_update, &mut handler); |
705 | assert!(handler.got_updated_child_node); |
706 | assert_eq!( |
707 | Some("bar" .into()), |
708 | tree.state().node_by_id(NodeId(1)).unwrap().label() |
709 | ); |
710 | } |
711 | |
712 | // Verify that if an update consists entirely of node data and tree data |
713 | // that's the same as before, no changes are reported. This is useful |
714 | // for a provider that constructs a fresh tree every time, such as |
715 | // an immediate-mode GUI. |
716 | #[test ] |
717 | fn no_change_update() { |
718 | let update = TreeUpdate { |
719 | nodes: vec![ |
720 | (NodeId(0), { |
721 | let mut node = Node::new(Role::Window); |
722 | node.set_children(vec![NodeId(1)]); |
723 | node |
724 | }), |
725 | (NodeId(1), { |
726 | let mut node = Node::new(Role::Button); |
727 | node.set_label("foo" ); |
728 | node |
729 | }), |
730 | ], |
731 | tree: Some(Tree::new(NodeId(0))), |
732 | focus: NodeId(0), |
733 | }; |
734 | let mut tree = super::Tree::new(update.clone(), false); |
735 | struct Handler; |
736 | fn unexpected_change() { |
737 | panic!("expected no changes" ); |
738 | } |
739 | impl super::ChangeHandler for Handler { |
740 | fn node_added(&mut self, _node: &crate::Node) { |
741 | unexpected_change(); |
742 | } |
743 | fn node_updated(&mut self, _old_node: &crate::Node, _new_node: &crate::Node) { |
744 | unexpected_change(); |
745 | } |
746 | fn focus_moved( |
747 | &mut self, |
748 | _old_node: Option<&crate::Node>, |
749 | _new_node: Option<&crate::Node>, |
750 | ) { |
751 | unexpected_change(); |
752 | } |
753 | fn node_removed(&mut self, _node: &crate::Node) { |
754 | unexpected_change(); |
755 | } |
756 | } |
757 | let mut handler = Handler {}; |
758 | tree.update_and_process_changes(update, &mut handler); |
759 | } |
760 | } |
761 | |