| 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 | |