Skip to main content

ferroui_base/media/
polyline_geometry.rs

1use crate::media::{FillRule, Geometry, GeometryImpl, Points};
2use crate::platform::{self, IGeometryImpl};
3use crate::reactive::IDisposable;
4use crate::{
5    ferro_class, ferro_property, instantiate, DirectProperty, FerroObjectImpl, FerroProperty,
6    Point, Ref, StyledProperty,
7};
8use std::cell::RefCell;
9use std::rc::Rc;
10use std::sync::Arc;
11
12/// Represents the geometry of a polyline or polygon.
13#[repr(C)]
14pub struct PolylineGeometry {
15    base: Geometry,
16    points: RefCell<Option<Points>>,
17    points_observer: RefCell<Option<Rc<dyn IDisposable>>>,
18    fill_rule: FillRule,
19}
20
21ferro_class!(PolylineGeometry: Geometry);
22crate::ferro_class_info!(PolylineGeometry { new: PolylineGeometry::new });
23
24crate::ferro_impl_classes!(PolylineGeometry: FerroObjectImpl);
25
26impl GeometryImpl for PolylineGeometry {
27    fn clone_geometry(this: &Self) -> Ref<Geometry> {
28        let result = PolylineGeometry::with_points_and_fill_rule(this.points().iter(), this.is_filled(), this.fill_rule);
29        result.set_transform(this.transform());
30        result.upcast()
31    }
32
33    fn create_defining_geometry(this: &Self) -> Option<Arc<dyn IGeometryImpl>> {
34        let factory = platform::render_interface();
35        let mut builder = factory.create_stream_geometry_builder(None);
36
37        builder.set_fill_rule(this.fill_rule);
38        let points = this.points().to_vec();
39        let is_filled = this.is_filled();
40        if !points.is_empty() {
41            builder.begin_figure(points[0], is_filled);
42            for point in &points[1..] {
43                builder.line_to(*point, true);
44            }
45            builder.end_figure(is_filled);
46        }
47
48        let geometry = builder.to_geometry();
49        builder.dispose();
50        Some(geometry)
51    }
52}
53
54crate::ferro_properties! { impl PolylineGeometry {
55    ferro_property!(pub fn points_property() -> DirectProperty<PolylineGeometry, Option<Points>> {
56        FerroProperty::register_direct::<PolylineGeometry, _>(
57            "Points",
58            |g| g.points.borrow().clone(),
59            Some(|g, f| {
60                g.set_and_raise(PolylineGeometry::points_property(), &g.points, f);
61            }),
62            None,
63        )
64    });
65
66    ferro_property!(pub fn is_filled_property() -> StyledProperty<bool> {
67        FerroProperty::register::<PolylineGeometry, _>("IsFilled", false)
68    });
69} }
70
71impl PolylineGeometry {
72    fn static_constructor() {
73        Geometry::affects_geometry(&[Self::is_filled_property().as_property()]);
74        Self::points_property().changed().add_class_handler::<PolylineGeometry>(|s, e| {
75            s.on_points_changed(e.get_new_value::<Option<Points>>())
76        });
77    }
78
79    /// Creates the class data.
80    pub fn construct() -> Self {
81        Self::construct_with(Points::new(), FillRule::EvenOdd)
82    }
83
84    fn construct_with(points: Points, fill_rule: FillRule) -> Self {
85        Self {
86            base: Geometry::construct(),
87            points: RefCell::new(Some(points)),
88            points_observer: RefCell::new(None),
89            fill_rule,
90        }
91    }
92
93    pub fn new() -> Ref<Self> {
94        instantiate(Self::construct())
95    }
96
97    /// Creates a polyline geometry with a copy of the given points.
98    pub fn with_points(points: impl IntoIterator<Item = Point>, is_filled: bool) -> Ref<Self> {
99        Self::with_points_and_fill_rule(points, is_filled, FillRule::EvenOdd)
100    }
101
102    /// Creates a polyline geometry with a copy of the given points and the
103    /// given fill rule.
104    pub fn with_points_and_fill_rule(
105        points: impl IntoIterator<Item = Point>,
106        is_filled: bool,
107        fill_rule: FillRule,
108    ) -> Ref<Self> {
109        let result = instantiate(Self::construct_with(Points::from_items(points), fill_rule));
110        result.set_is_filled(is_filled);
111        result
112    }
113
114    /// The figure's points. Panics if the property has been cleared.
115    pub fn points(&self) -> Points {
116        self.points.borrow().clone().expect("Points is not set")
117    }
118
119    pub fn set_points(&self, value: Points) {
120        self.set_and_raise(Self::points_property(), &self.points, Some(value));
121    }
122
123    /// Whether the geometry is filled.
124    pub fn is_filled(&self) -> bool {
125        self.get_value(Self::is_filled_property())
126    }
127
128    pub fn set_is_filled(&self, value: bool) {
129        self.set_value(Self::is_filled_property(), value)
130    }
131
132    /// How the intersecting areas of the geometry are combined.
133    pub fn fill_rule(&self) -> FillRule {
134        self.fill_rule
135    }
136
137    fn on_points_changed(&self, new_value: Option<Points>) {
138        if let Some(observer) = self.points_observer.take() {
139            observer.dispose();
140        }
141
142        if let Some(new_value) = new_value {
143            let weak = self.to_ref().downgrade();
144            let observer = new_value.collection_changed(move |_| {
145                if let Some(this) = weak.upgrade() {
146                    this.invalidate_geometry();
147                }
148            });
149            *self.points_observer.borrow_mut() = Some(observer);
150        }
151    }
152}