Skip to main content

ferroui_xaml_interactions_custom/writeable_bitmap/
writeable_bitmap_behavior.rs

1//! Port of `WriteableBitmap/WriteableBitmapBehavior.cs`.
2
3use crate::{IWriteableBitmapRenderer, WriteableBitmapRef};
4use ferroui_base::media::imaging::WriteableBitmap;
5use ferroui_base::platform::{AlphaFormat, PixelFormat};
6use ferroui_base::{
7    ferro_class, ferro_class_info, ferro_properties, instantiate, DirectProperty, FerroObjectImpl, FerroProperty,
8    PixelSize, Ref, StaticType, StyledElementImpl, StyledProperty, Vector,
9};
10use ferroui_controls::Image;
11use ferroui_xaml_interactivity::{StyledElementBehaviorImpl, StyledElementBehaviorOfT};
12use std::cell::RefCell;
13use std::rc::Rc;
14
15/// Creates a [`WriteableBitmap`] and optionally renders it once using a renderer.
16#[repr(C)]
17pub struct WriteableBitmapBehavior {
18    base: StyledElementBehaviorOfT,
19    bitmap: RefCell<Option<WriteableBitmapRef>>,
20}
21
22ferro_class!(WriteableBitmapBehavior: StyledElementBehaviorOfT);
23ferro_class_info!(WriteableBitmapBehavior { new: WriteableBitmapBehavior::new });
24ferroui_base::ferro_impl_classes!(WriteableBitmapBehavior: FerroObjectImpl, StyledElementImpl);
25
26ferro_properties! {
27    impl WriteableBitmapBehavior {
28        /// Identifies the `PixelWidth` property.
29        pub fn pixel_width_property() -> StyledProperty<i32> {
30            FerroProperty::register::<WriteableBitmapBehavior, _>("PixelWidth", 256)
31        }
32
33        /// Identifies the `PixelHeight` property.
34        pub fn pixel_height_property() -> StyledProperty<i32> {
35            FerroProperty::register::<WriteableBitmapBehavior, _>("PixelHeight", 256)
36        }
37
38        /// Identifies the `Renderer` property.
39        pub fn renderer_property() -> StyledProperty<Option<Rc<dyn IWriteableBitmapRenderer>>> {
40            FerroProperty::register::<WriteableBitmapBehavior, _>("Renderer", None)
41        }
42
43        /// Identifies the `Bitmap` property.
44        pub fn bitmap_property() -> DirectProperty<WriteableBitmapBehavior, Option<WriteableBitmapRef>> {
45            FerroProperty::register_direct::<WriteableBitmapBehavior, _>("Bitmap", |behavior| behavior.bitmap(), None, None)
46        }
47    }
48}
49
50ferroui_base::ferro_overrides! {
51    impl StyledElementBehaviorImpl for WriteableBitmapBehavior {
52        fn on_attached_to_visual_tree(this: &Self) {
53            let bitmap = WriteableBitmapRef::new(WriteableBitmap::new(
54                PixelSize::new(this.pixel_width(), this.pixel_height()),
55                Vector::new(96.0, 96.0),
56                Some(PixelFormat::BGRA8888),
57                Some(AlphaFormat::Unpremul),
58            ));
59            this.set_bitmap(Some(bitmap.clone()));
60
61            if let Some(associated_object) = this.associated_object_as::<Image>() {
62                associated_object.set_source(Some(bitmap.as_image()));
63            }
64
65            if let Some(renderer) = this.renderer() {
66                renderer.render(&bitmap);
67            }
68        }
69
70        fn on_detached_from_visual_tree(this: &Self) {
71            if let Some(associated_object) = this.associated_object_as::<Image>() {
72                associated_object.set_source(None);
73            }
74
75            if let Some(bitmap) = this.bitmap() {
76                bitmap.dispose();
77            }
78            this.set_bitmap(None);
79        }
80    }
81}
82
83impl WriteableBitmapBehavior {
84    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
85    pub fn construct() -> Self {
86        Self { base: StyledElementBehaviorOfT::construct(<Image as StaticType>::TYPE), bitmap: RefCell::new(None) }
87    }
88
89    /// Creates a behavior.
90    pub fn new() -> Ref<Self> {
91        instantiate(Self::construct())
92    }
93
94    /// Gets or sets the width of the bitmap in pixels. This is a property of the object model.
95    pub fn pixel_width(&self) -> i32 {
96        self.get_value(Self::pixel_width_property())
97    }
98
99    pub fn set_pixel_width(&self, value: i32) {
100        self.set_value(Self::pixel_width_property(), value)
101    }
102
103    /// Gets or sets the height of the bitmap in pixels. This is a property of the object model.
104    pub fn pixel_height(&self) -> i32 {
105        self.get_value(Self::pixel_height_property())
106    }
107
108    pub fn set_pixel_height(&self, value: i32) {
109        self.set_value(Self::pixel_height_property(), value)
110    }
111
112    /// Gets or sets the renderer used to update the bitmap. This is a property of the object
113    /// model.
114    pub fn renderer(&self) -> Option<Rc<dyn IWriteableBitmapRenderer>> {
115        self.get_value(Self::renderer_property())
116    }
117
118    pub fn set_renderer(&self, value: Option<Rc<dyn IWriteableBitmapRenderer>>) {
119        self.set_value(Self::renderer_property(), value)
120    }
121
122    /// Gets the created bitmap.
123    pub fn bitmap(&self) -> Option<WriteableBitmapRef> {
124        self.bitmap.borrow().clone()
125    }
126
127    fn set_bitmap(&self, value: Option<WriteableBitmapRef>) {
128        self.set_and_raise(Self::bitmap_property(), &self.bitmap, value);
129    }
130
131    /// Invokes the renderer to update the bitmap.
132    pub fn render(&self) {
133        let (Some(bitmap), Some(renderer)) = (self.bitmap(), self.renderer()) else {
134            return;
135        };
136
137        renderer.render(&bitmap);
138        if let Some(associated_object) = self.associated_object_as::<Image>() {
139            associated_object.invalidate_visual();
140        }
141    }
142}