Skip to main content

ferroui_xaml_interactions_custom/render_target/
render_target_bitmap_trigger.rs

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