vizia_core/layout/
node.rs

1use morphorm::Node;
2use skia_safe::wrapper::PointerWrapper;
3use vizia_storage::MorphormChildIter;
4
5use crate::prelude::*;
6use crate::resource::{ImageOrSvg, ResourceManager};
7use crate::text::TextContext;
8
9pub struct SubLayout<'a> {
10    pub text_context: &'a mut TextContext,
11    pub resource_manager: &'a ResourceManager,
12}
13
14impl Node for Entity {
15    type Store = Style;
16    type Tree = Tree<Entity>;
17    type CacheKey = Entity;
18    type ChildIter<'t> = MorphormChildIter<'t, Entity>;
19    type SubLayout<'a> = SubLayout<'a>;
20
21    fn children<'t>(&'t self, tree: &'t Self::Tree) -> Self::ChildIter<'t> {
22        MorphormChildIter::new(tree, *self)
23    }
24
25    fn key(&self) -> Self::CacheKey {
26        *self
27    }
28
29    fn visible(&self, store: &Self::Store) -> bool {
30        store.display.get(*self).copied().map(|display| display == Display::Flex).unwrap_or(true)
31    }
32
33    fn layout_type(&self, store: &Self::Store) -> Option<morphorm::LayoutType> {
34        store.layout_type.get(*self).copied()
35    }
36
37    fn position_type(&self, store: &Self::Store) -> Option<morphorm::PositionType> {
38        store.position_type.get(*self).copied()
39    }
40
41    fn left(&self, store: &Self::Store) -> Option<morphorm::Units> {
42        store.left.get(*self).cloned().map(|l| match l {
43            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
44            t => t,
45        })
46    }
47
48    fn right(&self, store: &Self::Store) -> Option<morphorm::Units> {
49        store.right.get(*self).cloned().map(|r| match r {
50            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
51            t => t,
52        })
53    }
54
55    fn top(&self, store: &Self::Store) -> Option<morphorm::Units> {
56        store.top.get(*self).cloned().map(|t| match t {
57            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
58            t => t,
59        })
60    }
61
62    fn bottom(&self, store: &Self::Store) -> Option<morphorm::Units> {
63        store.bottom.get(*self).cloned().map(|b| match b {
64            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
65            t => t,
66        })
67    }
68
69    fn width(&self, store: &Self::Store) -> Option<morphorm::Units> {
70        store.width.get(*self).cloned().map(|w| match w {
71            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
72            t => t,
73        })
74    }
75
76    fn min_width(&self, store: &Self::Store) -> Option<morphorm::Units> {
77        store.min_width.get(*self).cloned().map(|w| match w {
78            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
79            t => t,
80        })
81    }
82
83    fn max_width(&self, store: &Self::Store) -> Option<morphorm::Units> {
84        store.max_width.get(*self).cloned().map(|w| match w {
85            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
86            t => t,
87        })
88    }
89
90    fn content_size(
91        &self,
92        store: &Self::Store,
93        sublayout: &mut Self::SubLayout<'_>,
94        width: Option<f32>,
95        height: Option<f32>,
96    ) -> Option<(f32, f32)> {
97        if let Some(paragraph) = sublayout.text_context.text_paragraphs.get_mut(*self) {
98            // // If the width is known use that, else use 0 for wrapping text or 999999 for non-wrapping text.
99            // let max_width = if let Some(width) = width {
100            //     let padding_left =
101            //         store.padding_left.get(*self).cloned().unwrap_or_default().to_px(width, 0.0)
102            //             * store.scale_factor();
103            //     let padding_right =
104            //         store.padding_right.get(*self).cloned().unwrap_or_default().to_px(width, 0.0)
105            //             * store.scale_factor();
106            //     let border_width = store
107            //         .border_width
108            //         .get(*self)
109            //         .cloned()
110            //         .unwrap_or_default()
111            //         .to_pixels(0.0, store.scale_factor());
112
113            //     width.ceil() - padding_left - padding_right - border_width - border_width
114            // } else {
115            //     f32::MAX
116            // };
117
118            paragraph.layout(f32::MAX);
119
120            let padding_left = store.padding_left.get(*self).copied().unwrap_or_default();
121            let padding_right = store.padding_right.get(*self).copied().unwrap_or_default();
122            let padding_top = store.padding_top.get(*self).copied().unwrap_or_default();
123            let padding_bottom = store.padding_bottom.get(*self).copied().unwrap_or_default();
124
125            let mut child_space_x = 0.0;
126            let mut child_space_y = 0.0;
127
128            let mut p_left = 0.0;
129            let mut p_top = 0.0;
130
131            // shrink the bounding box based on pixel values
132            if let Pixels(val) = padding_left {
133                let val = val * store.scale_factor();
134                child_space_x += val;
135                p_left += val;
136            }
137            if let Pixels(val) = padding_right {
138                let val = val * store.scale_factor();
139                child_space_x += val;
140            }
141            if let Pixels(val) = padding_top {
142                let val = val * store.scale_factor();
143                child_space_y += val;
144                p_top += val;
145            }
146            if let Pixels(val) = padding_bottom {
147                let val = val * store.scale_factor();
148                child_space_y += val;
149            }
150
151            let border_width = store
152                .border_width
153                .get(*self)
154                .cloned()
155                .unwrap_or_default()
156                .to_pixels(0.0, store.scale_factor());
157
158            child_space_x += 2.0 * border_width;
159            child_space_y += 2.0 * border_width;
160
161            p_left += border_width;
162            p_top += border_width;
163
164            let text_width = match (
165                store.text_wrap.get(*self).copied().unwrap_or(true),
166                store.text_overflow.get(*self).copied(),
167            ) {
168                (true, _) => {
169                    if let Some(width) = width {
170                        width - child_space_x
171                    } else {
172                        paragraph.min_intrinsic_width().ceil()
173                    }
174                }
175                (false, Some(TextOverflow::Ellipsis)) => {
176                    if let Some(width) = width {
177                        width - child_space_x
178                    } else {
179                        paragraph.max_intrinsic_width().ceil()
180                    }
181                }
182                _ => {
183                    if let Some(width) = width {
184                        (width - child_space_x).max(paragraph.min_intrinsic_width().ceil())
185                    } else {
186                        paragraph.max_intrinsic_width().ceil()
187                    }
188                }
189            };
190
191            paragraph.layout(text_width);
192
193            let text_height = if let Some(height) = height { height } else { paragraph.height() };
194
195            let width =
196                if let Some(width) = width { width } else { text_width.round() + child_space_x };
197
198            let height = if let Some(height) = height {
199                height
200            } else {
201                text_height.round() + child_space_y
202            };
203
204            // Cache the text_width/ text_height in the text context so we can use it to compute transforms later
205            sublayout.text_context.set_text_bounds(
206                *self,
207                BoundingBox { x: p_left, y: p_top, w: text_width, h: text_height },
208            );
209
210            Some((width, height))
211        } else if let Some(images) = store.background_image.get(*self) {
212            let mut max_width = 0.0f32;
213            let mut max_height = 0.0f32;
214            for image in images.iter() {
215                match image {
216                    ImageOrGradient::Image(image_name) => {
217                        if let Some(image_id) = sublayout.resource_manager.image_ids.get(image_name)
218                        {
219                            match sublayout
220                                .resource_manager
221                                .images
222                                .get(image_id)
223                                .map(|stored_img| &stored_img.image)
224                            {
225                                Some(ImageOrSvg::Image(image)) => {
226                                    max_width =
227                                        max_width.max(image.width() as f32 * store.scale_factor());
228                                    max_height = max_height
229                                        .max(image.height() as f32 * store.scale_factor());
230                                }
231
232                                Some(ImageOrSvg::Svg(svg)) => {
233                                    max_width = max_width.max(
234                                        svg.inner().fContainerSize.fWidth * store.scale_factor(),
235                                    );
236                                    max_height = max_height.max(
237                                        svg.inner().fContainerSize.fWidth * store.scale_factor(),
238                                    );
239                                }
240
241                                _ => {}
242                            }
243                        }
244                    }
245                    _ => {}
246                }
247            }
248
249            let width = if let Some(width) = width { width } else { max_width };
250            let height = if let Some(height) = height { height } else { max_height };
251            Some((width, height))
252        } else {
253            None
254        }
255    }
256
257    fn height(&self, store: &Self::Store) -> Option<morphorm::Units> {
258        store.height.get(*self).cloned().map(|h| match h {
259            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
260            t => t,
261        })
262    }
263
264    fn min_height(&self, store: &Self::Store) -> Option<morphorm::Units> {
265        store.min_height.get(*self).cloned().map(|h| match h {
266            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
267            t => t,
268        })
269    }
270
271    fn max_height(&self, store: &Self::Store) -> Option<morphorm::Units> {
272        store.max_height.get(*self).cloned().map(|h| match h {
273            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
274            t => t,
275        })
276    }
277
278    fn padding_left(&self, store: &Self::Store) -> Option<morphorm::Units> {
279        store.padding_left.get(*self).cloned().map(|l| match l {
280            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
281            t => t,
282        })
283    }
284
285    fn padding_right(&self, store: &Self::Store) -> Option<morphorm::Units> {
286        store.padding_right.get(*self).cloned().map(|r| match r {
287            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
288            t => t,
289        })
290    }
291
292    fn padding_top(&self, store: &Self::Store) -> Option<morphorm::Units> {
293        store.padding_top.get(*self).cloned().map(|t| match t {
294            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
295            t => t,
296        })
297    }
298
299    fn padding_bottom(&self, store: &Self::Store) -> Option<morphorm::Units> {
300        store.padding_bottom.get(*self).cloned().map(|b| match b {
301            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
302            t => t,
303        })
304    }
305
306    fn vertical_gap(&self, store: &Self::Store) -> Option<morphorm::Units> {
307        store.vertical_gap.get(*self).cloned().map(|v| match v {
308            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
309            t => t,
310        })
311    }
312
313    fn horizontal_gap(&self, store: &Self::Store) -> Option<morphorm::Units> {
314        store.horizontal_gap.get(*self).cloned().map(|v| match v {
315            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
316            t => t,
317        })
318    }
319
320    fn border_left(&self, store: &Self::Store) -> Option<morphorm::Units> {
321        store.border_width.get(*self).map(|border_width| match border_width {
322            LengthOrPercentage::Length(val) => {
323                Units::Pixels(store.logical_to_physical(val.to_px().unwrap_or_default()))
324            }
325            LengthOrPercentage::Percentage(val) => Units::Percentage(*val),
326        })
327    }
328
329    fn border_right(&self, store: &Self::Store) -> Option<morphorm::Units> {
330        store.border_width.get(*self).map(|border_width| match border_width {
331            LengthOrPercentage::Length(val) => {
332                Units::Pixels(store.logical_to_physical(val.to_px().unwrap_or_default()))
333            }
334            LengthOrPercentage::Percentage(val) => Units::Percentage(*val),
335        })
336    }
337
338    fn border_top(&self, store: &Self::Store) -> Option<morphorm::Units> {
339        store.border_width.get(*self).map(|border_width| match border_width {
340            LengthOrPercentage::Length(val) => {
341                Units::Pixels(store.logical_to_physical(val.to_px().unwrap_or_default()))
342            }
343            LengthOrPercentage::Percentage(val) => Units::Percentage(*val),
344        })
345    }
346
347    fn border_bottom(&self, store: &Self::Store) -> Option<morphorm::Units> {
348        store.border_width.get(*self).map(|border_width| match border_width {
349            LengthOrPercentage::Length(val) => {
350                Units::Pixels(store.logical_to_physical(val.to_px().unwrap_or_default()))
351            }
352            LengthOrPercentage::Percentage(val) => Units::Percentage(*val),
353        })
354    }
355
356    fn alignment(&self, store: &Self::Store) -> Option<morphorm::Alignment> {
357        store.alignment.get(*self).copied()
358    }
359
360    fn vertical_scroll(&self, store: &Self::Store) -> Option<f32> {
361        store.vertical_scroll.get(*self).cloned().map(|val| store.logical_to_physical(val))
362    }
363
364    fn horizontal_scroll(&self, store: &Self::Store) -> Option<f32> {
365        store.horizontal_scroll.get(*self).cloned().map(|val| store.logical_to_physical(val))
366    }
367
368    fn min_vertical_gap(&self, store: &Self::Store) -> Option<Units> {
369        store.min_vertical_gap.get(*self).cloned().map(|h| match h {
370            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
371            t => t,
372        })
373    }
374
375    fn min_horizontal_gap(&self, store: &Self::Store) -> Option<Units> {
376        store.min_horizontal_gap.get(*self).cloned().map(|h| match h {
377            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
378            t => t,
379        })
380    }
381
382    fn max_vertical_gap(&self, store: &Self::Store) -> Option<Units> {
383        store.max_vertical_gap.get(*self).cloned().map(|h| match h {
384            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
385            t => t,
386        })
387    }
388
389    fn max_horizontal_gap(&self, store: &Self::Store) -> Option<Units> {
390        store.max_horizontal_gap.get(*self).cloned().map(|h| match h {
391            Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
392            t => t,
393        })
394    }
395
396    fn grid_columns(&self, store: &Self::Store) -> Option<Vec<Units>> {
397        store.grid_columns.get(*self).cloned().map(|cols| {
398            cols.into_iter()
399                .map(|col| match col {
400                    Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
401                    t => t,
402                })
403                .collect()
404        })
405    }
406
407    fn grid_rows(&self, store: &Self::Store) -> Option<Vec<Units>> {
408        store.grid_rows.get(*self).cloned().map(|rows| {
409            rows.into_iter()
410                .map(|row| match row {
411                    Units::Pixels(val) => Units::Pixels(store.logical_to_physical(val)),
412                    t => t,
413                })
414                .collect()
415        })
416    }
417
418    fn column_start(&self, store: &Self::Store) -> Option<usize> {
419        store.column_start.get(*self).copied()
420    }
421
422    fn column_span(&self, store: &Self::Store) -> Option<usize> {
423        store.column_span.get(*self).copied()
424    }
425
426    fn row_start(&self, store: &Self::Store) -> Option<usize> {
427        store.row_start.get(*self).copied()
428    }
429
430    fn row_span(&self, store: &Self::Store) -> Option<usize> {
431        store.row_span.get(*self).copied()
432    }
433}