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