Skip to main content

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