1use std::{fmt::Display, num::ParseFloatError, str::FromStr};
2
3use taffy::{
4 GridTemplateComponent, NodeId, Style, TaffyTree, TrackSizingFunction,
5 prelude::{auto, fr, length, line, span},
6};
7use winio_primitive::Failable;
8
9use super::{layout_child, rect_t2e, render};
10use crate::{HAlign, LayoutChild, LayoutError, Point, Rect, Size, VAlign};
11
12#[derive(Debug)]
14#[non_exhaustive]
15pub enum ParseGridLengthError {
16 InvalidLength(ParseFloatError),
18}
19
20impl Display for ParseGridLengthError {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 Self::InvalidLength(e) => write!(f, "invalid length value: {e}"),
24 }
25 }
26}
27
28impl std::error::Error for ParseGridLengthError {}
29
30#[derive(Debug, Clone, Copy, PartialEq)]
32pub enum GridLength {
33 Auto,
35 Stretch(f64),
37 Length(f64),
39}
40
41impl FromStr for GridLength {
42 type Err = ParseGridLengthError;
43
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 if s.eq_ignore_ascii_case("auto") {
46 Ok(Self::Auto)
47 } else if let Some(s) = s.strip_suffix('*') {
48 s.parse::<f64>()
49 .map(Self::Stretch)
50 .map_err(ParseGridLengthError::InvalidLength)
51 } else {
52 s.parse::<f64>()
53 .map(Self::Length)
54 .map_err(ParseGridLengthError::InvalidLength)
55 }
56 }
57}
58
59impl From<GridLength> for TrackSizingFunction {
60 fn from(value: GridLength) -> Self {
61 match value {
62 GridLength::Auto => auto(),
63 GridLength::Length(v) => length(v as f32),
64 GridLength::Stretch(v) => fr(v as f32),
65 }
66 }
67}
68
69impl From<&GridLength> for TrackSizingFunction {
70 fn from(value: &GridLength) -> Self {
71 TrackSizingFunction::from(*value)
72 }
73}
74
75layout_child! {
76 struct GridChild {
78 column: usize = 0,
80 row: usize = 0,
82 column_span: usize = 1,
84 row_span: usize = 1,
86 }
87}
88
89pub struct Grid<'a, E> {
91 children: Vec<GridChild<'a, E>>,
92 columns: Vec<GridLength>,
93 rows: Vec<GridLength>,
94 loc: Point,
95 size: Size,
96}
97
98impl<'a, E> Grid<'a, E> {
99 pub fn new(columns: Vec<GridLength>, rows: Vec<GridLength>) -> Self {
101 Self {
102 children: vec![],
103 columns,
104 rows,
105 loc: Point::zero(),
106 size: Size::zero(),
107 }
108 }
109
110 pub fn from_str(
112 columns: impl AsRef<str>,
113 rows: impl AsRef<str>,
114 ) -> Result<Self, ParseGridLengthError> {
115 Ok(Self::new(
116 Self::parse_grid_lengths(columns.as_ref())?,
117 Self::parse_grid_lengths(rows.as_ref())?,
118 ))
119 }
120
121 fn parse_grid_lengths(s: &str) -> Result<Vec<GridLength>, ParseGridLengthError> {
122 let mut lengths = vec![];
123 for s in s.split(',') {
124 let s = s.trim();
125 lengths.push(s.parse()?);
126 }
127 Ok(lengths)
128 }
129
130 pub fn push<'b>(
132 &'b mut self,
133 widget: &'a mut dyn LayoutChild<Error = E>,
134 ) -> GridChildBuilder<'a, 'b, E> {
135 GridChildBuilder {
136 child: GridChild::new(widget),
137 children: &mut self.children,
138 }
139 }
140
141 fn tree(&self) -> Result<(TaffyTree, NodeId, Vec<NodeId>), LayoutError<E>> {
142 let mut tree: TaffyTree<()> = TaffyTree::new();
143 let mut nodes = vec![];
144 for child in &self.children {
145 let preferred_size = child.child_preferred_size()?;
146 let mut style = Style::default();
147 style.size.width = match child.width {
148 Some(w) => length(w as f32),
149 None => match child.halign {
150 HAlign::Stretch => auto(),
151 _ => length(preferred_size.width as f32),
152 },
153 };
154 style.size.height = match child.height {
155 Some(h) => length(h as f32),
156 None => match child.valign {
157 VAlign::Stretch => auto(),
158 _ => length(preferred_size.height as f32),
159 },
160 };
161 let min_size = child.child_min_size()?;
162 style.min_size = taffy::Size {
163 width: length(min_size.width as f32),
164 height: length(min_size.height as f32),
165 };
166 if matches!(child.valign, VAlign::Top | VAlign::Center) {
167 style.margin.bottom = auto();
168 }
169 if matches!(child.valign, VAlign::Bottom | VAlign::Center) {
170 style.margin.top = auto();
171 }
172 if matches!(child.halign, HAlign::Left | HAlign::Center) {
173 style.margin.right = auto();
174 }
175 if matches!(child.halign, HAlign::Right | HAlign::Center) {
176 style.margin.left = auto();
177 }
178
179 style.grid_column.start = line(child.column as i16 + 1);
180 style.grid_row.start = line(child.row as i16 + 1);
181
182 let cspan = child.column_span;
183 if cspan > 1 {
184 style.grid_column.end = span(cspan as u16);
185 }
186
187 let rspan = child.row_span;
188 if rspan > 1 {
189 style.grid_row.end = span(rspan as u16);
190 }
191
192 let node = tree.new_leaf(style)?;
193 nodes.push(node);
194 }
195 let root = tree.new_with_children(
196 Style {
197 display: taffy::Display::Grid,
198 size: taffy::Size::from_percent(1.0, 1.0),
199 grid_template_columns: self
200 .columns
201 .iter()
202 .map(TrackSizingFunction::from)
203 .map(GridTemplateComponent::Single)
204 .collect(),
205 grid_template_rows: self
206 .rows
207 .iter()
208 .map(TrackSizingFunction::from)
209 .map(GridTemplateComponent::Single)
210 .collect(),
211 ..Default::default()
212 },
213 &nodes,
214 )?;
215 Ok((tree, root, nodes))
216 }
217
218 fn render(&mut self) -> Result<(), LayoutError<E>> {
219 let (tree, root, nodes) = self.tree()?;
220 render(tree, root, nodes, self.loc, self.size, &mut self.children)
221 }
222
223 pub fn set_loc(&mut self, p: Point) -> Result<(), LayoutError<E>> {
225 LayoutChild::set_child_loc(self, p)
226 }
227
228 pub fn set_size(&mut self, s: Size) -> Result<(), LayoutError<E>> {
230 LayoutChild::set_child_size(self, s)
231 }
232
233 pub fn set_rect(&mut self, r: Rect) -> Result<(), LayoutError<E>> {
235 LayoutChild::set_child_rect(self, r)
236 }
237}
238
239impl<E> Failable for Grid<'_, E> {
240 type Error = E;
241}
242
243impl<E> LayoutChild for Grid<'_, E> {
244 fn set_child_loc(&mut self, p: Point) -> Result<(), LayoutError<Self::Error>> {
245 self.loc = p;
246 self.render()
247 }
248
249 fn set_child_size(&mut self, s: Size) -> Result<(), LayoutError<Self::Error>> {
250 self.size = s;
251 self.render()
252 }
253
254 fn set_child_rect(&mut self, r: Rect) -> Result<(), LayoutError<Self::Error>> {
255 self.loc = r.origin;
256 self.size = r.size;
257 self.render()
258 }
259
260 fn child_preferred_size(&self) -> Result<Size, LayoutError<Self::Error>> {
261 let (mut tree, root, _) = self.tree()?;
262 tree.compute_layout(root, taffy::Size::max_content())?;
263 Ok(rect_t2e(tree.layout(root)?).size)
264 }
265
266 fn child_min_size(&self) -> Result<Size, LayoutError<Self::Error>> {
267 let (mut tree, root, _) = self.tree()?;
268 tree.compute_layout(root, taffy::Size::min_content())?;
269 Ok(rect_t2e(tree.layout(root)?).size)
270 }
271}