1#![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 pub fn width(mut self, v: f64) -> Self {
164 self.child.width = Some(v);
165 self
166 }
167
168 pub fn height(mut self, v: f64) -> Self {
170 self.child.height = Some(v);
171 self
172 }
173
174 pub fn size(self, s: $crate::Size) -> Self {
176 self.width(s.width).height(s.height)
177 }
178
179 pub fn margin(mut self, m: $crate::Margin) -> Self {
181 self.child.margin = m;
182 self
183 }
184
185 pub fn halign(mut self, v: $crate::HAlign) -> Self {
187 self.child.halign = v;
188 self
189 }
190
191 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 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#[derive(Debug, Error)]
217#[non_exhaustive]
218pub enum LayoutError<E> {
219 #[error("Taffy layout error: {0}")]
221 Taffy(#[from] TaffyError),
222 #[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#[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}