ferroui_base/media/
drawing_image.rs1use crate::media::ref_adapter::RefAdapter;
2use crate::media::{Drawing, DrawingContext, IAffectsRender, IImage};
3use crate::reactive::{Disposable, IDisposable};
4use crate::utilities::HandlerList;
5use crate::{
6 ferro_class, ferro_property, instantiate, FerroObject, FerroObjectImpl, FerroObjectImplExt, FerroProperty,
7 FerroPropertyChangedEventArgs, Matrix, Nullable, Rect, Ref, Size, StyledProperty,
8};
9use std::any::Any;
10use std::cell::RefCell;
11use std::rc::Rc;
12
13#[repr(C)]
15pub struct DrawingImage {
16 base: FerroObject,
17 invalidated: HandlerList<dyn Fn()>,
18 drawing_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
19}
20
21ferro_class!(DrawingImage: FerroObject);
22crate::ferro_class_info!(DrawingImage { new: DrawingImage::new, interfaces: [std::rc::Rc<dyn crate::media::IImage>] });
23
24impl FerroObjectImpl for DrawingImage {
25 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
26 Self::parent_on_property_changed(this, change);
27
28 if change.property() == Self::drawing_property().as_property() {
29 let new_value = change.get_new_value::<Option<Ref<Drawing>>>();
30
31 if let Some(subscription) = this.drawing_subscription.take() {
32 subscription.dispose();
33 }
34 if let Some(new_value) = new_value {
35 let weak = this.to_ref().downgrade();
36 let subscription = new_value.invalidated(move || {
37 if let Some(this) = weak.upgrade() {
38 this.raise_invalidated();
39 }
40 });
41 *this.drawing_subscription.borrow_mut() = Some(subscription);
42 }
43
44 this.raise_invalidated();
45 } else if change.property() == Self::viewbox_property().as_property() {
46 this.raise_invalidated();
47 }
48 }
49}
50
51crate::ferro_properties! { impl DrawingImage {
52 ferro_property!(
53 pub fn drawing_property() -> StyledProperty<Option<Ref<Drawing>>> {
55 FerroProperty::register::<DrawingImage, _>("Drawing", None)
56 }
57 );
58
59 ferro_property!(
60 pub fn viewbox_property() -> StyledProperty<Option<Rect>> {
62 FerroProperty::register::<DrawingImage, _>("Viewbox", None)
63 }
64 );
65} }
66
67impl DrawingImage {
68 pub fn construct() -> Self {
70 Self {
71 base: FerroObject::construct(),
72 invalidated: HandlerList::new(),
73 drawing_subscription: RefCell::new(None),
74 }
75 }
76
77 pub fn new() -> Ref<Self> {
78 instantiate(Self::construct())
79 }
80
81 pub fn with_drawing(drawing: impl Into<Nullable<Drawing>>) -> Ref<Self> {
83 let result = Self::new();
84 result.set_drawing(drawing);
85 result
86 }
87
88 pub fn invalidated(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
90 let token = self.invalidated.add(Rc::new(handler));
91 let weak = self.to_ref().downgrade();
92 Disposable::create(move || {
93 if let Some(this) = weak.upgrade() {
94 this.invalidated.remove(token);
95 }
96 })
97 }
98
99 pub fn raise_invalidated(&self) {
101 if self.invalidated.is_empty() {
102 return;
103 }
104 for (_, handler) in self.invalidated.snapshot().iter() {
105 handler();
106 }
107 }
108
109 pub fn drawing(&self) -> Option<Ref<Drawing>> {
111 self.get_value(Self::drawing_property())
112 }
113
114 pub fn set_drawing(&self, value: impl Into<Nullable<Drawing>>) {
115 self.set_value(Self::drawing_property(), value.into().0)
116 }
117
118 pub fn viewbox(&self) -> Option<Rect> {
125 self.get_value(Self::viewbox_property())
126 }
127
128 pub fn set_viewbox(&self, value: Option<Rect>) {
129 self.set_value(Self::viewbox_property(), value)
130 }
131
132 pub fn size(&self) -> Size {
134 self.get_bounds().size()
135 }
136
137 fn get_bounds(&self) -> Rect {
138 match self.viewbox() {
139 Some(viewbox) => viewbox,
140 None => self.drawing().map(|drawing| drawing.get_bounds()).unwrap_or_default(),
141 }
142 }
143
144 pub fn draw(&self, context: &mut DrawingContext<'_>, source_rect: Rect, dest_rect: Rect) {
146 let Some(drawing) = self.drawing() else { return };
147 if source_rect.size() == Size::default() || dest_rect.size() == Size::default() {
148 return;
149 }
150
151 let bounds = self.get_bounds();
152
153 if bounds.size() == Size::default() {
154 return;
155 }
156
157 let scale = Matrix::create_scale(dest_rect.width / source_rect.width, dest_rect.height / source_rect.height);
158 let translate = Matrix::create_translation(
159 -source_rect.x + dest_rect.x - bounds.x,
160 -source_rect.y + dest_rect.y - bounds.y,
161 );
162
163 let clip = context.push_clip(dest_rect);
164 let transform = context.push_transform(translate * scale);
165 drawing.draw(context);
166 context.pop(transform);
167 context.pop(clip);
168 }
169}
170
171impl IImage for RefAdapter<DrawingImage> {
172 fn size(&self) -> Size {
173 self.0.size()
174 }
175
176 fn draw(&self, context: &mut DrawingContext<'_>, source_rect: Rect, dest_rect: Rect) {
177 self.0.draw(context, source_rect, dest_rect)
178 }
179
180 fn as_any(&self) -> &dyn Any {
181 &*self.0
182 }
183
184 fn as_object(&self) -> Option<&FerroObject> {
185 Some(self.object())
186 }
187
188 fn as_affects_render(&self) -> Option<&dyn IAffectsRender> {
189 Some(&*self.0)
190 }
191
192 fn reference_id(&self) -> *const () {
193 RefAdapter::reference_id(self)
194 }
195}
196
197impl From<Ref<DrawingImage>> for Rc<dyn IImage> {
198 #[inline]
199 fn from(value: Ref<DrawingImage>) -> Self {
200 Rc::new(RefAdapter(value))
201 }
202}
203
204impl From<&Ref<DrawingImage>> for Rc<dyn IImage> {
205 #[inline]
206 fn from(value: &Ref<DrawingImage>) -> Self {
207 Rc::new(RefAdapter(value.clone()))
208 }
209}