Skip to main content

ferroui_base/media/
image_brush.rs

1use crate::media::immutable::ImmutableImageBrush;
2use crate::media::ref_adapter::RefAdapter;
3use crate::media::{BrushImpl, BrushImplExt, IImageBrushSource, IImmutableBrush, ServerBrushFactory, TileBrush};
4use crate::rendering::composition::server::ServerCompositionSimpleImageBrush;
5use crate::rendering::composition::transport::BatchStreamWriter;
6use crate::rendering::composition::Compositor;
7use crate::{ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObjectImpl, FerroProperty, Ref, StyledProperty};
8use std::rc::Rc;
9
10/// Paints an area with an image.
11#[repr(C)]
12pub struct ImageBrush {
13    base: TileBrush,
14}
15
16ferro_class!(ImageBrush: TileBrush);
17crate::ferro_class_info!(ImageBrush { new: ImageBrush::new });
18ferro_impl_classes!(ImageBrush: FerroObjectImpl);
19
20impl BrushImpl for ImageBrush {
21    fn factory(_this: &Self) -> Option<ServerBrushFactory> {
22        Some(|c| ServerCompositionSimpleImageBrush::new(c))
23    }
24
25    fn serialize_changes(this: &Self, c: &Compositor, writer: &mut BatchStreamWriter<'_>) {
26        Self::parent_serialize_changes(this, c, writer);
27        let cloned_ref = this.source().and_then(|source| source.bitmap().map(|bitmap| bitmap.clone_ref()));
28        writer.write_value(cloned_ref);
29    }
30}
31
32crate::ferro_properties! { impl ImageBrush {
33    ferro_property!(pub fn source_property() -> StyledProperty<Option<Rc<dyn IImageBrushSource>>> {
34        FerroProperty::register::<ImageBrush, _>("Source", None)
35    });
36} }
37
38impl ImageBrush {
39    /// Creates the class data.
40    pub fn construct() -> Self {
41        Self { base: TileBrush::construct() }
42    }
43
44    pub fn new() -> Ref<Self> {
45        instantiate(Self::construct())
46    }
47
48    /// Creates a brush that draws `source`.
49    pub fn with_source(source: Option<Rc<dyn IImageBrushSource>>) -> Ref<Self> {
50        let result = Self::new();
51        result.set_source(source);
52        result
53    }
54
55    /// The image to draw.
56    pub fn source(&self) -> Option<Rc<dyn IImageBrushSource>> {
57        self.get_value(Self::source_property())
58    }
59
60    pub fn set_source(&self, value: Option<Rc<dyn IImageBrushSource>>) {
61        self.set_value(Self::source_property(), value)
62    }
63
64    /// Creates an immutable clone of the brush.
65    pub fn to_immutable(&self) -> Rc<dyn IImmutableBrush> {
66        Rc::new(ImmutableImageBrush::from_brush(&RefAdapter(self.to_ref())))
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::media::{AlignmentX, BrushExtensions, IBrush, Stretch, TileMode};
74    use crate::platform::IBitmapImpl;
75    use crate::utilities::RefCounted;
76    use crate::RelativeRect;
77    use std::cell::Cell;
78
79    struct MockSource;
80
81    impl IImageBrushSource for MockSource {
82        fn bitmap(&self) -> Option<&RefCounted<crate::platform::SharedBitmapImpl>> {
83            None
84        }
85    }
86
87    #[test]
88    fn changing_source_raises_invalidated() {
89        let bitmap1: Rc<dyn IImageBrushSource> = Rc::new(MockSource);
90        let bitmap2: Rc<dyn IImageBrushSource> = Rc::new(MockSource);
91        let target = ImageBrush::with_source(Some(bitmap1));
92
93        let raised = Rc::new(Cell::new(false));
94        let r = raised.clone();
95        target.invalidated(move || r.set(true));
96        target.set_source(Some(bitmap2));
97        assert!(raised.get());
98    }
99
100    // --- not from upstream ---
101
102    #[test]
103    fn setting_the_same_source_does_not_raise_invalidated() {
104        let bitmap: Rc<dyn IImageBrushSource> = Rc::new(MockSource);
105        let target = ImageBrush::with_source(Some(bitmap.clone()));
106        let raised = Rc::new(Cell::new(false));
107        let r = raised.clone();
108        target.invalidated(move || r.set(true));
109        target.set_source(Some(bitmap));
110        assert!(!raised.get());
111    }
112
113    #[test]
114    fn defaults_interfaces_and_immutable_copy() {
115        let source: Rc<dyn IImageBrushSource> = Rc::new(MockSource);
116        let target = ImageBrush::with_source(Some(source.clone()));
117        assert_eq!(AlignmentX::Center, target.alignment_x());
118        assert_eq!(RelativeRect::FILL, target.destination_rect());
119        assert_eq!(RelativeRect::FILL, target.source_rect());
120        assert_eq!(Stretch::Uniform, target.stretch());
121        assert_eq!(TileMode::None, target.tile_mode());
122
123        target.set_tile_mode(TileMode::FlipXY);
124        target.set_opacity(0.5);
125
126        let brush: Rc<dyn IBrush> = target.clone().into();
127        assert!(brush.as_solid_color_brush().is_none());
128        assert_eq!(TileMode::FlipXY, brush.as_tile_brush().unwrap().tile_mode());
129        assert!(*brush.as_image_brush().unwrap().source().unwrap() == *source);
130        assert!(brush.as_mutable_brush().is_some());
131        assert!(brush.as_scene_brush().is_none());
132
133        let immutable = BrushExtensions::to_immutable(&brush);
134        target.set_tile_mode(TileMode::Tile);
135        let image = immutable.as_image_brush().unwrap();
136        assert_eq!(TileMode::FlipXY, image.tile_mode());
137        assert_eq!(0.5, image.opacity());
138        assert_eq!(Stretch::Uniform, image.stretch());
139        assert!(*image.source().unwrap() == *source);
140        assert!(immutable.as_mutable_brush().is_none());
141    }
142}