freya_core/elements/
utils.rs

1use freya_engine::prelude::{
2    Canvas,
3    FontCollection,
4    FontMgr,
5};
6use freya_native_core::{
7    tags::TagName,
8    NodeId,
9};
10use torin::{
11    prelude::{
12        Area,
13        AreaModel,
14        CursorPoint,
15        LayoutNode,
16    },
17    torin::Torin,
18};
19
20use super::*;
21use crate::{
22    dom::{
23        DioxusNode,
24        ImagesCache,
25    },
26    states::{
27        StyleState,
28        TransformState,
29        ViewportState,
30    },
31};
32
33pub trait ElementUtils {
34    fn is_point_inside_area(
35        &self,
36        point: &CursorPoint,
37        _node_ref: &DioxusNode,
38        layout_node: &LayoutNode,
39        _scale_factor: f32,
40    ) -> bool {
41        layout_node.area.contains(point.to_f32())
42    }
43
44    fn clip(
45        &self,
46        _layout_node: &LayoutNode,
47        _node_ref: &DioxusNode,
48        _canvas: &Canvas,
49        _scale_factor: f32,
50    ) {
51    }
52
53    #[allow(clippy::too_many_arguments)]
54    fn render(
55        self,
56        layout_node: &LayoutNode,
57        node_ref: &DioxusNode,
58        canvas: &Canvas,
59        font_collection: &mut FontCollection,
60        font_manager: &FontMgr,
61        default_fonts: &[String],
62        images_cache: &mut ImagesCache,
63        scale_factor: f32,
64    );
65
66    fn element_drawing_area(
67        &self,
68        layout_node: &LayoutNode,
69        _node_ref: &DioxusNode,
70        _scale_factor: f32,
71        _node_style: &StyleState,
72    ) -> Area {
73        // Images neither SVG elements have support for shadows or borders, so its fine so simply return the visible area.
74        layout_node.visible_area()
75    }
76
77    #[allow(clippy::too_many_arguments)]
78    fn drawing_area_with_viewports(
79        &self,
80        layout_node: &LayoutNode,
81        node_ref: &DioxusNode,
82        layout: &Torin<NodeId>,
83        scale_factor: f32,
84        node_style: &StyleState,
85        node_viewports: &ViewportState,
86        transform_state: &TransformState,
87    ) -> Option<Area> {
88        let mut drawing_area = self.drawing_area(
89            layout_node,
90            node_ref,
91            layout,
92            scale_factor,
93            node_style,
94            transform_state,
95        );
96
97        for viewport_id in &node_viewports.viewports {
98            let viewport = layout.get(*viewport_id).unwrap().visible_area();
99            drawing_area.clip(&viewport);
100            if !viewport.intersects(&drawing_area) {
101                return None;
102            }
103        }
104
105        // Inflate the area by 1px in each side to cover potential off-bounds rendering caused by antialising
106        Some(drawing_area.inflate(1.0, 1.0))
107    }
108
109    /// Measure the area for this element considering other
110    /// factors like shadows or borders, which are not part of the layout.
111    fn drawing_area(
112        &self,
113        layout_node: &LayoutNode,
114        node_ref: &DioxusNode,
115        layout: &Torin<NodeId>,
116        scale_factor: f32,
117        node_style: &StyleState,
118        transform_state: &TransformState,
119    ) -> Area {
120        let mut drawing_area =
121            self.element_drawing_area(layout_node, node_ref, scale_factor, node_style);
122
123        for (id, scale_x, scale_y) in &transform_state.scales {
124            let layout_node = layout.get(*id).unwrap();
125            let center = layout_node.area.center();
126            drawing_area = drawing_area.translate(-center.to_vector());
127            drawing_area = drawing_area.scale(*scale_x, *scale_y);
128            drawing_area = drawing_area.translate(center.to_vector());
129            drawing_area = drawing_area.inflate(1.0, 1.0);
130        }
131
132        if !transform_state.rotations.is_empty() {
133            let area = layout_node.visible_area();
134            drawing_area.max_area_when_rotated(area.center())
135        } else {
136            drawing_area
137        }
138    }
139
140    /// Check if this element requires any kind of special caching.
141    /// Mainly used for text-like elements with shadows.
142    /// See [crate::render::CompositorCache].
143    /// Default to `false`.
144    #[inline]
145    fn element_needs_cached_area(&self, _node_ref: &DioxusNode, _style_state: &StyleState) -> bool {
146        false
147    }
148
149    #[inline]
150    fn needs_cached_area(
151        &self,
152        node_ref: &DioxusNode,
153        transform_state: &TransformState,
154        style_state: &StyleState,
155    ) -> bool {
156        let element_check = self.element_needs_cached_area(node_ref, style_state);
157
158        let rotate_effect = !transform_state.rotations.is_empty();
159        let scales_effect = !transform_state.scales.is_empty();
160
161        element_check || rotate_effect || scales_effect
162    }
163}
164
165pub trait ElementUtilsResolver {
166    fn utils(&self) -> Option<ElementWithUtils>;
167}
168
169impl ElementUtilsResolver for TagName {
170    #[inline]
171    fn utils(&self) -> Option<ElementWithUtils> {
172        match self {
173            TagName::Rect => Some(ElementWithUtils::Rect(RectElement)),
174            TagName::Svg => Some(ElementWithUtils::Svg(SvgElement)),
175            TagName::Paragraph => Some(ElementWithUtils::Paragraph(ParagraphElement)),
176            TagName::Image => Some(ElementWithUtils::Image(ImageElement)),
177            TagName::Label => Some(ElementWithUtils::Label(LabelElement)),
178            _ => None,
179        }
180    }
181}
182
183pub enum ElementWithUtils {
184    Rect(RectElement),
185    Svg(SvgElement),
186    Paragraph(ParagraphElement),
187    Image(ImageElement),
188    Label(LabelElement),
189}
190
191impl ElementUtils for ElementWithUtils {
192    fn clip(
193        &self,
194        layout_node: &LayoutNode,
195        node_ref: &DioxusNode,
196        canvas: &Canvas,
197        scale_factor: f32,
198    ) {
199        match self {
200            Self::Rect(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
201            Self::Svg(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
202            Self::Paragraph(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
203            Self::Image(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
204            Self::Label(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
205        }
206    }
207
208    fn is_point_inside_area(
209        &self,
210        point: &CursorPoint,
211        node_ref: &DioxusNode,
212        layout_node: &LayoutNode,
213        scale_factor: f32,
214    ) -> bool {
215        match self {
216            Self::Rect(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
217            Self::Svg(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
218            Self::Paragraph(el) => {
219                el.is_point_inside_area(point, node_ref, layout_node, scale_factor)
220            }
221            Self::Image(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
222            Self::Label(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
223        }
224    }
225
226    fn render(
227        self,
228        layout_node: &LayoutNode,
229        node_ref: &DioxusNode,
230        canvas: &Canvas,
231        font_collection: &mut FontCollection,
232        font_manager: &FontMgr,
233        default_fonts: &[String],
234        images_cache: &mut ImagesCache,
235        scale_factor: f32,
236    ) {
237        match self {
238            Self::Rect(el) => el.render(
239                layout_node,
240                node_ref,
241                canvas,
242                font_collection,
243                font_manager,
244                default_fonts,
245                images_cache,
246                scale_factor,
247            ),
248            Self::Svg(el) => el.render(
249                layout_node,
250                node_ref,
251                canvas,
252                font_collection,
253                font_manager,
254                default_fonts,
255                images_cache,
256                scale_factor,
257            ),
258            Self::Paragraph(el) => el.render(
259                layout_node,
260                node_ref,
261                canvas,
262                font_collection,
263                font_manager,
264                default_fonts,
265                images_cache,
266                scale_factor,
267            ),
268            Self::Image(el) => el.render(
269                layout_node,
270                node_ref,
271                canvas,
272                font_collection,
273                font_manager,
274                default_fonts,
275                images_cache,
276                scale_factor,
277            ),
278            Self::Label(el) => el.render(
279                layout_node,
280                node_ref,
281                canvas,
282                font_collection,
283                font_manager,
284                default_fonts,
285                images_cache,
286                scale_factor,
287            ),
288        }
289    }
290
291    fn drawing_area(
292        &self,
293        layout_node: &LayoutNode,
294        node_ref: &DioxusNode,
295        layout: &Torin<NodeId>,
296        scale_factor: f32,
297        node_style: &StyleState,
298        transform_state: &TransformState,
299    ) -> Area {
300        match self {
301            Self::Rect(el) => el.drawing_area(
302                layout_node,
303                node_ref,
304                layout,
305                scale_factor,
306                node_style,
307                transform_state,
308            ),
309            Self::Svg(el) => el.drawing_area(
310                layout_node,
311                node_ref,
312                layout,
313                scale_factor,
314                node_style,
315                transform_state,
316            ),
317            Self::Paragraph(el) => el.drawing_area(
318                layout_node,
319                node_ref,
320                layout,
321                scale_factor,
322                node_style,
323                transform_state,
324            ),
325            Self::Image(el) => el.drawing_area(
326                layout_node,
327                node_ref,
328                layout,
329                scale_factor,
330                node_style,
331                transform_state,
332            ),
333            Self::Label(el) => el.drawing_area(
334                layout_node,
335                node_ref,
336                layout,
337                scale_factor,
338                node_style,
339                transform_state,
340            ),
341        }
342    }
343
344    fn needs_cached_area(
345        &self,
346        node_ref: &DioxusNode,
347        transform_state: &TransformState,
348        style_state: &StyleState,
349    ) -> bool {
350        match self {
351            Self::Rect(el) => el.needs_cached_area(node_ref, transform_state, style_state),
352            Self::Svg(el) => el.needs_cached_area(node_ref, transform_state, style_state),
353            Self::Paragraph(el) => el.needs_cached_area(node_ref, transform_state, style_state),
354            Self::Image(el) => el.needs_cached_area(node_ref, transform_state, style_state),
355            Self::Label(el) => el.needs_cached_area(node_ref, transform_state, style_state),
356        }
357    }
358}