ferroui_base/media/
drawing.rs1use crate::media::effects::{Effect, IEffect};
2use crate::media::{DrawingContext, IImage, Transform};
3use crate::reactive::{Disposable, IDisposable};
4use crate::utilities::HandlerList;
5use crate::{
6 ferro_class, FerroObject, FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Rect,
7 Ref,
8};
9use std::any::Any;
10use std::cell::RefCell;
11use std::rc::Rc;
12
13type ValueSubscription = (&'static FerroProperty, Rc<dyn IDisposable>);
14
15#[repr(C)]
17pub struct Drawing {
18 base: FerroObject,
19 invalidated: HandlerList<dyn Fn()>,
20 value_subscriptions: RefCell<Vec<ValueSubscription>>,
21}
22
23ferro_class! {
24 Drawing: FerroObject, virtuals DrawingImpl: FerroObjectImpl {
25 fn draw_core(this, context: &mut DrawingContext);
27 fn get_bounds(this) -> Rect;
29 }
30}
31
32impl FerroObjectImpl for Drawing {
33 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
34 Self::parent_on_property_changed(this, change);
35
36 this.update_value_subscription(change.property(), None);
41 this.update_value_subscription(change.property(), Some(change.new_value()));
42 this.raise_invalidated();
43 }
44}
45
46impl DrawingImpl for Drawing {
47 fn draw_core(_this: &Self, _context: &mut DrawingContext) {
48 panic!("Drawing is abstract: 'draw_core' must be implemented by the deriving class")
49 }
50
51 fn get_bounds(_this: &Self) -> Rect {
52 panic!("Drawing is abstract: 'get_bounds' must be implemented by the deriving class")
53 }
54}
55
56impl Drawing {
57 pub fn construct() -> Self {
59 Self {
60 base: FerroObject::construct(),
61 invalidated: HandlerList::new(),
62 value_subscriptions: RefCell::new(Vec::new()),
63 }
64 }
65
66 pub fn draw(&self, context: &mut DrawingContext<'_>) {
68 self.draw_core(context)
69 }
70
71 pub(crate) fn invalidated(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
74 let token = self.invalidated.add(Rc::new(handler));
75 let weak = self.to_ref().downgrade();
76 Disposable::create(move || {
77 if let Some(this) = weak.upgrade() {
78 this.invalidated.remove(token);
79 }
80 })
81 }
82
83 pub(crate) fn raise_invalidated(&self) {
84 if self.invalidated.is_empty() {
85 return;
86 }
87 for (_, handler) in self.invalidated.snapshot().iter() {
88 handler();
89 }
90 }
91
92 fn update_value_subscription(&self, property: &'static FerroProperty, value: Option<&dyn Any>) {
95 let Some(value) = value else {
96 let position = self.value_subscriptions.borrow().iter().position(|(p, _)| *p == property);
97 if let Some(position) = position {
98 let (_, subscription) = self.value_subscriptions.borrow_mut().swap_remove(position);
99 subscription.dispose();
100 }
101 return;
102 };
103
104 let weak = self.to_ref().downgrade();
105 let value_invalidated = move || {
106 if let Some(this) = weak.upgrade() {
107 this.raise_invalidated();
108 }
109 };
110
111 let subscription = if let Some(value) = value.downcast_ref::<Option<Ref<Transform>>>() {
112 value.as_ref().map(|transform| transform.changed(value_invalidated))
116 } else if let Some(value) = value.downcast_ref::<Option<Rc<dyn IEffect>>>() {
117 value
121 .as_ref()
122 .and_then(|effect| effect.as_object())
123 .and_then(|object| object.downcast_ref::<Effect>())
124 .map(|effect| effect.invalidated(value_invalidated))
125 } else if let Some(value) = value.downcast_ref::<Option<Rc<dyn IImage>>>() {
126 value
127 .as_ref()
128 .and_then(|image| image.as_affects_render())
129 .map(|affects_render| affects_render.invalidated(Rc::new(value_invalidated)))
130 } else {
131 None
132 };
133
134 if let Some(subscription) = subscription {
135 self.value_subscriptions.borrow_mut().push((property, subscription));
136 }
137 }
138}