ferroui_diagnostics/diagnostics/view_models/
event_tree_node.rs1use super::{EventTreeNodeBase, EventTreeNodeKind, EventsPageViewModel, FiredEvent};
4use crate::diagnostics::models::EventChainLink;
5use crate::diagnostics::DevTools;
6use ferroui_base::interactivity::{IRoutedEventArgs, Interactive, RoutedEvent, RoutedEventArgs, RoutingStrategies};
7use ferroui_base::threading::{Dispatcher, DispatcherPriority};
8use ferroui_base::utilities::DateTime;
9use ferroui_base::{BoxedValue, FerroObject, Ref, Visual};
10use std::cell::{Cell, RefCell};
11use std::rc::{Rc, Weak};
12
13const MAX_RECORDED_EVENTS: usize = 100;
15
16pub struct EventTreeNode {
21 event: RoutedEvent,
22 parent_view_model: Weak<EventsPageViewModel>,
23 is_registered: Cell<bool>,
24 current_event: RefCell<Option<Rc<FiredEvent>>>,
25}
26
27impl EventTreeNode {
28 pub fn new(
32 parent: &Rc<EventTreeNodeBase>,
33 event: RoutedEvent,
34 vm: Weak<EventsPageViewModel>,
35 ) -> Rc<EventTreeNodeBase> {
36 EventTreeNodeBase::new(Some(parent), event.name().to_string(), |_| {
37 EventTreeNodeKind::Event(EventTreeNode {
38 event,
39 parent_view_model: vm,
40 is_registered: Cell::new(false),
41 current_event: RefCell::new(None),
42 })
43 })
44 }
45
46 pub fn event(&self) -> RoutedEvent {
48 self.event
49 }
50
51 pub(crate) fn set_is_enabled(this: &EventTreeNodeBase, value: Option<bool>) {
55 if this.is_enabled() != value {
56 this.base_set_is_enabled(value);
57 Self::update_tracker(this);
58 if let Some(parent) = this.parent() {
59 if this.update_parent.get() {
60 parent.update_children.set(false);
62 parent.update_checked();
63 parent.update_children.set(true);
64 }
65 }
66 }
67 }
68
69 fn part(this: &EventTreeNodeBase) -> &EventTreeNode {
70 this.as_event_tree_node().expect("a node of an event")
71 }
72
73 fn update_tracker(this: &EventTreeNodeBase) {
74 let node = Self::part(this);
75 if this.is_enabled().unwrap_or_default() && !node.is_registered.get() {
76 let all_routes = RoutingStrategies::DIRECT | RoutingStrategies::TUNNEL | RoutingStrategies::BUBBLE;
77
78 let weak = Rc::downgrade(&this.to_rc());
82 let _ = node.event.add_class_handler_untyped(
83 FerroObject::TYPE,
84 move |sender, e| {
85 if let Some(this) = weak.upgrade() {
86 Self::handle_event(&this, sender, e);
87 }
88 },
89 all_routes,
90 true,
91 );
92 let weak = Rc::downgrade(&this.to_rc());
93 let _ = node.event.route_finished().subscribe(move |e| {
94 if let Some(this) = weak.upgrade() {
95 Self::handle_route_finished(&this, e);
96 }
97 });
98
99 node.is_registered.set(true);
100 }
101 }
102
103 fn chain_link(handler: &Ref<Interactive>, handled: bool, route: RoutingStrategies) -> Rc<EventChainLink> {
105 let handler: BoxedValue = Rc::new(handler.clone().upcast::<FerroObject>());
107 EventChainLink::new(handler, handled, route)
108 }
109
110 fn run_on_ui_thread(handler: impl FnOnce() + 'static) {
117 let dispatcher = Dispatcher::ui_thread();
118 if !dispatcher.check_access() {
119 dispatcher.post_local(handler, DispatcherPriority::default());
120 } else {
121 handler();
122 }
123 }
124
125 fn handle_event(this: &Rc<EventTreeNodeBase>, sender: &Interactive, e: &dyn IRoutedEventArgs) {
126 let node = Self::part(this);
127 if !node.is_registered.get() || this.is_enabled() == Some(false) {
128 return;
129 }
130 if let Some(v) = sender.downcast_ref::<Visual>() {
131 if DevTools::does_belong_to_dev_tool(v) {
132 return;
133 }
134 }
135
136 let s = sender.to_ref();
137 let handled = e.handled();
138 let route = e.route();
139 let trigger_time = DateTime::now();
140 let args: RoutedEventArgs = e.as_routed_event_args().clone();
141 let weak = Rc::downgrade(this);
142
143 let handler = move || {
144 let Some(this) = weak.upgrade() else {
145 return;
146 };
147 let node = Self::part(&this);
148
149 let current_event = node.current_event.borrow().clone();
150 match current_event {
151 Some(current_event) if current_event.is_part_of_same_event_chain(&args) => {
152 current_event.add_to_chain(Self::chain_link(&s, handled, route));
153 }
154 _ => {
155 let current_event = FiredEvent::new(&args, Self::chain_link(&s, handled, route), trigger_time);
156 *node.current_event.borrow_mut() = Some(current_event.clone());
157
158 if let Some(parent_view_model) = node.parent_view_model.upgrade() {
159 let recorded_events = parent_view_model.recorded_events();
160 recorded_events.add(current_event);
161
162 while recorded_events.count() > MAX_RECORDED_EVENTS {
163 recorded_events.remove_at(0);
164 }
165 }
166 }
167 }
168 };
169
170 Self::run_on_ui_thread(handler);
171 }
172
173 fn handle_route_finished(this: &Rc<EventTreeNodeBase>, e: &dyn IRoutedEventArgs) {
174 let node = Self::part(this);
175 if !node.is_registered.get() || this.is_enabled() == Some(false) {
176 return;
177 }
178 if let Some(v) = e.source().and_then(|source| source.cast::<Visual>()) {
179 if DevTools::does_belong_to_dev_tool(&v) {
180 return;
181 }
182 }
183
184 let handled = e.handled();
185 let weak = Rc::downgrade(this);
186
187 let handler = move || {
188 let Some(this) = weak.upgrade() else {
189 return;
190 };
191 let node = Self::part(&this);
192
193 let current_event = node.current_event.borrow().clone();
194 if let Some(current_event) = current_event {
195 if handled {
196 let event_chain = current_event.event_chain();
197 let link_index = event_chain.count() - 1;
198 let link = event_chain.get(link_index);
199
200 link.set_handled(true);
201 if current_event.handled_by().is_none() {
202 current_event.set_handled_by(Some(link));
203 }
204 }
205 }
206 };
207
208 Self::run_on_ui_thread(handler);
209 }
210}