ferroui_diagnostics/diagnostics/view_models/
logical_tree_node.rs1use super::{TreeNode, TreeNodeCollection, TreeNodeCollectionImpl, TreeNodeKind};
4use crate::diagnostics::controls::TopLevelGroup;
5use crate::diagnostics::views::MainWindow;
6use ferroui_base::collections::{FerroList, FerroListExtensions};
7use ferroui_base::reactive::{CompositeDisposable, Disposable, IDisposable};
8use ferroui_base::{FerroObject, Nullable, Ref, StyledElement};
9use ferroui_controls::TopLevel;
10use std::cell::RefCell;
11use std::rc::{Rc, Weak};
12
13pub struct LogicalTreeNode;
19
20impl LogicalTreeNode {
21 pub fn new(ferro_object: Ref<FerroObject>, parent: Option<&Rc<TreeNode>>) -> Rc<TreeNode> {
25 let logical = ferro_object.cast::<StyledElement>();
26 let host = ferro_object.cast::<TopLevelGroup>();
27 TreeNode::new_node(ferro_object, parent, None, TreeNodeKind::Logical, move |this| {
28 if let Some(logical) = logical {
29 LogicalTreeNodeCollection::new(this, logical)
30 } else if let Some(host) = host {
31 TopLevelGroupHostLogical::new(this, host)
32 } else {
33 TreeNodeCollection::empty()
34 }
35 })
36 }
37
38 pub fn create(control: impl Into<Nullable<FerroObject>>) -> Vec<Rc<TreeNode>> {
41 match control.into().0 {
42 Some(logical) => vec![LogicalTreeNode::new(logical, None)],
43 None => Vec::new(),
44 }
45 }
46}
47
48pub struct LogicalTreeNodeCollection {
51 control: Ref<StyledElement>,
52 subscription: RefCell<Option<Rc<dyn IDisposable>>>,
53}
54
55impl LogicalTreeNodeCollection {
56 pub fn new(owner: Weak<TreeNode>, control: Ref<StyledElement>) -> Rc<TreeNodeCollection> {
57 TreeNodeCollection::new(owner, Box::new(Self { control, subscription: RefCell::new(None) }))
58 }
59}
60
61impl TreeNodeCollectionImpl for LogicalTreeNodeCollection {
62 fn dispose(&self, this: &TreeNodeCollection) {
63 this.base_dispose();
64 let subscription = self.subscription.borrow().clone();
65 if let Some(subscription) = subscription {
66 subscription.dispose();
67 }
68 }
69
70 fn initialize(&self, this: &TreeNodeCollection, nodes: &FerroList<Rc<TreeNode>>) {
71 let owner = this.owner();
72 let owner = owner.as_ref().map(Rc::downgrade).unwrap_or_default();
73 let (added, removed, reset) = (nodes.clone(), nodes.clone(), nodes.clone());
74 let subscription = FerroListExtensions::for_each_item_indexed(
75 self.control.logical_children(),
76 move |i, item: &Ref<StyledElement>| {
77 let owner = owner.upgrade();
78 added.insert(i, LogicalTreeNode::new(item.clone().upcast(), owner.as_ref()));
79 },
80 move |i, _| removed.remove_at(i),
81 move || reset.clear(),
82 false,
83 );
84 *self.subscription.borrow_mut() = Some(subscription);
85 }
86}
87
88pub struct TopLevelGroupHostLogical {
91 group: Ref<TopLevelGroup>,
92 subscriptions: CompositeDisposable,
93 factory: fn(Ref<FerroObject>, Option<&Rc<TreeNode>>) -> Rc<TreeNode>,
96}
97
98impl TopLevelGroupHostLogical {
99 pub fn new(owner: Weak<TreeNode>, host: Ref<TopLevelGroup>) -> Rc<TreeNodeCollection> {
100 Self::new_with(owner, host, LogicalTreeNode::new)
101 }
102
103 pub fn new_with(
105 owner: Weak<TreeNode>,
106 host: Ref<TopLevelGroup>,
107 factory: fn(Ref<FerroObject>, Option<&Rc<TreeNode>>) -> Rc<TreeNode>,
108 ) -> Rc<TreeNodeCollection> {
109 TreeNodeCollection::new(
110 owner,
111 Box::new(Self { group: host, subscriptions: CompositeDisposable::with_capacity(1), factory }),
112 )
113 }
114}
115
116impl TreeNodeCollectionImpl for TopLevelGroupHostLogical {
117 fn initialize(&self, this: &TreeNodeCollection, nodes: &FerroList<Rc<TreeNode>>) {
118 let owner = this.owner();
119 let items = self.group.items();
120 for window in items.iter() {
121 if window.is::<MainWindow>() {
122 continue;
123 }
124 nodes.add((self.factory)(window.clone().upcast(), owner.as_ref()));
125 }
126
127 let owner = owner.as_ref().map(Rc::downgrade).unwrap_or_default();
128 let factory = self.factory;
129
130 let group_on_added = {
131 let nodes = nodes.clone();
132 let owner = owner.clone();
133 move |e: &Ref<TopLevel>| {
134 if e.is::<MainWindow>() {
135 return;
136 }
137
138 nodes.add(factory(e.clone().upcast(), owner.upgrade().as_ref()));
139 }
140 };
141 let group_on_removed = {
142 let nodes = nodes.clone();
143 move |e: &Ref<TopLevel>| {
144 if e.is::<MainWindow>() {
145 return;
146 }
147
148 nodes.add(factory(e.clone().upcast(), owner.upgrade().as_ref()));
150 }
151 };
152
153 let added = self.group.added(group_on_added);
154 let removed = self.group.removed(group_on_removed);
155
156 self.subscriptions.add(Disposable::create(move || {
157 added.dispose();
158 removed.dispose();
159 }));
160 }
161
162 fn dispose(&self, this: &TreeNodeCollection) {
163 self.subscriptions.dispose();
164 this.base_dispose();
165 }
166}