1use std::{fmt::Display, num::ParseFloatError, str::FromStr};
2
3use taffy::{
4 NodeId, Style, TaffyTree, TrackSizingFunction,
5 prelude::{auto, fr, length, line, span},
6};
7
8use super::{layout_child, rect_t2e, render};
9use crate::{HAlign, Layoutable, Margin, Point, Rect, Size, VAlign};
10
11#[derive(Debug)]
13#[non_exhaustive]
14pub enum ParseGridLengthError {
15 InvalidLength(ParseFloatError),
17}
18
19impl Display for ParseGridLengthError {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 match self {
22 Self::InvalidLength(e) => write!(f, "invalid length value: {e}"),
23 }
24 }
25}
26
27impl std::error::Error for ParseGridLengthError {}
28
29#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum GridLength {
32 Auto,
34 Stretch(f64),
36 Length(f64),
38}
39
40impl FromStr for GridLength {
41 type Err = ParseGridLengthError;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 if s.eq_ignore_ascii_case("auto") {
45 Ok(Self::Auto)
46 } else if let Some(s) = s.strip_suffix('*') {
47 s.parse::<f64>()
48 .map(Self::Stretch)
49 .map_err(ParseGridLengthError::InvalidLength)
50 } else {
51 s.parse::<f64>()
52 .map(Self::Length)
53 .map_err(ParseGridLengthError::InvalidLength)
54 }
55 }
56}
57
58impl From<GridLength> for TrackSizingFunction {
59 fn from(value: GridLength) -> Self {
60 match value {
61 GridLength::Auto => auto(),
62 GridLength::Length(v) => length(v as f32),
63 GridLength::Stretch(v) => fr(v as f32),
64 }
65 }
66}
67
68impl From<&GridLength> for TrackSizingFunction {
69 fn from(value: &GridLength) -> Self {
70 TrackSizingFunction::from(*value)
71 }
72}
73
74layout_child! {
75 struct GridChild {
77 column: usize = 0,
79 row: usize = 0,
81 column_span: usize = 1,
83 row_span: usize = 1,
85 }
86}
87
88pub struct Grid<'a> {
90 children: Vec<GridChild<'a>>,
91 columns: Vec<GridLength>,
92 rows: Vec<GridLength>,
93 loc: Point,
94 size: Size,
95}
96
97impl<'a> Grid<'a> {
98 pub fn new(columns: Vec<GridLength>, rows: Vec<GridLength>) -> Self {
100 Self {
101 children: vec![],
102 columns,
103 rows,
104 loc: Point::zero(),
105 size: Size::zero(),
106 }
107 }
108
109 pub fn from_str(
111 columns: impl AsRef<str>,
112 rows: impl AsRef<str>,
113 ) -> Result<Self, ParseGridLengthError> {
114 Ok(Self::new(
115 Self::parse_grid_lengths(columns.as_ref())?,
116 Self::parse_grid_lengths(rows.as_ref())?,
117 ))
118 }
119
120 fn parse_grid_lengths(s: &str) -> Result<Vec<GridLength>, ParseGridLengthError> {
121 let mut lengths = vec![];
122 for s in s.split(',') {
123 let s = s.trim();
124 lengths.push(s.parse()?);
125 }
126 Ok(lengths)
127 }
128
129 pub fn push<'b>(&'b mut self, widget: &'a mut dyn Layoutable) -> GridChildBuilder<'a, 'b> {
131 GridChildBuilder {
132 child: GridChild::new(widget),
133 children: &mut self.children,
134 }
135 }
136
137 fn tree(&self) -> (TaffyTree, NodeId, Vec<NodeId>) {
138 let mut tree: TaffyTree<()> = TaffyTree::new();
139 let mut nodes = vec![];
140 for child in &self.children {
141 let mut preferred_size = child.widget.preferred_size();
142 preferred_size.width += child.margin.horizontal();
143 preferred_size.height += child.margin.vertical();
144 let mut style = Style::default();
145 style.size.width = match child.width {
146 Some(w) => length(w as f32),
147 None => match child.halign {
148 HAlign::Stretch => auto(),
149 _ => length(preferred_size.width as f32),
150 },
151 };
152 style.size.height = match child.height {
153 Some(h) => length(h as f32),
154 None => match child.valign {
155 VAlign::Stretch => auto(),
156 _ => length(preferred_size.height as f32),
157 },
158 };
159 let mut min_size = child.widget.min_size();
160 min_size.width += child.margin.horizontal();
161 min_size.height += child.margin.vertical();
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).unwrap();
193 nodes.push(node);
194 }
195 let root = tree
196 .new_with_children(
197 Style {
198 display: taffy::Display::Grid,
199 size: taffy::Size::from_percent(1.0, 1.0),
200 grid_template_columns: self
201 .columns
202 .iter()
203 .map(TrackSizingFunction::from)
204 .collect(),
205 grid_template_rows: self.rows.iter().map(TrackSizingFunction::from).collect(),
206 ..Default::default()
207 },
208 &nodes,
209 )
210 .unwrap();
211 (tree, root, nodes)
212 }
213
214 fn render(&mut self) {
215 let (tree, root, nodes) = self.tree();
216 render(tree, root, nodes, self.loc, self.size, &mut self.children)
217 }
218}
219
220impl Layoutable for Grid<'_> {
221 fn loc(&self) -> Point {
222 self.loc
223 }
224
225 fn set_loc(&mut self, p: Point) {
226 self.loc = p;
227 self.render();
228 }
229
230 fn size(&self) -> Size {
231 self.size
232 }
233
234 fn set_size(&mut self, s: Size) {
235 self.size = s;
236 self.render();
237 }
238
239 fn set_rect(&mut self, r: Rect) {
240 self.loc = r.origin;
241 self.size = r.size;
242 self.render();
243 }
244
245 fn preferred_size(&self) -> Size {
246 let (mut tree, root, _) = self.tree();
247 tree.compute_layout(root, taffy::Size::max_content())
248 .unwrap();
249 rect_t2e(tree.layout(root).unwrap(), Margin::zero()).size
250 }
251}