1use crate::{
2 core::{algebra::Vector2, math::Rect, pool::Handle, scope_profile},
3 draw::{CommandTexture, Draw, DrawingContext},
4 message::UiMessage,
5 widget::{Widget, WidgetBuilder},
6 BuildContext, Control, UiNode, UserInterface,
7};
8use std::{
9 any::{Any, TypeId},
10 cell::RefCell,
11 ops::{Deref, DerefMut},
12};
13
14#[derive(Clone, Copy, PartialEq)]
15pub enum SizeMode {
16 Strict,
17 Auto,
18 Stretch,
19}
20
21#[derive(Clone, Copy, PartialEq)]
22pub struct GridDimension {
23 size_mode: SizeMode,
24 desired_size: f32,
25 actual_size: f32,
26 location: f32,
27}
28
29impl GridDimension {
30 pub fn generic(size_mode: SizeMode, desired_size: f32) -> Self {
31 Self {
32 size_mode,
33 desired_size,
34 actual_size: 0.0,
35 location: 0.0,
36 }
37 }
38
39 pub fn strict(desired_size: f32) -> Self {
40 Self::generic(SizeMode::Strict, desired_size)
41 }
42
43 pub fn stretch() -> Self {
44 Self::generic(SizeMode::Stretch, 0.0)
45 }
46
47 pub fn auto() -> Self {
48 Self::generic(SizeMode::Auto, 0.0)
49 }
50}
51
52pub type Column = GridDimension;
53pub type Row = GridDimension;
54
55#[derive(Clone)]
57pub struct Grid {
58 widget: Widget,
59 rows: RefCell<Vec<Row>>,
60 columns: RefCell<Vec<Column>>,
61 draw_border: bool,
62 border_thickness: f32,
63 cells: RefCell<Vec<Cell>>,
64 groups: RefCell<[Vec<usize>; 4]>,
65}
66
67crate::define_widget_deref!(Grid);
68
69#[derive(Clone)]
70struct Cell {
71 nodes: Vec<Handle<UiNode>>,
72 width_constraint: Option<f32>,
73 height_constraint: Option<f32>,
74 row_index: usize,
75 column_index: usize,
76}
77
78fn group_index(row_size_mode: SizeMode, column_size_mode: SizeMode) -> usize {
79 match (row_size_mode, column_size_mode) {
80 (SizeMode::Strict, SizeMode::Strict)
81 | (SizeMode::Strict, SizeMode::Auto)
82 | (SizeMode::Auto, SizeMode::Strict)
83 | (SizeMode::Auto, SizeMode::Auto) => 0,
84 (SizeMode::Stretch, SizeMode::Auto) => 1,
85 (SizeMode::Strict, SizeMode::Stretch) | (SizeMode::Auto, SizeMode::Stretch) => 2,
86 (SizeMode::Stretch, SizeMode::Strict) | (SizeMode::Stretch, SizeMode::Stretch) => 3,
87 }
88}
89
90fn choose_constraint(dimension: &GridDimension, available_size: f32) -> Option<f32> {
91 match dimension.size_mode {
92 SizeMode::Strict => Some(dimension.desired_size),
93 SizeMode::Auto => Some(available_size),
94 SizeMode::Stretch => None,
95 }
96}
97
98fn choose_actual_size(
99 dimension: &GridDimension,
100 cell_size: f32,
101 available_size: f32,
102 stretch_size: f32,
103) -> f32 {
104 let current_actual_size = dimension.actual_size;
105 match dimension.size_mode {
106 SizeMode::Strict => dimension.desired_size,
107 SizeMode::Auto => current_actual_size.max(cell_size),
108 SizeMode::Stretch => current_actual_size.max(if available_size.is_infinite() {
109 cell_size
110 } else {
111 stretch_size
112 }),
113 }
114}
115
116fn calc_total_size_of_non_stretch_dims(
117 dims: &[GridDimension],
118 children: &[Handle<UiNode>],
119 ui: &UserInterface,
120 desired_size_fetcher: fn(&UiNode, usize) -> Option<f32>,
121) -> f32 {
122 let mut preset_size = 0.0;
123
124 for (i, dim) in dims.iter().enumerate() {
125 if dim.size_mode == SizeMode::Strict {
126 preset_size += dim.desired_size;
127 } else if dim.size_mode == SizeMode::Auto {
128 let mut dim_size = 0.0f32;
129 for child_handle in children {
130 let child = ui.nodes.borrow(*child_handle);
131 if let Some(desired_size) = (desired_size_fetcher)(child, i) {
132 dim_size = dim_size.max(desired_size);
133 }
134 }
135 preset_size += dim_size;
136 }
137 }
138
139 preset_size
140}
141
142fn count_stretch_dims(dims: &[GridDimension]) -> usize {
143 let mut stretch_sized_dims = 0;
144 for dim in dims.iter() {
145 if dim.size_mode == SizeMode::Stretch {
146 stretch_sized_dims += 1;
147 }
148 }
149 stretch_sized_dims
150}
151
152fn calc_avg_size_for_stretch_dim(
153 dims: &[GridDimension],
154 children: &[Handle<UiNode>],
155 available_size: f32,
156 ui: &UserInterface,
157 desired_size_fetcher: fn(&UiNode, usize) -> Option<f32>,
158) -> f32 {
159 let preset_size = calc_total_size_of_non_stretch_dims(dims, children, ui, desired_size_fetcher);
160
161 let rest_width = available_size - preset_size;
162
163 let stretch_sized_dims = count_stretch_dims(dims);
164 if stretch_sized_dims > 0 {
165 rest_width / stretch_sized_dims as f32
166 } else {
167 0.0
168 }
169}
170
171fn fetch_width(child: &UiNode, i: usize) -> Option<f32> {
172 if child.column() == i && child.visibility() {
173 Some(child.desired_size().x)
174 } else {
175 None
176 }
177}
178
179fn fetch_height(child: &UiNode, i: usize) -> Option<f32> {
180 if child.row() == i && child.visibility() {
181 Some(child.desired_size().y)
182 } else {
183 None
184 }
185}
186
187fn arrange_dims(dims: &mut [GridDimension], final_size: f32) {
188 let mut preset_width = 0.0;
189 for dim in dims.iter() {
190 if dim.size_mode == SizeMode::Auto || dim.size_mode == SizeMode::Strict {
191 preset_width += dim.actual_size;
192 }
193 }
194
195 let stretch_count = count_stretch_dims(dims);
196 let avg_size = if stretch_count > 0 {
197 (final_size - preset_width) / stretch_count as f32
198 } else {
199 0.0
200 };
201
202 let mut location = 0.0;
203 for dim in dims.iter_mut() {
204 dim.location = location;
205 location += match dim.size_mode {
206 SizeMode::Strict | SizeMode::Auto => dim.actual_size,
207 SizeMode::Stretch => avg_size,
208 };
209 }
210}
211
212impl Control for Grid {
213 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
214 if type_id == TypeId::of::<Self>() {
215 Some(self)
216 } else {
217 None
218 }
219 }
220
221 fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
222 scope_profile!();
223
224 let mut rows = self.rows.borrow_mut();
225 let mut columns = self.columns.borrow_mut();
226 let mut groups = self.groups.borrow_mut();
227 let mut cells = self.cells.borrow_mut();
228
229 if columns.is_empty() || rows.is_empty() {
231 return self.widget.measure_override(ui, available_size);
232 }
233
234 for row in rows.iter_mut() {
235 row.actual_size = 0.0;
236 }
237 for column in columns.iter_mut() {
238 column.actual_size = 0.0;
239 }
240 for group in groups.iter_mut() {
241 group.clear();
242 }
243 cells.clear();
244
245 for (column_index, column) in columns.iter().enumerate() {
246 for (row_index, row) in rows.iter().enumerate() {
247 groups[group_index(row.size_mode, column.size_mode)].push(cells.len());
248
249 cells.push(Cell {
250 nodes: self
251 .children()
252 .iter()
253 .filter_map(|&c| {
254 let child_ref = ui.node(c);
255 if child_ref.row() == row_index && child_ref.column() == column_index {
256 Some(c)
257 } else {
258 None
259 }
260 })
261 .collect(),
262 width_constraint: choose_constraint(column, available_size.x),
263 height_constraint: choose_constraint(row, available_size.y),
264 row_index,
265 column_index,
266 })
267 }
268 }
269
270 for group in groups.iter() {
271 for &cell_index in group.iter() {
272 let cell = &cells[cell_index];
273
274 let stretch_sized_width = calc_avg_size_for_stretch_dim(
275 &columns,
276 self.children(),
277 available_size.x,
278 ui,
279 fetch_width,
280 );
281
282 let stretch_sized_height = calc_avg_size_for_stretch_dim(
283 &rows,
284 self.children(),
285 available_size.y,
286 ui,
287 fetch_height,
288 );
289
290 let child_constraint = Vector2::new(
291 cell.width_constraint.unwrap_or(stretch_sized_width),
292 cell.height_constraint.unwrap_or(stretch_sized_height),
293 );
294
295 let mut cell_size = Vector2::<f32>::default();
296 for &node in cell.nodes.iter() {
297 ui.measure_node(node, child_constraint);
298 let node_ref = ui.node(node);
299 let desired_size = node_ref.desired_size();
300 cell_size.x = cell_size.x.max(desired_size.x);
301 cell_size.y = cell_size.y.max(desired_size.y);
302 }
303
304 let column = &mut columns[cell.column_index];
305 column.actual_size =
306 choose_actual_size(column, cell_size.x, available_size.x, stretch_sized_width);
307
308 let row = &mut rows[cell.row_index];
309 row.actual_size =
310 choose_actual_size(row, cell_size.y, available_size.y, stretch_sized_height);
311 }
312 }
313
314 let mut desired_size = Vector2::default();
315 for column in columns.iter() {
317 desired_size.x += column.actual_size;
318 }
319 for row in rows.iter() {
320 desired_size.y += row.actual_size;
321 }
322 desired_size
323 }
324
325 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
326 scope_profile!();
327
328 let mut columns = self.columns.borrow_mut();
329 let mut rows = self.rows.borrow_mut();
330
331 if columns.is_empty() || rows.is_empty() {
332 let rect = Rect::new(0.0, 0.0, final_size.x, final_size.y);
333 for child_handle in self.widget.children() {
334 ui.arrange_node(*child_handle, &rect);
335 }
336 return final_size;
337 }
338
339 arrange_dims(&mut columns, final_size.x);
340 arrange_dims(&mut rows, final_size.y);
341
342 for child_handle in self.widget.children() {
343 let child = ui.nodes.borrow(*child_handle);
344 if let Some(column) = columns.get(child.column()) {
345 if let Some(row) = rows.get(child.row()) {
346 ui.arrange_node(
347 *child_handle,
348 &Rect::new(
349 column.location,
350 row.location,
351 column.actual_size,
352 row.actual_size,
353 ),
354 );
355 }
356 }
357 }
358
359 final_size
360 }
361
362 fn draw(&self, drawing_context: &mut DrawingContext) {
363 if self.draw_border {
364 let bounds = self.widget.screen_bounds();
365
366 let left_top = Vector2::new(bounds.x(), bounds.y());
367 let right_top = Vector2::new(bounds.x() + bounds.w(), bounds.y());
368 let right_bottom = Vector2::new(bounds.x() + bounds.w(), bounds.y() + bounds.h());
369 let left_bottom = Vector2::new(bounds.x(), bounds.y() + bounds.h());
370
371 drawing_context.push_line(left_top, right_top, self.border_thickness);
372 drawing_context.push_line(right_top, right_bottom, self.border_thickness);
373 drawing_context.push_line(right_bottom, left_bottom, self.border_thickness);
374 drawing_context.push_line(left_bottom, left_top, self.border_thickness);
375
376 for column in self.columns.borrow().iter() {
377 let a = Vector2::new(bounds.x() + column.location, bounds.y());
378 let b = Vector2::new(bounds.x() + column.location, bounds.y() + bounds.h());
379 drawing_context.push_line(a, b, self.border_thickness);
380 }
381 for row in self.rows.borrow().iter() {
382 let a = Vector2::new(bounds.x(), bounds.y() + row.location);
383 let b = Vector2::new(bounds.x() + bounds.w(), bounds.y() + row.location);
384 drawing_context.push_line(a, b, self.border_thickness);
385 }
386
387 drawing_context.commit(
388 self.clip_bounds(),
389 self.widget.foreground(),
390 CommandTexture::None,
391 None,
392 );
393 }
394 }
395
396 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
397 self.widget.handle_routed_message(ui, message);
398 }
399}
400
401pub struct GridBuilder {
402 widget_builder: WidgetBuilder,
403 rows: Vec<Row>,
404 columns: Vec<Column>,
405 draw_border: bool,
406 border_thickness: f32,
407}
408
409impl GridBuilder {
410 pub fn new(widget_builder: WidgetBuilder) -> Self {
411 Self {
412 widget_builder,
413 rows: Vec::new(),
414 columns: Vec::new(),
415 draw_border: false,
416 border_thickness: 1.0,
417 }
418 }
419
420 pub fn add_row(mut self, row: Row) -> Self {
421 self.rows.push(row);
422 self
423 }
424
425 pub fn add_column(mut self, column: Column) -> Self {
426 self.columns.push(column);
427 self
428 }
429
430 pub fn add_rows(mut self, mut rows: Vec<Row>) -> Self {
431 self.rows.append(&mut rows);
432 self
433 }
434
435 pub fn add_columns(mut self, mut columns: Vec<Column>) -> Self {
436 self.columns.append(&mut columns);
437 self
438 }
439
440 pub fn draw_border(mut self, value: bool) -> Self {
441 self.draw_border = value;
442 self
443 }
444
445 pub fn with_border_thickness(mut self, value: f32) -> Self {
446 self.border_thickness = value;
447 self
448 }
449
450 pub fn build(self, ui: &mut BuildContext) -> Handle<UiNode> {
451 let grid = Grid {
452 widget: self.widget_builder.build(),
453 rows: RefCell::new(self.rows),
454 columns: RefCell::new(self.columns),
455 draw_border: self.draw_border,
456 border_thickness: self.border_thickness,
457 cells: Default::default(),
458 groups: Default::default(),
459 };
460 ui.add_node(UiNode::new(grid))
461 }
462}
463
464impl Grid {
465 pub fn new(widget: Widget) -> Self {
466 Self {
467 widget,
468 rows: Default::default(),
469 columns: Default::default(),
470 draw_border: false,
471 border_thickness: 1.0,
472 cells: Default::default(),
473 groups: Default::default(),
474 }
475 }
476
477 pub fn add_row(&mut self, row: Row) -> &mut Self {
478 self.rows.borrow_mut().push(row);
479 self
480 }
481
482 pub fn add_column(&mut self, column: Column) -> &mut Self {
483 self.columns.borrow_mut().push(column);
484 self
485 }
486
487 pub fn clear_columns(&mut self) {
488 self.columns.borrow_mut().clear();
489 }
490
491 pub fn clear_rows(&mut self) {
492 self.rows.borrow_mut().clear();
493 }
494
495 pub fn set_columns(&mut self, columns: Vec<Column>) {
496 self.columns = RefCell::new(columns);
497 }
498
499 pub fn set_rows(&mut self, rows: Vec<Row>) {
500 self.rows = RefCell::new(rows);
501 }
502
503 pub fn set_draw_border(&mut self, value: bool) -> &mut Self {
504 self.draw_border = value;
505 self
506 }
507
508 pub fn is_draw_border(&self) -> bool {
509 self.draw_border
510 }
511
512 pub fn set_border_thickness(&mut self, value: f32) -> &mut Self {
513 self.border_thickness = value;
514 self
515 }
516
517 pub fn border_thickness(&self) -> f32 {
518 self.border_thickness
519 }
520}