ferroui_controls/
viewbox.rs1use crate::{Control, ControlImpl, Decorator};
2use ferroui_base::input::InputElementImpl;
3use ferroui_base::interactivity::InteractiveImpl;
4use ferroui_base::layout::{Layoutable, LayoutableImpl};
5use ferroui_base::media::immutable::ImmutableTransform;
6use ferroui_base::media::{ITransform, MediaExtensions, Stretch, StretchDirection};
7use ferroui_base::{
8 ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObject, FerroObjectImpl, FerroObjectImplExt,
9 FerroProperty, FerroPropertyChangedEventArgs, Matrix, Nullable, Rect, Ref, RelativePoint, Size, StyledElement,
10 StyledElementImpl, StyledProperty, Visual, VisualImpl,
11};
12use std::cell::{OnceCell, RefCell};
13use std::rc::Rc;
14
15#[repr(C)]
17pub struct Viewbox {
18 base: Control,
19 container_visual: OnceCell<Ref<ViewboxContainer>>,
20}
21
22ferro_class!(Viewbox: Control);
23ferroui_base::ferro_class_info!(Viewbox { new: Viewbox::new });
24ferro_impl_classes!(Viewbox: StyledElementImpl, VisualImpl, InteractiveImpl, InputElementImpl, ControlImpl);
25
26impl FerroObjectImpl for Viewbox {
27 fn constructed(this: &Self) {
28 Self::parent_constructed(this);
29
30 let container_visual = ViewboxContainer::new();
34 container_visual.set_render_transform_origin(RelativePoint::TOP_LEFT);
35 container_visual.set_parent(this.to_ref());
36 this.visual_children().add(container_visual.clone().upcast());
37 let _ = this.container_visual.set(container_visual);
38 }
39
40 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
41 Self::parent_on_property_changed(this, change);
42
43 if change.property() == Self::child_property().as_property() {
44 let (old_child, new_child) = change.get_old_and_new_value::<Option<Ref<Control>>>();
45
46 if let Some(old_child) = old_child {
47 old_child.set_parent(None);
48 this.logical_children().remove(&old_child.upcast());
49 }
50
51 this.container().set_child(new_child.clone());
52
53 if let Some(new_child) = new_child {
54 new_child.set_parent(this.to_ref());
55 this.logical_children().add(new_child.upcast());
56 }
57
58 this.invalidate_measure();
59 } else if change.property() == StyledElement::templated_parent_property().as_property() {
60 if let Some(templated_parent) = change.get_new_value::<Option<Ref<FerroObject>>>() {
64 this.container().set_templated_parent(templated_parent);
65 }
66 }
67 }
68}
69
70impl LayoutableImpl for Viewbox {
71 fn measure_override(this: &Self, available_size: Size) -> Size {
72 let child = this.container();
73
74 child.measure(Size::INFINITY);
75
76 let child_size = child.desired_size();
77
78 MediaExtensions::calculate_size(this.stretch(), available_size, child_size, this.stretch_direction())
79 }
80
81 fn arrange_override(this: &Self, final_size: Size) -> Size {
82 let child = this.container();
83
84 let child_size = child.desired_size();
85 let scale =
86 MediaExtensions::calculate_scaling(this.stretch(), final_size, child_size, this.stretch_direction());
87
88 this.set_internal_transform(Some(Rc::new(ImmutableTransform::new(Matrix::create_scale(scale.x, scale.y)))));
89
90 child.arrange(Rect::from_size(child_size));
91
92 child_size * scale
93 }
94}
95
96ferroui_base::ferro_properties! { impl Viewbox {
97 ferro_property!(
98 pub fn stretch_property() -> StyledProperty<Stretch> {
100 FerroProperty::register::<Viewbox, _>("Stretch", Stretch::Uniform)
101 }
102 );
103
104 ferro_property!(
105 pub fn stretch_direction_property() -> StyledProperty<StretchDirection> {
107 FerroProperty::register::<Viewbox, _>("StretchDirection", StretchDirection::Both)
108 }
109 );
110
111 ferro_property!(
112 pub fn child_property() -> StyledProperty<Option<Ref<Control>>> {
114 Decorator::child_property().add_owner::<Viewbox>()
115 }
116 );
117} }
118
119impl Viewbox {
120 fn static_constructor() {
121 Visual::clip_to_bounds_property().override_default_value::<Viewbox>(true);
122 Layoutable::use_layout_rounding_property().override_default_value::<Viewbox>(true);
123 Layoutable::affects_measure::<Viewbox>(&[
124 Self::stretch_property().as_property(),
125 Self::stretch_direction_property().as_property(),
126 ]);
127 }
128
129 pub fn construct() -> Self {
131 Self { base: Control::construct(), container_visual: OnceCell::new() }
132 }
133
134 pub fn new() -> Ref<Self> {
135 instantiate(Self::construct())
136 }
137
138 fn container(&self) -> &Ref<ViewboxContainer> {
139 self.container_visual.get().expect("the viewbox has been constructed")
140 }
141
142 pub fn stretch(&self) -> Stretch {
145 self.get_value(Self::stretch_property())
146 }
147
148 pub fn set_stretch(&self, value: Stretch) {
149 self.set_value(Self::stretch_property(), value)
150 }
151
152 pub fn stretch_direction(&self) -> StretchDirection {
154 self.get_value(Self::stretch_direction_property())
155 }
156
157 pub fn set_stretch_direction(&self, value: StretchDirection) {
158 self.set_value(Self::stretch_direction_property(), value)
159 }
160
161 pub fn child(&self) -> Option<Ref<Control>> {
163 self.get_value(Self::child_property())
164 }
165
166 pub fn set_child(&self, value: impl Into<Nullable<Control>>) {
167 self.set_value(Self::child_property(), value.into().0)
168 }
169
170 #[cfg(test)]
172 pub(crate) fn internal_transform(&self) -> Option<Rc<dyn ITransform>> {
173 self.container().render_transform()
174 }
175
176 pub(crate) fn set_internal_transform(&self, value: Option<Rc<dyn ITransform>>) {
177 self.container().set_render_transform(value)
178 }
179}
180
181#[repr(C)]
184pub(crate) struct ViewboxContainer {
185 base: Control,
186 child: RefCell<Option<Ref<Control>>>,
187}
188
189ferro_class!(ViewboxContainer: Control);
190ferro_impl_classes!(
191 ViewboxContainer: FerroObjectImpl,
192 StyledElementImpl,
193 VisualImpl,
194 LayoutableImpl,
195 InteractiveImpl,
196 InputElementImpl,
197 ControlImpl
198);
199
200impl ViewboxContainer {
201 fn new() -> Ref<Self> {
202 instantiate(Self { base: Control::construct(), child: RefCell::new(None) })
203 }
204
205 fn set_child(&self, value: Option<Ref<Control>>) {
206 let old = self.child.borrow().clone();
207 if old != value {
208 if let Some(old) = old {
209 self.visual_children().remove(&old.upcast());
210 }
211
212 *self.child.borrow_mut() = value.clone();
213
214 if let Some(value) = value {
215 self.visual_children().add(value.upcast());
216 }
217
218 self.invalidate_measure();
219 }
220 }
221}