ferroui_xaml_interactivity/ferro_object/
behavior.rs1use crate::{IBehavior, IBehaviorEventsHandler};
4use ferroui_base::{
5 ferro_class, ferro_properties, FerroObject, FerroObjectImpl, FerroProperty, Nullable, Ref, StyledProperty, WeakRef,
6};
7use std::cell::{Cell, RefCell};
8
9#[repr(C)]
20pub struct Behavior {
21 base: FerroObject,
22 associated_object: RefCell<Option<WeakRef<FerroObject>>>,
25 is_attached_to_logical_tree: Cell<bool>,
26 is_attached_to_visual_tree: Cell<bool>,
27 is_initialized_notified: Cell<bool>,
28 is_loaded: Cell<bool>,
29}
30
31ferro_class! {
32 Behavior: FerroObject, virtuals BehaviorImpl: FerroObjectImpl {
33 fn on_attached(this);
37 fn on_detaching(this);
41 fn on_attached_to_visual_tree(this);
45 fn on_detached_from_visual_tree(this);
49 fn on_attached_to_logical_tree_event(this);
54 fn on_detached_from_logical_tree_event(this);
59 fn on_loaded(this);
63 fn on_unloaded(this);
67 fn on_initialized_event(this);
71 fn on_data_context_changed_event(this);
75 fn on_resources_changed_event(this);
79 fn on_actual_theme_variant_changed_event(this);
83 }
84}
85
86ferroui_base::ferro_impl_classes!(Behavior: FerroObjectImpl);
87
88impl BehaviorImpl for Behavior {
89 fn on_attached(_this: &Self) {}
90
91 fn on_detaching(_this: &Self) {}
92
93 fn on_attached_to_visual_tree(_this: &Self) {}
94
95 fn on_detached_from_visual_tree(_this: &Self) {}
96
97 fn on_attached_to_logical_tree_event(_this: &Self) {}
98
99 fn on_detached_from_logical_tree_event(_this: &Self) {}
100
101 fn on_loaded(_this: &Self) {}
102
103 fn on_unloaded(_this: &Self) {}
104
105 fn on_initialized_event(_this: &Self) {}
106
107 fn on_data_context_changed_event(_this: &Self) {}
108
109 fn on_resources_changed_event(_this: &Self) {}
110
111 fn on_actual_theme_variant_changed_event(_this: &Self) {}
112}
113
114ferro_properties! {
115 impl Behavior {
116 pub fn is_enabled_property() -> StyledProperty<bool> {
118 FerroProperty::register::<Behavior, _>("IsEnabled", true)
119 }
120 }
121}
122
123impl Behavior {
124 fn static_constructor() {
126 <dyn IBehavior>::register::<Behavior>();
127 <dyn IBehaviorEventsHandler>::register::<Behavior>();
128 }
129
130 pub fn construct() -> Self {
132 Self {
133 base: FerroObject::construct(),
134 associated_object: RefCell::new(None),
135 is_attached_to_logical_tree: Cell::new(false),
136 is_attached_to_visual_tree: Cell::new(false),
137 is_initialized_notified: Cell::new(false),
138 is_loaded: Cell::new(false),
139 }
140 }
141
142 pub fn associated_object(&self) -> Option<Ref<FerroObject>> {
144 self.associated_object.borrow().as_ref().and_then(WeakRef::upgrade)
145 }
146
147 pub fn is_enabled(&self) -> bool {
149 self.get_value(Self::is_enabled_property())
150 }
151
152 pub fn set_is_enabled(&self, value: bool) {
153 self.set_value(Self::is_enabled_property(), value)
154 }
155
156 pub fn attach(&self, associated_object: impl Into<Nullable<FerroObject>>) {
161 let associated_object = associated_object.into().0;
162 let current = self.associated_object();
163 if associated_object == current {
164 return;
165 }
166
167 if current.is_some() {
168 panic!("An instance of a behavior cannot be attached to more than one object at a time.");
169 }
170
171 debug_assert!(associated_object.is_some(), "Cannot attach the behavior to a null object.");
172 let Some(associated_object) = associated_object else {
173 panic!("Value cannot be null. (Parameter 'associatedObject')");
174 };
175 *self.associated_object.borrow_mut() = Some(associated_object.downgrade());
176
177 self.on_attached();
178 }
179
180 pub fn detach(&self) {
182 self.on_detaching();
183 self.is_attached_to_logical_tree.set(false);
184 self.is_attached_to_visual_tree.set(false);
185 self.is_initialized_notified.set(false);
186 self.is_loaded.set(false);
187 *self.associated_object.borrow_mut() = None;
188 }
189}
190
191impl IBehavior for Behavior {
192 fn associated_object(&self) -> Option<Ref<FerroObject>> {
193 Behavior::associated_object(self)
194 }
195
196 fn attach(&self, associated_object: Option<Ref<FerroObject>>) {
197 Behavior::attach(self, associated_object)
198 }
199
200 fn detach(&self) {
201 Behavior::detach(self)
202 }
203}
204
205impl IBehaviorEventsHandler for Behavior {
206 fn attached_to_visual_tree_event_handler(&self) {
207 if self.is_attached_to_visual_tree.get() {
208 return;
209 }
210
211 self.is_attached_to_visual_tree.set(true);
212 self.on_attached_to_visual_tree();
213 }
214
215 fn detached_from_visual_tree_event_handler(&self) {
216 if !self.is_attached_to_visual_tree.get() {
217 return;
218 }
219
220 self.is_attached_to_visual_tree.set(false);
221 self.on_detached_from_visual_tree();
222 }
223
224 fn attached_to_logical_tree_event_handler(&self) {
225 if self.is_attached_to_logical_tree.get() {
226 return;
227 }
228
229 self.is_attached_to_logical_tree.set(true);
230 self.on_attached_to_logical_tree_event();
231 }
232
233 fn detached_from_logical_tree_event_handler(&self) {
234 if !self.is_attached_to_logical_tree.get() {
235 return;
236 }
237
238 self.is_attached_to_logical_tree.set(false);
239 self.on_detached_from_logical_tree_event();
240 }
241
242 fn loaded_event_handler(&self) {
243 if self.is_loaded.get() {
244 return;
245 }
246
247 self.is_loaded.set(true);
248 self.on_loaded();
249 }
250
251 fn unloaded_event_handler(&self) {
252 if !self.is_loaded.get() {
253 return;
254 }
255
256 self.is_loaded.set(false);
257 self.on_unloaded();
258 }
259
260 fn initialized_event_handler(&self) {
261 if self.is_initialized_notified.get() {
262 return;
263 }
264
265 self.is_initialized_notified.set(true);
266 self.on_initialized_event();
267 }
268
269 fn data_context_changed_event_handler(&self) {
270 self.on_data_context_changed_event();
271 }
272
273 fn resources_changed_event_handler(&self) {
274 self.on_resources_changed_event();
275 }
276
277 fn actual_theme_variant_changed_event_handler(&self) {
278 self.on_actual_theme_variant_changed_event();
279 }
280}