Skip to main content

ferroui_controls/
canvas.rs

1use crate::{Control, ControlImpl, Panel, PanelImpl};
2use ferroui_base::input::{INavigableContainer, InputElement, InputElementImpl, NavigationDirection};
3use ferroui_base::interactivity::InteractiveImpl;
4use ferroui_base::layout::LayoutableImpl;
5use ferroui_base::{
6    ferro_class, ferro_impl_classes, ferro_property, instantiate, AttachedProperty, FerroObject, FerroObjectImpl, FerroProperty, Point, Rect, Ref, Size, StyledElementImpl, Visual, VisualImpl,
7};
8
9/// A panel that displays child controls at arbitrary locations.
10///
11/// Unlike other [`Panel`] implementations, the canvas doesn't lay out its
12/// children in any particular layout. Instead, the positioning of each child
13/// control is defined by the `Canvas.Left`, `Canvas.Top`, `Canvas.Right` and
14/// `Canvas.Bottom` attached properties.
15#[repr(C)]
16pub struct Canvas {
17    base: Panel,
18}
19
20ferro_class! {
21    Canvas: Panel, virtuals CanvasImpl: PanelImpl {
22        /// Arranges a single child within the size allocated to the canvas.
23        fn arrange_child(this, child: &Ref<Control>, final_size: Size);
24    }
25}
26ferroui_base::ferro_class_info!(Canvas { new: Canvas::new });
27
28ferro_impl_classes!(Canvas: StyledElementImpl, VisualImpl, InteractiveImpl, InputElementImpl, ControlImpl, PanelImpl);
29
30ferroui_base::ferro_impl_classes!(Canvas: FerroObjectImpl);
31
32impl LayoutableImpl for Canvas {
33    fn measure_override(this: &Self, _available_size: Size) -> Size {
34        let available_size = Size::new(f64::INFINITY, f64::INFINITY);
35
36        for child in this.children().snapshot().iter() {
37            child.measure(available_size);
38        }
39
40        Size::default()
41    }
42
43    fn arrange_override(this: &Self, final_size: Size) -> Size {
44        for child in this.children().snapshot().iter() {
45            this.arrange_child(child, final_size);
46        }
47
48        final_size
49    }
50}
51
52impl CanvasImpl for Canvas {
53    fn arrange_child(_this: &Self, child: &Ref<Control>, final_size: Size) {
54        let mut x = 0.0;
55        let mut y = 0.0;
56        let element_left = Self::get_left(child);
57
58        if !element_left.is_nan() {
59            x = element_left;
60        } else {
61            // Arrange with right.
62            let element_right = Self::get_right(child);
63            if !element_right.is_nan() {
64                x = final_size.width - child.desired_size().width - element_right;
65            }
66        }
67
68        let element_top = Self::get_top(child);
69        if !element_top.is_nan() {
70            y = element_top;
71        } else {
72            let element_bottom = Self::get_bottom(child);
73            if !element_bottom.is_nan() {
74                y = final_size.height - child.desired_size().height - element_bottom;
75            }
76        }
77
78        child.arrange(Rect::from_position_size(Point::new(x, y), child.desired_size()));
79    }
80}
81
82ferroui_base::ferro_properties! { impl Canvas {
83    ferro_property!(
84        /// Defines the `Left` attached property.
85        pub fn left_property() -> AttachedProperty<f64> {
86            FerroProperty::register_attached::<Canvas, Control, _>("Left", f64::NAN)
87        }
88    );
89
90    ferro_property!(
91        /// Defines the `Top` attached property.
92        pub fn top_property() -> AttachedProperty<f64> {
93            FerroProperty::register_attached::<Canvas, Control, _>("Top", f64::NAN)
94        }
95    );
96
97    ferro_property!(
98        /// Defines the `Right` attached property.
99        pub fn right_property() -> AttachedProperty<f64> {
100            FerroProperty::register_attached::<Canvas, Control, _>("Right", f64::NAN)
101        }
102    );
103
104    ferro_property!(
105        /// Defines the `Bottom` attached property.
106        pub fn bottom_property() -> AttachedProperty<f64> {
107            FerroProperty::register_attached::<Canvas, Control, _>("Bottom", f64::NAN)
108        }
109    );
110} }
111
112impl Canvas {
113    fn static_constructor() {
114        Visual::clip_to_bounds_property().override_default_value::<Canvas>(false);
115        Panel::affects_parent_arrange::<Canvas>(&[
116            Self::left_property().as_property(),
117            Self::top_property().as_property(),
118            Self::right_property().as_property(),
119            Self::bottom_property().as_property(),
120        ]);
121    }
122
123    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
124    pub fn construct() -> Self {
125        Self { base: Panel::construct() }
126    }
127
128    pub fn new() -> Ref<Self> {
129        instantiate(Self::construct())
130    }
131
132    /// Gets the value of the `Left` attached property for a control.
133    pub fn get_left(element: &FerroObject) -> f64 {
134        element.get_value(Self::left_property())
135    }
136
137    /// Sets the value of the `Left` attached property for a control.
138    pub fn set_left(element: &FerroObject, value: f64) {
139        element.set_value(Self::left_property(), value)
140    }
141
142    /// Gets the value of the `Top` attached property for a control.
143    pub fn get_top(element: &FerroObject) -> f64 {
144        element.get_value(Self::top_property())
145    }
146
147    /// Sets the value of the `Top` attached property for a control.
148    pub fn set_top(element: &FerroObject, value: f64) {
149        element.set_value(Self::top_property(), value)
150    }
151
152    /// Gets the value of the `Right` attached property for a control.
153    pub fn get_right(element: &FerroObject) -> f64 {
154        element.get_value(Self::right_property())
155    }
156
157    /// Sets the value of the `Right` attached property for a control.
158    pub fn set_right(element: &FerroObject, value: f64) {
159        element.set_value(Self::right_property(), value)
160    }
161
162    /// Gets the value of the `Bottom` attached property for a control.
163    pub fn get_bottom(element: &FerroObject) -> f64 {
164        element.get_value(Self::bottom_property())
165    }
166
167    /// Sets the value of the `Bottom` attached property for a control.
168    pub fn set_bottom(element: &FerroObject, value: f64) {
169        element.set_value(Self::bottom_property(), value)
170    }
171}
172
173impl INavigableContainer for Canvas {
174    /// Gets the next control in the specified direction.
175    fn get_control(
176        &self,
177        _direction: NavigationDirection,
178        _from: Option<&Ref<InputElement>>,
179        _wrap: bool,
180    ) -> Option<Ref<InputElement>> {
181        // Not implemented by the reference either.
182        None
183    }
184}