Skip to main content

ferroui_xaml_interactions_custom/debugging/
visual_debug_behavior.rs

1//! Port of `Debugging/VisualDebugBehavior.cs`.
2
3use super::visual_debug_adorner::VisualDebugAdorner;
4use ferroui_base::animation::TimeSpan;
5use ferroui_base::media::{Color, Colors, SolidColorBrush};
6use ferroui_base::threading::{DispatcherPriority, DispatcherTimer};
7use ferroui_base::{
8    ferro_class, ferro_class_info, ferro_properties, instantiate, BoxedValue, FerroObjectImpl, FerroProperty, Ref,
9    StyledElementImpl, StyledProperty, Visual,
10};
11use ferroui_controls::primitives::AdornerLayer;
12use ferroui_controls::Control;
13use ferroui_xaml_interactivity::{
14    EventTriggerBase, EventTriggerBaseImpl, EventTriggerBaseImplExt, StyledElementBehaviorImpl,
15};
16
17/// A behavior that visualizes events on the attached control for debugging purposes: each time
18/// the event fires, the actions run and the control is highlighted for
19/// [`duration`](Self::duration).
20#[repr(C)]
21pub struct VisualDebugBehavior {
22    base: EventTriggerBase,
23}
24
25ferro_class!(VisualDebugBehavior: EventTriggerBase);
26ferro_class_info!(VisualDebugBehavior { new: VisualDebugBehavior::new });
27ferroui_base::ferro_impl_classes!(
28    VisualDebugBehavior: FerroObjectImpl,
29    StyledElementImpl,
30    StyledElementBehaviorImpl,
31);
32
33ferro_properties! {
34    impl VisualDebugBehavior {
35        /// Identifies the `HighlightColor` property.
36        pub fn highlight_color_property() -> StyledProperty<Color> {
37            FerroProperty::register::<VisualDebugBehavior, _>("HighlightColor", Colors::RED)
38        }
39
40        /// Identifies the `Duration` property.
41        pub fn duration_property() -> StyledProperty<TimeSpan> {
42            FerroProperty::register::<VisualDebugBehavior, _>("Duration", TimeSpan::from_seconds(0.5))
43        }
44    }
45}
46
47ferroui_base::ferro_overrides! {
48    impl EventTriggerBaseImpl for VisualDebugBehavior {
49        fn on_event(this: &Self, event_args: Option<&BoxedValue>) {
50            // Execute any associated actions.
51            Self::parent_on_event(this, event_args);
52
53            if let Some(control) = this.associated_object().and_then(|associated_object| associated_object.cast::<Control>()) {
54                this.show_highlight(&control);
55            }
56        }
57    }
58}
59
60impl VisualDebugBehavior {
61    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
62    pub fn construct() -> Self {
63        Self { base: EventTriggerBase::construct() }
64    }
65
66    /// Creates a behavior.
67    pub fn new() -> Ref<Self> {
68        instantiate(Self::construct())
69    }
70
71    /// Gets or sets the color used to highlight the control when the event fires.
72    pub fn highlight_color(&self) -> Color {
73        self.get_value(Self::highlight_color_property())
74    }
75
76    pub fn set_highlight_color(&self, value: Color) {
77        self.set_value(Self::highlight_color_property(), value)
78    }
79
80    /// Gets or sets the duration of the highlight.
81    pub fn duration(&self) -> TimeSpan {
82        self.get_value(Self::duration_property())
83    }
84
85    pub fn set_duration(&self, value: TimeSpan) {
86        self.set_value(Self::duration_property(), value)
87    }
88
89    fn show_highlight(&self, control: &Ref<Control>) {
90        let visual: &Visual = control;
91        let Some(adorner_layer) = AdornerLayer::get_adorner_layer(visual) else {
92            return;
93        };
94
95        let brush = SolidColorBrush::with_color(self.highlight_color());
96        let adorner = VisualDebugAdorner::new(control.clone(), brush);
97
98        let adorner_visual: &Visual = &adorner;
99        AdornerLayer::set_adorned_element(adorner_visual, control);
100        adorner_layer.children().add(adorner.clone());
101
102        let _ = DispatcherTimer::run_once(
103            move || {
104                adorner_layer.children().remove(adorner.clone());
105            },
106            self.duration().to_duration().unwrap_or_default(),
107            DispatcherPriority::BACKGROUND,
108        );
109    }
110}