ferroui_xaml_interactions_custom/render_target/
render_target_bitmap_trigger.rs1use 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#[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 pub fn target_property() -> StyledProperty<Option<Rc<dyn IRenderTargetBitmapRenderHost>>> {
42 FerroProperty::register::<RenderTargetBitmapTrigger, _>("Target", None)
43 }
44
45 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 pub fn construct() -> Self {
71 Self { base: StyledElementTrigger::construct(), timer: RefCell::new(None) }
72 }
73
74 pub fn new() -> Ref<Self> {
76 instantiate(Self::construct())
77 }
78
79 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 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(¶meter));
132 }
133}