Introduce useful helpers in layout module

This commit is contained in:
Héctor Ramón Jiménez 2024-01-09 06:35:33 +01:00
parent d24e50c1a6
commit d62bb8193c
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
26 changed files with 189 additions and 127 deletions

View file

@ -7,7 +7,7 @@ pub mod flex;
pub use limits::Limits; pub use limits::Limits;
pub use node::Node; pub use node::Node;
use crate::{Point, Rectangle, Size, Vector}; use crate::{Length, Padding, Point, Rectangle, Size, Vector};
/// The bounds of a [`Node`] and its children, using absolute coordinates. /// The bounds of a [`Node`] and its children, using absolute coordinates.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -96,3 +96,95 @@ pub fn next_to_each_other(
], ],
) )
} }
/// Computes the resulting [`Node`] that fits the [`Limits`] given
/// some width and height requirements and no intrinsic size.
pub fn atomic(
limits: &Limits,
width: impl Into<Length>,
height: impl Into<Length>,
) -> Node {
let width = width.into();
let height = height.into();
Node::new(limits.resolve(width, height, Size::ZERO))
}
/// Computes the resulting [`Node`] that fits the [`Limits`] given
/// some width and height requirements and a closure that produces
/// the intrinsic [`Size`] inside the given [`Limits`].
pub fn sized(
limits: &Limits,
width: impl Into<Length>,
height: impl Into<Length>,
f: impl FnOnce(&Limits) -> Size,
) -> Node {
let width = width.into();
let height = height.into();
let limits = limits.width(width).height(height);
let intrinsic_size = f(&limits);
Node::new(limits.resolve(width, height, intrinsic_size))
}
/// Computes the resulting [`Node`] that fits the [`Limits`] given
/// some width and height requirements and a closure that produces
/// the content [`Node`] inside the given [`Limits`].
pub fn contained(
limits: &Limits,
width: impl Into<Length>,
height: impl Into<Length>,
f: impl FnOnce(&Limits) -> Node,
) -> Node {
let width = width.into();
let height = height.into();
let limits = limits.width(width).height(height);
let content = f(&limits);
Node::with_children(
limits.resolve(width, height, content.size()),
vec![content],
)
}
/// Computes the [`Node`] that fits the [`Limits`] given some width, height, and
/// [`Padding`] requirements and a closure that produces the content [`Node`]
/// inside the given [`Limits`].
pub fn padded(
limits: &Limits,
width: impl Into<Length>,
height: impl Into<Length>,
padding: impl Into<Padding>,
layout: impl FnOnce(&Limits) -> Node,
) -> Node {
positioned(limits, width, height, padding, layout, |content, _| content)
}
/// Computes a [`padded`] [`Node`] with a positioning step.
pub fn positioned(
limits: &Limits,
width: impl Into<Length>,
height: impl Into<Length>,
padding: impl Into<Padding>,
layout: impl FnOnce(&Limits) -> Node,
position: impl FnOnce(Node, Size) -> Node,
) -> Node {
let width = width.into();
let height = height.into();
let padding = padding.into();
let limits = limits.width(width).height(height);
let content = layout(&limits.shrink(padding));
let padding = padding.fit(content.size(), limits.max());
let size = limits
.shrink(padding)
.resolve(width, height, content.size());
Node::with_children(
size.expand(padding),
vec![position(content.move_to((padding.left, padding.top)), size)],
)
}

View file

@ -239,9 +239,9 @@ where
let (intrinsic_width, intrinsic_height) = axis.pack(main - pad.0, cross); let (intrinsic_width, intrinsic_height) = axis.pack(main - pad.0, cross);
let size = limits.resolve( let size = limits.resolve(
Size::new(intrinsic_width, intrinsic_height),
width, width,
height, height,
Size::new(intrinsic_width, intrinsic_height),
); );
Node::with_children(size.expand(padding), nodes) Node::with_children(size.expand(padding), nodes)

View file

@ -114,13 +114,14 @@ impl Limits {
} }
} }
/// Computes the resulting [`Size`] that fits the [`Limits`] given the /// Computes the resulting [`Size`] that fits the [`Limits`] given
/// intrinsic size of some content. /// some width and height requirements and the intrinsic size of
/// some content.
pub fn resolve( pub fn resolve(
&self, &self,
intrinsic_size: Size,
width: impl Into<Length>, width: impl Into<Length>,
height: impl Into<Length>, height: impl Into<Length>,
intrinsic_size: Size,
) -> Size { ) -> Size {
let width = match width.into() { let width = match width.into() {
Length::Fill | Length::FillPortion(_) => self.max.width, Length::Fill | Length::FillPortion(_) => self.max.width,

View file

@ -89,19 +89,23 @@ impl Node {
} }
/// Moves the [`Node`] to the given position. /// Moves the [`Node`] to the given position.
pub fn move_to(mut self, position: Point) -> Self { pub fn move_to(mut self, position: impl Into<Point>) -> Self {
self.move_to_mut(position); self.move_to_mut(position);
self self
} }
/// Mutable reference version of [`move_to`]. /// Mutable reference version of [`move_to`].
pub fn move_to_mut(&mut self, position: Point) { pub fn move_to_mut(&mut self, position: impl Into<Point>) {
let position = position.into();
self.bounds.x = position.x; self.bounds.x = position.x;
self.bounds.y = position.y; self.bounds.y = position.y;
} }
/// Translates the [`Node`] by the given translation. /// Translates the [`Node`] by the given translation.
pub fn translate(self, translation: Vector) -> Self { pub fn translate(self, translation: impl Into<Vector>) -> Self {
let translation = translation.into();
Self { Self {
bounds: self.bounds + translation, bounds: self.bounds + translation,
..self ..self

View file

@ -36,20 +36,26 @@ impl<T: Num> Point<T> {
} }
} }
impl From<[f32; 2]> for Point { impl<T> From<[T; 2]> for Point<T>
fn from([x, y]: [f32; 2]) -> Self { where
T: Num,
{
fn from([x, y]: [T; 2]) -> Self {
Point { x, y } Point { x, y }
} }
} }
impl From<[u16; 2]> for Point<u16> { impl<T> From<(T, T)> for Point<T>
fn from([x, y]: [u16; 2]) -> Self { where
Point::new(x, y) T: Num,
{
fn from((x, y): (T, T)) -> Self {
Self { x, y }
} }
} }
impl From<Point> for [f32; 2] { impl<T> From<Point<T>> for [T; 2] {
fn from(point: Point) -> [f32; 2] { fn from(point: Point<T>) -> [T; 2] {
[point.x, point.y] [point.x, point.y]
} }
} }

View file

@ -206,28 +206,27 @@ pub fn layout<Renderer>(
where where
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
let limits = limits.width(width).height(height); layout::sized(limits, width, height, |limits| {
let bounds = limits.max(); let bounds = limits.max();
let size = size.unwrap_or_else(|| renderer.default_size()); let size = size.unwrap_or_else(|| renderer.default_size());
let font = font.unwrap_or_else(|| renderer.default_font()); let font = font.unwrap_or_else(|| renderer.default_font());
let State(ref mut paragraph) = state; let State(ref mut paragraph) = state;
paragraph.update(text::Text { paragraph.update(text::Text {
content, content,
bounds, bounds,
size, size,
line_height, line_height,
font, font,
horizontal_alignment, horizontal_alignment,
vertical_alignment, vertical_alignment,
shaping, shaping,
}); });
let size = limits.resolve(paragraph.min_bounds(), width, height); paragraph.min_bounds()
})
layout::Node::new(size)
} }
/// Draws text using the same logic as the [`Text`] widget. /// Draws text using the same logic as the [`Text`] widget.

View file

@ -29,9 +29,9 @@ mod rainbow {
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let size = limits.resolve(Size::ZERO, Length::Fill, Length::Shrink); let width = limits.max().width;
layout::Node::new(Size::new(size.width, size.width)) layout::Node::new(Size::new(width, width))
} }
fn draw( fn draw(

View file

@ -257,10 +257,7 @@ where
_renderer: &iced::Renderer<Theme>, _renderer: &iced::Renderer<Theme>,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let limits = limits.width(self.size).height(self.size); layout::atomic(limits, self.size, self.size)
let size = limits.resolve(Size::ZERO, self.size, self.size);
layout::Node::new(size)
} }
fn on_event( fn on_event(

View file

@ -178,10 +178,7 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let limits = limits.width(self.width).height(self.height); layout::atomic(limits, self.width, self.height)
let size = limits.resolve(Size::ZERO, self.width, self.height);
layout::Node::new(size)
} }
fn on_event( fn on_event(

View file

@ -10,8 +10,8 @@ use crate::core::touch;
use crate::core::widget::tree::{self, Tree}; use crate::core::widget::tree::{self, Tree};
use crate::core::widget::Operation; use crate::core::widget::Operation;
use crate::core::{ use crate::core::{
Background, Clipboard, Color, Element, Layout, Length, Padding, Point, Background, Clipboard, Color, Element, Layout, Length, Padding, Rectangle,
Rectangle, Shell, Size, Vector, Widget, Shell, Size, Vector, Widget,
}; };
pub use iced_style::button::{Appearance, StyleSheet}; pub use iced_style::button::{Appearance, StyleSheet};
@ -430,20 +430,7 @@ pub fn layout(
padding: Padding, padding: Padding,
layout_content: impl FnOnce(&layout::Limits) -> layout::Node, layout_content: impl FnOnce(&layout::Limits) -> layout::Node,
) -> layout::Node { ) -> layout::Node {
let limits = limits.width(width).height(height); layout::padded(limits, width, height, padding, layout_content)
let content = layout_content(&limits.shrink(padding));
let padding = padding.fit(content.size(), limits.max());
let size = limits
.shrink(padding)
.resolve(content.size(), width, height)
.expand(padding);
layout::Node::with_children(
size,
vec![content.move_to(Point::new(padding.left, padding.top))],
)
} }
/// Returns the [`mouse::Interaction`] of a [`Button`]. /// Returns the [`mouse::Interaction`] of a [`Button`].

View file

@ -133,9 +133,7 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let size = limits.resolve(Size::ZERO, self.width, self.height); layout::atomic(limits, self.width, self.height)
layout::Node::new(size)
} }
fn on_event( fn on_event(

View file

@ -321,27 +321,19 @@ pub fn layout(
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
layout_content: impl FnOnce(&layout::Limits) -> layout::Node, layout_content: impl FnOnce(&layout::Limits) -> layout::Node,
) -> layout::Node { ) -> layout::Node {
let limits = limits layout::positioned(
.width(width) &limits.max_width(max_width).max_height(max_height),
.height(height) width,
.max_width(max_width) height,
.max_height(max_height); padding,
|limits| layout_content(&limits.loose()),
let content = layout_content(&limits.shrink(padding).loose()); |content, size| {
let padding = padding.fit(content.size(), limits.max()); content.align(
let size = limits
.shrink(padding)
.resolve(content.size(), width, height);
layout::Node::with_children(
size.expand(padding),
vec![content
.move_to(Point::new(padding.left, padding.top))
.align(
Alignment::from(horizontal_alignment), Alignment::from(horizontal_alignment),
Alignment::from(vertical_alignment), Alignment::from(vertical_alignment),
size, size,
)], )
},
) )
} }

View file

@ -99,7 +99,7 @@ where
}; };
// The size to be available to the widget prior to `Shrink`ing // The size to be available to the widget prior to `Shrink`ing
let raw_size = limits.resolve(image_size, width, height); let raw_size = limits.resolve(width, height, image_size);
// The uncropped size of the image when fit to the bounds above // The uncropped size of the image when fit to the bounds above
let full_size = content_fit.fit(image_size, raw_size); let full_size = content_fit.fit(image_size, raw_size);

View file

@ -113,9 +113,9 @@ where
let Size { width, height } = renderer.dimensions(&self.handle); let Size { width, height } = renderer.dimensions(&self.handle);
let mut size = limits.resolve( let mut size = limits.resolve(
Size::new(width as f32, height as f32),
self.width, self.width,
self.height, self.height,
Size::new(width as f32, height as f32),
); );
let expansion_size = if height > width { let expansion_size = if height > width {

View file

@ -369,7 +369,7 @@ where
* self.options.len() as f32, * self.options.len() as f32,
); );
limits.resolve(intrinsic, Length::Fill, Length::Shrink) limits.resolve(Length::Fill, Length::Shrink, intrinsic)
}; };
layout::Node::new(size) layout::Node::new(size)

View file

@ -489,7 +489,7 @@ pub fn layout<Renderer, T>(
&layout::Limits, &layout::Limits,
) -> layout::Node, ) -> layout::Node,
) -> layout::Node { ) -> layout::Node {
let size = limits.resolve(Size::ZERO, width, height); let size = limits.resolve(width, height, Size::ZERO);
let regions = node.pane_regions(spacing, size); let regions = node.pane_regions(spacing, size);
let children = contents let children = contents

View file

@ -392,7 +392,6 @@ where
{ {
use std::f32; use std::f32;
let limits = limits.width(width).height(Length::Shrink);
let font = font.unwrap_or_else(|| renderer.default_font()); let font = font.unwrap_or_else(|| renderer.default_font());
let text_size = text_size.unwrap_or_else(|| renderer.default_size()); let text_size = text_size.unwrap_or_else(|| renderer.default_size());
@ -451,8 +450,10 @@ where
); );
limits limits
.width(width)
.height(Length::Shrink)
.shrink(padding) .shrink(padding)
.resolve(intrinsic, width, Length::Shrink) .resolve(width, Length::Shrink, intrinsic)
.expand(padding) .expand(padding)
}; };

View file

@ -98,13 +98,11 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let size = limits.resolve( layout::atomic(
Size::ZERO, limits,
self.width, self.width,
self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)), self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
); )
layout::Node::new(size)
} }
fn draw( fn draw(

View file

@ -75,7 +75,7 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
layout::Node::new(limits.resolve(Size::ZERO, self.width, self.height)) layout::atomic(limits, self.width, self.height)
} }
fn draw( fn draw(

View file

@ -469,28 +469,25 @@ pub fn layout<Renderer>(
direction: &Direction, direction: &Direction,
layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
) -> layout::Node { ) -> layout::Node {
let limits = limits.width(width).height(height); layout::contained(limits, width, height, |limits| {
let child_limits = layout::Limits::new(
Size::new(limits.min().width, limits.min().height),
Size::new(
if direction.horizontal().is_some() {
f32::INFINITY
} else {
limits.max().width
},
if direction.vertical().is_some() {
f32::MAX
} else {
limits.max().height
},
),
);
let child_limits = layout::Limits::new( layout_content(renderer, &child_limits)
Size::new(limits.min().width, limits.min().height), })
Size::new(
if direction.horizontal().is_some() {
f32::INFINITY
} else {
limits.max().width
},
if direction.vertical().is_some() {
f32::MAX
} else {
limits.max().height
},
),
);
let content = layout_content(renderer, &child_limits);
let size = limits.resolve(content.size(), width, height);
layout::Node::with_children(size, vec![content])
} }
/// Processes an [`Event`] and updates the [`State`] of a [`Scrollable`] /// Processes an [`Event`] and updates the [`State`] of a [`Scrollable`]

View file

@ -83,10 +83,7 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let limits = limits.width(self.width).height(self.height); layout::atomic(limits, self.width, self.height)
let size = limits.resolve(Size::ZERO, self.width, self.height);
layout::Node::new(size)
} }
fn on_event( fn on_event(

View file

@ -172,9 +172,7 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let size = limits.resolve(Size::ZERO, self.width, self.height); layout::atomic(limits, self.width, self.height)
layout::Node::new(size)
} }
fn on_event( fn on_event(

View file

@ -58,7 +58,7 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
layout::Node::new(limits.resolve(Size::ZERO, self.width, self.height)) layout::atomic(limits, self.width, self.height)
} }
fn draw( fn draw(

View file

@ -114,7 +114,7 @@ where
let image_size = Size::new(width as f32, height as f32); let image_size = Size::new(width as f32, height as f32);
// The size to be available to the widget prior to `Shrink`ing // The size to be available to the widget prior to `Shrink`ing
let raw_size = limits.resolve(image_size, self.width, self.height); let raw_size = limits.resolve(self.width, self.height, image_size);
// The uncropped size of the image when fit to the bounds above // The uncropped size of the image when fit to the bounds above
let full_size = self.content_fit.fit(image_size, raw_size); let full_size = self.content_fit.fit(image_size, raw_size);

View file

@ -508,8 +508,8 @@ where
let padding = padding.fit(Size::ZERO, limits.max()); let padding = padding.fit(Size::ZERO, limits.max());
let height = line_height.to_absolute(text_size); let height = line_height.to_absolute(text_size);
let limits = limits.width(width).shrink(padding).height(height); let limits = limits.width(width).shrink(padding);
let text_bounds = limits.resolve(Size::ZERO, width, height); let text_bounds = limits.resolve(width, height, Size::ZERO);
let placeholder_text = Text { let placeholder_text = Text {
font, font,

View file

@ -169,9 +169,7 @@ where
_renderer: &Renderer, _renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let size = limits.resolve(Size::ZERO, self.width, self.height); layout::atomic(limits, self.width, self.height)
layout::Node::new(size)
} }
fn on_event( fn on_event(