Make grid sizing strategy explicit and more intuitive

This commit is contained in:
Héctor Ramón Jiménez 2025-04-10 15:39:10 +02:00
parent 504d9c2959
commit 687750e026
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
4 changed files with 74 additions and 28 deletions

View file

@ -22,6 +22,10 @@ use std::collections::HashMap;
fn main() -> iced::Result {
iced::application(Gallery::new, Gallery::update, Gallery::view)
.window_size((
Preview::WIDTH as f32 * 4.0,
Preview::HEIGHT as f32 * 2.5,
))
.subscription(Gallery::subscription)
.theme(Gallery::theme)
.run()
@ -183,7 +187,7 @@ impl Gallery {
let gallery = grid(images)
.fluid(Preview::WIDTH)
.ratio(Preview::WIDTH as f32 / Preview::HEIGHT as f32)
.height(grid::aspect_ratio(Preview::WIDTH, Preview::HEIGHT))
.spacing(10);
let content = container(scrollable(gallery).spacing(10)).padding(10);
@ -225,7 +229,7 @@ fn card<'a>(
horizontal_space().into()
};
let card = mouse_area(container(image).height(Fill).style(container::dark))
let card = mouse_area(container(image).style(container::dark))
.on_enter(Message::ThumbnailHovered(metadata.id, true))
.on_exit(Message::ThumbnailHovered(metadata.id, false));
@ -245,10 +249,7 @@ fn card<'a>(
}
fn placeholder<'a>() -> Element<'a, Message> {
container(horizontal_space())
.height(Fill)
.style(container::dark)
.into()
container(horizontal_space()).style(container::dark).into()
}
enum Preview {

View file

@ -15,7 +15,7 @@ pub struct Grid<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
spacing: f32,
columns: Constraint,
width: Option<Pixels>,
ratio: Option<Pixels>,
height: Sizing,
children: Vec<Element<'a, Message, Theme, Renderer>>,
}
@ -55,7 +55,7 @@ where
spacing: 0.0,
columns: Constraint::Amount(3),
width: None,
ratio: None,
height: Sizing::AspectRatio(1.0),
children,
}
}
@ -76,6 +76,14 @@ where
self
}
/// Sets the height of the [`Grid`].
///
/// By default, a [`Grid`] uses a cell aspect ratio of `1.0` (i.e. squares).
pub fn height(mut self, height: impl Into<Sizing>) -> Self {
self.height = height.into();
self
}
/// Sets the amount of columns in the [`Grid`].
pub fn columns(mut self, column: usize) -> Self {
self.columns = Constraint::Amount(column);
@ -89,12 +97,6 @@ where
self
}
/// Sets the amount of horizontal pixels per each vertical pixel of a cell in the [`Grid`].
pub fn ratio(mut self, ratio: impl Into<Pixels>) -> Self {
self.ratio = Some(ratio.into());
self
}
/// Adds an [`Element`] to the [`Grid`].
pub fn push(
mut self,
@ -166,7 +168,10 @@ where
.width
.map(|pixels| Length::Fixed(pixels.0))
.unwrap_or(Length::Fill),
height: Length::Shrink,
height: match self.height {
Sizing::AspectRatio(_) => Length::Shrink,
Sizing::EvenlyDistribute(length) => length,
},
}
}
@ -194,22 +199,23 @@ where
- self.spacing * (cells_per_row - 1) as f32)
/ cells_per_row as f32;
let cell_height = if let Some(ratio) = self.ratio {
cell_width / ratio.0
} else if available.height.is_finite() {
available.height / total_rows as f32
} else {
f32::INFINITY
let cell_height = match self.height {
Sizing::AspectRatio(ratio) => Some(cell_width / ratio),
Sizing::EvenlyDistribute(Length::Shrink) => None,
Sizing::EvenlyDistribute(_) => {
Some(available.height / total_rows as f32)
}
};
let cell_limits = layout::Limits::new(
Size::new(cell_width, 0.0),
Size::new(cell_width, cell_height),
Size::new(cell_width, cell_height.unwrap_or(0.0)),
Size::new(cell_width, cell_height.unwrap_or(available.height)),
);
let mut nodes = Vec::new();
let mut x = 0.0;
let mut y = 0.0;
let mut row_height = 0.0f32;
for (i, (child, tree)) in
self.children.iter().zip(&mut tree.children).enumerate()
@ -219,11 +225,15 @@ where
.layout(tree, renderer, &cell_limits)
.move_to((x, y));
x += node.size().width + self.spacing;
let size = node.size();
x += size.width + self.spacing;
row_height = row_height.max(size.height);
if (i + 1) % cells_per_row == 0 {
y += cell_height + self.spacing;
y += cell_height.unwrap_or(row_height) + self.spacing;
x = 0.0;
row_height = 0.0;
}
nodes.push(node);
@ -232,7 +242,7 @@ where
if x == 0.0 {
y -= self.spacing;
} else {
y += cell_height;
y += cell_height.unwrap_or(row_height);
}
layout::Node::with_children(Size::new(available.width, y), nodes)
@ -356,3 +366,38 @@ where
Self::new(row)
}
}
/// The sizing strategy of a [`Grid`].
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Sizing {
/// The [`Grid`] will ensure each cell follows the given aspect ratio and the
/// total size will be the sum of the cells and the spacing between them.
///
/// The ratio is the amount of horizontal pixels per each vertical pixel of a cell
/// in the [`Grid`].
AspectRatio(f32),
/// The [`Grid`] will evenly distribute the space available in the given [`Length`]
/// for each cell.
EvenlyDistribute(Length),
}
impl From<f32> for Sizing {
fn from(height: f32) -> Self {
Self::EvenlyDistribute(Length::from(height))
}
}
impl From<Length> for Sizing {
fn from(height: Length) -> Self {
Self::EvenlyDistribute(height)
}
}
/// Creates a new [`Sizing`] strategy that maintains the given aspect ratio.
pub fn aspect_ratio(
width: impl Into<Pixels>,
height: impl Into<Pixels>,
) -> Sizing {
Sizing::AspectRatio(width.into().0 / height.into().0)
}

View file

@ -10,7 +10,6 @@ pub use iced_runtime::core;
mod action;
mod column;
mod grid;
mod mouse_area;
mod pin;
mod space;
@ -21,6 +20,7 @@ pub mod button;
pub mod checkbox;
pub mod combo_box;
pub mod container;
pub mod grid;
pub mod keyed;
pub mod overlay;
pub mod pane_grid;

View file

@ -461,7 +461,7 @@ where
limits.max().width
},
if self.direction.vertical().is_some() {
f32::MAX
f32::INFINITY
} else {
limits.max().height
},