winio_layout/
lib.rs

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