fyrox_ui/
grid.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Grid widget is able to position children widgets into a grid of specifically sized rows and columns. See
22//! [`Grid`] doc for more info and usage examples.
23
24#![warn(missing_docs)]
25
26use crate::{
27    core::{
28        algebra::Vector2, log::Log, math::Rect, pool::Handle, reflect::prelude::*,
29        type_traits::prelude::*, uuid_provider, variable::InheritableVariable, visitor::prelude::*,
30    },
31    define_constructor,
32    draw::{CommandTexture, Draw, DrawingContext},
33    message::{MessageDirection, UiMessage},
34    widget::{Widget, WidgetBuilder},
35    BuildContext, Control, UiNode, UserInterface,
36};
37use core::f32;
38use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
39use fyrox_graph::BaseSceneGraph;
40use std::{
41    cell::RefCell,
42    ops::{Deref, DerefMut},
43};
44use strum_macros::{AsRefStr, EnumString, VariantNames};
45
46/// A set of messages that can be used to modify [`Grid`] widget state.
47#[derive(Debug, PartialEq, Clone)]
48pub enum GridMessage {
49    /// Sets new rows for the grid widget.
50    Rows(Vec<Row>),
51    /// Sets new columns for the grid widget.
52    Columns(Vec<Column>),
53    /// Sets whether the grid should draw its border or not.
54    DrawBorder(bool),
55    /// Sets new border thickness for the grid.
56    BorderThickness(f32),
57}
58
59impl GridMessage {
60    define_constructor!(
61        /// Creates a new [`Self::Rows`] message.
62        GridMessage:Rows => fn rows(Vec<Row>), layout: false
63    );
64
65    define_constructor!(
66        /// Creates a new [`Self::Columns`] message.
67        GridMessage:Columns => fn columns(Vec<Column>), layout: false
68    );
69
70    define_constructor!(
71        /// Creates a new [`Self::DrawBorder`] message.
72        GridMessage:DrawBorder => fn draw_border(bool), layout: false
73    );
74
75    define_constructor!(
76        /// Creates a new [`Self::BorderThickness`] message.
77        GridMessage:BorderThickness => fn border_thickness(f32), layout: false
78    );
79}
80
81/// Size mode defines how grid's dimension (see [`GridDimension`]) will behave on layout step.
82#[derive(
83    Clone, Copy, PartialEq, Eq, Debug, Reflect, Visit, Default, AsRefStr, EnumString, VariantNames,
84)]
85pub enum SizeMode {
86    /// The desired size of this dimension must be provided in advance,
87    /// and it will always be rendered with exactly that size, regardless of what nodes it contains.
88    #[default]
89    Strict,
90    /// The desired size of this dimension is the maximum of the desired sizes of all the nodes when
91    /// they are measured with infinite available size.
92    Auto,
93    /// The size of this dimension is determined by subtracting the desired size of the other rows/columns
94    /// from the total available size, if the available size is finite.
95    /// If the total available size is infinite, then Stretch is equivalent to Auto.
96    Stretch,
97}
98
99uuid_provider!(SizeMode = "9c5dfbce-5df2-4a7f-8c57-c4473743a718");
100
101/// Grid dimension defines sizing rules and constraints for [`Grid`]'s rows and columns.
102#[derive(Clone, Copy, PartialEq, Debug, Reflect, Visit, Default)]
103pub struct GridDimension {
104    /// Current size mode of the dimension.
105    pub size_mode: SizeMode,
106    /// Desired size of the dimension. This must be supplied if [`SizeMode::Strict`],
107    /// and it is automatically calculated if [`SizeMode::Auto`].
108    /// If [`SizeMode::Stretch`]. this represents the size of the dimension before excess space is added.
109    pub desired_size: f32,
110    /// Measured size of the dimension. It could be considered as "output" parameter of the dimension
111    /// that will be filled after measurement layout step. It is used to calculate the grid's desired size.
112    pub actual_size: f32,
113    /// Local position along the axis of the dimension after arrangement step.
114    pub location: f32,
115    /// The number of children in this dimension that still need to be measured before the size is known.
116    /// For Auto rows and columns this is initially the number of nodes in that row or column,
117    /// and then it is reduced as nodes are measured.
118    /// This is zero for all non-Auto rows and columns.
119    #[visit(skip)]
120    #[reflect(hidden)]
121    unmeasured_node_count: usize,
122}
123
124uuid_provider!(GridDimension = "5e894900-c14a-4eb6-acb9-1636efead4b4");
125
126impl GridDimension {
127    /// Generic constructor for [`GridDimension`].
128    pub fn generic(size_mode: SizeMode, desired_size: f32) -> Self {
129        Self {
130            size_mode,
131            desired_size,
132            actual_size: 0.0,
133            location: 0.0,
134            unmeasured_node_count: 0,
135        }
136    }
137
138    /// Creates new [`GridDimension`] with [`SizeMode::Strict`] and the specified size constraint.
139    pub fn strict(desired_size: f32) -> Self {
140        Self::generic(SizeMode::Strict, desired_size)
141    }
142
143    /// Creates new [`GridDimension`] with [`SizeMode::Stretch`].
144    pub fn stretch() -> Self {
145        Self::generic(SizeMode::Stretch, 0.0)
146    }
147
148    /// Creates new [`GridDimension`] with [`SizeMode::Auto`].
149    pub fn auto() -> Self {
150        Self::generic(SizeMode::Auto, 0.0)
151    }
152
153    fn update_size(&mut self, node_size: f32, available_size: f32) {
154        match self.size_mode {
155            SizeMode::Strict => (),
156            SizeMode::Auto => {
157                self.desired_size = self.desired_size.max(node_size);
158                self.actual_size = self.desired_size;
159            }
160            SizeMode::Stretch => {
161                if available_size.is_finite() {
162                    self.actual_size = self.desired_size + available_size;
163                } else {
164                    self.actual_size = node_size;
165                }
166            }
167        }
168    }
169}
170
171/// Type alias for grid columns.
172pub type Column = GridDimension;
173
174/// Type alias for grid rows.
175pub type Row = GridDimension;
176
177/// Grids are one of several methods to position multiple widgets in relation to each other. A Grid widget, as the name
178/// implies, is able to position children widgets into a grid of specifically sized rows and columns.
179///
180/// Here is a simple example that positions several text widgets into a 2 by 2 grid:
181///
182/// ```rust,no_run
183/// # use fyrox_ui::{
184/// #     UiNode, core::pool::Handle,
185/// #     BuildContext,
186/// #     widget::WidgetBuilder,
187/// #     text::TextBuilder,
188/// #     grid::{GridBuilder, GridDimension},
189/// # };
190/// fn create_text_grid(ctx: &mut BuildContext) -> Handle<UiNode> {
191///     GridBuilder::new(
192///         WidgetBuilder::new()
193///             .with_child(
194///                 TextBuilder::new(WidgetBuilder::new())
195///                     .with_text("top left ")
196///                     .build(ctx),
197///             )
198///             .with_child(
199///                 TextBuilder::new(WidgetBuilder::new().on_column(1))
200///                     .with_text(" top right")
201///                     .build(ctx),
202///             )
203///             .with_child(
204///                 TextBuilder::new(WidgetBuilder::new().on_row(1))
205///                     .with_text("bottom left ")
206///                     .build(ctx),
207///             )
208///             .with_child(
209///                 TextBuilder::new(WidgetBuilder::new().on_row(1).on_column(1))
210///                     .with_text(" bottom right")
211///                     .build(ctx),
212///             ),
213///     )
214///     .add_row(GridDimension::auto())
215///     .add_row(GridDimension::auto())
216///     .add_column(GridDimension::auto())
217///     .add_column(GridDimension::auto())
218///     .build(ctx)
219/// }
220/// ```
221///
222/// As with other UI widgets, Grids are created via the [`GridBuilder`] struct. Each widget whose position should be controlled
223/// by the Grid should be added as a child of the [`GridBuilder`]'s base widget.
224///
225/// You then need to tell each child what row and column it belongs to via the [`WidgetBuilder::on_column`] and [`WidgetBuilder::on_row`]
226/// functions of their base widget. By default, all children will be placed into row 0, column 0.
227///
228/// After that you need to provide sizing constraints for each row and column to the [`GridBuilder`] by using the [`GridBuilder::add_row`]
229/// and [`GridBuilder::add_column`] functions while providing a [`GridDimension`] instance to the call. [`GridDimension`] can be
230/// constructed with the following functions:
231///
232/// * [`GridDimension::auto`] - Sizes the row or column so it's just large enough to fit the largest child's size.
233/// * [`GridDimension::stretch`] - Stretches the row or column to fill the parent's available space, if multiple rows or
234/// columns have this option the size is evenly distributed between them.
235/// * [`GridDimension::strict`] - Sets the row or column to be exactly the given value of pixels long. So a row will only
236/// be the given number of pixels wide, while a column will be that many pixels tall.
237///
238/// You can add any number of rows and columns to a grid widget, and each grid cell does **not** need to have a UI widget
239/// in it to be valid. For example you can add a column and set it to a specific size via strict to provide spacing between
240/// two other columns.
241#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
242pub struct Grid {
243    /// Base widget of the grid.
244    pub widget: Widget,
245    /// A set of rows of the grid.
246    pub rows: InheritableVariable<RefCell<Vec<Row>>>,
247    /// A set of columns of the grid.
248    pub columns: InheritableVariable<RefCell<Vec<Column>>>,
249    /// Defines whether to draw grid's border or not. It could be useful for debugging purposes.
250    pub draw_border: InheritableVariable<bool>,
251    /// Defines border thickness when `draw_border` is on.
252    pub border_thickness: InheritableVariable<f32>,
253    /// Current set of cells of the grid.
254    #[visit(skip)]
255    #[reflect(hidden)]
256    pub cells: RefCell<Vec<Cell>>,
257    /// A set of four groups, where each group contains cell indices. It is used for measurement
258    /// purposes to group the cells in specific way, so it can be measured in the correct order
259    /// later.
260    #[visit(skip)]
261    #[reflect(hidden)]
262    pub groups: RefCell<[Vec<usize>; 4]>,
263}
264
265impl ConstructorProvider<UiNode, UserInterface> for Grid {
266    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
267        GraphNodeConstructor::new::<Self>()
268            .with_variant("Grid", |ui| {
269                GridBuilder::new(WidgetBuilder::new().with_name("Grid"))
270                    .build(&mut ui.build_ctx())
271                    .into()
272            })
273            .with_group("Layout")
274    }
275}
276
277crate::define_widget_deref!(Grid);
278
279/// Cell of the grid, that contains additional information for layout purposes. It does not have any
280/// particular use outside of grid's internals.
281#[derive(Clone, Debug)]
282pub struct Cell {
283    /// A set of nodes of the cell.
284    pub nodes: Vec<Handle<UiNode>>,
285    /// Vertical location of the cell (row number).
286    pub row_index: usize,
287    /// Horizontal location of the cell (column number).
288    pub column_index: usize,
289}
290
291/// ```text
292///                Strict   Auto   Stretch
293///               +-------+-------+-------+
294///               |       |       |       |
295///        Strict |   0   |   0   |   2   |
296///               |       |       |       |
297///               +-------+-------+-------+
298///               |       |       |       |
299///          Auto |   0   |   0   |   2   |
300///               |       |       |       |
301///               +-------+-------+-------+
302///               |       |       |       |
303///       Stretch |   3   |   1   |   3   |
304///               |       |       |       |
305///               +-------+-------+-------+
306/// ```
307/// Group 0 represents all nodes with no stretch. They can be measured without needing any
308/// desired size information from other nodes, and so they are always measured first.
309///
310/// Group 1 is special because it contains all the remaining auto-width nodes
311/// after group 0 has been measured, and group 1 may blocked from being measured
312/// due to group 2 not yet being measured to provide the desired size of the
313/// remaining auto rows.
314///
315/// In order to allow measurement to proceed in that situation, group 1 may be forced
316/// to measure despite not yet knowing its true vertical available size.
317/// The width information gained from the measurement of group 1 makes it possible to
318/// measure group 2, and then group 1 will be measured a second time to get its
319/// correct desired height. Group 1 is the only group that is ever measured twice.
320fn group_index(row_size_mode: SizeMode, column_size_mode: SizeMode) -> usize {
321    match (row_size_mode, column_size_mode) {
322        (SizeMode::Strict, SizeMode::Strict)
323        | (SizeMode::Strict, SizeMode::Auto)
324        | (SizeMode::Auto, SizeMode::Strict)
325        | (SizeMode::Auto, SizeMode::Auto) => 0,
326        (SizeMode::Stretch, SizeMode::Auto) => 1,
327        (SizeMode::Strict, SizeMode::Stretch) | (SizeMode::Auto, SizeMode::Stretch) => 2,
328        (SizeMode::Stretch, SizeMode::Strict) | (SizeMode::Stretch, SizeMode::Stretch) => 3,
329    }
330}
331
332fn choose_constraint(dimension: &GridDimension, available_size: f32) -> f32 {
333    match dimension.size_mode {
334        // Strict always has a constraint of its desired size.
335        SizeMode::Strict => dimension.desired_size,
336        // For Stretch rows and columns, the available size is whatever size is not used up
337        // by the other rows and columns.
338        // First we give the node the desired size, which is most likely zero for a Stretch row/column,
339        // then we expand it to include the available size.
340        SizeMode::Stretch => dimension.desired_size + available_size,
341        // Auto means being free to choose whatever size the widget pleases.
342        // If the constraint were set to `available_size` then the widget might choose
343        // to use all of that size and crowd out all other cells of the grid.
344        // A constraint of infinity encourages the node to pick a more reasonable size.
345        SizeMode::Auto => f32::INFINITY,
346    }
347}
348
349fn calc_total_size_of_non_stretch_dims(dims: &[GridDimension]) -> Option<f32> {
350    if dims.iter().all(|d| d.size_mode != SizeMode::Stretch) {
351        // If there are no stretch rows/columns, then the value we return will never be used.
352        Some(0.0) // Arbitrarily choose 0.0, but it should not matter.
353    } else if dims.iter().all(|d| d.unmeasured_node_count == 0) {
354        // We have at least one stretch, so seriously calculate the size
355        // This requires that all the autos be already measured.
356        Some(dims.iter().map(|d| d.desired_size).sum())
357    } else {
358        // We have at least one stretch but not all the autos are measured
359        // so we fail.
360        None
361    }
362}
363
364fn count_stretch_dims(dims: &[GridDimension]) -> usize {
365    let mut stretch_sized_dims = 0;
366    for dim in dims.iter() {
367        if dim.size_mode == SizeMode::Stretch {
368            stretch_sized_dims += 1;
369        }
370    }
371    stretch_sized_dims
372}
373
374fn calc_avg_size_for_stretch_dim(
375    dims: &RefCell<Vec<GridDimension>>,
376    available_size: f32,
377) -> Option<f32> {
378    if available_size.is_infinite() {
379        // If we have limitless available size, then short-circuit to avoid the possibility
380        // of returning None due to missing Auto measurements. Measuring Auto nodes does not matter
381        // when available_size is infinite, and returning None might force an unnecessary double-measure.
382        return Some(available_size);
383    }
384    let dims = dims.borrow();
385    let stretch_sized_dims = count_stretch_dims(&dims);
386    if stretch_sized_dims > 0 {
387        let rest_size = available_size - calc_total_size_of_non_stretch_dims(&dims)?;
388        Some(rest_size / stretch_sized_dims as f32)
389    } else {
390        // If there are no stretch nodes in this row/column, then this result will never be used.
391        Some(0.0) // Choose 0.0 arbitrarily.
392    }
393}
394
395fn arrange_dims(dims: &mut [GridDimension], final_size: f32) {
396    // Every row/column has a desired size, so summing all the desired sizes is correct.
397    // Strict rows/columns have their desired size set when building the grid.
398    // Auto rows/columns are calculated in the measure step.
399    // Stretch rows/columns default to zero.
400    let preset_width: f32 = dims.iter().map(|d| d.desired_size).sum();
401
402    let stretch_count = count_stretch_dims(dims);
403    let avg_stretch = if stretch_count > 0 {
404        (final_size - preset_width) / stretch_count as f32
405    } else {
406        // Since stretch_count is zero, this value will never be used.
407        0.0
408    };
409
410    let mut location = 0.0;
411    for dim in dims.iter_mut() {
412        dim.location = location;
413        dim.actual_size = match dim.size_mode {
414            SizeMode::Strict | SizeMode::Auto => dim.desired_size,
415            SizeMode::Stretch => dim.desired_size + avg_stretch,
416        };
417        location += dim.actual_size;
418    }
419}
420
421uuid_provider!(Grid = "98ce15e2-bd62-497d-a37b-9b1cb4a1918c");
422
423impl Grid {
424    fn initialize_measure(&self, ui: &UserInterface) {
425        self.calc_needed_measurements(ui);
426
427        let mut groups = self.groups.borrow_mut();
428        for group in groups.iter_mut() {
429            group.clear();
430        }
431
432        let mut cells = self.cells.borrow_mut();
433        cells.clear();
434
435        let rows = self.rows.borrow();
436        let columns = self.columns.borrow();
437        for (column_index, column) in columns.iter().enumerate() {
438            for (row_index, row) in rows.iter().enumerate() {
439                groups[group_index(row.size_mode, column.size_mode)].push(cells.len());
440
441                cells.push(Cell {
442                    nodes: self
443                        .children()
444                        .iter()
445                        .copied()
446                        .filter(|&c| {
447                            let Some(child_ref) = ui.try_get(c) else {
448                                return false;
449                            };
450                            child_ref.row() == row_index && child_ref.column() == column_index
451                        })
452                        .collect(),
453                    row_index,
454                    column_index,
455                })
456            }
457        }
458    }
459    fn calc_needed_measurements(&self, ui: &UserInterface) {
460        let mut rows = self.rows.borrow_mut();
461        let mut cols = self.columns.borrow_mut();
462        for dim in rows.iter_mut().chain(cols.iter_mut()) {
463            dim.unmeasured_node_count = 0;
464            match dim.size_mode {
465                SizeMode::Auto => dim.desired_size = 0.0,
466                SizeMode::Strict => dim.actual_size = dim.desired_size,
467                SizeMode::Stretch => (),
468            }
469        }
470        for handle in self.children() {
471            let Some(node) = ui.try_get(*handle) else {
472                continue;
473            };
474            let Some(row) = rows.get_mut(node.row()) else {
475                Log::err(format!(
476                    "Node row out of bounds: {} row:{}, column:{}",
477                    node.type_name(),
478                    node.row(),
479                    node.column()
480                ));
481                continue;
482            };
483            let Some(col) = cols.get_mut(node.column()) else {
484                Log::err(format!(
485                    "Node column out of bounds: {} row:{}, column:{}",
486                    node.type_name(),
487                    node.row(),
488                    node.column()
489                ));
490                continue;
491            };
492            if col.size_mode == SizeMode::Auto {
493                col.unmeasured_node_count += 1
494            }
495            if row.size_mode == SizeMode::Auto {
496                row.unmeasured_node_count += 1
497            }
498        }
499    }
500    fn measure_width_and_height(
501        &self,
502        child: Handle<UiNode>,
503        ui: &UserInterface,
504        available_size: Vector2<f32>,
505        measure_width: bool,
506        measure_height: bool,
507    ) {
508        let Some(node) = ui.try_get(child) else {
509            return;
510        };
511        let mut rows = self.rows.borrow_mut();
512        let mut cols = self.columns.borrow_mut();
513        let Some(row) = rows.get_mut(node.row()) else {
514            return;
515        };
516        let Some(col) = cols.get_mut(node.column()) else {
517            return;
518        };
519        let constraint = Vector2::new(
520            choose_constraint(col, available_size.x),
521            choose_constraint(row, available_size.y),
522        );
523        ui.measure_node(child, constraint);
524        if measure_width {
525            col.update_size(node.desired_size().x, available_size.x);
526            if col.size_mode == SizeMode::Auto {
527                col.unmeasured_node_count -= 1;
528            }
529        }
530        if measure_height {
531            row.update_size(node.desired_size().y, available_size.y);
532            if row.size_mode == SizeMode::Auto {
533                row.unmeasured_node_count -= 1;
534            }
535        }
536    }
537    fn measure_group_width(
538        &self,
539        group: &[usize],
540        ui: &UserInterface,
541        available_size: Vector2<f32>,
542    ) {
543        let cells = self.cells.borrow();
544        for cell in group.iter().map(|&i| &cells[i]) {
545            for n in cell.nodes.iter() {
546                self.measure_width_and_height(*n, ui, available_size, true, false);
547            }
548        }
549    }
550    fn measure_group_height(
551        &self,
552        group: &[usize],
553        ui: &UserInterface,
554        available_size: Vector2<f32>,
555    ) {
556        let cells = self.cells.borrow();
557        for cell in group.iter().map(|&i| &cells[i]) {
558            for n in cell.nodes.iter() {
559                self.measure_width_and_height(*n, ui, available_size, false, true);
560            }
561        }
562    }
563    fn measure_group(&self, group: &[usize], ui: &UserInterface, available_size: Vector2<f32>) {
564        let cells = self.cells.borrow();
565        for cell in group.iter().map(|&i| &cells[i]) {
566            for n in cell.nodes.iter() {
567                self.measure_width_and_height(*n, ui, available_size, true, true);
568            }
569        }
570    }
571}
572
573impl Control for Grid {
574    fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
575        // In case of no rows or columns, grid acts like default panel.
576        if self.columns.borrow().is_empty() || self.rows.borrow().is_empty() {
577            return self.widget.measure_override(ui, available_size);
578        }
579
580        self.initialize_measure(ui);
581
582        let groups = self.groups.borrow_mut();
583
584        // Start by measuring all the nodes with no stretch in either dimension: group 0
585        self.measure_group(&groups[0], ui, available_size);
586
587        if let Some(space_y) = calc_avg_size_for_stretch_dim(&self.rows, available_size.y) {
588            // Measuring group 0 was enough to allow us to calculate the needed stretch along the height of the grid,
589            // so use that stretch to measure group 1 (auto width, stretch height).
590            self.measure_group(&groups[1], ui, Vector2::new(available_size.x, space_y));
591            // Measuring group 0 and group 1 guarantees that we have measured all the auto-width nodes, so this is safe to unwrap.
592            let space_x = calc_avg_size_for_stretch_dim(&self.columns, available_size.x).unwrap();
593            // Use the calculated horizontal stretch to measure all the remaining nodes.
594            self.measure_group(&groups[2], ui, Vector2::new(space_x, available_size.y));
595            self.measure_group(&groups[3], ui, Vector2::new(space_x, space_y));
596        } else if let Some(space_x) = calc_avg_size_for_stretch_dim(&self.columns, available_size.x)
597        {
598            // We were unable to calculate the vertical stretch, but we can calculate the horizontal stretch,
599            // so use the horizontal stretch to measure group 2 (stretch width, strict/auto height).
600            // We know that group 1 is empty, since group 1 has auto width and we have not yet measured group 1.
601            self.measure_group(&groups[2], ui, Vector2::new(space_x, available_size.y));
602            // Measuring group 0 and group 2 guarantees that we have measured all the auto-height nodes, so this is safe to unwrap.
603            let space_y = calc_avg_size_for_stretch_dim(&self.rows, available_size.y).unwrap();
604            // Use the calculated vertical stretch to measure the remaining nodes.
605            self.measure_group(&groups[3], ui, Vector2::new(space_x, space_y));
606        } else {
607            // We could not calculate either the vertical stretch or the horizontal stretch.
608            // The only horizontal autos we have not measured are in group 1 (auto width, stretch height),
609            // so we are forced to measure group 1 as it if had auto height, just so it can provide its width to its column.
610            // The desired height provided by this measurement is ignored.
611            self.measure_group_width(&groups[1], ui, Vector2::new(f32::INFINITY, f32::INFINITY));
612            // Measuring group 0 and group 1 guarantees that we have measured all the auto-width nodes, so this is safe to unwrap.
613            let space_x = calc_avg_size_for_stretch_dim(&self.columns, available_size.x).unwrap();
614            // Use the calculated horizontal stretch to measure group 2 (stretch width, strict/auto height).
615            self.measure_group(&groups[2], ui, Vector2::new(space_x, available_size.y));
616            // Measuring group 0 and group 2 guarantees that we have measured all the auto-height nodes, so this is safe to unwrap.
617            let space_y = calc_avg_size_for_stretch_dim(&self.rows, available_size.y).unwrap();
618            // Now that we finally have the vertical stretch amount, we can properly measure group 1 (auto width, stretch height).
619            // This is the only time we measure a node twice. The first time was just to discover the width.
620            // This measurement is just for height, now that we can give the node the true available veritical size.
621            self.measure_group_height(&groups[1], ui, Vector2::new(available_size.x, space_y));
622            self.measure_group(&groups[3], ui, Vector2::new(space_x, space_y));
623        }
624
625        let desired_size = Vector2::<f32>::new(
626            self.columns.borrow().iter().map(|c| c.actual_size).sum(),
627            self.rows.borrow().iter().map(|r| r.actual_size).sum(),
628        );
629        desired_size
630    }
631
632    fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
633        let mut columns = self.columns.borrow_mut();
634        let mut rows = self.rows.borrow_mut();
635
636        if columns.is_empty() || rows.is_empty() {
637            let rect = Rect::new(0.0, 0.0, final_size.x, final_size.y);
638            for child_handle in self.widget.children() {
639                ui.arrange_node(*child_handle, &rect);
640            }
641            return final_size;
642        }
643
644        arrange_dims(&mut columns, final_size.x);
645        arrange_dims(&mut rows, final_size.y);
646
647        for child_handle in self.widget.children() {
648            let child = ui.nodes.borrow(*child_handle);
649            if let Some(column) = columns.get(child.column()) {
650                if let Some(row) = rows.get(child.row()) {
651                    ui.arrange_node(
652                        *child_handle,
653                        &Rect::new(
654                            column.location,
655                            row.location,
656                            column.actual_size,
657                            row.actual_size,
658                        ),
659                    );
660                }
661            }
662        }
663
664        final_size
665    }
666
667    fn draw(&self, drawing_context: &mut DrawingContext) {
668        if *self.draw_border {
669            let bounds = self.widget.bounding_rect();
670
671            let left_top = Vector2::new(bounds.x(), bounds.y());
672            let right_top = Vector2::new(bounds.x() + bounds.w(), bounds.y());
673            let right_bottom = Vector2::new(bounds.x() + bounds.w(), bounds.y() + bounds.h());
674            let left_bottom = Vector2::new(bounds.x(), bounds.y() + bounds.h());
675
676            drawing_context.push_line(left_top, right_top, *self.border_thickness);
677            drawing_context.push_line(right_top, right_bottom, *self.border_thickness);
678            drawing_context.push_line(right_bottom, left_bottom, *self.border_thickness);
679            drawing_context.push_line(left_bottom, left_top, *self.border_thickness);
680
681            for column in self.columns.borrow().iter() {
682                let a = Vector2::new(bounds.x() + column.location, bounds.y());
683                let b = Vector2::new(bounds.x() + column.location, bounds.y() + bounds.h());
684                drawing_context.push_line(a, b, *self.border_thickness);
685            }
686            for row in self.rows.borrow().iter() {
687                let a = Vector2::new(bounds.x(), bounds.y() + row.location);
688                let b = Vector2::new(bounds.x() + bounds.w(), bounds.y() + row.location);
689                drawing_context.push_line(a, b, *self.border_thickness);
690            }
691
692            drawing_context.commit(
693                self.clip_bounds(),
694                self.widget.foreground(),
695                CommandTexture::None,
696                None,
697            );
698        }
699    }
700
701    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
702        self.widget.handle_routed_message(ui, message);
703
704        if let Some(msg) = message.data::<GridMessage>() {
705            if message.direction() == MessageDirection::ToWidget
706                && message.destination() == self.handle
707            {
708                match msg {
709                    GridMessage::Rows(rows) => {
710                        if &*self.rows.borrow() != rows {
711                            self.rows
712                                .set_value_and_mark_modified(RefCell::new(rows.clone()));
713                            self.invalidate_layout();
714                        }
715                    }
716                    GridMessage::Columns(columns) => {
717                        if &*self.columns.borrow() != columns {
718                            self.columns
719                                .set_value_and_mark_modified(RefCell::new(columns.clone()));
720                            self.invalidate_layout();
721                        }
722                    }
723                    GridMessage::DrawBorder(draw_border) => {
724                        self.draw_border.set_value_and_mark_modified(*draw_border);
725                    }
726                    GridMessage::BorderThickness(border_thickness) => {
727                        self.border_thickness
728                            .set_value_and_mark_modified(*border_thickness);
729                    }
730                }
731            }
732        }
733    }
734}
735
736/// Grid builder creates [`Grid`] instances and adds it to the user interface.
737pub struct GridBuilder {
738    widget_builder: WidgetBuilder,
739    rows: Vec<Row>,
740    columns: Vec<Column>,
741    draw_border: bool,
742    border_thickness: f32,
743}
744
745impl GridBuilder {
746    /// Creates new grid builder with the base widget builder.
747    pub fn new(widget_builder: WidgetBuilder) -> Self {
748        Self {
749            widget_builder,
750            rows: Vec::new(),
751            columns: Vec::new(),
752            draw_border: false,
753            border_thickness: 1.0,
754        }
755    }
756
757    /// Adds a new row to the grid builder. Number of rows is unlimited.
758    pub fn add_row(mut self, row: Row) -> Self {
759        self.rows.push(row);
760        self
761    }
762
763    /// Adds a new column to the grid builder. Number of columns is unlimited.
764    pub fn add_column(mut self, column: Column) -> Self {
765        self.columns.push(column);
766        self
767    }
768
769    /// Adds a set of rows to the grid builder. Number of rows is unlimited.
770    pub fn add_rows(mut self, mut rows: Vec<Row>) -> Self {
771        self.rows.append(&mut rows);
772        self
773    }
774
775    /// Adds a set of columnds to the grid builder. Number of columnds is unlimited.
776    pub fn add_columns(mut self, mut columns: Vec<Column>) -> Self {
777        self.columns.append(&mut columns);
778        self
779    }
780
781    /// Specifies whether the grid should draw its border or not.
782    pub fn draw_border(mut self, value: bool) -> Self {
783        self.draw_border = value;
784        self
785    }
786
787    /// Specifies grid's border thickness.
788    pub fn with_border_thickness(mut self, value: f32) -> Self {
789        self.border_thickness = value;
790        self
791    }
792
793    /// Creates new [`Grid`] widget instance and adds it to the user interface.
794    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
795        let grid = Grid {
796            widget: self.widget_builder.build(ctx),
797            rows: RefCell::new(self.rows).into(),
798            columns: RefCell::new(self.columns).into(),
799            draw_border: self.draw_border.into(),
800            border_thickness: self.border_thickness.into(),
801            cells: Default::default(),
802            groups: Default::default(),
803        };
804        ctx.add_node(UiNode::new(grid))
805    }
806}
807
808#[cfg(test)]
809mod test {
810    use crate::grid::GridBuilder;
811    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
812
813    #[test]
814    fn test_deletion() {
815        test_widget_deletion(|ctx| GridBuilder::new(WidgetBuilder::new()).build(ctx));
816    }
817}