rg3d_ui/
grid.rs

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/// Automatically arranges children by rows and columns
56#[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        // In case of no rows or columns, grid acts like default panel.
230        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        // Step 4. Calculate desired size of grid.
316        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}