ferroui_diagnostics/diagnostics/view_models/
event_tree_node_base.rs1use super::{EventOwnerTreeNode, EventTreeNode, ViewModelBase};
4use ferroui_base::collections::FerroList;
5use ferroui_base::data::model::{Event, INotifyPropertyChanged, ModelTypes};
6use ferroui_base::ferro_markup_type;
7use std::cell::{Cell, OnceCell, RefCell};
8use std::rc::{Rc, Weak};
9
10pub enum EventTreeNodeKind {
23 Owner(EventOwnerTreeNode),
25 Event(EventTreeNode),
27}
28
29pub struct EventTreeNodeBase {
33 base: ViewModelBase,
34 this: Weak<EventTreeNodeBase>,
35 pub(crate) update_children: Cell<bool>,
36 pub(crate) update_parent: Cell<bool>,
37 is_expanded: Cell<bool>,
38 is_enabled: Cell<Option<bool>>,
39 is_visible: Cell<bool>,
40 parent: Option<Weak<EventTreeNodeBase>>,
41 text: String,
42 children: RefCell<Option<FerroList<Rc<EventTreeNodeBase>>>>,
43 visible_children: OnceCell<Rc<FerroList<Rc<EventTreeNodeBase>>>>,
46 kind: EventTreeNodeKind,
47}
48
49impl PartialEq for EventTreeNodeBase {
51 fn eq(&self, other: &Self) -> bool {
52 std::ptr::eq(self, other)
53 }
54}
55
56impl INotifyPropertyChanged for EventTreeNodeBase {
57 fn property_changed(&self) -> &Event<str> {
58 self.base.property_changed()
59 }
60}
61
62impl EventTreeNodeBase {
63 pub(crate) fn new(
66 parent: Option<&Rc<EventTreeNodeBase>>,
67 text: String,
68 kind: impl FnOnce(&Weak<EventTreeNodeBase>) -> EventTreeNodeKind,
69 ) -> Rc<Self> {
70 let this = Rc::new_cyclic(|this| Self {
71 base: ViewModelBase::new(),
72 this: this.clone(),
73 update_children: Cell::new(true),
74 update_parent: Cell::new(true),
75 is_expanded: Cell::new(false),
76 is_enabled: Cell::new(Some(false)),
77 is_visible: Cell::new(false),
78 parent: parent.map(Rc::downgrade),
79 text,
80 children: RefCell::new(None),
81 visible_children: OnceCell::new(),
82 kind: kind(this),
83 });
84 this.set_is_visible(true);
85 this
86 }
87
88 pub(crate) fn to_rc(&self) -> Rc<EventTreeNodeBase> {
90 self.this.upgrade().expect("a node is reached through its shared handle")
91 }
92
93 pub fn kind(&self) -> &EventTreeNodeKind {
95 &self.kind
96 }
97
98 pub fn as_event_tree_node(&self) -> Option<&EventTreeNode> {
100 match &self.kind {
101 EventTreeNodeKind::Event(node) => Some(node),
102 EventTreeNodeKind::Owner(_) => None,
103 }
104 }
105
106 pub fn as_event_owner_tree_node(&self) -> Option<&EventOwnerTreeNode> {
108 match &self.kind {
109 EventTreeNodeKind::Owner(node) => Some(node),
110 EventTreeNodeKind::Event(_) => None,
111 }
112 }
113
114 pub fn children(&self) -> Option<FerroList<Rc<EventTreeNodeBase>>> {
116 self.children.borrow().clone()
117 }
118
119 pub fn visible_children(&self) -> Option<Rc<FerroList<Rc<EventTreeNodeBase>>>> {
127 let children = self.children()?;
128 let visible = self.visible_children.get_or_init(|| {
129 Rc::new(FerroList::from_items(children.to_vec().into_iter().filter(|child| child.is_visible())))
130 });
131 Some(visible.clone())
132 }
133
134 pub(crate) fn update_visible_children(&self) {
137 if let (Some(children), Some(visible)) = (self.children(), self.visible_children.get()) {
138 sync_visible_nodes(&children.to_vec(), visible);
139 }
140 }
141
142 pub(crate) fn register_model() {
145 if !ModelTypes::is_registered(std::any::TypeId::of::<EventTreeNodeBase>()) {
146 ModelTypes::register::<EventTreeNodeBase>(|builder| builder.notify_property_changed());
147 }
148 }
149
150 pub(crate) fn set_children(&self, value: Option<FerroList<Rc<EventTreeNodeBase>>>) {
152 *self.children.borrow_mut() = value;
153 }
154
155 pub fn is_expanded(&self) -> bool {
156 self.is_expanded.get()
157 }
158
159 pub fn set_is_expanded(&self, value: bool) {
160 self.base.raise_and_set_if_changed_cell(&self.is_expanded, value, "IsExpanded");
161 }
162
163 pub fn is_enabled(&self) -> Option<bool> {
166 self.is_enabled.get()
167 }
168
169 pub fn set_is_enabled(&self, value: Option<bool>) {
172 match &self.kind {
173 EventTreeNodeKind::Owner(_) => EventOwnerTreeNode::set_is_enabled(self, value),
174 EventTreeNodeKind::Event(_) => EventTreeNode::set_is_enabled(self, value),
175 }
176 }
177
178 pub(crate) fn base_set_is_enabled(&self, value: Option<bool>) {
180 self.base.raise_and_set_if_changed_cell(&self.is_enabled, value, "IsEnabled");
181 }
182
183 pub fn is_visible(&self) -> bool {
184 self.is_visible.get()
185 }
186
187 pub fn set_is_visible(&self, value: bool) {
188 self.base.raise_and_set_if_changed_cell(&self.is_visible, value, "IsVisible");
189 }
190
191 pub fn parent(&self) -> Option<Rc<EventTreeNodeBase>> {
192 self.parent.as_ref().and_then(Weak::upgrade)
193 }
194
195 pub fn text(&self) -> &str {
196 &self.text
197 }
198
199 pub(crate) fn update_checked(&self) {
202 let get_value = || -> Option<bool> {
203 let Some(children) = self.children() else {
204 return Some(false);
205 };
206
207 let mut value = Some(false);
208
209 for i in 0..children.count() {
210 if i == 0 {
211 value = children.get(i).is_enabled();
212 continue;
213 }
214
215 if value != children.get(i).is_enabled() {
216 value = None;
217 break;
218 }
219 }
220
221 value
222 };
223
224 self.set_is_enabled(get_value());
225 }
226}
227
228pub(crate) fn sync_visible_nodes(all: &[Rc<EventTreeNodeBase>], visible: &FerroList<Rc<EventTreeNodeBase>>) {
234 let mut index = 0;
235 for node in all {
236 let shown = index < visible.count() && Rc::ptr_eq(&visible.get(index), node);
237 if node.is_visible() {
238 if !shown {
239 visible.insert(index, node.clone());
240 }
241 index += 1;
242 } else if shown {
243 visible.remove_at(index);
244 }
245 }
246}
247
248ferroui_controls::ferro_markup_list!(pub EventTreeNodeList: Rc<EventTreeNodeBase>);
251
252ferro_markup_type!(class EventTreeNodeBase {
253 this: Rc<EventTreeNodeBase>,
254 handles: [EventTreeNodeBase, Rc<EventTreeNodeBase>, Option<Rc<EventTreeNodeBase>>],
255 base: ViewModelBase,
256 properties: [
257 Children: Option<FerroList<Rc<EventTreeNodeBase>>> { get: EventTreeNodeBase::children },
258 IsExpanded: bool { get: EventTreeNodeBase::is_expanded, set: EventTreeNodeBase::set_is_expanded },
259 IsEnabled: Option<bool> { get: EventTreeNodeBase::is_enabled, set: EventTreeNodeBase::set_is_enabled },
260 IsVisible: bool { get: EventTreeNodeBase::is_visible, set: EventTreeNodeBase::set_is_visible },
261 Parent: Option<Rc<EventTreeNodeBase>> { get: EventTreeNodeBase::parent },
262 Text: String { get: |this: &Rc<EventTreeNodeBase>| this.text().to_string() },
263 ],
264 notify_property_changed: EventTreeNodeBase,
265});