1use taffy::{
2 NodeId, Style, TaffyTree,
3 prelude::{auto, length, percent},
4};
5use winio_primitive::{Failable, HAlign, Orient, Point, Rect, Size, VAlign};
6
7use crate::{LayoutChild, LayoutError, layout_child, rect_t2e, render};
8
9layout_child! {
10 struct StackPanelChild {
12 grow: bool = false,
14 }
15}
16
17pub struct StackPanel<'a, E> {
19 children: Vec<StackPanelChild<'a, E>>,
20 orient: Orient,
21 loc: Point,
22 size: Size,
23}
24
25impl<'a, E> StackPanel<'a, E> {
26 pub fn new(orient: Orient) -> Self {
28 Self {
29 children: vec![],
30 orient,
31 loc: Point::zero(),
32 size: Size::zero(),
33 }
34 }
35
36 pub fn push<'b>(
38 &'b mut self,
39 widget: &'a mut dyn LayoutChild<Error = E>,
40 ) -> StackPanelChildBuilder<'a, 'b, E> {
41 StackPanelChildBuilder {
42 child: StackPanelChild::new(widget),
43 children: &mut self.children,
44 }
45 }
46
47 fn tree(&self) -> Result<(TaffyTree, NodeId, Vec<NodeId>), LayoutError<E>> {
48 let mut tree: TaffyTree<()> = TaffyTree::new();
49 let mut nodes = vec![];
50 for child in &self.children {
51 let preferred_size = child.child_preferred_size()?;
52 let mut style = Style::default();
53 style.size.width = match child.width {
54 Some(w) => length(w as f32),
55 None => match (self.orient, child.halign, child.grow) {
56 (Orient::Vertical, HAlign::Stretch, _) => percent(1.0),
57 (Orient::Horizontal, _, true) => auto(),
58 _ => length(preferred_size.width as f32),
59 },
60 };
61 style.size.height = match child.height {
62 Some(h) => length(h as f32),
63 None => match (self.orient, child.valign, child.grow) {
64 (Orient::Horizontal, VAlign::Stretch, _) => percent(1.0),
65 (Orient::Vertical, _, true) => auto(),
66 _ => length(preferred_size.height as f32),
67 },
68 };
69 let min_size = child.child_min_size()?;
70 style.min_size = taffy::Size {
71 width: length(min_size.width as f32),
72 height: length(min_size.height as f32),
73 };
74 match self.orient {
75 Orient::Horizontal => {
76 if matches!(child.valign, VAlign::Top | VAlign::Center) {
77 style.margin.bottom = auto();
78 }
79 if matches!(child.valign, VAlign::Bottom | VAlign::Center) {
80 style.margin.top = auto();
81 }
82 }
83 Orient::Vertical => {
84 if matches!(child.halign, HAlign::Left | HAlign::Center) {
85 style.margin.right = auto();
86 }
87 if matches!(child.halign, HAlign::Right | HAlign::Center) {
88 style.margin.left = auto();
89 }
90 }
91 }
92 if child.grow {
93 style.flex_grow = 1.0
94 }
95 let node = tree.new_leaf(style)?;
96 nodes.push(node);
97 }
98 let root = tree.new_with_children(
99 Style {
100 size: taffy::Size::from_percent(1.0, 1.0),
101 flex_direction: match self.orient {
102 Orient::Horizontal => taffy::FlexDirection::Row,
103 Orient::Vertical => taffy::FlexDirection::Column,
104 },
105 ..Default::default()
106 },
107 &nodes,
108 )?;
109 Ok((tree, root, nodes))
110 }
111
112 fn render(&mut self) -> Result<(), LayoutError<E>> {
113 let (tree, root, nodes) = self.tree()?;
114 render(tree, root, nodes, self.loc, self.size, &mut self.children)
115 }
116
117 pub fn set_loc(&mut self, p: Point) -> Result<(), LayoutError<E>> {
119 LayoutChild::set_child_loc(self, p)
120 }
121
122 pub fn set_size(&mut self, s: Size) -> Result<(), LayoutError<E>> {
124 LayoutChild::set_child_size(self, s)
125 }
126
127 pub fn set_rect(&mut self, r: Rect) -> Result<(), LayoutError<E>> {
129 LayoutChild::set_child_rect(self, r)
130 }
131}
132
133impl<E> Failable for StackPanel<'_, E> {
134 type Error = E;
135}
136
137impl<E> LayoutChild for StackPanel<'_, E> {
138 fn set_child_loc(&mut self, p: Point) -> Result<(), LayoutError<Self::Error>> {
139 self.loc = p;
140 self.render()
141 }
142
143 fn set_child_size(&mut self, s: Size) -> Result<(), LayoutError<Self::Error>> {
144 self.size = s;
145 self.render()
146 }
147
148 fn set_child_rect(&mut self, r: Rect) -> Result<(), LayoutError<Self::Error>> {
149 self.loc = r.origin;
150 self.size = r.size;
151 self.render()
152 }
153
154 fn child_preferred_size(&self) -> Result<Size, LayoutError<Self::Error>> {
155 let (mut tree, root, _) = self.tree()?;
156 tree.compute_layout(root, taffy::Size::max_content())?;
157 Ok(rect_t2e(tree.layout(root)?).size)
158 }
159
160 fn child_min_size(&self) -> Result<Size, LayoutError<Self::Error>> {
161 let (mut tree, root, _) = self.tree()?;
162 tree.compute_layout(root, taffy::Size::min_content())?;
163 Ok(rect_t2e(tree.layout(root)?).size)
164 }
165}