Skip to main content

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(
117        limits
118            .width(width)
119            .height(height)
120            .resolve(width, height, Size::ZERO),
121    )
122}
123
124/// Computes the resulting [`Node`] that fits the [`Limits`] given
125/// some width and height requirements and a closure that produces
126/// the intrinsic [`Size`] inside the given [`Limits`].
127pub fn sized(
128    limits: &Limits,
129    width: impl Into<Length>,
130    height: impl Into<Length>,
131    f: impl FnOnce(&Limits) -> Size,
132) -> Node {
133    let width = width.into();
134    let height = height.into();
135
136    let limits = limits.width(width).height(height);
137    let intrinsic_size = f(&limits);
138
139    Node::new(limits.resolve(width, height, intrinsic_size))
140}
141
142/// Computes the resulting [`Node`] that fits the [`Limits`] given
143/// some width and height requirements and a closure that produces
144/// the content [`Node`] inside the given [`Limits`].
145pub fn contained(
146    limits: &Limits,
147    width: impl Into<Length>,
148    height: impl Into<Length>,
149    f: impl FnOnce(&Limits) -> Node,
150) -> Node {
151    let width = width.into();
152    let height = height.into();
153
154    let limits = limits.width(width).height(height);
155    let content = f(&limits);
156
157    Node::with_children(limits.resolve(width, height, content.size()), vec![content])
158}
159
160/// Computes the [`Node`] that fits the [`Limits`] given some width, height, and
161/// [`Padding`] requirements and a closure that produces the content [`Node`]
162/// inside the given [`Limits`].
163pub fn padded(
164    limits: &Limits,
165    width: impl Into<Length>,
166    height: impl Into<Length>,
167    padding: impl Into<Padding>,
168    layout: impl FnOnce(&Limits) -> Node,
169) -> Node {
170    positioned(limits, width, height, padding, layout, |content, _| content)
171}
172
173/// Computes a [`padded`] [`Node`] with a positioning step.
174pub fn positioned(
175    limits: &Limits,
176    width: impl Into<Length>,
177    height: impl Into<Length>,
178    padding: impl Into<Padding>,
179    layout: impl FnOnce(&Limits) -> Node,
180    position: impl FnOnce(Node, Size) -> Node,
181) -> Node {
182    let width = width.into();
183    let height = height.into();
184    let padding = padding.into();
185
186    let limits = limits.width(width).height(height);
187    let content = layout(&limits.shrink(padding));
188    let padding = padding.fit(content.size(), limits.max());
189
190    let size = limits
191        .shrink(padding)
192        .resolve(width, height, content.size());
193
194    Node::with_children(
195        size.expand(padding),
196        vec![position(content.move_to((padding.left, padding.top)), size)],
197    )
198}