ferroui_base/media/
geometry_group.rs1use crate::media::{FillRule, Geometry, GeometryCollection, GeometryImpl};
2use crate::platform::{self, IGeometryImpl};
3use crate::{
4 ferro_class, ferro_property, instantiate, DirectProperty, FerroObjectImpl, FerroObjectImplExt, FerroProperty,
5 FerroPropertyChangedEventArgs, Ref, StyledProperty,
6};
7use std::cell::RefCell;
8use std::sync::Arc;
9
10#[repr(C)]
12pub struct GeometryGroup {
13 base: Geometry,
14 children: RefCell<Option<GeometryCollection>>,
15}
16
17ferro_class!(GeometryGroup: Geometry);
18crate::ferro_class_info!(GeometryGroup { new: GeometryGroup::new });
19
20impl FerroObjectImpl for GeometryGroup {
21 fn constructed(this: &Self) {
22 Self::parent_constructed(this);
23 this.children_or_panic().set_parent(Some(&this.to_ref()));
24 }
25
26 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
27 Self::parent_on_property_changed(this, change);
28
29 match change.property().name() {
30 "FillRule" | "Children" => this.invalidate_geometry(),
31 _ => {}
32 }
33 }
34}
35
36impl GeometryImpl for GeometryGroup {
37 fn clone_geometry(this: &Self) -> Ref<Geometry> {
38 let result = GeometryGroup::new();
39 result.set_fill_rule(this.fill_rule());
40 result.set_transform(this.transform());
41 let children = this.children_or_panic();
42 if !children.is_empty() {
43 result.set_children(GeometryCollection::from_items(children.iter()));
44 }
45 result.upcast()
46 }
47
48 fn create_defining_geometry(this: &Self) -> Option<Arc<dyn IGeometryImpl>> {
49 let children = this.children_or_panic();
50 if children.is_empty() {
51 return None;
52 }
53
54 let factory = platform::render_interface();
55 let children: Vec<Arc<dyn IGeometryImpl>> = children.iter().filter_map(|child| child.platform_impl()).collect();
57
58 Some(factory.create_geometry_group(this.fill_rule(), &children))
59 }
60}
61
62crate::ferro_properties! { impl GeometryGroup {
63 ferro_property!(pub fn children_property() -> DirectProperty<GeometryGroup, Option<GeometryCollection>> {
64 FerroProperty::register_direct::<GeometryGroup, _>(
65 "Children",
66 |o| o.children.borrow().clone(),
67 Some(|o, v| o.set_children_core(v)),
68 None,
69 )
70 });
71
72 ferro_property!(pub fn fill_rule_property() -> StyledProperty<FillRule> {
73 FerroProperty::register::<GeometryGroup, _>("FillRule", FillRule::EvenOdd)
74 });
75} }
76
77impl GeometryGroup {
78 pub fn construct() -> Self {
80 Self { base: Geometry::construct(), children: RefCell::new(Some(GeometryCollection::new())) }
81 }
82
83 pub fn new() -> Ref<Self> {
84 instantiate(Self::construct())
85 }
86
87 fn children_or_panic(&self) -> GeometryCollection {
88 self.children.borrow().clone().expect("Children is not set")
89 }
90
91 pub fn children(&self) -> GeometryCollection {
94 self.children_or_panic()
95 }
96
97 pub fn set_children(&self, value: GeometryCollection) {
98 self.set_children_core(Some(value));
99 }
100
101 fn set_children_core(&self, value: Option<GeometryCollection>) {
102 let old = self.children.borrow().clone();
103 self.on_children_changed(old.as_ref(), value.as_ref());
104 self.set_and_raise(Self::children_property(), &self.children, value);
105 }
106
107 pub fn fill_rule(&self) -> FillRule {
110 self.get_value(Self::fill_rule_property())
111 }
112
113 pub fn set_fill_rule(&self, value: FillRule) {
114 self.set_value(Self::fill_rule_property(), value)
115 }
116
117 fn on_children_changed(&self, old_children: Option<&GeometryCollection>, new_children: Option<&GeometryCollection>) {
118 if let Some(old_children) = old_children {
119 old_children.set_parent(None);
120 }
121 if let Some(new_children) = new_children {
122 new_children.set_parent(Some(&self.to_ref()));
123 }
124 }
125
126 pub(crate) fn invalidate(&self) {
127 self.invalidate_geometry();
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use std::rc::Rc;
134 use super::*;
135 use crate::media::StreamGeometry;
136 use std::cell::Cell;
137
138 #[test]
139 fn children_should_have_initial_collection() {
140 let target = GeometryGroup::new();
141 assert!(target.children().is_empty());
142 assert!(target.children().parent().unwrap().ptr_eq(&target));
143 }
144
145 #[test]
146 fn children_change_should_raise_changed() {
147 let target = GeometryGroup::new();
148
149 let children = GeometryCollection::new();
150
151 target.set_children(children.clone());
152
153 let is_called = Rc::new(Cell::new(false));
154 let c = is_called.clone();
155 target.changed(move || c.set(true));
156
157 children.add(StreamGeometry::new().upcast());
158
159 assert!(is_called.get());
160 }
161
162 #[test]
163 fn replaced_children_no_longer_raise_changed() {
164 let target = GeometryGroup::new();
165 let old = target.children();
166 target.set_children(GeometryCollection::new());
167 assert!(old.parent().is_none());
168
169 let is_called = Rc::new(Cell::new(false));
170 let c = is_called.clone();
171 target.changed(move || c.set(true));
172
173 old.add(StreamGeometry::new().upcast());
174
175 assert!(!is_called.get());
176 }
177
178 #[test]
179 fn empty_group_has_no_platform_impl() {
180 let target = GeometryGroup::new();
181 assert!(target.platform_impl().is_none());
182 assert_eq!(crate::Rect::default(), target.bounds());
183 }
184}