ferroui_base/media/
image_brush.rs1use 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#[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 pub fn construct() -> Self {
41 Self { base: TileBrush::construct() }
42 }
43
44 pub fn new() -> Ref<Self> {
45 instantiate(Self::construct())
46 }
47
48 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 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 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 #[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}