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