ferroui_diagnostics/diagnostics/view_models/
fired_event.rs1use super::ViewModelBase;
4use crate::diagnostics::models::EventChainLink;
5use ferroui_base::collections::FerroList;
6use ferroui_base::data::model::{Event, INotifyPropertyChanged};
7use ferroui_base::ferro_markup_type;
8use ferroui_base::interactivity::{RoutedEvent, RoutedEventArgs, RoutingStrategies};
9use ferroui_base::utilities::DateTime;
10use std::cell::RefCell;
11use std::rc::Rc;
12
13const NEW_LINE: &str = if cfg!(windows) { "\r\n" } else { "\n" };
15
16pub(crate) fn routing_strategies_text(strategies: RoutingStrategies) -> String {
19 let names: Vec<&str> = [
20 (RoutingStrategies::DIRECT, "Direct"),
21 (RoutingStrategies::TUNNEL, "Tunnel"),
22 (RoutingStrategies::BUBBLE, "Bubble"),
23 ]
24 .into_iter()
25 .filter(|(flag, _)| strategies.contains(*flag))
26 .map(|(_, name)| name)
27 .collect();
28
29 if names.is_empty() {
30 strategies.bits().to_string()
31 } else {
32 names.join(", ")
33 }
34}
35
36fn same_link(a: &Option<Rc<EventChainLink>>, b: &Option<Rc<EventChainLink>>) -> bool {
39 match (a, b) {
40 (Some(a), Some(b)) => Rc::ptr_eq(a, b),
41 (None, None) => true,
42 _ => false,
43 }
44}
45
46pub struct FiredEvent {
49 base: ViewModelBase,
50 event_args: RoutedEventArgs,
51 original_event: Option<RoutedEvent>,
52 handled_by: RefCell<Option<Rc<EventChainLink>>>,
53 trigger_time: DateTime,
54 event_chain: FerroList<Rc<EventChainLink>>,
55 originator: Rc<EventChainLink>,
56}
57
58impl PartialEq for FiredEvent {
60 fn eq(&self, other: &Self) -> bool {
61 std::ptr::eq(self, other)
62 }
63}
64
65impl INotifyPropertyChanged for FiredEvent {
66 fn property_changed(&self) -> &Event<str> {
67 self.base.property_changed()
68 }
69}
70
71impl FiredEvent {
72 pub fn new(event_args: &RoutedEventArgs, originator: Rc<EventChainLink>, trigger_time: DateTime) -> Rc<Self> {
76 let this = Rc::new(Self {
77 base: ViewModelBase::new(),
78 event_args: event_args.clone(),
79 original_event: event_args.routed_event(),
80 handled_by: RefCell::new(None),
81 trigger_time,
82 event_chain: FerroList::new(),
83 originator: originator.clone(),
84 });
85 this.add_to_chain(originator);
86 this
87 }
88
89 pub fn is_part_of_same_event_chain(&self, e: &RoutedEventArgs) -> bool {
91 *e == self.event_args && e.routed_event() == self.original_event
94 }
95
96 pub fn trigger_time(&self) -> DateTime {
97 self.trigger_time
98 }
99
100 pub fn event(&self) -> RoutedEvent {
102 self.original_event.expect("the args of a raised event name the routed event")
103 }
104
105 pub fn is_handled(&self) -> bool {
107 self.handled_by().is_some_and(|handled_by| handled_by.handled())
108 }
109
110 pub fn event_chain(&self) -> FerroList<Rc<EventChainLink>> {
112 self.event_chain.clone()
113 }
114
115 pub fn display_text(&self) -> String {
118 let event = self.event();
119 if self.is_handled() {
120 let handled_by = self.handled_by().expect("a handled event has the link that handled it");
121 return format!("{} on {};", event.name(), self.originator.handler_name())
122 + NEW_LINE
123 + &format!(
124 "strategies: {}; handled by: {}",
125 routing_strategies_text(event.routing_strategies()),
126 handled_by.handler_name()
127 );
128 }
129
130 format!(
131 "{} on {}; strategies: {}",
132 event.name(),
133 self.originator.handler_name(),
134 routing_strategies_text(event.routing_strategies())
135 )
136 }
137
138 pub fn originator(&self) -> Rc<EventChainLink> {
140 self.originator.clone()
141 }
142
143 pub fn handled_by(&self) -> Option<Rc<EventChainLink>> {
145 self.handled_by.borrow().clone()
146 }
147
148 pub fn set_handled_by(&self, value: Option<Rc<EventChainLink>>) {
149 let changed = !same_link(&self.handled_by.borrow(), &value);
150 if changed {
151 *self.handled_by.borrow_mut() = value;
152 self.base.raise_property_changed("HandledBy");
153 self.base.raise_property_changed("IsHandled");
154 self.base.raise_property_changed("DisplayText");
155 }
156 }
157
158 pub fn add_to_chain(&self, link: Rc<EventChainLink>) {
161 if self.event_chain.count() > 0 {
162 let prev_link = self.event_chain.get(self.event_chain.count() - 1);
163
164 if prev_link.route() != link.route() {
165 link.set_begins_new_route(true);
166 }
167 }
168
169 self.event_chain.add(link.clone());
170
171 if self.handled_by().is_none() && link.handled() {
172 self.set_handled_by(Some(link));
173 }
174 }
175}
176
177ferroui_controls::ferro_markup_list!(pub EventChainLinkList: Rc<EventChainLink>);
179
180ferroui_controls::ferro_markup_list!(pub FiredEventList: Rc<FiredEvent>);
182
183ferro_markup_type!(class FiredEvent {
184 this: Rc<FiredEvent>,
185 handles: [FiredEvent, Rc<FiredEvent>, Option<Rc<FiredEvent>>],
186 base: ViewModelBase,
187 properties: [
188 TriggerTime: DateTime { get: FiredEvent::trigger_time },
189 Event: RoutedEvent { get: FiredEvent::event },
190 IsHandled: bool { get: FiredEvent::is_handled },
191 EventChain: FerroList<Rc<EventChainLink>> { get: FiredEvent::event_chain },
192 DisplayText: String { get: FiredEvent::display_text },
193 Originator: Rc<EventChainLink> { get: FiredEvent::originator },
194 HandledBy: Option<Rc<EventChainLink>> { get: FiredEvent::handled_by, set: FiredEvent::set_handled_by },
195 ],
196 notify_property_changed: FiredEvent,
197});