iced_core/
layout.rs

1//! Position your widgets properly.
2mod limits;
3mod node;
4
5pub mod flex;
6
7pub use limits::Limits;
8pub use node::Node;
9
10use crate::{Length, Padding, Point, Rectangle, Size, Vector};
11
12/// The bounds of a [`Node`] and its children, using absolute coordinates.
13#[derive(Debug, Clone, Copy)]
14pub struct Layout<'a> {
15    position: Point,
16    node: &'a Node,
17}
18
19impl<'a> Layout<'a> {
20    /// Creates a new [`Layout`] for the given [`Node`] at the origin.
21    pub fn new(node: &'a Node) -> Self {
22        Self::with_offset(Vector::new(0.0, 0.0), node)
23    }
24
25    /// Creates a new [`Layout`] for the given [`Node`] with the provided offset
26    /// from the origin.
27    pub fn with_offset(offset: Vector, node: &'a Node) -> Self {
28        let bounds = node.bounds();
29
30        Self {
31            position: Point::new(bounds.x, bounds.y) + offset,
32            node,
33        }
34    }
35
36    /// Returns the position of the [`Layout`].
37    pub fn position(&self) -> Point {
38        self.position
39    }
40
41    /// Returns the bounds of the [`Layout`].
42    ///
43    /// The returned [`Rectangle`] describes the position and size of a
44    /// [`Node`].
45    pub fn bounds(&self) -> Rectangle {
46        let bounds = self.node.bounds();
47
48        Rectangle {
49            x: self.position.x,
50            y: self.position.y,
51            width: bounds.width,
52            height: bounds.height,
53        }
54    }
55
56    /// Returns an iterator over the children of this [`Layout`].
57    pub fn children(self) -> impl DoubleEndedIterator<Item = Layout<'a>> + ExactSizeIterator {
58        self.node.children().iter().map(move |node| {
59            Layout::with_offset(Vector::new(self.position.x, self.position.y), node)
60        })
61    }
62
63    /// Returns the [`Layout`] of the child at the given index.
64    ///
65    /// This can be useful if you ever need to access children out of order
66    /// for layering purposes.
67    ///
68    /// # Panics
69    /// Panics if index is out of bounds.
70    pub fn child(self, index: usize) -> Layout<'a> {
71        let node = &self.node.children()[index];
72
73        Layout::with_offset(Vector::new(self.position.x, self.position.y), node)
74    }
75}
76
77/// Produces a [`Node`] with two children nodes one right next to each other.
78pub fn next_to_each_other(
79    limits: &Limits,
80    spacing: f32,
81    left: impl FnOnce(&Limits) -> Node,
82    right: impl FnOnce(&Limits) -> Node,
83) -> Node {
84    let left_node = left(limits);
85    let left_size = left_node.size();
86
87    let right_limits = limits.shrink(Size::new(left_size.width + spacing, 0.0));
88
89    let right_node = right(&right_limits);
90    let right_size = right_node.size();
91
92    let (left_y, right_y) = if left_size.height > right_size.height {
93        (0.0, (left_size.height - right_size.height) / 2.0)
94    } else {
95        ((right_size.height - left_size.height) / 2.0, 0.0)
96    };
97
98    Node::with_children(
99        Size::new(
100            left_size.width + spacing + right_size.width,
101            left_size.height.max(right_size.height),
102        ),
103        vec![
104            left_node.move_to(Point::new(0.0, left_y)),
105            right_node.move_to(Point::new(left_size.width + spacing, right_y)),
106        ],
107    )
108}
109
110/// Computes the resulting [`Node`] that fits the [`Limits`] given
111/// some width and height requirements and no intrinsic size.
112pub fn atomic(limits: &Limits, width: impl Into<Length>, height: impl Into<Length>) -> Node {
113    let width = width.into();
114    let height = height.into();
115
116    Node::new(limits.resolve(width, height, Size::ZERO))
117}
118
119/// Computes the resulting [`Node`] that fits the [`Limits`] given
120/// some width and height requirements and a closure that produces
121/// the intrinsic [`Size`] inside the given [`Limits`].
122pub fn sized(
123    limits: &Limits,
124    width: impl Into<Length>,
125    height: impl Into<Length>,
126    f: impl FnOnce(&Limits) -> Size,
127) -> Node {
128    let width = width.into();
129    let height = height.into();
130
131    let limits = limits.width(width).height(height);
132    let intrinsic_size = f(&limits);
133
134    Node::new(limits.resolve(width, height, intrinsic_size))
135}
136
137/// Computes the resulting [`Node`] that fits the [`Limits`] given
138/// some width and height requirements and a closure that produces
139/// the content [`Node`] inside the given [`Limits`].
140pub fn contained(
141    limits: &Limits,
142    width: impl Into<Length>,
143    height: impl Into<Length>,
144    f: impl FnOnce(&Limits) -> Node,
145) -> Node {
146    let width = width.into();
147    let height = height.into();
148
149    let limits = limits.width(width).height(height);
150    let content = f(&limits);
151
152    Node::with_children(limits.resolve(width, height, content.size()), vec![content])
153}
154
155/// Computes the [`Node`] that fits the [`Limits`] given some width, height, and
156/// [`Padding`] requirements and a closure that produces the content [`Node`]
157/// inside the given [`Limits`].
158pub fn padded(
159    limits: &Limits,
160    width: impl Into<Length>,
161    height: impl Into<Length>,
162    padding: impl Into<Padding>,
163    layout: impl FnOnce(&Limits) -> Node,
164) -> Node {
165    positioned(limits, width, height, padding, layout, |content, _| content)
166}
167
168/// Computes a [`padded`] [`Node`] with a positioning step.
169pub fn positioned(
170    limits: &Limits,
171    width: impl Into<Length>,
172    height: impl Into<Length>,
173    padding: impl Into<Padding>,
174    layout: impl FnOnce(&Limits) -> Node,
175    position: impl FnOnce(Node, Size) -> Node,
176) -> Node {
177    let width = width.into();
178    let height = height.into();
179    let padding = padding.into();
180
181    let limits = limits.width(width).height(height);
182    let content = layout(&limits.shrink(padding));
183    let padding = padding.fit(content.size(), limits.max());
184
185    let size = limits
186        .shrink(padding)
187        .resolve(width, height, content.size());
188
189    Node::with_children(
190        size.expand(padding),
191        vec![position(content.move_to((padding.left, padding.top)), size)],
192    )
193}