1use crate::{
2 buffer::Buffer,
3 geometry::{Rect, Unit, Vec2},
4 widgets::cache::{Cache, GridCache},
5};
6
7use super::{widget::Widget, Element};
8
9#[derive(Debug, Default)]
38pub struct Grid {
39 children: Vec<GridChild>,
40 rows: Vec<Unit>,
41 cols: Vec<Unit>,
42}
43
44#[derive(Debug)]
46struct GridChild {
47 pub child: Element,
48 pub row: usize,
49 pub col: usize,
50}
51
52impl Grid {
53 #[must_use]
68 pub fn new<T1, T2>(cols: T1, rows: T2) -> Self
69 where
70 T1: IntoIterator,
71 T1::Item: Into<Unit>,
72 T2: IntoIterator,
73 T2::Item: Into<Unit>,
74 {
75 Self {
76 children: vec![],
77 rows: rows.into_iter().map(|r| r.into()).collect(),
78 cols: cols.into_iter().map(|c| c.into()).collect(),
79 }
80 }
81
82 #[must_use]
84 pub fn empty() -> Self {
85 Self::default()
86 }
87
88 pub fn row(&mut self, row: Unit) {
90 self.rows.push(row);
91 }
92
93 pub fn col(&mut self, col: Unit) {
95 self.cols.push(col);
96 }
97
98 #[deprecated(
100 since = "0.6.0",
101 note = "Kept for compatibility purposes; use `push` function instead"
102 )]
103 pub fn add_child<T>(&mut self, child: T, col: usize, row: usize)
104 where
105 T: Into<Element>,
106 {
107 self.children.push(GridChild {
108 child: child.into(),
109 row,
110 col,
111 })
112 }
113
114 pub fn push<T>(&mut self, child: T, col: usize, row: usize)
121 where
122 T: Into<Element>,
123 {
124 self.children.push(GridChild {
125 child: child.into(),
126 row,
127 col,
128 })
129 }
130}
131
132impl Widget for Grid {
133 fn render(&self, buffer: &mut Buffer, rect: Rect, cache: &mut Cache) {
134 if rect.is_empty() || self.children.is_empty() {
135 return;
136 }
137
138 let (cols, cols_pos, rows, rows_pos) = self.get_sizes(&rect, cache);
139
140 for (i, GridChild { child, row, col }) in
141 self.children.iter().enumerate()
142 {
143 let crect = Rect::new(
144 rect.x() + cols_pos[*col],
145 rect.y() + rows_pos[*row],
146 cols[*col],
147 rows[*row],
148 );
149 child.render(buffer, crect, &mut cache.children[i]);
150 }
151 }
152
153 fn height(&self, size: &Vec2) -> usize {
154 let mut height = 0;
155 for row in self.rows.iter() {
156 match row {
157 Unit::Length(len) => height += len,
158 Unit::Percent(p) => height += size.y * p / 100,
159 _ => {}
160 }
161 }
162 height
163 }
164
165 fn width(&self, size: &Vec2) -> usize {
166 let mut width = 0;
167 for col in self.cols.iter() {
168 match col {
169 Unit::Length(len) => width += len,
170 Unit::Percent(p) => width += size.y * p / 100,
171 _ => {}
172 }
173 }
174 width
175 }
176
177 fn children(&self) -> Vec<&Element> {
178 self.children.iter().map(|c| &c.child).collect()
179 }
180}
181
182impl Grid {
183 fn get_sizes(
185 &self,
186 rect: &Rect,
187 cache: &mut Cache,
188 ) -> (Vec<usize>, Vec<usize>, Vec<usize>, Vec<usize>) {
189 match self.get_cache(rect, cache) {
190 Some((cols, rows)) => {
191 let cols_pos = Self::get_positions(&cols);
192 let rows_pos = Self::get_positions(&rows);
193 (cols, cols_pos, rows, rows_pos)
194 }
195 None => {
196 let cols = Self::get_size(&self.cols, rect.width());
197 let rows = Self::get_size(&self.rows, rect.height());
198 self.create_cache(rect, cache, &cols.0, &rows.0);
199 (cols.0, cols.1, rows.0, rows.1)
200 }
201 }
202 }
203
204 fn get_size(units: &[Unit], size: usize) -> (Vec<usize>, Vec<usize>) {
206 let mut total = 0;
207 let mut fills_total = 0;
208
209 let mut sizes = Vec::new();
210 let mut positions = Vec::new();
211 let mut fills = Vec::new();
212 for unit in units {
213 let len = match unit {
214 Unit::Length(len) => *len,
215 Unit::Percent(p) => size * p / 100,
216 Unit::Fill(f) => {
217 fills_total += f;
218 fills.push(sizes.len());
219 *f
220 }
221 };
222 sizes.push(len);
223 positions.push(total);
224 total += len;
225 }
226
227 if fills_total == 0 {
228 return (sizes, positions);
229 }
230
231 let mut pos = 0;
232 let remain = size.saturating_sub(total);
233 for (i, row) in units.iter().enumerate() {
234 match row {
235 Unit::Fill(f) => {
236 sizes[i] = remain * f / fills_total;
237 positions[i] = pos;
238 pos += sizes[i];
239 }
240 _ => {
241 positions[i] = pos;
242 pos += sizes[i];
243 }
244 }
245 }
246
247 (sizes, positions)
248 }
249
250 fn get_positions(sizes: &[usize]) -> Vec<usize> {
251 let mut total = 0;
252 let mut pos = vec![];
253 for size in sizes {
254 pos.push(total);
255 total += size;
256 }
257 pos
258 }
259
260 fn get_cache(
261 &self,
262 rect: &Rect,
263 cache: &mut Cache,
264 ) -> Option<(Vec<usize>, Vec<usize>)> {
265 let lcache = cache.local::<GridCache>()?;
266 if !lcache.same_key(rect.size(), &self.cols, &self.rows) {
267 return None;
268 }
269 Some((lcache.col_sizes.clone(), lcache.row_sizes.clone()))
270 }
271
272 fn create_cache(
273 &self,
274 rect: &Rect,
275 cache: &mut Cache,
276 cols: &[usize],
277 rows: &[usize],
278 ) {
279 let lcache =
280 GridCache::new(*rect.size(), self.cols.clone(), self.rows.clone())
281 .sizes(cols.to_owned(), rows.to_owned());
282 cache.local = Some(Box::new(lcache));
283 }
284}
285
286impl From<Grid> for Box<dyn Widget> {
287 fn from(value: Grid) -> Self {
288 Box::new(value)
289 }
290}
291
292impl From<Grid> for Element {
293 fn from(value: Grid) -> Self {
294 Element::new(value)
295 }
296}