ferroui_base/media/
rectangle_geometry.rs1use crate::media::{Geometry, GeometryImpl, GeometryBuilder};
2use crate::platform::{self, IGeometryImpl};
3use crate::{
4 ferro_class, ferro_property, instantiate, FerroObjectImpl, FerroProperty, Rect, Ref,
5 StyledProperty,
6};
7use std::sync::Arc;
8
9#[repr(C)]
11pub struct RectangleGeometry {
12 base: Geometry,
13}
14
15ferro_class!(RectangleGeometry: Geometry);
16crate::ferro_class_info!(RectangleGeometry { new: RectangleGeometry::new });
17
18crate::ferro_impl_classes!(RectangleGeometry: FerroObjectImpl);
19
20impl GeometryImpl for RectangleGeometry {
21 fn clone_geometry(this: &Self) -> Ref<Geometry> {
22 RectangleGeometry::with_rect_and_radii(this.rect(), this.radius_x(), this.radius_y()).upcast()
23 }
24
25 fn create_defining_geometry(this: &Self) -> Option<Arc<dyn IGeometryImpl>> {
26 let radius_x = this.radius_x();
27 let radius_y = this.radius_y();
28 let factory = platform::render_interface();
29
30 if radius_x == 0.0 && radius_y == 0.0 {
31 Some(factory.create_rectangle_geometry(this.rect()))
33 } else {
34 let mut builder = factory.create_stream_geometry_builder(None);
35 GeometryBuilder::draw_rounded_corners_rectangle(&mut *builder, this.rect(), radius_x, radius_y);
36 let geometry = builder.to_geometry();
37 builder.dispose();
38 Some(geometry)
39 }
40 }
41}
42
43crate::ferro_properties! { impl RectangleGeometry {
44 ferro_property!(pub fn radius_x_property() -> StyledProperty<f64> {
45 FerroProperty::register::<RectangleGeometry, _>("RadiusX", 0.0)
46 });
47
48 ferro_property!(pub fn radius_y_property() -> StyledProperty<f64> {
49 FerroProperty::register::<RectangleGeometry, _>("RadiusY", 0.0)
50 });
51
52 ferro_property!(pub fn rect_property() -> StyledProperty<Rect> {
53 FerroProperty::register::<RectangleGeometry, _>("Rect", Rect::default())
54 });
55} }
56
57impl RectangleGeometry {
58 fn static_constructor() {
59 Geometry::affects_geometry(&[
60 Self::radius_x_property().as_property(),
61 Self::radius_y_property().as_property(),
62 Self::rect_property().as_property(),
63 ]);
64 }
65
66 pub fn construct() -> Self {
68 Self { base: Geometry::construct() }
69 }
70
71 pub fn new() -> Ref<Self> {
72 instantiate(Self::construct())
73 }
74
75 pub fn with_rect(rect: Rect) -> Ref<Self> {
77 let result = Self::new();
78 result.set_rect(rect);
79 result
80 }
81
82 pub fn with_rect_and_radii(rect: Rect, radius_x: f64, radius_y: f64) -> Ref<Self> {
84 let result = Self::new();
85 result.set_rect(rect);
86 result.set_radius_x(radius_x);
87 result.set_radius_y(radius_y);
88 result
89 }
90
91 pub fn radius_x(&self) -> f64 {
93 self.get_value(Self::radius_x_property())
94 }
95
96 pub fn set_radius_x(&self, value: f64) {
97 self.set_value(Self::radius_x_property(), value)
98 }
99
100 pub fn radius_y(&self) -> f64 {
102 self.get_value(Self::radius_y_property())
103 }
104
105 pub fn set_radius_y(&self, value: f64) {
106 self.set_value(Self::radius_y_property(), value)
107 }
108
109 pub fn rect(&self) -> Rect {
111 self.get_value(Self::rect_property())
112 }
113
114 pub fn set_rect(&self, value: Rect) {
115 self.set_value(Self::rect_property(), value)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::media::geometry::tests::with_mock_render_interface;
123 use crate::media::RotateTransform;
124
125 #[test]
126 fn rectangle_with_transform_can_be_changed() {
127 with_mock_render_interface(|_| {
128 let target = RectangleGeometry::new();
129 target.set_rect(Rect::new(0.0, 0.0, 100.0, 100.0));
130 target.set_transform(RotateTransform::with_angle(45.0));
131
132 target.set_rect(Rect::new(50.0, 50.0, 150.0, 150.0));
133
134 assert!(target.platform_impl().unwrap().as_transformed_geometry().is_some());
135 });
136 }
137
138 #[test]
139 fn rounded_rectangle_is_drawn_into_a_stream_geometry() {
140 with_mock_render_interface(|factory| {
141 let target = RectangleGeometry::with_rect_and_radii(Rect::new(0.0, 0.0, 100.0, 50.0), 10.0, 5.0);
142 assert!(target.platform_impl().is_some());
143 assert_eq!(vec!["stream"], *factory.created.borrow());
144 assert_eq!(
145 vec![
146 "begin 10, 0 true",
147 "line 90, 0 true",
148 "arc 100, 5 10, 5 0 false Clockwise true",
149 "line 100, 45 true",
150 "arc 90, 50 10, 5 0 false Clockwise true",
151 "line 10, 50 true",
152 "arc 0, 45 10, 5 0 false Clockwise true",
153 "line 0, 5 true",
154 "arc 10, 0 10, 5 0 false Clockwise true",
155 "end true",
156 "dispose",
157 ],
158 *factory.streams.borrow()[0].lock().unwrap()
159 );
160
161 let clone = target.clone_geometry().cast::<RectangleGeometry>().unwrap();
162 assert_eq!((Rect::new(0.0, 0.0, 100.0, 50.0), 10.0, 5.0), (clone.rect(), clone.radius_x(), clone.radius_y()));
163
164 target.set_radius_x(0.0);
165 target.set_radius_y(0.0);
166 assert!(target.platform_impl().is_some());
167 assert_eq!(vec!["stream", "rectangle 0, 0, 100, 50"], *factory.created.borrow());
168 });
169 }
170}