Skip to main content

ferroui_xaml_interactions_custom/writeable_bitmap/
writeable_bitmap_timer_trigger.rs

1//! Port of `WriteableBitmap/WriteableBitmapTimerTrigger.cs`.
2
3use crate::WriteableBitmapRef;
4use ferroui_base::reactive::IDisposable;
5use ferroui_base::threading::DispatcherTimer;
6use ferroui_base::{
7    ferro_class, ferro_class_info, ferro_properties, instantiate, BoxedValue, FerroObjectImpl, FerroProperty, Ref,
8    StyledElementImpl, StyledProperty,
9};
10use ferroui_xaml_interactivity::{
11    Interaction, StyledElementBehaviorImpl, StyledElementBehaviorImplExt, StyledElementTrigger,
12};
13use std::cell::RefCell;
14use std::rc::Rc;
15use std::time::Duration;
16
17/// A trigger that fires its actions on a timer and passes a
18/// [`WriteableBitmap`](ferroui_base::media::imaging::WriteableBitmap) as parameter.
19#[repr(C)]
20pub struct WriteableBitmapTimerTrigger {
21    base: StyledElementTrigger,
22    timer: RefCell<Option<(Rc<DispatcherTimer>, Rc<dyn IDisposable>)>>,
23}
24
25ferro_class!(WriteableBitmapTimerTrigger: StyledElementTrigger);
26ferro_class_info!(WriteableBitmapTimerTrigger {
27    new: WriteableBitmapTimerTrigger::new,
28    markup: {
29        property_attributes: [
30            Bitmap: [ResolveByName],
31        ],
32    },
33});
34ferroui_base::ferro_impl_classes!(WriteableBitmapTimerTrigger: FerroObjectImpl, StyledElementImpl);
35
36ferro_properties! {
37    impl WriteableBitmapTimerTrigger {
38        /// Identifies the `Bitmap` property.
39        pub fn bitmap_property() -> StyledProperty<Option<WriteableBitmapRef>> {
40            FerroProperty::register::<WriteableBitmapTimerTrigger, _>("Bitmap", None)
41        }
42
43        /// Identifies the `MillisecondsPerTick` property.
44        pub fn milliseconds_per_tick_property() -> StyledProperty<i32> {
45            FerroProperty::register::<WriteableBitmapTimerTrigger, _>("MillisecondsPerTick", 16)
46        }
47    }
48}
49
50ferroui_base::ferro_overrides! {
51    impl StyledElementBehaviorImpl for WriteableBitmapTimerTrigger {
52        fn on_attached(this: &Self) {
53            Self::parent_on_attached(this);
54
55            this.start_timer();
56        }
57
58        fn on_detaching(this: &Self) {
59            this.stop_timer();
60
61            Self::parent_on_detaching(this);
62        }
63    }
64}
65
66impl WriteableBitmapTimerTrigger {
67    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
68    pub fn construct() -> Self {
69        Self { base: StyledElementTrigger::construct(), timer: RefCell::new(None) }
70    }
71
72    /// Creates a trigger.
73    pub fn new() -> Ref<Self> {
74        instantiate(Self::construct())
75    }
76
77    /// Gets or sets the bitmap passed to actions. This is a property of the object model.
78    pub fn bitmap(&self) -> Option<WriteableBitmapRef> {
79        self.get_value(Self::bitmap_property())
80    }
81
82    pub fn set_bitmap(&self, value: Option<WriteableBitmapRef>) {
83        self.set_value(Self::bitmap_property(), value)
84    }
85
86    /// Gets or sets the timer interval in milliseconds. This is a property of the object model.
87    pub fn milliseconds_per_tick(&self) -> i32 {
88        self.get_value(Self::milliseconds_per_tick_property())
89    }
90
91    pub fn set_milliseconds_per_tick(&self, value: i32) {
92        self.set_value(Self::milliseconds_per_tick_property(), value)
93    }
94
95    fn start_timer(&self) {
96        if self.timer.borrow().is_some() {
97            return;
98        }
99
100        let timer = DispatcherTimer::new();
101        timer.set_interval(Duration::from_millis(self.milliseconds_per_tick().max(0) as u64));
102        let trigger = self.to_ref().downgrade();
103        let tick = timer.tick(move |_timer: &DispatcherTimer| {
104            if let Some(trigger) = trigger.upgrade() {
105                trigger.on_tick();
106            }
107        });
108        timer.start();
109        *self.timer.borrow_mut() = Some((timer, tick));
110    }
111
112    fn stop_timer(&self) {
113        let timer = self.timer.borrow_mut().take();
114        if let Some((timer, tick)) = timer {
115            tick.dispose();
116            timer.stop();
117        }
118    }
119
120    fn on_tick(&self) {
121        if !self.is_enabled() {
122            return;
123        }
124
125        let sender = self.associated_object().map(|associated_object| Rc::new(associated_object) as BoxedValue);
126        let parameter = self.bitmap().map(|bitmap| Rc::new(bitmap) as BoxedValue);
127        Interaction::execute_actions(sender.as_ref(), self.actions().as_ref(), parameter.as_ref());
128    }
129}