diff --git a/examples/grid/Cargo.toml b/examples/grid/Cargo.toml index 23ad6eec..f8aed8f0 100644 --- a/examples/grid/Cargo.toml +++ b/examples/grid/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "grid" version = "0.1.0" -authors = ["daxpedda ", "Luni-4 "] +authors = ["Alexander van Saase "] edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] iced_aw = { workspace = true, features = ["grid"] } -iced.workspace = true \ No newline at end of file +iced.workspace = true diff --git a/examples/grid/src/main.rs b/examples/grid/src/main.rs index e0afda9e..174ae2ce 100644 --- a/examples/grid/src/main.rs +++ b/examples/grid/src/main.rs @@ -1,79 +1,194 @@ +use iced::widget::{checkbox, column, container, pick_list, radio, row, slider}; use iced::{ - theme, - widget::{Button, Column, Container, Scrollable, Text}, - Alignment, Color, Element, Length, Sandbox, Settings, + alignment::{Horizontal, Vertical}, + Color, Element, Length, Sandbox, Settings, }; +use iced_aw::{grid, grid_row, Strategy}; -use iced_aw::grid; - -// Number of columns for the grid -const COLUMNS: usize = 2; - -fn main() -> iced::Result { - GridExample::run(Settings::default()) +struct App { + horizontal_alignment: Horizontal, + vertical_alignemnt: Vertical, + column_spacing: f32, + row_spacing: f32, + row_strategy: Strategy, + column_strategy: Strategy, + debug_layout: bool, } #[derive(Debug, Clone)] enum Message { - AddElement, -} - -struct GridExample { - element_index: usize, + HorizontalAlignment(Horizontal), + VerticalAlignment(Vertical), + ColumnSpacing(f32), + RowSpacing(f32), + RowStrategy(Strategy), + ColumnStrategy(Strategy), + DebugToggled(bool), } -impl Sandbox for GridExample { +impl Sandbox for App { type Message = Message; fn new() -> Self { - GridExample { element_index: 0 } + Self { + horizontal_alignment: Horizontal::Left, + vertical_alignemnt: Vertical::Center, + column_spacing: 5.0, + row_spacing: 5.0, + row_strategy: Strategy::Minimum, + column_strategy: Strategy::Minimum, + debug_layout: false, + } } fn title(&self) -> String { - String::from("Grid example") + "Iced Grid widget example".into() } - fn update(&mut self, message: self::Message) { + fn update(&mut self, message: Self::Message) { match message { - Message::AddElement => { - self.element_index += 1; - } + Message::HorizontalAlignment(align) => self.horizontal_alignment = align, + Message::VerticalAlignment(align) => self.vertical_alignemnt = align, + Message::ColumnSpacing(spacing) => self.column_spacing = spacing, + Message::RowSpacing(spacing) => self.row_spacing = spacing, + Message::RowStrategy(strategy) => self.row_strategy = strategy, + Message::ColumnStrategy(strategy) => self.column_strategy = strategy, + Message::DebugToggled(enabled) => self.debug_layout = enabled, } } - fn view(&self) -> Element<'_, self::Message> { - // Creates a grid with two columns - let mut grid = grid!( - Text::new("Column 1").style(theme::Text::Color(Color::from_rgb8(255, 0, 0))), - Text::new("Column 2").style(theme::Text::Color(Color::from_rgb8(255, 0, 0))), - ) - .strategy(iced_aw::Strategy::Columns(2)); + fn view(&self) -> iced::Element<'_, Self::Message> { + let horizontal_align_pick = pick_list( + HORIZONTAL_ALIGNMENTS + .iter() + .map(horizontal_align_to_string) + .collect::>(), + Some(horizontal_align_to_string(&self.horizontal_alignment)), + |selected| Message::HorizontalAlignment(string_to_horizontal_align(&selected)), + ); - // Add elements to the grid - for i in 0..self.element_index { - grid.insert(Text::new(format!("Row {} Element {}", (i / COLUMNS), i))); - } + let vertical_align_pick = pick_list( + VERTICAL_ALIGNMENTS + .iter() + .map(vertical_alignment_to_string) + .collect::>(), + Some(vertical_alignment_to_string(&self.vertical_alignemnt)), + |selected| Message::VerticalAlignment(string_to_vertical_align(&selected)), + ); - let add_button: Element<'_, Message> = Button::new(Text::new("Add element")) - .on_press(Message::AddElement) - .into(); + let row_spacing_slider = + slider(0.0..=100.0, self.row_spacing, Message::RowSpacing).width(200.0); + let col_spacing_slider = + slider(0.0..=100.0, self.column_spacing, Message::ColumnSpacing).width(200.0); - let column: Element<'_, Message> = Column::new() - .spacing(15) - .max_width(600) - .align_items(Alignment::Center) - .push(grid) - .push(add_button) - .into(); + let debug_mode_check = checkbox("", self.debug_layout, Message::DebugToggled); - let content = Scrollable::new(column); + let row_height_radio = column( + STRATEGIES + .iter() + .map(|strategy| { + let name = strategy_to_string(strategy); + radio(name, strategy, Some(&self.row_strategy), |click| { + Message::RowStrategy(click.clone()) + }) + }) + .map(Element::from) + .collect(), + ) + .spacing(5); - Container::new(content) + let col_width_radio = row(STRATEGIES + .iter() + .map(|strategy| { + let name = strategy_to_string(strategy); + radio(name, strategy, Some(&self.column_strategy), |click| { + Message::ColumnStrategy(click.clone()) + }) + }) + .map(Element::from) + .collect()) + .spacing(10); + + let grid = grid!( + grid_row!("Horizontal alignment", horizontal_align_pick), + grid_row!("Vertical alignment", vertical_align_pick), + grid_row!("Row spacing", row_spacing_slider), + grid_row!("Column spacing", col_spacing_slider), + grid_row!("Row height", row_height_radio), + grid_row!("Column width", col_width_radio), + grid_row!("Debug mode", debug_mode_check) + ) + .horizontal_alignment(self.horizontal_alignment) + .vertical_alignment(self.vertical_alignemnt) + .row_spacing(self.row_spacing) + .column_spacing(self.column_spacing) + .row_height_strategy(self.row_strategy.clone()) + .column_width_strategy(self.column_strategy.clone()); + + let mut contents = Element::from(grid); + if self.debug_layout { + contents = contents.explain(Color::BLACK); + } + container(contents) .width(Length::Fill) .height(Length::Fill) - .padding(10) .center_x() .center_y() .into() } } + +const HORIZONTAL_ALIGNMENTS: [Horizontal; 3] = + [Horizontal::Left, Horizontal::Center, Horizontal::Right]; + +const VERTICAL_ALIGNMENTS: [Vertical; 3] = [Vertical::Top, Vertical::Center, Vertical::Bottom]; + +const STRATEGIES: [Strategy; 2] = [Strategy::Minimum, Strategy::Equal]; + +fn horizontal_align_to_string(alignment: &Horizontal) -> String { + match alignment { + Horizontal::Left => "Left", + Horizontal::Center => "Center", + Horizontal::Right => "Right", + } + .to_string() +} + +fn string_to_horizontal_align(input: &str) -> Horizontal { + match input { + "Left" => Horizontal::Left, + "Center" => Horizontal::Center, + "Right" => Horizontal::Right, + _ => panic!(), + } +} + +fn vertical_alignment_to_string(alignment: &Vertical) -> String { + match alignment { + Vertical::Top => "Top", + Vertical::Center => "Center", + Vertical::Bottom => "Bottom", + } + .to_string() +} + +fn string_to_vertical_align(input: &str) -> Vertical { + match input { + "Top" => Vertical::Top, + "Center" => Vertical::Center, + "Bottom" => Vertical::Bottom, + _ => panic!(), + } +} + +fn strategy_to_string(strategy: &Strategy) -> String { + match strategy { + Strategy::Minimum => "Minimum", + Strategy::Equal => "Equal", + } + .to_string() +} + +fn main() -> iced::Result { + App::run(Settings::default()) +} diff --git a/src/lib.rs b/src/lib.rs index 746153d3..f0d6f096 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ mod platform { #[cfg(feature = "grid")] pub use { crate::native::grid, - grid::{Grid, Strategy}, + grid::{Grid, GridRow, Strategy}, }; #[doc(no_inline)] diff --git a/src/native/grid.rs b/src/native/grid.rs index e473ff7b..29e965ef 100644 --- a/src/native/grid.rs +++ b/src/native/grid.rs @@ -1,149 +1,202 @@ -//! Use a grid as an input element for creating grids. -//! -//! *This API requires the following crate features to be activated: `grid`* +//! A container to layout widgets in a grid. + use iced_widget::core::{ - self, event, + alignment::{Horizontal, Vertical}, + event, layout::{Limits, Node}, - mouse::{self, Cursor}, - overlay, renderer, + mouse, overlay, + overlay::Group, + renderer::Style, widget::{Operation, Tree}, - Clipboard, Element, Event, Layout, Length, Point, Rectangle, Shell, Size, Widget, + Clipboard, Element, Event, Layout, Length, Pixels, Point, Rectangle, Shell, Size, Widget, }; -/// A container that distributes its contents in a grid. -/// -/// # Example -/// -/// ```ignore -/// # use iced::widget::Text; -/// # use iced_aw::Grid; -/// # -/// #[derive(Debug, Clone)] -/// enum Message { -/// } +/// A container that distributes its contents in a grid of rows and columns. /// -/// let grid = Grid::::with_columns(2) -/// .push(Text::new("First row, first column")) -/// .push(Text::new("First row, second column")) -/// .push(Text::new("Second row, first column")) -/// .push(Text::new("Second row, second column")); -/// -/// ``` +/// The number of columns is determined by the row with the most elements. #[allow(missing_debug_implementations)] pub struct Grid<'a, Message, Renderer = crate::Renderer> { - /// The distribution [`Strategy`] of the [`Grid`]. - strategy: Strategy, - /// The elements in the [`Grid`]. - elements: Vec>, -} - -/// The [`Strategy`] of how to distribute the columns of the [`Grid`]. -#[derive(Debug)] -pub enum Strategy { - /// Use `n` columns. - Columns(usize), - /// Try to fit as much columns that have a fixed width. - ColumnWidth(f32), + rows: Vec>, + horizontal_alignment: Horizontal, + vertical_alignment: Vertical, + row_height_strategy: Strategy, + columng_width_stratgey: Strategy, + row_spacing: Pixels, + column_spacing: Pixels, } -impl Default for Strategy { +impl<'a, Message, Renderer> Default for Grid<'a, Message, Renderer> +where + Renderer: iced_widget::core::Renderer, +{ fn default() -> Self { - Self::Columns(1) + Self { + rows: Vec::new(), + horizontal_alignment: Horizontal::Left, + vertical_alignment: Vertical::Center, + row_height_strategy: Strategy::Minimum, + columng_width_stratgey: Strategy::Minimum, + row_spacing: 1.0.into(), + column_spacing: 1.0.into(), + } } } impl<'a, Message, Renderer> Grid<'a, Message, Renderer> where - Renderer: core::Renderer, + Renderer: iced_widget::core::Renderer, { - /// Creates a [`Grid`] with ``Strategy::Columns(1)`` - /// Use ``strategy()`` to update the Strategy. + /// Creates a new [`Grid`]. #[must_use] pub fn new() -> Self { Self::default() } - /// Inserts an [`Element`] into the [`Grid`]. - pub fn insert(&mut self, element: E) - where - E: Into>, - { - self.elements.push(element.into()); + /// Creates a [`Grid`] with the given [`GridRow`]s. + #[must_use] + pub fn with_rows(rows: Vec>) -> Self { + Self { + rows, + ..Default::default() + } } - /// Adds an [`Element`] to the [`Grid`]. + /// Adds a [`GridRow`] to the [`Grid`]. #[must_use] - pub fn push(mut self, element: E) -> Self - where - E: Into>, - { - self.elements.push(element.into()); + pub fn push(mut self, row: GridRow<'a, Message, Renderer>) -> Self { + self.rows.push(row); self } - /// Sets the [`Grid`] Strategy. - /// Default is ``Strategy::Columns(1)``. + /// Sets the horizontal alignment of the widgets within their cells. Default: + /// [`Horizontal::Left`] #[must_use] - pub fn strategy(mut self, strategy: Strategy) -> Self { - self.strategy = strategy; + pub fn horizontal_alignment(mut self, align: Horizontal) -> Self { + self.horizontal_alignment = align; self } - /// Creates a [`Grid`] with given elements and ``Strategy::Columns(1)`` - /// Use ``strategy()`` to update the Strategy. + /// Sets the vertical alignment of the widgets within their cells. Default: + /// [`Vertical::Center`] #[must_use] - pub fn with_children(children: Vec>) -> Self { - Self { - strategy: Strategy::default(), - elements: children, - } + pub fn vertical_alignment(mut self, align: Vertical) -> Self { + self.vertical_alignment = align; + self } - /// Creates a new empty [`Grid`]. - /// Elements will be laid out in a specific amount of columns. + /// Sets the [`Strategy`] used to determine the height of the rows. #[must_use] - pub fn with_columns(columns: usize) -> Self { - Self { - strategy: Strategy::Columns(columns), - elements: Vec::new(), - } + pub fn row_height_strategy(mut self, strategy: Strategy) -> Self { + self.row_height_strategy = strategy; + self } - /// Creates a new empty [`Grid`]. - /// Columns will be generated to fill the given space. + /// Sets the [`Strategy`] used to determine the width of the columns. #[must_use] - pub fn with_column_width(column_width: f32) -> Self { - Self { - strategy: Strategy::ColumnWidth(column_width), - elements: Vec::new(), - } + pub fn column_width_strategy(mut self, strategy: Strategy) -> Self { + self.columng_width_stratgey = strategy; + self + } + + /// Sets the spacing between the rows and columns. + // pub fn spacing(mut self, spacing: impl Into) -> Self { + #[must_use] + pub fn spacing(mut self, spacing: f32) -> Self { + let spacing: Pixels = spacing.into(); + self.row_spacing = spacing; + self.column_spacing = spacing; + self + } + + /// Sets the spacing between the rows. + #[must_use] + pub fn row_spacing(mut self, spacing: impl Into) -> Self { + self.row_spacing = spacing.into(); + self + } + + /// Sets the spacing between the columns. + #[must_use] + pub fn column_spacing(mut self, spacing: impl Into) -> Self { + self.column_spacing = spacing.into(); + self + } + + fn elements_iter(&self) -> impl Iterator> { + self.rows.iter().flat_map(|row| row.elements.iter()) + } + + fn elements_iter_mut(&mut self) -> impl Iterator> { + self.rows.iter_mut().flat_map(|row| row.elements.iter_mut()) + } + + fn column_count(&self) -> usize { + self.rows + .iter() + .map(|row| row.elements.len()) + .max() + .unwrap_or(0) + } + + fn row_count(&self) -> usize { + self.rows.len() + } + + fn element_count(&self) -> usize { + self.rows.iter().map(|row| row.elements.len()).sum() } } -impl<'a, Message, Renderer> Default for Grid<'a, Message, Renderer> +/// A container that distributes its contents in a row of a [`crate::Grid`]. +#[allow(missing_debug_implementations)] +pub struct GridRow<'a, Message, Renderer = crate::Renderer> { + pub(crate) elements: Vec>, +} + +impl<'a, Message, Renderer> Default for GridRow<'a, Message, Renderer> where - Renderer: core::Renderer, + Renderer: iced_widget::core::Renderer, { fn default() -> Self { Self { - strategy: Strategy::default(), elements: Vec::new(), } } } -impl<'a, Message, Renderer> Widget for Grid<'a, Message, Renderer> +impl<'a, Message, Renderer> GridRow<'a, Message, Renderer> where - Renderer: core::Renderer, + Renderer: iced_widget::core::Renderer, { - fn children(&self) -> Vec { - self.elements.iter().map(Tree::new).collect() + /// Creates a new [`GridRow`]. + #[must_use] + pub fn new() -> Self { + Self::default() } - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.elements); + /// Creates a new [`GridRow`] with the given widgets. + #[must_use] + pub fn with_elements(children: Vec>>) -> Self { + Self { + elements: children.into_iter().map(std::convert::Into::into).collect(), + } + } + + /// Adds a widget to the [`GridRow`]. + #[must_use] + pub fn push(mut self, element: E) -> Self + where + E: Into>, + { + self.elements.push(element.into()); + self } +} +impl<'a, Message, Renderer> Widget for Grid<'a, Message, Renderer> +where + Renderer: iced_widget::core::Renderer, +{ fn width(&self) -> Length { Length::Shrink } @@ -153,60 +206,124 @@ where } fn layout(&self, renderer: &Renderer, limits: &Limits) -> Node { - if self.elements.is_empty() { + if self.element_count() == 0 { return Node::new(Size::ZERO); } - match self.strategy { - // find out how wide a column is by finding the widest cell in it - Strategy::Columns(columns) => { - if columns == 0 { - return Node::new(Size::ZERO); + let limits = limits.width(self.width()).height(self.height()); + + // Calculate the column widths and row heights to fit the contents + let mut min_columns_widths = Vec::::with_capacity(self.column_count()); + let mut min_row_heights = Vec::::with_capacity(self.row_count()); + let mut max_row_height = 0.0f32; + let mut max_column_width = 0.0f32; + for row in &self.rows { + let mut row_height = 0.0f32; + + for (col_idx, element) in row.elements.iter().enumerate() { + let layout = element.as_widget().layout(renderer, &limits); + let Size { width, height } = layout.size(); + + #[allow(clippy::option_if_let_else)] + if let Some(column_width) = min_columns_widths.get_mut(col_idx) { + *column_width = column_width.max(width); + } else { + min_columns_widths.insert(col_idx, width); } - let mut layouts = Vec::with_capacity(self.elements.len()); - let mut column_widths = Vec::::with_capacity(columns); - - for (column, element) in (0..columns).cycle().zip(&self.elements) { - let layout = element.as_widget().layout(renderer, limits); - #[allow(clippy::option_if_let_else)] - match column_widths.get_mut(column) { - Some(column_width) => *column_width = column_width.max(layout.size().width), - None => column_widths.insert(column, layout.size().width), - } + row_height = row_height.max(height); + max_column_width = max_column_width.max(width); + } + min_row_heights.push(row_height); + max_row_height = max_row_height.max(row_height); + } - layouts.push(layout); + // Create the grid layout + let mut x = 0.0; + let mut y = 0.0; + let mut nodes = Vec::with_capacity(self.element_count()); + for (row_idx, row) in self.rows.iter().enumerate() { + x = 0.0; + let row_height = match self.row_height_strategy { + Strategy::Minimum => min_row_heights[row_idx], + Strategy::Equal => max_row_height, + }; + for (col_idx, element) in row.elements.iter().enumerate() { + let col_width = match self.columng_width_stratgey { + Strategy::Minimum => min_columns_widths[col_idx], + Strategy::Equal => max_column_width, + }; + let cell_size = Size::new(col_width, row_height); + + let mut node = element.as_widget().layout(renderer, &limits); + node.move_to(Point::new(x, y)); + node.align( + self.horizontal_alignment.into(), + self.vertical_alignment.into(), + cell_size, + ); + nodes.push(node); + x += col_width; + if col_idx < row.elements.len() - 1 { + x += self.column_spacing.0; } - - let column_aligns = - std::iter::once(&0.) - .chain(column_widths.iter()) - .scan(0., |state, width| { - *state += width; - Some(*state) - }); - let grid_width = column_widths.iter().sum(); - - build_grid(columns, column_aligns, layouts.into_iter(), grid_width) } - // find number of columns by checking how many can fit - Strategy::ColumnWidth(column_width) => { - let column_limits = limits.width(Length::Fixed(column_width)); - let max_width = limits.max().width; - let columns = (max_width / column_width).floor() as usize; - - let layouts = self - .elements - .iter() - .map(|element| element.as_widget().layout(renderer, &column_limits)); - let column_aligns = - std::iter::successors(Some(0.), |width| Some(width + column_width)); - #[allow(clippy::cast_precision_loss)] // TODO: possible precision loss - let grid_width = (columns as f32) * column_width; - - build_grid(columns, column_aligns, layouts, grid_width) + y += row_height; + if row_idx < self.rows.len() - 1 { + y += self.row_spacing.0; } } + + let grid_size = Size::new(x, y); + + Node::with_children(grid_size, nodes) + } + + fn draw( + &self, + state: &Tree, + renderer: &mut Renderer, + theme: &::Theme, + style: &Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + for ((element, state), layout) in self + .elements_iter() + .zip(&state.children) + .zip(layout.children()) + { + element + .as_widget() + .draw(state, renderer, theme, style, layout, cursor, viewport); + } + } + + fn children(&self) -> Vec { + self.elements_iter().map(Tree::new).collect() + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&self.elements_iter().collect::>()); + } + + fn operate( + &self, + state: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + for ((element, state), layout) in self + .elements_iter() + .zip(&mut state.children) + .zip(layout.children()) + { + element + .as_widget() + .operate(state, layout, renderer, operation); + } } fn on_event( @@ -214,15 +331,14 @@ where state: &mut Tree, event: Event, layout: Layout<'_>, - cursor: Cursor, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { let children_status = self - .elements - .iter_mut() + .elements_iter_mut() .zip(&mut state.children) .zip(layout.children()) .map(|((child, state), layout)| { @@ -245,12 +361,11 @@ where &self, state: &Tree, layout: Layout<'_>, - cursor: Cursor, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - self.elements - .iter() + self.elements_iter() .zip(&state.children) .zip(layout.children()) .map(|((e, state), layout)| { @@ -262,90 +377,42 @@ where }) } - fn operate( - &self, - state: &mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn Operation, - ) { - for ((element, state), layout) in self - .elements - .iter() - .zip(&mut state.children) - .zip(layout.children()) - { - element - .as_widget() - .operate(state, layout, renderer, operation); - } - } - - fn draw( - &self, - state: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: Cursor, - viewport: &Rectangle, - ) { - for ((element, state), layout) in self - .elements - .iter() - .zip(&state.children) - .zip(layout.children()) - { - element - .as_widget() - .draw(state, renderer, theme, style, layout, cursor, viewport); - } - } - fn overlay<'b>( &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - overlay::from_children(&mut self.elements, tree, layout, renderer) - } -} - -/// Builds the layout of the [`Grid`]. -fn build_grid( - columns: usize, - column_aligns: impl Iterator + Clone, - layouts: impl Iterator + ExactSizeIterator, - grid_width: f32, -) -> Node { - let mut nodes = Vec::with_capacity(layouts.len()); - let mut grid_height = 0.; - let mut row_height = 0.; - - for ((column, column_align), mut node) in (0..columns).zip(column_aligns).cycle().zip(layouts) { - if column == 0 { - grid_height += row_height; - row_height = 0.; - } + let children = self + .elements_iter_mut() + .zip(&mut tree.children) + .zip(layout.children()) + .filter_map(|((child, state), layout)| { + child.as_widget_mut().overlay(state, layout, renderer) + }) + .collect::>(); - node.move_to(Point::new(column_align, grid_height)); - row_height = row_height.max(node.size().height); - nodes.push(node); + (!children.is_empty()).then(|| Group::with_children(children).overlay()) } - - grid_height += row_height; - - Node::with_children(Size::new(grid_width, grid_height), nodes) } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: core::Renderer + 'a, + Renderer: iced_widget::core::Renderer + 'a, Message: 'static, { - fn from(grid: Grid<'a, Message, Renderer>) -> Element<'a, Message, Renderer> { + fn from(grid: Grid<'a, Message, Renderer>) -> Self { Element::new(grid) } } + +#[derive(Debug, Clone, PartialEq, Eq)] +/// Strategy used for determining the widths and height of columns and rows. +pub enum Strategy { + /// Each row (column) has the height (width) needed to fit its contents. + Minimum, + + /// All rows (columns) have the same height (width). The height (width) is determined by the + /// row (column) with the talest (widest) contents. + Equal, +} diff --git a/src/native/helpers.rs b/src/native/helpers.rs index a9598e15..5afc9d91 100644 --- a/src/native/helpers.rs +++ b/src/native/helpers.rs @@ -7,9 +7,10 @@ use iced_widget::core::{self, Color, Element}; #[allow(unused_imports)] use std::{borrow::Cow, fmt::Display, hash::Hash}; -/// Creates a [`Grid`] with the given children. +/// Creates a [`Grid`] with the given [`GridRow`]s. /// /// [`Grid`]: crate::Grid +/// [`GridRow`]: crate::GridRow #[cfg(feature = "grid")] #[macro_export] macro_rules! grid { @@ -17,7 +18,21 @@ macro_rules! grid { $crate::Grid::new() ); ($($x:expr),+ $(,)?) => ( - $crate::Grid::with_children(vec![$($crate::Element::from($x)),+]) + $crate::Grid::with_rows(vec![$($x),+]) + ); +} + +/// Creates a [`GridRow`] with the given widgets. +/// +/// [`GridRow`]: crate::GridRow +#[cfg(feature = "grid")] +#[macro_export] +macro_rules! grid_row { + () => ( + $crate::GridRow::new() + ); + ($($x:expr),+ $(,)?) => ( + $crate::GridRow::with_elements(vec![$(iced::Element::from($x)),+]) ); } @@ -199,15 +214,30 @@ where #[cfg(feature = "grid")] /// Shortcut helper to create a [`Grid`] Widget. /// -/// [`Grid`]: crate::Grid +/// [`Grid`]: crate::grid::Grid #[must_use] pub fn grid( - children: Vec>, -) -> crate::Grid + rows: Vec>, +) -> crate::Grid<'_, Message, Renderer> +where + Renderer: core::Renderer, +{ + crate::Grid::with_rows(rows) +} + +#[cfg(feature = "grid")] +/// Shortcut helper to create a [`GridRow`] for the [`Grid`] Widget. +/// +/// [`GridRow`]: crate::GridRow +/// [`Grid`]: crate::Grid +#[must_use] +pub fn grid_row<'a, Message, Renderer>( + elements: Vec>>, +) -> crate::GridRow<'a, Message, Renderer> where Renderer: core::Renderer, { - crate::Grid::with_children(children) + crate::GridRow::with_elements(elements) } #[cfg(feature = "wrap")] diff --git a/src/native/mod.rs b/src/native/mod.rs index b680ae1b..92860114 100644 --- a/src/native/mod.rs +++ b/src/native/mod.rs @@ -60,7 +60,10 @@ pub type FloatingElement<'a, Message, Renderer> = pub mod grid; #[cfg(feature = "grid")] /// A container that distributes its contents in a grid. -pub type Grid<'a, Message, Renderer> = grid::Grid<'a, Message, Renderer>; +// pub type Grid<'a, Message, Renderer> = grid::Grid<'a, Message, Renderer>; +pub use grid::Grid; +#[cfg(feature = "grid")] +pub use grid::GridRow; #[cfg(feature = "grid")] pub use grid::Strategy;