ferroui_base/media/
stream_geometry.rs1use crate::media::{FillRule, Geometry, GeometryImpl, PathMarkupParser, StreamGeometryContext, SweepDirection};
2use crate::platform::{self, IGeometryContext, IGeometryImpl, IStreamGeometryBuilder};
3use crate::utilities::FormatError;
4use crate::{ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, Point, Ref, Size};
5use std::cell::RefCell;
6use std::sync::Arc;
7
8#[repr(C)]
10pub struct StreamGeometry {
11 base: Geometry,
12 impl_: RefCell<Option<Arc<dyn IGeometryImpl>>>,
13}
14
15ferro_class! {
16 StreamGeometry: Geometry, virtuals StreamGeometryImpl: GeometryImpl {
17 fn open_core(this) -> Box<dyn IGeometryContext>;
19 }
20}
21crate::ferro_class_info!(StreamGeometry { new: StreamGeometry::new });
22ferro_impl_classes!(StreamGeometry: FerroObjectImpl);
23
24impl GeometryImpl for StreamGeometry {
25 fn clone_geometry(this: &Self) -> Ref<Geometry> {
26 let result = StreamGeometry::with_impl(this.create_defining_geometry());
27 result.set_transform(this.transform());
28 result.upcast()
29 }
30
31 fn create_defining_geometry(this: &Self) -> Option<Arc<dyn IGeometryImpl>> {
32 let mut impl_ = this.impl_.borrow_mut();
33 if impl_.is_none() {
34 let mut builder = Self::create_builder(None);
35 *impl_ = Some(builder.to_geometry());
36 builder.dispose();
37 }
38
39 impl_.clone()
40 }
41}
42
43impl StreamGeometryImpl for StreamGeometry {
44 fn open_core(this: &Self) -> Box<dyn IGeometryContext> {
45 let source = this.impl_.borrow().clone();
46 Box::new(BuilderWrapper { builder: Self::create_builder(source.as_deref()), owner: this.to_ref() })
47 }
48}
49
50impl StreamGeometry {
51 pub fn construct() -> Self {
53 Self { base: Geometry::construct(), impl_: RefCell::new(None) }
54 }
55
56 pub fn new() -> Ref<Self> {
57 instantiate(Self::construct())
58 }
59
60 fn with_impl(impl_: Option<Arc<dyn IGeometryImpl>>) -> Ref<Self> {
62 instantiate(Self { base: Geometry::construct(), impl_: RefCell::new(impl_) })
63 }
64
65 pub fn parse(s: &str) -> Result<Ref<StreamGeometry>, FormatError> {
67 let stream_geometry = StreamGeometry::new();
68
69 let mut builder = Self::create_builder(None);
70 {
71 let mut parser = PathMarkupParser::new(&mut *builder);
72 let result = parser.parse(s);
73 parser.dispose();
74 result?;
75 }
76
77 *stream_geometry.impl_.borrow_mut() = Some(builder.to_geometry());
78
79 Ok(stream_geometry)
80 }
81
82 pub fn open(&self) -> StreamGeometryContext {
90 StreamGeometryContext::from_context(self.open_core())
91 }
92
93 fn set_impl(&self, impl_: Arc<dyn IGeometryImpl>) {
94 *self.impl_.borrow_mut() = Some(impl_);
95 self.invalidate_geometry();
96 }
97
98 fn create_builder(source: Option<&dyn IGeometryImpl>) -> Box<dyn IStreamGeometryBuilder> {
99 let factory = platform::render_interface();
100 factory.create_stream_geometry_builder(source)
101 }
102}
103
104struct BuilderWrapper {
107 builder: Box<dyn IStreamGeometryBuilder>,
108 owner: Ref<StreamGeometry>,
109}
110
111impl IGeometryContext for BuilderWrapper {
112 fn arc_to(
113 &mut self,
114 point: Point,
115 size: Size,
116 rotation_angle: f64,
117 is_large_arc: bool,
118 sweep_direction: SweepDirection,
119 is_stroked: bool,
120 ) {
121 self.builder.arc_to(point, size, rotation_angle, is_large_arc, sweep_direction, is_stroked);
122 }
123
124 fn begin_figure(&mut self, start_point: Point, is_filled: bool) {
125 self.builder.begin_figure(start_point, is_filled);
126 }
127
128 fn cubic_bezier_to(&mut self, control_point1: Point, control_point2: Point, end_point: Point, is_stroked: bool) {
129 self.builder.cubic_bezier_to(control_point1, control_point2, end_point, is_stroked);
130 }
131
132 fn quadratic_bezier_to(&mut self, control_point: Point, end_point: Point, is_stroked: bool) {
133 self.builder.quadratic_bezier_to(control_point, end_point, is_stroked);
134 }
135
136 fn line_to(&mut self, point: Point, is_stroked: bool) {
137 self.builder.line_to(point, is_stroked);
138 }
139
140 fn end_figure(&mut self, is_closed: bool) {
141 self.builder.end_figure(is_closed);
142 }
143
144 fn set_fill_rule(&mut self, fill_rule: FillRule) {
145 self.builder.set_fill_rule(fill_rule);
146 }
147
148 fn dispose(&mut self) {
149 self.owner.set_impl(self.builder.to_geometry());
150 self.builder.dispose();
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::media::geometry::tests::with_mock_render_interface;
158 use crate::media::TranslateTransform;
159 use std::cell::Cell;
160 use std::rc::Rc;
161
162 fn count_changed(target: &StreamGeometry) -> Rc<Cell<i32>> {
163 let changed = Rc::new(Cell::new(0));
164 let c = changed.clone();
165 target.changed(move || c.set(c.get() + 1));
166 changed
167 }
168
169 #[test]
170 fn changed_is_raised_when_the_drawing_is_finished() {
171 with_mock_render_interface(|_| {
172 let target = StreamGeometry::new();
173 let changed = count_changed(&target);
174
175 {
176 let mut context = target.open();
177 assert_eq!(0, changed.get());
178
179 context.begin_figure(Point::new(0.0, 0.0), true);
180 context.line_to(Point::new(10.0, 0.0), true);
181 context.end_figure(true);
182
183 assert_eq!(0, changed.get());
184 }
185
186 assert_eq!(1, changed.get());
187 });
188 }
189
190 #[test]
191 fn changed_is_raised_when_the_drawing_is_finished_and_a_transform_is_set() {
192 with_mock_render_interface(|_| {
193 let target = StreamGeometry::new();
194 target.set_transform(TranslateTransform::with_offset(10.0, 20.0));
195 let changed = count_changed(&target);
196
197 {
198 let mut context = target.open();
199 assert_eq!(0, changed.get());
200
201 context.begin_figure(Point::new(0.0, 0.0), true);
202 context.line_to(Point::new(10.0, 0.0), true);
203 context.end_figure(true);
204
205 assert_eq!(0, changed.get());
206 }
207
208 assert_eq!(1, changed.get());
209 });
210 }
211}