ferroui_base/media/
linear_gradient_brush.rs1use crate::media::immutable::ImmutableLinearGradientBrush;
2use crate::media::{BrushImpl, BrushImplExt, GradientBrush, GradientBrushImpl, IImmutableBrush, ServerBrushFactory};
3use crate::rendering::composition::generated::ServerCompositionSimpleLinearGradientBrushProps;
4use crate::rendering::composition::server::ServerCompositionSimpleLinearGradientBrush;
5use crate::rendering::composition::transport::BatchStreamWriter;
6use crate::rendering::composition::Compositor;
7use crate::{
8 ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObjectImpl, FerroProperty, Ref, RelativePoint,
9 StyledProperty,
10};
11use std::rc::Rc;
12
13#[repr(C)]
15pub struct LinearGradientBrush {
16 base: GradientBrush,
17}
18
19ferro_class!(LinearGradientBrush: GradientBrush);
20crate::ferro_class_info!(LinearGradientBrush { new: LinearGradientBrush::new });
21ferro_impl_classes!(LinearGradientBrush: FerroObjectImpl);
22
23impl BrushImpl for LinearGradientBrush {
24 fn factory(_this: &Self) -> Option<ServerBrushFactory> {
25 Some(|c| ServerCompositionSimpleLinearGradientBrush::new(c))
26 }
27
28 fn serialize_changes(this: &Self, c: &Compositor, writer: &mut BatchStreamWriter<'_>) {
29 Self::parent_serialize_changes(this, c, writer);
30 ServerCompositionSimpleLinearGradientBrushProps::serialize_all_changes(writer, this.start_point(), this.end_point());
31 }
32}
33
34impl GradientBrushImpl for LinearGradientBrush {
35 fn to_immutable(this: &Self) -> Rc<dyn IImmutableBrush> {
36 Rc::new(ImmutableLinearGradientBrush::from_brush(this))
37 }
38}
39
40crate::ferro_properties! { impl LinearGradientBrush {
41 ferro_property!(pub fn start_point_property() -> StyledProperty<RelativePoint> {
42 FerroProperty::register::<LinearGradientBrush, _>("StartPoint", RelativePoint::TOP_LEFT)
43 });
44
45 ferro_property!(pub fn end_point_property() -> StyledProperty<RelativePoint> {
46 FerroProperty::register::<LinearGradientBrush, _>("EndPoint", RelativePoint::BOTTOM_RIGHT)
47 });
48} }
49
50impl LinearGradientBrush {
51 pub fn construct() -> Self {
53 Self { base: GradientBrush::construct() }
54 }
55
56 pub fn new() -> Ref<Self> {
57 instantiate(Self::construct())
58 }
59
60 pub fn start_point(&self) -> RelativePoint {
62 self.get_value(Self::start_point_property())
63 }
64
65 pub fn set_start_point(&self, value: RelativePoint) {
66 self.set_value(Self::start_point_property(), value)
67 }
68
69 pub fn end_point(&self) -> RelativePoint {
71 self.get_value(Self::end_point_property())
72 }
73
74 pub fn set_end_point(&self, value: RelativePoint) {
75 self.set_value(Self::end_point_property(), value)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::media::{Colors, GradientSpreadMethod, GradientStop, GradientStops, IBrush};
83 use crate::RelativeUnit;
84 use std::cell::Cell;
85
86 fn assert_invalidated(target: &Ref<LinearGradientBrush>, action: impl FnOnce()) {
87 let raised = Rc::new(Cell::new(false));
88 let r = raised.clone();
89 let subscription = target.invalidated(move || r.set(true));
90 action();
91 assert!(raised.get());
92 subscription.dispose();
93 }
94
95 #[test]
96 fn changing_start_point_raises_invalidated() {
97 let target = LinearGradientBrush::new();
98 target.set_start_point(RelativePoint::default());
99 assert_invalidated(&target, || target.set_start_point(RelativePoint::new(10.0, 10.0, RelativeUnit::Absolute)));
100 }
101
102 #[test]
103 fn changing_end_point_raises_invalidated() {
104 let target = LinearGradientBrush::new();
105 target.set_end_point(RelativePoint::default());
106 assert_invalidated(&target, || target.set_end_point(RelativePoint::new(10.0, 10.0, RelativeUnit::Absolute)));
107 }
108
109 #[test]
110 fn changing_gradient_stops_raises_invalidated() {
111 let target = LinearGradientBrush::new();
112 target.set_gradient_stops(GradientStops::from_items([GradientStop::with_color_and_offset(Colors::RED, 0.0)]));
113 assert_invalidated(&target, || {
114 target
115 .set_gradient_stops(GradientStops::from_items([GradientStop::with_color_and_offset(Colors::GREEN, 0.0)]))
116 });
117 }
118
119 #[test]
120 fn adding_gradient_stop_raises_invalidated() {
121 let target = LinearGradientBrush::new();
122 target.set_gradient_stops(GradientStops::from_items([GradientStop::with_color_and_offset(Colors::RED, 0.0)]));
123 assert_invalidated(&target, || {
124 target.gradient_stops().add(GradientStop::with_color_and_offset(Colors::GREEN, 1.0))
125 });
126 }
127
128 #[test]
129 fn changing_gradient_stop_offset_raises_invalidated() {
130 let target = LinearGradientBrush::new();
131 target.set_gradient_stops(GradientStops::from_items([GradientStop::with_color_and_offset(Colors::RED, 0.0)]));
132 assert_invalidated(&target, || target.gradient_stops().get(0).set_offset(0.5));
133 }
134
135 #[test]
136 fn replaced_gradient_stops_no_longer_invalidate() {
137 let target = LinearGradientBrush::new();
138 let old = target.gradient_stops();
139 let stop = GradientStop::with_color_and_offset(Colors::RED, 0.0);
140 old.add(stop.clone());
141 target.set_gradient_stops(GradientStops::new());
142 let raised = Rc::new(Cell::new(false));
143 let r = raised.clone();
144 target.invalidated(move || r.set(true));
145 old.add(GradientStop::new());
146 stop.set_offset(1.0);
147 assert!(!raised.get());
148 }
149
150 #[test]
151 fn defaults_and_immutable_copy() {
152 let target = LinearGradientBrush::new();
153 assert_eq!(RelativePoint::TOP_LEFT, target.start_point());
154 assert_eq!(RelativePoint::BOTTOM_RIGHT, target.end_point());
155 target.gradient_stops().add(GradientStop::with_color_and_offset(Colors::RED, 2.0));
156 target.set_spread_method(GradientSpreadMethod::Reflect);
157 target.set_opacity(0.25);
158
159 let brush: Rc<dyn IBrush> = target.clone().into();
160 let linear = brush.as_linear_gradient_brush().unwrap();
161 assert_eq!(RelativePoint::BOTTOM_RIGHT, linear.end_point());
162 assert_eq!(1, linear.gradient_stops().len());
163 assert_eq!(2.0, linear.gradient_stops()[0].offset());
164
165 let immutable = crate::media::BrushExtensions::to_immutable(&brush);
166 let linear = immutable.as_linear_gradient_brush().unwrap();
167 assert_eq!(0.25, linear.opacity());
168 assert_eq!(GradientSpreadMethod::Reflect, linear.spread_method());
169 assert_eq!(1.0, linear.gradient_stops()[0].offset());
171 assert_eq!(Colors::RED, linear.gradient_stops()[0].color());
172 assert!(immutable.as_mutable_brush().is_none());
173 }
174}