1use crate::automation::peers::{AutomationPeer, NoneAutomationPeer};
2use crate::primitives::{AdornerLayer, FlyoutBase};
3use crate::templates::{DataTemplates, IDataTemplateHost, ITemplateOf};
4use crate::Panel;
5use crate::ContextMenu;
6use ferroui_base::data::core::ValueTypes;
7use ferroui_base::reactive::IDisposable;
8use crate::{RequestBringIntoViewEventArgs, SizeChangedEventArgs};
9use ferroui_base::input::{
10 ContextRequestedEventArgs, FocusChangedEventArgs, InputElement, InputElementImpl, InputElementImplExt, KeyEventArgs,
11 MouseButton, NavigationMethod,
12 PointerReleasedEventArgs,
13};
14use ferroui_base::interactivity::{
15 Interactive, InteractiveImpl, RoutedEvent, RoutedEventArgs, RoutedEventHandlerToken, RoutingStrategies,
16};
17use ferroui_base::layout::LayoutableImpl;
18use ferroui_base::styling::{ISetterValue, Setter};
19use ferroui_base::threading::{Dispatcher, DispatcherPriority};
20use ferroui_base::{
21 ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, instantiate, AnyValue, BoxedValue,
22 FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, IntoRef, Nullable, Rect,
23 Ref, Size, StyledElement, StyledElementImpl, StyledProperty, Visual, VisualImpl, VisualImplExt, VisualTreeAttachmentEventArgs,
24};
25use std::cell::{Cell, OnceCell, RefCell};
26use std::collections::{HashSet, VecDeque};
27use std::rc::Rc;
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30enum LoadState {
31 Unloaded,
32 Loaded,
33 LoadPending,
34}
35
36#[derive(Default)]
45struct LoadedQueues {
46 is_loaded_processing: bool,
47 queue: Vec<Ref<Control>>,
49 queued: HashSet<Ref<Control>>,
51 processing_queue: VecDeque<Ref<Control>>,
52}
53
54impl LoadedQueues {
55 fn add(&mut self, control: Ref<Control>) -> bool {
56 if self.queued.insert(control.clone()) {
57 self.queue.push(control);
58 true
59 } else {
60 false
61 }
62 }
63
64 fn remove(&mut self, control: &Ref<Control>) {
65 if self.queued.remove(control) {
66 self.queue.retain(|c| c != control);
67 }
68 }
69}
70
71ferroui_base::ferro_thread_local! {
72 static LOADED: RefCell<LoadedQueues> = RefCell::new(LoadedQueues::default());
73}
74
75#[repr(C)]
83pub struct Control {
84 base: InputElement,
85 load_state: Cell<LoadState>,
86 data_templates: OnceCell<DataTemplates>,
87 focus_adorner: RefCell<Option<Ref<Control>>>,
88 context_flyout_handlers: Cell<Option<(RoutedEventHandlerToken, RoutedEventHandlerToken)>>,
89 context_menu_handlers: Cell<Option<ContextMenuHandlers>>,
90 automation_peer: RefCell<Option<Ref<AutomationPeer>>>,
91}
92
93pub(crate) type ContextMenuHandlers =
97 (RoutedEventHandlerToken, RoutedEventHandlerToken, Rc<dyn IDisposable>, Rc<dyn IDisposable>);
98
99ferro_class! {
100 Control: InputElement, virtuals ControlImpl: InputElementImpl {
101 fn get_template_focus_target(this) -> Option<Ref<Control>>;
103 fn on_loaded(this, e: &RoutedEventArgs);
105 fn on_unloaded(this, e: &RoutedEventArgs);
107 fn on_size_changed(this, e: &SizeChangedEventArgs);
109 fn on_create_automation_peer(this) -> Ref<AutomationPeer>;
112 }
113}
114ferroui_base::ferro_class_info!(Control { new: Control::new });
115
116ferro_impl_classes!(Control: StyledElementImpl, LayoutableImpl, InteractiveImpl);
117
118ferroui_base::ferro_overrides! { impl FerroObjectImpl for Control {
119 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
120 Self::parent_on_property_changed(this, change);
121
122 if change.property() == Visual::bounds_property().as_property() {
123 let (old_value, new_value) = change.get_old_and_new_value::<Rect>();
124
125 if new_value.size() != old_value.size() {
130 let size_changed_event_args = SizeChangedEventArgs::new(
131 Self::size_changed_event(),
132 this.to_ref(),
133 Size::new(old_value.width, old_value.height),
134 Size::new(new_value.width, new_value.height),
135 );
136
137 this.on_size_changed(&size_changed_event_args);
138 }
139 }
140 }
141
142 fn update_data_validation(
143 this: &Self,
144 property: &'static ferroui_base::FerroProperty,
145 state: ferroui_base::data::BindingValueType,
146 error: Option<&ferroui_base::data::BindingError>,
147 ) {
148 crate::DataValidationErrors::set_error(this, error);
149 Self::parent_update_data_validation(this, property, state, error);
150 }
151} }
152
153impl VisualImpl for Control {
154 fn on_attached_to_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
155 Self::parent_on_attached_to_visual_tree_core(this, e);
156
157 this.initialize_if_needed();
158
159 this.schedule_on_loaded_core();
160 }
161
162 fn on_detached_from_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
163 Self::parent_on_detached_from_visual_tree_core(this, e);
164
165 this.on_unloaded_core();
166 }
167
168 fn ensure_initialized_for_visual_brush(this: &Self) {
169 this.ensure_initialized();
170 }
171}
172
173impl InputElementImpl for Control {
174 fn on_got_focus(this: &Self, e: &FocusChangedEventArgs) {
175 Self::parent_on_got_focus(this, e);
176
177 if this.is_focused()
178 && (e.navigation_method == NavigationMethod::Tab || e.navigation_method == NavigationMethod::Directional)
179 {
180 if let Some(adorner_layer) = AdornerLayer::get_adorner_layer(this) {
181 if this.focus_adorner.borrow().is_none() {
182 let template = if this.is_set(Self::focus_adorner_property().as_property()) {
183 this.get_value(Self::focus_adorner_property())
184 } else {
185 adorner_layer.default_focus_adorner()
186 };
187
188 if let Some(template) = template {
189 *this.focus_adorner.borrow_mut() = template.build_typed();
190 }
191 }
192
193 let focus_adorner = this.focus_adorner.borrow().clone();
194 if let (Some(focus_adorner), Some(target)) = (focus_adorner, this.get_template_focus_target()) {
195 AdornerLayer::set_adorned_element(&focus_adorner, target);
196 adorner_layer.children().add(focus_adorner);
197 }
198 }
199 }
200 }
201
202 fn on_lost_focus(this: &Self, e: &FocusChangedEventArgs) {
203 Self::parent_on_lost_focus(this, e);
204
205 let focus_adorner = this.focus_adorner.borrow().clone();
206 if let Some(focus_adorner) = focus_adorner {
207 if let Some(adorner_layer) = focus_adorner.parent().and_then(|parent| parent.cast::<Panel>()) {
208 adorner_layer.children().remove(focus_adorner);
209 *this.focus_adorner.borrow_mut() = None;
210 }
211 }
212 }
213
214 fn on_pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
215 Self::parent_on_pointer_released(this, e);
216
217 if e.is_source(this) && !e.handled() && e.initial_press_mouse_button() == MouseButton::Right {
218 let args = ContextRequestedEventArgs::from_pointer_event_args(e);
219 this.raise_event(&args);
220 e.set_handled(args.handled());
221 }
222 }
223
224 fn on_key_up(this: &Self, e: &KeyEventArgs) {
225 Self::parent_on_key_up(this, e);
226
227 if e.is_source(this) && !e.handled() {
228 let Some(settings) = this.get_platform_settings() else { return };
229 let configuration = settings.hotkey_configuration();
230 let keymap = &configuration.open_context_menu;
231
232 let matches = keymap.iter().any(|key| key.matches(Some(e)));
233
234 if matches {
235 let args = ContextRequestedEventArgs::new();
236 this.raise_event(&args);
237 e.set_handled(args.handled());
238 }
239 }
240 }
241}
242
243impl ControlImpl for Control {
244 fn get_template_focus_target(this: &Self) -> Option<Ref<Control>> {
245 Some(this.to_ref())
246 }
247
248 fn on_loaded(this: &Self, e: &RoutedEventArgs) {
249 this.raise_event(e);
250 }
251
252 fn on_unloaded(this: &Self, e: &RoutedEventArgs) {
253 this.raise_event(e);
254 }
255
256 fn on_create_automation_peer(this: &Self) -> Ref<AutomationPeer> {
257 NoneAutomationPeer::new(this).upcast()
258 }
259
260 fn on_size_changed(this: &Self, e: &SizeChangedEventArgs) {
261 this.raise_event(e);
262 }
263}
264
265ferroui_base::ferro_properties! { impl Control {
266 ferro_property!(
267 pub fn focus_adorner_property() -> StyledProperty<Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>>> {
269 FerroProperty::register::<Control, _>("FocusAdorner", None)
270 }
271 );
272
273 ferro_property!(
274 pub fn tag_property() -> StyledProperty<Option<BoxedValue>> {
276 FerroProperty::register::<Control, _>("Tag", None)
277 }
278 );
279
280 ferro_property!(
281 pub fn context_menu_property() -> StyledProperty<Option<Ref<ContextMenu>>> {
283 FerroProperty::register::<Control, _>("ContextMenu", None)
284 }
285 );
286
287 ferro_property!(
288 pub fn context_flyout_property() -> StyledProperty<Option<Ref<FlyoutBase>>> {
290 FerroProperty::register::<Control, _>("ContextFlyout", None)
291 }
292 );
293} }
294
295impl Control {
296 ferro_routed_event!(
297 pub fn request_bring_into_view_event() -> RoutedEvent<RequestBringIntoViewEventArgs> {
299 RoutedEvent::register::<Control, _>("RequestBringIntoView", RoutingStrategies::BUBBLE)
300 }
301 );
302
303 ferro_routed_event!(
304 pub fn loaded_event() -> RoutedEvent<RoutedEventArgs> {
306 RoutedEvent::register::<Control, _>("Loaded", RoutingStrategies::DIRECT)
307 }
308 );
309
310 ferro_routed_event!(
311 pub fn unloaded_event() -> RoutedEvent<RoutedEventArgs> {
313 RoutedEvent::register::<Control, _>("Unloaded", RoutingStrategies::DIRECT)
314 }
315 );
316
317 ferro_routed_event!(
318 pub fn size_changed_event() -> RoutedEvent<SizeChangedEventArgs> {
320 RoutedEvent::register::<Control, _>("SizeChanged", RoutingStrategies::DIRECT)
321 }
322 );
323
324 pub fn construct() -> Self {
326 Self { base: InputElement::construct(), load_state: Cell::new(LoadState::Unloaded), data_templates: OnceCell::new(), focus_adorner: RefCell::new(None), context_flyout_handlers: Cell::new(None), context_menu_handlers: Cell::new(None), automation_peer: RefCell::new(None) }
327 }
328
329 pub fn new() -> Ref<Self> {
330 instantiate(Self::construct())
331 }
332
333 pub(crate) fn get_automation_peer(&self) -> Option<Ref<AutomationPeer>> {
335 self.verify_access();
336 self.automation_peer.borrow().clone()
337 }
338
339 pub(crate) fn get_or_create_automation_peer(&self) -> Ref<AutomationPeer> {
342 self.verify_access();
343
344 if let Some(peer) = self.automation_peer.borrow().clone() {
345 return peer;
346 }
347
348 let peer = self.on_create_automation_peer();
349 *self.automation_peer.borrow_mut() = Some(peer.clone());
350 peer
351 }
352
353 pub fn focus_adorner(&self) -> Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>> {
355 self.get_value(Self::focus_adorner_property())
356 }
357
358 pub fn set_focus_adorner(&self, value: Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>>) {
359 self.set_value(Self::focus_adorner_property(), value)
360 }
361
362 pub fn data_templates(&self) -> DataTemplates {
367 self.data_templates.get_or_init(DataTemplates::new).clone()
368 }
369
370 pub fn is_data_templates_initialized(&self) -> bool {
372 self.data_templates.get().is_some()
373 }
374
375 pub fn context_menu(&self) -> Option<Ref<ContextMenu>> {
377 self.get_value(Self::context_menu_property())
378 }
379
380 pub fn set_context_menu(&self, value: impl Into<Nullable<ContextMenu>>) {
381 self.set_value(Self::context_menu_property(), value.into().0)
382 }
383
384 pub(crate) fn context_menu_handlers(&self) -> &Cell<Option<ContextMenuHandlers>> {
386 &self.context_menu_handlers
387 }
388
389 pub fn context_flyout(&self) -> Option<Ref<FlyoutBase>> {
391 self.get_value(Self::context_flyout_property())
392 }
393
394 pub fn set_context_flyout(&self, value: impl Into<Nullable<FlyoutBase>>) {
395 self.set_value(Self::context_flyout_property(), value.into().0)
396 }
397
398 pub(crate) fn context_flyout_handlers(&self) -> &Cell<Option<(RoutedEventHandlerToken, RoutedEventHandlerToken)>> {
401 &self.context_flyout_handlers
402 }
403
404 pub fn is_loaded(&self) -> bool {
409 self.load_state.get() == LoadState::Loaded
410 }
411
412 pub fn tag(&self) -> Option<BoxedValue> {
414 self.get_value(Self::tag_property())
415 }
416
417 pub fn set_tag(&self, value: Option<BoxedValue>) {
418 self.set_value(Self::tag_property(), value)
419 }
420
421 pub fn loaded(&self, handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static) -> RoutedEventHandlerToken {
431 self.add_handler(Self::loaded_event(), handler)
432 }
433
434 pub fn unloaded(&self, handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static) -> RoutedEventHandlerToken {
440 self.add_handler(Self::unloaded_event(), handler)
441 }
442
443 pub fn size_changed(
445 &self,
446 handler: impl Fn(&Interactive, &SizeChangedEventArgs) + 'static,
447 ) -> RoutedEventHandlerToken {
448 self.add_handler(Self::size_changed_event(), handler)
449 }
450
451 pub fn boxed(control: impl IntoRef<Control>) -> BoxedValue {
454 Rc::new(control.into_ref())
455 }
456
457 pub fn from_boxed(value: &BoxedValue) -> Option<Ref<Control>> {
467 let value: &dyn AnyValue = &**value;
468 if let Some(control) = value.downcast_ref::<Ref<Control>>() {
469 return Some(control.clone());
470 }
471 ValueTypes::as_object(value)?.cast::<Control>()
472 }
473
474 pub fn logical_from_boxed(value: &BoxedValue) -> Option<Ref<StyledElement>> {
478 let value: &dyn AnyValue = &**value;
479 if let Some(control) = value.downcast_ref::<Ref<Control>>() {
480 return Some(control.clone().upcast());
481 }
482 if let Some(element) = value.downcast_ref::<Ref<StyledElement>>() {
483 return Some(element.clone());
484 }
485 ValueTypes::as_object(value)?.cast::<StyledElement>()
486 }
487
488 pub fn ensure_initialized(&self) {
491 if self.visual_root().is_none() {
492 if !self.is_initialized() {
493 for i in self.get_self_and_visual_descendants() {
494 if let Some(c) = i.downcast_ref::<Control>() {
495 if !c.is_initialized() {
496 c.begin_init();
497 c.end_init();
498 }
499 }
500 }
501 }
502
503 if !self.is_arrange_valid() {
504 self.measure(Size::INFINITY);
505 self.arrange(Rect::from_size(self.desired_size()));
506 }
507 }
508 }
509
510 fn loaded_processing_action() {
511 LOADED.with(|loaded| {
516 let mut loaded = loaded.borrow_mut();
517 let queue = std::mem::take(&mut loaded.queue);
518 loaded.queued.clear();
519 loaded.processing_queue.extend(queue);
520 });
521
522 struct Finally;
528
529 impl Drop for Finally {
530 fn drop(&mut self) {
531 let restart = LOADED.with(|loaded| {
532 let mut loaded = loaded.borrow_mut();
533 let remaining = std::mem::take(&mut loaded.processing_queue);
534 for control in remaining {
535 loaded.add(control);
536 }
537 loaded.is_loaded_processing = false;
538
539 if !loaded.queue.is_empty() {
542 loaded.is_loaded_processing = true;
543 true
544 } else {
545 false
546 }
547 });
548
549 if restart {
550 Dispatcher::ui_thread().post_local(Control::loaded_processing_action, DispatcherPriority::LOADED);
551 }
552 }
553 }
554
555 let _finally = Finally;
556
557 loop {
558 let next = LOADED.with(|loaded| loaded.borrow_mut().processing_queue.pop_front());
559 match next {
560 Some(control) => control.on_loaded_core(),
561 None => break,
562 }
563 }
564 }
565
566 pub(crate) fn schedule_on_loaded_core(&self) {
569 if self.load_state.get() == LoadState::Unloaded {
570 let post = LOADED.with(|loaded| {
571 let mut loaded = loaded.borrow_mut();
572 let is_added = loaded.add(self.to_ref());
573 if is_added && !loaded.is_loaded_processing {
574 loaded.is_loaded_processing = true;
575 true
576 } else {
577 false
578 }
579 });
580 self.load_state.set(LoadState::LoadPending);
581
582 if post {
583 Dispatcher::ui_thread().post_local(Self::loaded_processing_action, DispatcherPriority::LOADED);
584 }
585 }
586 }
587
588 pub(crate) fn on_loaded_core(&self) {
591 if self.load_state.get() == LoadState::LoadPending && self.is_attached_to_logical_tree() {
592 self.load_state.set(LoadState::Loaded);
593
594 self.on_loaded(&RoutedEventArgs::with_event_and_source(Self::loaded_event(), self.to_ref()));
595 } else {
596 self.load_state.set(LoadState::Unloaded);
598 }
599 }
600
601 pub(crate) fn on_unloaded_core(&self) {
604 match self.load_state.get() {
605 LoadState::Loaded => {
606 self.load_state.set(LoadState::Unloaded);
607
608 self.on_unloaded(&RoutedEventArgs::with_event_and_source(Self::unloaded_event(), self.to_ref()));
609 }
610 LoadState::LoadPending => {
611 let this = self.to_ref();
615 LOADED.with(|loaded| loaded.borrow_mut().remove(&this));
616
617 self.load_state.set(LoadState::Unloaded);
618 }
619 LoadState::Unloaded => {}
620 }
621 }
622
623 #[doc(hidden)]
626 pub fn reset_loaded_queue_for_unit_tests() {
627 LOADED.with(|loaded| {
628 let mut loaded = loaded.borrow_mut();
629 loaded.queue.clear();
630 loaded.queued.clear();
631 loaded.processing_queue.clear();
632 loaded.is_loaded_processing = false;
633 });
634 }
635}
636
637impl IDataTemplateHost for Control {
638 fn data_templates(&self) -> DataTemplates {
639 Control::data_templates(self)
640 }
641
642 fn is_data_templates_initialized(&self) -> bool {
643 Control::is_data_templates_initialized(self)
644 }
645}
646
647impl ISetterValue for Control {
648 fn initialize(&self, setter: &Setter) {
649 if let Some(context_menu) = self.downcast_ref::<ContextMenu>() {
651 return context_menu.initialize(setter);
652 }
653
654 if setter.property() == Some(Self::context_flyout_property().as_property()) {
655 return; }
657
658 panic!("Cannot use a control as a Setter value. Wrap the control in a <Template>.");
659 }
660}