1use crate::generators::RecycleKey;
2use crate::items_source::{items_equal, ItemsChangedEventArgs, ItemsView};
3use crate::primitives::{
4 ItemSelectionEventTriggers, SelectedItemsList, SelectingItemsControl, TemplatedControlImpl,
5};
6use crate::{
7 Control, ControlImpl, ItemsControl, ItemsControlImpl, ItemsControlImplExt, SelectionChangedEventArgs, SelectionMode, TreeViewItem,
8};
9use ferroui_base::collections::{NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs};
10use ferroui_base::input::{
11 FocusChangedEventArgs, ICustomKeyboardNavigation, InputElement, InputElementImpl, KeyEventArgs, KeyModifiers,
12 NavigationDirection, NavigationMethod, PointerEventArgs,
13};
14use ferroui_base::interactivity::{
15 IRoutedEventArgs, Interactive, InteractiveImpl, RoutedEventArgs, RoutedEventHandlerToken,
16};
17use ferroui_base::layout::LayoutableImpl;
18use ferroui_base::{
19 ferro_class, ferro_impl_classes, ferro_property, instantiate, BoxedValue, DirectProperty, FerroObject,
20 FerroObjectImpl, FerroProperty, Ref, StyledElementImpl, StyledProperty, Visual, VisualImpl,
21};
22use std::cell::{Cell, RefCell};
23use std::rc::Rc;
24
25#[repr(C)]
27pub struct TreeView {
28 base: ItemsControl,
29 selected_item: RefCell<Option<BoxedValue>>,
30 selected_items: RefCell<Option<SelectedItemsList>>,
31 selected_items_subscription: Cell<Option<u64>>,
32 syncing_selected_items: Cell<bool>,
33 suppress_selection_changed: Cell<bool>,
34 reported_selection: RefCell<Vec<Option<BoxedValue>>>,
35}
36
37ferro_class! {
38 TreeView: ItemsControl, virtuals TreeViewImpl: ItemsControlImpl {
39 fn should_trigger_selection(this, selectable: &Visual, event_args: &PointerEventArgs) -> bool;
43 fn should_trigger_selection_key(this, selectable: &Visual, event_args: &KeyEventArgs) -> bool;
46 fn update_selection_from_event(this, container: &Ref<Control>, event_args: &dyn IRoutedEventArgs) -> bool;
52 }
53}
54ferroui_base::ferro_class_info!(TreeView { new: TreeView::new });
55
56ferro_impl_classes!(
57 TreeView: StyledElementImpl,
58 VisualImpl,
59 LayoutableImpl,
60 InteractiveImpl,
61 TemplatedControlImpl
62);
63
64impl ControlImpl for TreeView {
65 fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
66 crate::automation::peers::TreeViewAutomationPeer::new(this).upcast()
67 }
68}
69
70ferroui_base::ferro_impl_classes!(TreeView: FerroObjectImpl);
71
72impl InputElementImpl for TreeView {
73 fn as_custom_keyboard_navigation(this: &Self) -> Option<&dyn ICustomKeyboardNavigation> {
74 Some(this)
75 }
76
77 fn on_got_focus(this: &Self, e: &FocusChangedEventArgs) {
78 if e.navigation_method == NavigationMethod::Directional {
79 e.set_handled(this.update_selection_from_event_source(
80 e.source(),
81 true,
82 e.key_modifiers.contains(KeyModifiers::SHIFT),
83 false,
84 false,
85 ));
86 }
87 }
88
89 fn on_key_down(this: &Self, e: &KeyEventArgs) {
90 let direction = e.key.to_navigation_direction(KeyModifiers::NONE);
91
92 if let (Some(direction), false) = (direction.filter(|direction| direction.is_directional()), e.handled()) {
93 if this.selected_item().is_some() {
94 let from = this.get_container_from_event_source(e.source());
95 let next = this.get_container_in_direction(from, direction, true);
96
97 if let Some(next) = next {
98 e.set_handled(next.focus_with(NavigationMethod::Directional, KeyModifiers::NONE));
99 }
100 } else {
101 this.set_selected_item(this.items_view().get_at(0));
102 }
103 }
104
105 if !e.handled() {
106 let keymap = this.get_platform_settings().map(|settings| settings.hotkey_configuration());
107 let select_all = keymap.is_some_and(|keymap| keymap.select_all.iter().any(|g| g.matches(Some(e))));
108
109 if this.selection_mode() == SelectionMode::MULTIPLE && select_all {
110 this.select_all();
111 e.set_handled(true);
112 }
113 }
114 }
115}
116
117impl ItemsControlImpl for TreeView {
118 fn create_container_for_item_override(
119 _this: &Self,
120 _item: &Option<BoxedValue>,
121 _index: i32,
122 _recycle_key: Option<RecycleKey>,
123 ) -> Ref<Control> {
124 TreeViewItem::new().upcast()
125 }
126
127 fn needs_container_override(this: &Self, item: &Option<BoxedValue>, _index: i32) -> (bool, Option<RecycleKey>) {
128 this.needs_container::<TreeViewItem>(item)
129 }
130
131 fn container_for_item_prepared_override(
132 this: &Self,
133 container: &Ref<Control>,
134 item: &Option<BoxedValue>,
135 index: i32,
136 ) {
137 Self::parent_container_for_item_prepared_override(this, container, item, index);
138
139 if container.is_set(SelectingItemsControl::is_selected_property().as_property()) {
143 let container_is_selected = SelectingItemsControl::get_is_selected(container);
148 this.update_selection_from_container(container, container_is_selected, false, true, false);
149 }
150
151 this.mark_container_selected(container, this.selected_items().contains(item));
155
156 if this.auto_scroll_to_selected_item() && items_equal(&this.selected_item(), item) {
159 container.bring_into_view();
160 }
161 }
162
163 fn on_items_view_collection_changed(this: &Self, e: &ItemsChangedEventArgs<'_>) {
164 Self::parent_on_items_view_collection_changed(this, e);
165
166 match e.action {
167 NotifyCollectionChangedAction::Remove | NotifyCollectionChangedAction::Replace => {
168 for i in e.old_items {
169 this.selected_items().remove(&i);
170 }
171 }
172 NotifyCollectionChangedAction::Reset => this.selected_items().clear(),
173 _ => {}
174 }
175 }
176}
177
178impl TreeViewImpl for TreeView {
179 fn should_trigger_selection(_this: &Self, selectable: &Visual, event_args: &PointerEventArgs) -> bool {
180 ItemSelectionEventTriggers::should_trigger_selection(selectable, event_args)
181 }
182
183 fn should_trigger_selection_key(_this: &Self, selectable: &Visual, event_args: &KeyEventArgs) -> bool {
184 ItemSelectionEventTriggers::should_trigger_selection_key(selectable, event_args)
185 }
186
187 fn update_selection_from_event(this: &Self, container: &Ref<Control>, event_args: &dyn IRoutedEventArgs) -> bool {
188 let args = event_args.as_routed_event_args();
189
190 if args.handled() {
191 return false;
192 }
193
194 let pointer_event = event_args.downcast_ref::<PointerEventArgs>();
195 let key_event = event_args.downcast_ref::<KeyEventArgs>();
196
197 let triggers = match (pointer_event, key_event) {
198 (Some(pointer_event), _) => this.should_trigger_selection(container, pointer_event),
199 (None, Some(key_event)) => this.should_trigger_selection_key(container, key_event),
200 (None, None) => false,
201 };
202
203 if !triggers {
204 return false;
205 }
206
207 this.update_selection_from_container(
208 container,
209 true,
210 ItemSelectionEventTriggers::has_range_selection_modifier(container, event_args),
211 ItemSelectionEventTriggers::has_toggle_selection_modifier(container, event_args),
212 pointer_event.is_some_and(|e| e.properties().is_right_button_pressed),
213 );
214
215 if pointer_event.is_some() {
216 crate::platform::PlatformFeedbackExtensions::perform_feedback(
217 &**container as &InputElement,
218 crate::platform::FeedbackAction::click(),
219 );
220 }
221
222 args.set_handled(true);
223 true
224 }
225}
226
227impl ICustomKeyboardNavigation for TreeView {
228 fn get_next(&self, element: &Ref<InputElement>, direction: NavigationDirection) -> (bool, Option<Ref<InputElement>>) {
229 if direction == NavigationDirection::Next || direction == NavigationDirection::Previous {
230 if !self.is_visual_ancestor_of(element) {
231 let selected_item = self.selected_item.borrow().clone();
232 let result = match selected_item {
233 Some(_) => self.tree_container_from_item(&selected_item),
234 None => self.container_from_index(0),
235 };
236
237 return (result.is_some(), result.map(Ref::upcast));
239 }
240
241 return (true, None);
242 }
243
244 (false, None)
245 }
246}
247
248ferroui_base::ferro_properties! { impl TreeView {
249 ferro_property!(
250 pub fn auto_scroll_to_selected_item_property() -> StyledProperty<bool> {
252 SelectingItemsControl::auto_scroll_to_selected_item_property().add_owner::<TreeView>()
253 }
254 );
255
256 ferro_property!(
257 pub fn selected_item_property() -> DirectProperty<TreeView, Option<BoxedValue>> {
259 SelectingItemsControl::selected_item_property().add_owner::<TreeView>(
260 |o| o.selected_item(),
261 Some(|o, v| o.set_selected_item(v)),
262 None,
263 )
264 }
265 );
266
267 ferro_property!(
268 pub fn selected_items_property() -> DirectProperty<TreeView, Option<SelectedItemsList>> {
270 FerroProperty::register_direct::<TreeView, _>(
271 "SelectedItems",
272 |o| Some(o.selected_items()),
273 Some(|o, v| o.set_selected_items(v)),
274 None,
275 )
276 }
277 );
278
279 ferro_property!(
280 pub fn selection_mode_property() -> StyledProperty<SelectionMode> {
282 SelectingItemsControl::selection_mode_property().add_owner::<TreeView>()
283 }
284 );
285} }
286
287impl TreeView {
288 fn static_constructor() {
290 SelectingItemsControl::is_selected_changed_event()
291 .add_class_handler::<TreeView>(|x, e| x.container_selection_changed(e));
292 }
293
294 pub fn construct() -> Self {
296 Self {
297 base: ItemsControl::construct(),
298 selected_item: RefCell::new(None),
299 selected_items: RefCell::new(None),
300 selected_items_subscription: Cell::new(None),
301 syncing_selected_items: Cell::new(false),
302 suppress_selection_changed: Cell::new(false),
303 reported_selection: RefCell::new(Vec::new()),
304 }
305 }
306
307 pub fn new() -> Ref<Self> {
308 instantiate(Self::construct())
309 }
310
311 pub fn selection_changed(
313 &self,
314 handler: impl Fn(&Interactive, &SelectionChangedEventArgs) + 'static,
315 ) -> RoutedEventHandlerToken {
316 self.add_handler(SelectingItemsControl::selection_changed_event(), handler)
317 }
318
319 pub fn auto_scroll_to_selected_item(&self) -> bool {
326 self.get_value(Self::auto_scroll_to_selected_item_property())
327 }
328
329 pub fn set_auto_scroll_to_selected_item(&self, value: bool) {
330 self.set_value(Self::auto_scroll_to_selected_item_property(), value)
331 }
332
333 pub fn selection_mode(&self) -> SelectionMode {
335 self.get_value(Self::selection_mode_property())
336 }
337
338 pub fn set_selection_mode(&self, value: SelectionMode) {
339 self.set_value(Self::selection_mode_property(), value)
340 }
341
342 pub fn selected_item(&self) -> Option<BoxedValue> {
347 self.selected_item.borrow().clone()
348 }
349
350 pub fn set_selected_item(&self, value: Option<BoxedValue>) {
351 let selected_items = self.selected_items();
352
353 self.set_and_raise(Self::selected_item_property(), &self.selected_item, value.clone());
354
355 if value.is_some() {
356 if selected_items.count() != 1 || !items_equal(&selected_items.get(0), &value) {
357 self.select_single_item(value);
358 }
359 } else if self.selected_items().count() > 0 {
360 self.selected_items().clear();
361 }
362 }
363
364 pub fn selected_items(&self) -> SelectedItemsList {
366 let existing = self.selected_items.borrow().clone();
367 match existing {
368 Some(selected_items) => selected_items,
369 None => {
370 let selected_items = SelectedItemsList::new();
371 *self.selected_items.borrow_mut() = Some(selected_items.clone());
372 self.subscribe_to_selected_items();
373 selected_items
374 }
375 }
376 }
377
378 pub fn set_selected_items(&self, value: Option<SelectedItemsList>) {
379 self.unsubscribe_from_selected_items();
382 *self.selected_items.borrow_mut() = Some(value.unwrap_or_default());
383 self.subscribe_to_selected_items();
384 }
385
386 pub fn expand_sub_tree(&self, item: &TreeViewItem) {
389 item.set_is_expanded(true);
390
391 if item.items_panel_root().is_none() {
392 if let Some(layout_manager) = self.get_layout_manager() {
393 layout_manager.execute_layout_pass();
394 }
395 }
396
397 if let Some(panel) = item.items_panel_root() {
398 for child in panel.children().snapshot().iter() {
399 if let Some(tree_view_item) = child.downcast_ref::<TreeViewItem>() {
400 self.expand_sub_tree(tree_view_item);
401 }
402 }
403 }
404 }
405
406 pub fn collapse_sub_tree(&self, item: &TreeViewItem) {
409 item.set_is_expanded(false);
410
411 if let Some(panel) = item.items_panel_root() {
412 for child in panel.children().snapshot().iter() {
413 if let Some(tree_view_item) = child.downcast_ref::<TreeViewItem>() {
414 self.collapse_sub_tree(tree_view_item);
415 }
416 }
417 }
418 }
419
420 pub fn select_all(&self) {
425 fn add_items(items_control: &ItemsControl, all_items: &mut Vec<Option<BoxedValue>>) {
426 all_items.extend(items_control.items_view().iter());
427
428 for child in items_control.get_realized_containers() {
429 if let Some(child_items_control) = child.downcast_ref::<ItemsControl>() {
430 add_items(child_items_control, all_items);
431 }
432 }
433 }
434
435 let mut all_items = Vec::new();
436 add_items(self, &mut all_items);
437
438 self.suppress_selection_changed.set(true);
439 Self::synchronize_items(&self.selected_items(), &all_items);
440 self.suppress_selection_changed.set(false);
441
442 self.raise_pending_selection_changed();
443 }
444
445 pub fn unselect_all(&self) {
447 self.selected_items().clear();
448 }
449
450 pub fn get_realized_tree_containers(&self) -> Vec<Ref<Control>> {
452 fn get_realized_containers(items_control: &ItemsControl, result: &mut Vec<Ref<Control>>) {
453 for container in items_control.get_realized_containers() {
454 result.push(container.clone());
455 if let Some(items_control_container) = container.downcast_ref::<ItemsControl>() {
456 get_realized_containers(items_control_container, result);
457 }
458 }
459 }
460
461 let mut result = Vec::new();
462 get_realized_containers(self, &mut result);
463 result
464 }
465
466 pub fn tree_container_from_item(&self, item: &Option<BoxedValue>) -> Option<Ref<Control>> {
469 fn tree_container_from_item(items_control: &ItemsControl, item: &Option<BoxedValue>) -> Option<Ref<Control>> {
470 if let Some(container) = items_control.container_from_item(item) {
471 return Some(container);
472 }
473
474 for child in items_control.get_realized_containers() {
475 if let Some(child_items_control) = child.downcast_ref::<ItemsControl>() {
476 if let Some(child_container) = tree_container_from_item(child_items_control, item) {
477 return Some(child_container);
478 }
479 }
480 }
481
482 None
483 }
484
485 tree_container_from_item(self, item)
486 }
487
488 pub fn tree_item_from_container(&self, container: &Ref<Control>) -> Option<BoxedValue> {
491 fn tree_item_from_container(items_control: &ItemsControl, container: &Ref<Control>) -> Option<BoxedValue> {
492 if let Some(item) = items_control.item_from_container(container) {
493 return Some(item);
494 }
495
496 for child in items_control.get_realized_containers() {
497 if let Some(child_items_control) = child.downcast_ref::<ItemsControl>() {
498 if let Some(child_item) = tree_item_from_container(child_items_control, container) {
499 return Some(child_item);
500 }
501 }
502 }
503
504 None
505 }
506
507 tree_item_from_container(self, container)
508 }
509
510 fn subscribe_to_selected_items(&self) {
512 let selected_items = self.selected_items.borrow().clone();
513
514 if let Some(selected_items) = selected_items {
515 let weak = self.to_ref().downgrade();
516 let token = selected_items.add_collection_changed(Rc::new(
517 move |e: &NotifyCollectionChangedEventArgs<'_, Option<BoxedValue>>| {
518 if let Some(this) = weak.upgrade() {
519 this.selected_items_collection_changed(&e.into());
520 }
521 },
522 ));
523 self.selected_items_subscription.set(Some(token));
524 }
525
526 self.selected_items_collection_changed(&ItemsChangedEventArgs::RESET);
527 }
528
529 fn select_single_item(&self, item: Option<BoxedValue>) {
530 let old_value = self.selected_item.borrow().clone();
531 self.syncing_selected_items.set(true);
532 self.suppress_selection_changed.set(true);
533
534 self.selected_items().clear();
535 *self.selected_item.borrow_mut() = item.clone();
536 self.selected_items().add(item);
537
538 self.syncing_selected_items.set(false);
539 self.suppress_selection_changed.set(false);
540
541 self.raise_pending_selection_changed();
542
543 let new_value = self.selected_item.borrow().clone();
544 self.raise_direct_property_changed(Self::selected_item_property(), &old_value, &new_value);
545 }
546
547 fn selected_items_collection_changed(&self, e: &ItemsChangedEventArgs<'_>) {
550 let mut added: Option<Vec<Option<BoxedValue>>> = None;
551 let mut removed: Option<Vec<Option<BoxedValue>>> = None;
552
553 match e.action {
554 NotifyCollectionChangedAction::Add => {
555 self.selected_items_added(e.new_items);
556
557 let selected_item = self.selected_item();
558
559 if self.auto_scroll_to_selected_item()
560 && selected_item.is_some()
561 && items_equal(&e.new_items.get(0), &selected_item)
562 {
563 if let Some(container) = self.tree_container_from_item(&selected_item) {
564 container.bring_into_view();
565 }
566 }
567
568 added = Some(e.new_items.to_vec());
569 }
570 NotifyCollectionChangedAction::Remove => {
571 if !self.syncing_selected_items.get() {
572 if self.selected_items().count() == 0 {
573 self.set_selected_item(None);
574 } else {
575 let selected_item = self.selected_item.borrow().clone();
576 let selected_index = self.selected_items().index_of(&selected_item);
577
578 if selected_index.is_none() {
579 let old = selected_item;
580 let new = self.selected_items().get(0);
581 *self.selected_item.borrow_mut() = new.clone();
582
583 self.raise_direct_property_changed(Self::selected_item_property(), &old, &new);
584 }
585 }
586 }
587
588 for item in e.old_items {
589 self.mark_item_selected(&item, false);
590 }
591
592 removed = Some(e.old_items.to_vec());
593 }
594 NotifyCollectionChangedAction::Reset => {
595 for container in self.get_realized_tree_containers() {
596 self.mark_container_selected(&container, false);
597 }
598
599 let (reset_removed, reset_added) = self.get_selection_delta();
601 removed = Some(reset_removed);
602 added = Some(reset_added);
603
604 if self.selected_items().count() > 0 {
605 let selected_items = self.selected_items().to_vec();
606 self.selected_items_added(ItemsView::from_slice(&selected_items));
607 } else if !self.syncing_selected_items.get() {
608 self.set_selected_item(None);
609 }
610 }
611 NotifyCollectionChangedAction::Replace => {
612 for item in e.old_items {
613 self.mark_item_selected(&item, false);
614 }
615
616 for item in e.new_items {
617 self.mark_item_selected(&item, true);
618 }
619
620 let first = self.selected_items().get(0);
621 if !items_equal(&self.selected_item(), &first) && !self.syncing_selected_items.get() {
622 let old_item = self.selected_item();
623 *self.selected_item.borrow_mut() = first.clone();
624 self.raise_direct_property_changed(Self::selected_item_property(), &old_item, &first);
625 }
626
627 added = Some(e.new_items.to_vec());
628 removed = Some(e.old_items.to_vec());
629 }
630 _ => {}
631 }
632
633 if !self.suppress_selection_changed.get() {
635 self.raise_selection_changed(removed, added);
636 }
637 }
638
639 fn get_selection_delta(&self) -> (Vec<Option<BoxedValue>>, Vec<Option<BoxedValue>>) {
642 let selected: Vec<Option<BoxedValue>> =
643 self.selected_items().to_vec().into_iter().filter(Option::is_some).collect();
644 let reported = self.reported_selection.borrow();
645 let contains = |list: &[Option<BoxedValue>], item: &Option<BoxedValue>| list.iter().any(|x| items_equal(x, item));
646
647 let removed = reported.iter().filter(|x| !contains(&selected[..], *x)).cloned().collect();
648 let added = selected.iter().filter(|x| !contains(&reported[..], *x)).cloned().collect();
649 (removed, added)
650 }
651
652 fn raise_pending_selection_changed(&self) {
655 let (removed, added) = self.get_selection_delta();
656 self.raise_selection_changed(Some(removed), Some(added));
657 }
658
659 fn raise_selection_changed(&self, removed: Option<Vec<Option<BoxedValue>>>, added: Option<Vec<Option<BoxedValue>>>) {
660 let reported: Vec<Option<BoxedValue>> =
661 self.selected_items().to_vec().into_iter().filter(Option::is_some).collect();
662 *self.reported_selection.borrow_mut() = reported;
663
664 if added.as_ref().is_some_and(|added| !added.is_empty())
665 || removed.as_ref().is_some_and(|removed| !removed.is_empty())
666 {
667 self.raise_event(&SelectionChangedEventArgs::new(
668 Some(SelectingItemsControl::selection_changed_event()),
669 removed.unwrap_or_default(),
670 added.unwrap_or_default(),
671 ));
672 }
673 }
674
675 fn mark_item_selected(&self, item: &Option<BoxedValue>, selected: bool) {
676 if let Some(container) = self.tree_container_from_item(item) {
677 self.mark_container_selected(&container, selected);
678 }
679 }
680
681 fn selected_items_added(&self, items: ItemsView<'_>) {
682 if items.is_empty() {
683 return;
684 }
685
686 for item in items {
687 self.mark_item_selected(&item, true);
688 }
689
690 if self.selected_item().is_none() && !self.syncing_selected_items.get() {
691 self.set_and_raise(Self::selected_item_property(), &self.selected_item, items.get(0));
692 }
693 }
694
695 fn unsubscribe_from_selected_items(&self) {
698 let selected_items = self.selected_items.borrow().clone();
699
700 if let (Some(selected_items), Some(token)) = (selected_items, self.selected_items_subscription.take()) {
701 selected_items.remove_collection_changed(token);
702 }
703 }
704
705 fn get_container_in_direction(
706 &self,
707 from: Option<Ref<TreeViewItem>>,
708 direction: NavigationDirection,
709 into_children: bool,
710 ) -> Option<Ref<TreeViewItem>> {
711 let from = from?;
714 let parent_item = from.parent().and_then(|parent| parent.cast::<TreeViewItem>());
715 let parent: Ref<ItemsControl> = match &parent_item {
716 Some(parent_item) => parent_item.clone().upcast(),
717 None => from.parent().and_then(|parent| parent.cast::<TreeView>())?.upcast(),
718 };
719
720 let index = parent.index_from_container(&from.clone().upcast());
721 let as_item = |container: Option<Ref<Control>>| container.and_then(|container| container.cast::<TreeViewItem>());
722
723 match direction {
724 NavigationDirection::Up => {
725 if index > 0 {
726 let previous = as_item(parent.container_from_index(index - 1))?;
727 if previous.is_expanded() && previous.item_count() > 0 {
728 as_item(previous.container_from_index(previous.item_count() - 1))
729 } else {
730 Some(previous)
731 }
732 } else {
733 parent_item
734 }
735 }
736 NavigationDirection::Down | NavigationDirection::Right => {
737 if from.is_expanded() && into_children && from.item_count() > 0 {
738 as_item(from.container_from_index(0))
739 } else if index < parent.item_count() - 1 {
740 as_item(parent.container_from_index(index + 1))
741 } else if let Some(parent_item) = parent_item {
742 self.get_container_in_direction(Some(parent_item), direction, false)
743 } else {
744 None
745 }
746 }
747 _ => None,
748 }
749 }
750
751 pub fn update_selection_from_container(
759 &self,
760 container: &Ref<Control>,
761 select: bool,
762 range_modifier: bool,
763 toggle_modifier: bool,
764 right_button: bool,
765 ) {
766 let Some(item) = self.tree_item_from_container(container) else { return };
767 let item = Some(item);
768
769 let mut selected_container = None;
770
771 if self.selected_item().is_some() {
772 selected_container = self.tree_container_from_item(&self.selected_item());
773 }
774
775 let mode = self.selection_mode();
776 let toggle = toggle_modifier || mode.contains(SelectionMode::TOGGLE);
777 let multi = mode.contains(SelectionMode::MULTIPLE);
778 let range = multi && range_modifier && selected_container.is_some();
779
780 if !select {
781 self.selected_items().remove(&item);
782 } else if right_button {
783 if !self.selected_items().contains(&item) {
784 self.select_single_item(item);
785 }
786 } else if !toggle && !range {
787 self.select_single_item(item);
788 } else if multi && range {
789 Self::synchronize_items(
790 &self.selected_items(),
791 &self.get_items_in_range(
792 selected_container.and_then(|container| container.cast::<TreeViewItem>()),
793 container.clone().cast::<TreeViewItem>(),
794 ),
795 );
796 } else {
797 let i = self.selected_items().index_of(&item);
798
799 if i.is_some() {
800 self.selected_items().remove(&item);
801 } else if multi {
802 self.selected_items().add(item);
803 } else {
804 self.set_selected_item(item);
805 }
806 }
807 }
808
809 fn find_first_node(tree_view: &TreeView, node_a: &Ref<TreeViewItem>, node_b: &Ref<TreeViewItem>) -> Option<Ref<TreeViewItem>> {
811 Self::find_in_containers(tree_view, node_a, node_b)
812 }
813
814 fn find_in_containers(
815 items_control: &ItemsControl,
816 node_a: &Ref<TreeViewItem>,
817 node_b: &Ref<TreeViewItem>,
818 ) -> Option<Ref<TreeViewItem>> {
819 for container in items_control.get_realized_containers() {
820 let node = Self::find_first_node_in(container.cast::<TreeViewItem>(), node_a, node_b);
821
822 if node.is_some() {
823 return node;
824 }
825 }
826
827 None
828 }
829
830 fn find_first_node_in(
831 node: Option<Ref<TreeViewItem>>,
832 node_a: &Ref<TreeViewItem>,
833 node_b: &Ref<TreeViewItem>,
834 ) -> Option<Ref<TreeViewItem>> {
835 let node = node?;
836
837 if node == *node_a {
838 return Some(node_a.clone());
839 }
840
841 if node == *node_b {
842 return Some(node_b.clone());
843 }
844
845 Self::find_in_containers(&node, node_a, node_b)
846 }
847
848 fn get_items_in_range(
851 &self,
852 from: Option<Ref<TreeViewItem>>,
853 to: Option<Ref<TreeViewItem>>,
854 ) -> Vec<Option<BoxedValue>> {
855 let mut items = Vec::new();
856
857 let (Some(mut from), Some(mut to)) = (from, to) else { return items };
858
859 let Some(first_item) = Self::find_first_node(self, &from, &to) else { return items };
860
861 let mut was_reversed = false;
862
863 if first_item == to {
864 std::mem::swap(&mut from, &mut to);
865
866 was_reversed = true;
867 }
868
869 let mut node = Some(from);
870
871 while let Some(current) = node.take() {
872 if current == to {
873 break;
874 }
875
876 let item = self.tree_item_from_container(¤t.clone().upcast());
877
878 if item.is_some() {
879 items.push(item);
880 }
881
882 node = self.get_container_in_direction(Some(current), NavigationDirection::Down, true);
883 }
884
885 let to_item = self.tree_item_from_container(&to.upcast());
886
887 if to_item.is_some() {
888 items.push(to_item);
889 }
890
891 if was_reversed {
892 items.reverse();
893 }
894
895 items
896 }
897
898 pub fn update_selection_from_event_source(
908 &self,
909 event_source: Option<Ref<FerroObject>>,
910 select: bool,
911 range_modifier: bool,
912 toggle_modifier: bool,
913 right_button: bool,
914 ) -> bool {
915 let container = self.get_container_from_event_source(event_source);
916
917 if let Some(container) = container {
918 self.update_selection_from_container(
919 &container.upcast(),
920 select,
921 range_modifier,
922 toggle_modifier,
923 right_button,
924 );
925 return true;
926 }
927
928 false
929 }
930
931 pub fn get_container_from_event_source(&self, event_source: Option<Ref<FerroObject>>) -> Option<Ref<TreeViewItem>> {
936 let source = event_source.and_then(|source| source.cast::<Visual>())?;
937 let item = source.get_self_and_visual_ancestors().find_map(|visual| visual.cast::<TreeViewItem>())?;
938
939 if item.tree_view_owner().is_some_and(|owner| owner == self.to_ref()) {
940 Some(item)
941 } else {
942 None
943 }
944 }
945
946 fn container_selection_changed(&self, e: &RoutedEventArgs) {
948 let source = e.source();
949 let container = source.as_ref().and_then(|source| source.clone().cast::<TreeViewItem>());
950
951 if let Some(container) = container {
952 if container.tree_view_owner().is_some_and(|owner| owner == self.to_ref()) {
953 let container: Ref<Control> = container.upcast();
954
955 if let Some(item) = self.tree_item_from_container(&container) {
956 let item = Some(item);
957 let container_is_selected = SelectingItemsControl::get_is_selected(&container);
958 let our_is_selected = self.selected_items().contains(&item);
959
960 if container_is_selected != our_is_selected {
961 if container_is_selected {
962 self.selected_items().add(item);
963 } else {
964 self.selected_items().remove(&item);
965 }
966 }
967 }
968 }
969 }
970
971 if !e.is_source(self) {
972 e.set_handled(true);
973 }
974 }
975
976 fn mark_container_selected(&self, container: &Control, selected: bool) {
978 container.set_current_value(SelectingItemsControl::is_selected_property(), selected);
979 }
980
981 fn synchronize_items(items: &SelectedItemsList, desired: &[Option<BoxedValue>]) {
984 let items_count = items.count();
985 let desired_count = desired.len();
986
987 if items_count == 0 && desired_count > 0 {
988 for item in desired {
990 items.add(item.clone());
991 }
992 } else if items_count > 0 && desired_count == 0 {
993 items.clear();
995 } else {
996 fn except(first: &[Option<BoxedValue>], second: &[Option<BoxedValue>]) -> Vec<Option<BoxedValue>> {
998 let mut result: Vec<Option<BoxedValue>> = Vec::new();
999 for item in first {
1000 if !second.iter().any(|other| items_equal(other, item))
1001 && !result.iter().any(|other| items_equal(other, item))
1002 {
1003 result.push(item.clone());
1004 }
1005 }
1006 result
1007 }
1008
1009 let list = items.to_vec();
1010 let to_remove = except(&list, desired);
1011 let to_add = except(desired, &list);
1012
1013 for i in &to_remove {
1014 items.remove(i);
1015 }
1016
1017 for i in to_add {
1018 items.add(i);
1019 }
1020 }
1021 }
1022}