ori_core/
children.rs

1use deref_derive::{Deref, DerefMut};
2use glam::Vec2;
3use ori_graphics::Rect;
4use smallvec::SmallVec;
5
6use crate::{
7    AlignItems, Axis, BoxConstraints, DrawContext, Event, EventContext, JustifyContent,
8    LayoutContext, Node,
9};
10
11/// A layout that lays out children in a flexbox-like manner.
12pub struct FlexLayout {
13    /// The offset to apply to the children.
14    ///
15    /// This is useful for scrolling or padding.
16    pub offset: Vec2,
17    /// The axis to use for laying out the children.
18    pub axis: Axis,
19    /// The justification of the children.
20    pub justify_content: JustifyContent,
21    /// The alignment of the children.
22    pub align_items: AlignItems,
23    /// The gap between the children.
24    pub gap: f32,
25}
26
27impl Default for FlexLayout {
28    fn default() -> Self {
29        Self {
30            offset: Vec2::ZERO,
31            axis: Axis::Vertical,
32            justify_content: JustifyContent::Start,
33            align_items: AlignItems::Start,
34            gap: 0.0,
35        }
36    }
37}
38
39impl FlexLayout {
40    pub fn new() -> Self {
41        Self::default()
42    }
43
44    pub fn vertical() -> Self {
45        Self {
46            axis: Axis::Vertical,
47            ..Self::default()
48        }
49    }
50
51    pub fn horizontal() -> Self {
52        Self {
53            axis: Axis::Horizontal,
54            ..Self::default()
55        }
56    }
57
58    pub fn row() -> Self {
59        Self::horizontal()
60    }
61
62    pub fn column() -> Self {
63        Self::vertical()
64    }
65}
66
67#[derive(Default, Deref, DerefMut)]
68pub struct Children {
69    nodes: Vec<Node>,
70}
71
72impl Children {
73    pub const fn new() -> Self {
74        Self { nodes: Vec::new() }
75    }
76
77    pub fn len(&self) -> usize {
78        self.nodes.len()
79    }
80
81    pub fn is_empty(&self) -> bool {
82        self.nodes.is_empty()
83    }
84
85    pub fn add_child(&mut self, child: impl Into<Node>) {
86        self.nodes.push(child.into());
87    }
88
89    /// Call the `event` method on all the children.
90    pub fn event(&self, cx: &mut EventContext, event: &Event) {
91        for child in self.iter() {
92            child.event(cx, event);
93        }
94    }
95
96    /// Layout the children using a FlexLayout.
97    pub fn flex_layout(
98        &self,
99        cx: &mut LayoutContext,
100        bc: BoxConstraints,
101        flex: FlexLayout,
102    ) -> Vec2 {
103        let FlexLayout {
104            offset,
105            axis,
106            justify_content,
107            align_items,
108            gap,
109        } = flex;
110
111        let max_minor = axis.minor(bc.max);
112        let min_minor = axis.minor(bc.min);
113
114        let max_major = axis.major(bc.max);
115        let min_major = axis.major(bc.min);
116
117        let mut minor = min_minor;
118        let mut major = 0.0f32;
119
120        // first we need to measure the children to determine their size
121        //
122        // NOTE: using a SmallVec here is a bit faster than using a Vec, but it's not a huge
123        // difference
124        let mut children = SmallVec::<[f32; 8]>::with_capacity(self.len());
125        for (i, child) in self.iter().enumerate() {
126            let child_bc = BoxConstraints {
127                min: axis.pack(0.0, 0.0),
128                max: axis.pack(max_major, max_minor),
129            };
130            let size = child.layout(cx, child_bc);
131            let child_minor = axis.minor(size);
132            let child_major = axis.major(size);
133
134            children.push(child_major);
135
136            minor = minor.max(child_minor);
137            major += child_major;
138
139            if i > 0 {
140                major += gap;
141            }
142        }
143
144        if align_items == AlignItems::Stretch {
145            // we need to re-measure the children to determine their size
146            major = 0.0;
147            children.clear();
148
149            for (i, child) in self.iter().enumerate() {
150                let child_bc = BoxConstraints {
151                    min: axis.pack(0.0, minor),
152                    max: axis.pack(max_major, minor),
153                };
154                // FIXME: calling layout again is not ideal, but it's the only way to get the
155                // correct size for the child, since we don't know the minor size until we've
156                // measured all the children
157                let size = child.layout(cx, child_bc);
158                let child_major = axis.major(size);
159
160                children.push(child_major);
161
162                major += child_major;
163
164                if i > 0 {
165                    major += gap;
166                }
167            }
168        }
169
170        major = major.max(min_major);
171
172        let child_offsets = justify_content.justify(&children, major, gap);
173
174        // now we can layout the children
175        for (child, align_major) in self.iter().zip(child_offsets) {
176            let child_minor = axis.minor(child.size());
177            let align_minor = align_items.align(0.0, minor, child_minor);
178
179            let child_offset = axis.pack(align_major, align_minor);
180            child.set_offset(offset + child_offset);
181        }
182
183        axis.pack(major, minor)
184    }
185
186    pub fn local_rect(&self) -> Rect {
187        let mut rect = None;
188
189        for child in self.iter() {
190            let rect = rect.get_or_insert_with(|| child.local_rect());
191            *rect = rect.union(child.local_rect());
192        }
193
194        rect.unwrap_or_default()
195    }
196
197    pub fn rect(&self) -> Rect {
198        let mut rect = None;
199
200        for child in self.iter() {
201            let rect = rect.get_or_insert_with(|| child.global_rect());
202            *rect = rect.union(child.global_rect());
203        }
204
205        rect.unwrap_or_default()
206    }
207
208    pub fn size(&self) -> Vec2 {
209        self.rect().size()
210    }
211
212    pub fn set_offset(&self, offset: Vec2) {
213        if self.is_empty() {
214            return;
215        }
216
217        let min = self.local_rect().min;
218
219        for child in self.iter() {
220            let child_offset = child.local_rect().min - min;
221            child.set_offset(offset + child_offset);
222        }
223    }
224
225    pub fn draw(&self, cx: &mut DrawContext) {
226        for child in self.iter() {
227            child.draw(cx);
228        }
229    }
230}
231
232impl IntoIterator for Children {
233    type Item = Node;
234    type IntoIter = std::vec::IntoIter<Self::Item>;
235
236    fn into_iter(self) -> Self::IntoIter {
237        self.nodes.into_iter()
238    }
239}
240
241impl<'a> IntoIterator for &'a Children {
242    type Item = &'a Node;
243    type IntoIter = std::slice::Iter<'a, Node>;
244
245    fn into_iter(self) -> Self::IntoIter {
246        self.nodes.iter()
247    }
248}
249
250impl<'a> IntoIterator for &'a mut Children {
251    type Item = &'a mut Node;
252    type IntoIter = std::slice::IterMut<'a, Node>;
253
254    fn into_iter(self) -> Self::IntoIter {
255        self.nodes.iter_mut()
256    }
257}