winio_layout/
lib.rs

1//! Layout primitives and containers.
2
3#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
4#![warn(missing_docs)]
5
6#[doc(hidden)]
7pub use paste::paste as __paste;
8use taffy::{Layout, NodeId, TaffyTree};
9#[doc(hidden)]
10pub use winio_primitive::{HAlign, VAlign};
11use winio_primitive::{Margin, Point, Rect, Size};
12
13/// Trait for a widget to set visibility.
14pub trait Visible {
15    /// If the widget is visible.
16    fn is_visible(&self) -> bool;
17
18    /// Set the visibility.
19    fn set_visible(&mut self, v: bool);
20
21    /// Show the widget.
22    fn show(&mut self) {
23        self.set_visible(true);
24    }
25
26    /// Hide the widget.
27    fn hide(&mut self) {
28        self.set_visible(false);
29    }
30}
31
32/// Trait for a widget to enable or disable.
33pub trait Enable {
34    /// If the widget is enabled.
35    fn is_enabled(&self) -> bool;
36
37    /// Set if the widget is enabled.
38    fn set_enabled(&mut self, v: bool);
39
40    /// Enable the widget.
41    fn enable(&mut self) {
42        self.set_enabled(true);
43    }
44
45    /// Disable the widget.
46    fn disable(&mut self) {
47        self.set_enabled(false);
48    }
49}
50
51/// Trait for a layoutable widget.
52///
53/// To create a responsive layout, always set location and size together.
54pub trait Layoutable {
55    /// The left top location.
56    fn loc(&self) -> Point;
57
58    /// Move the location.
59    fn set_loc(&mut self, p: Point);
60
61    /// The size.
62    fn size(&self) -> Size;
63
64    /// Resize.
65    fn set_size(&mut self, s: Size);
66
67    /// The bounding rectangle.
68    fn rect(&self) -> Rect {
69        Rect::new(self.loc(), self.size())
70    }
71
72    /// Set the location and size.
73    fn set_rect(&mut self, r: Rect) {
74        self.set_loc(r.origin);
75        self.set_size(r.size);
76    }
77
78    /// The preferred size.
79    fn preferred_size(&self) -> Size {
80        Size::zero()
81    }
82
83    /// Min acceptable size.
84    fn min_size(&self) -> Size {
85        self.preferred_size()
86    }
87}
88
89trait LayoutChild {
90    fn margin(&self) -> Margin;
91
92    fn set_rect(&mut self, r: Rect);
93
94    fn layout(&mut self, layout: &Layout, loc: Point) {
95        self.set_rect(offset(rect_t2e(layout, self.margin()), loc))
96    }
97}
98
99mod grid;
100pub use grid::*;
101
102mod stack_panel;
103pub use stack_panel::*;
104
105#[cfg(test)]
106mod test;
107
108fn rect_t2e(rect: &taffy::Layout, margin: Margin) -> Rect {
109    Rect::new(
110        Point::new(
111            rect.location.x as f64 + margin.left,
112            rect.location.y as f64 + margin.top,
113        ),
114        Size::new(
115            rect.size.width as f64 - margin.horizontal(),
116            rect.size.height as f64 - margin.vertical(),
117        ),
118    )
119}
120
121fn offset(mut a: Rect, offset: Point) -> Rect {
122    a.origin += offset.to_vector();
123    a
124}
125
126macro_rules! __layout_child {
127    ($(#[$sm:meta])* struct $name:ident { $($(#[$m:meta])* $f:ident: $t:ty = $e:expr),*$(,)? }) => {
128        struct $name<'a> {
129            widget: &'a mut dyn $crate::Layoutable,
130            width: Option<f64>,
131            height: Option<f64>,
132            margin: $crate::Margin,
133            halign: $crate::HAlign,
134            valign: $crate::VAlign,
135            $(
136                $(#[$m])*
137                $f: $t,
138            )*
139        }
140        impl<'a> $name<'a> {
141            #[allow(unused_doc_comments)]
142            pub fn new(widget: &'a mut dyn $crate::Layoutable) -> Self {
143                Self {
144                    widget,
145                    width: None,
146                    height: None,
147                    margin: $crate::Margin::zero(),
148                    halign: $crate::HAlign::Stretch,
149                    valign: $crate::VAlign::Stretch,
150                    $(
151                        $(#[$m])*
152                        $f: $e,
153                    )*
154                }
155            }
156        }
157        impl $crate::LayoutChild for $name<'_> {
158            fn margin(&self) -> $crate::Margin {
159                self.margin
160            }
161
162            fn set_rect(&mut self, r: Rect) {
163                self.widget.set_rect(r)
164            }
165        }
166        $crate::__paste! {
167            $(#[$sm])*
168            pub struct [<$name Builder>]<'a, 'b> {
169                child: $name<'a>,
170                children: &'b mut Vec<$name<'a>>,
171            }
172            impl [<$name Builder>]<'_, '_> {
173                /// Specify the child width.
174                pub fn width(mut self, v: f64) -> Self {
175                    self.child.width = Some(v);
176                    self
177                }
178
179                /// Specify the child height.
180                pub fn height(mut self, v: f64) -> Self {
181                    self.child.height = Some(v);
182                    self
183                }
184
185                /// Specify the child size.
186                pub fn size(self, s: $crate::Size) -> Self {
187                    self.width(s.width).height(s.height)
188                }
189
190                /// Margin of the child.
191                pub fn margin(mut self, m: $crate::Margin) -> Self {
192                    self.child.margin = m;
193                    self
194                }
195
196                /// Horizontal alignment in the available area.
197                pub fn halign(mut self, v: $crate::HAlign) -> Self {
198                    self.child.halign = v;
199                    self
200                }
201
202                /// Vertical alignment in the available area.
203                pub fn valign(mut self, v: $crate::VAlign) -> Self {
204                    self.child.valign = v;
205                    self
206                }
207
208                $(
209                    $(#[$m])*
210                    pub fn $f(mut self, v: $t) -> Self {
211                        self.child.$f = v;
212                        self
213                    }
214                )*
215
216                /// Add the child to the container.
217                pub fn finish(self) {
218                    self.children.push(self.child);
219                }
220            }
221        }
222    };
223}
224pub(crate) use __layout_child as layout_child;
225
226fn render(
227    mut tree: TaffyTree,
228    root: NodeId,
229    nodes: Vec<NodeId>,
230    loc: Point,
231    size: Size,
232    children: &mut [impl LayoutChild],
233) {
234    tree.compute_layout(
235        root,
236        taffy::Size {
237            width: taffy::AvailableSpace::Definite(size.width as _),
238            height: taffy::AvailableSpace::Definite(size.height as _),
239        },
240    )
241    .unwrap();
242    for (id, child) in nodes.iter().zip(children) {
243        let layout = tree.layout(*id).unwrap();
244        child.layout(layout, loc);
245    }
246}
247
248/// Helper macro for layouts in `Component::render`.
249///
250/// ```ignore
251/// # use winio::prelude::*;
252/// # struct MainModel {
253/// #     window: Child<Window>,
254/// #     canvas: Child<Canvas>,
255/// # }
256/// # impl MainModel { fn foo(&mut self) {
257/// let csize = self.window.client_size();
258/// {
259///     let mut grid = layout! {
260///         Grid::from_str("1*,2*,1*", "1*,2*,1*").unwrap(),
261///         self.canvas => { column: 1, row: 1 },
262///     };
263///     grid.set_size(csize);
264/// }
265/// # } }
266/// ```
267#[macro_export]
268macro_rules! layout {
269    ($root:expr, $($e:expr $(=>  { $($t:tt)* })?),+$(,)?) => {{
270        #[allow(unused_mut)]
271        let mut root = $root;
272        $(
273            $crate::__layout_push!(root, &mut $e, $($($t)*)?);
274        )+
275        root
276    }};
277}
278
279#[macro_export]
280#[doc(hidden)]
281macro_rules! __layout_push {
282    ($root:expr, $e:expr,) => {
283        $root.push($e).finish();
284    };
285    ($root:expr, $e:expr, $($(#[$me:meta])* $p:ident : $v:expr),+$(,)?) => {
286        let builder = $root.push($e);
287        $(
288            $(#[$me])*
289            let builder = builder.$p($v);
290        )+
291        builder.finish();
292    };
293}