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