1#![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
13pub trait Visible {
15 fn is_visible(&self) -> bool;
17
18 fn set_visible(&mut self, v: bool);
20
21 fn show(&mut self) {
23 self.set_visible(true);
24 }
25
26 fn hide(&mut self) {
28 self.set_visible(false);
29 }
30}
31
32pub trait Enable {
34 fn is_enabled(&self) -> bool;
36
37 fn set_enabled(&mut self, v: bool);
39
40 fn enable(&mut self) {
42 self.set_enabled(true);
43 }
44
45 fn disable(&mut self) {
47 self.set_enabled(false);
48 }
49}
50
51pub trait Layoutable {
55 fn loc(&self) -> Point;
57
58 fn set_loc(&mut self, p: Point);
60
61 fn size(&self) -> Size;
63
64 fn set_size(&mut self, s: Size);
66
67 fn rect(&self) -> Rect {
69 Rect::new(self.loc(), self.size())
70 }
71
72 fn set_rect(&mut self, r: Rect) {
74 self.set_loc(r.origin);
75 self.set_size(r.size);
76 }
77
78 fn preferred_size(&self) -> Size {
80 Size::zero()
81 }
82
83 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 pub fn width(mut self, v: f64) -> Self {
175 self.child.width = Some(v);
176 self
177 }
178
179 pub fn height(mut self, v: f64) -> Self {
181 self.child.height = Some(v);
182 self
183 }
184
185 pub fn size(self, s: $crate::Size) -> Self {
187 self.width(s.width).height(s.height)
188 }
189
190 pub fn margin(mut self, m: $crate::Margin) -> Self {
192 self.child.margin = m;
193 self
194 }
195
196 pub fn halign(mut self, v: $crate::HAlign) -> Self {
198 self.child.halign = v;
199 self
200 }
201
202 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 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#[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}