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
160        element_check || rotate_effect
161    }
162}
163
164pub trait ElementUtilsResolver {
165    fn utils(&self) -> Option<ElementWithUtils>;
166}
167
168impl ElementUtilsResolver for TagName {
169    #[inline]
170    fn utils(&self) -> Option<ElementWithUtils> {
171        match self {
172            TagName::Rect => Some(ElementWithUtils::Rect(RectElement)),
173            TagName::Svg => Some(ElementWithUtils::Svg(SvgElement)),
174            TagName::Paragraph => Some(ElementWithUtils::Paragraph(ParagraphElement)),
175            TagName::Image => Some(ElementWithUtils::Image(ImageElement)),
176            TagName::Label => Some(ElementWithUtils::Label(LabelElement)),
177            _ => None,
178        }
179    }
180}
181
182pub enum ElementWithUtils {
183    Rect(RectElement),
184    Svg(SvgElement),
185    Paragraph(ParagraphElement),
186    Image(ImageElement),
187    Label(LabelElement),
188}
189
190impl ElementUtils for ElementWithUtils {
191    fn clip(
192        &self,
193        layout_node: &LayoutNode,
194        node_ref: &DioxusNode,
195        canvas: &Canvas,
196        scale_factor: f32,
197    ) {
198        match self {
199            Self::Rect(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
200            Self::Svg(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
201            Self::Paragraph(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
202            Self::Image(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
203            Self::Label(el) => el.clip(layout_node, node_ref, canvas, scale_factor),
204        }
205    }
206
207    fn is_point_inside_area(
208        &self,
209        point: &CursorPoint,
210        node_ref: &DioxusNode,
211        layout_node: &LayoutNode,
212        scale_factor: f32,
213    ) -> bool {
214        match self {
215            Self::Rect(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
216            Self::Svg(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
217            Self::Paragraph(el) => {
218                el.is_point_inside_area(point, node_ref, layout_node, scale_factor)
219            }
220            Self::Image(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
221            Self::Label(el) => el.is_point_inside_area(point, node_ref, layout_node, scale_factor),
222        }
223    }
224
225    fn render(
226        self,
227        layout_node: &LayoutNode,
228        node_ref: &DioxusNode,
229        canvas: &Canvas,
230        font_collection: &mut FontCollection,
231        font_manager: &FontMgr,
232        default_fonts: &[String],
233        images_cache: &mut ImagesCache,
234        scale_factor: f32,
235    ) {
236        match self {
237            Self::Rect(el) => el.render(
238                layout_node,
239                node_ref,
240                canvas,
241                font_collection,
242                font_manager,
243                default_fonts,
244                images_cache,
245                scale_factor,
246            ),
247            Self::Svg(el) => el.render(
248                layout_node,
249                node_ref,
250                canvas,
251                font_collection,
252                font_manager,
253                default_fonts,
254                images_cache,
255                scale_factor,
256            ),
257            Self::Paragraph(el) => el.render(
258                layout_node,
259                node_ref,
260                canvas,
261                font_collection,
262                font_manager,
263                default_fonts,
264                images_cache,
265                scale_factor,
266            ),
267            Self::Image(el) => el.render(
268                layout_node,
269                node_ref,
270                canvas,
271                font_collection,
272                font_manager,
273                default_fonts,
274                images_cache,
275                scale_factor,
276            ),
277            Self::Label(el) => el.render(
278                layout_node,
279                node_ref,
280                canvas,
281                font_collection,
282                font_manager,
283                default_fonts,
284                images_cache,
285                scale_factor,
286            ),
287        }
288    }
289
290    fn drawing_area(
291        &self,
292        layout_node: &LayoutNode,
293        node_ref: &DioxusNode,
294        layout: &Torin<NodeId>,
295        scale_factor: f32,
296        node_style: &StyleState,
297        transform_state: &TransformState,
298    ) -> Area {
299        match self {
300            Self::Rect(el) => el.drawing_area(
301                layout_node,
302                node_ref,
303                layout,
304                scale_factor,
305                node_style,
306                transform_state,
307            ),
308            Self::Svg(el) => el.drawing_area(
309                layout_node,
310                node_ref,
311                layout,
312                scale_factor,
313                node_style,
314                transform_state,
315            ),
316            Self::Paragraph(el) => el.drawing_area(
317                layout_node,
318                node_ref,
319                layout,
320                scale_factor,
321                node_style,
322                transform_state,
323            ),
324            Self::Image(el) => el.drawing_area(
325                layout_node,
326                node_ref,
327                layout,
328                scale_factor,
329                node_style,
330                transform_state,
331            ),
332            Self::Label(el) => el.drawing_area(
333                layout_node,
334                node_ref,
335                layout,
336                scale_factor,
337                node_style,
338                transform_state,
339            ),
340        }
341    }
342
343    fn needs_cached_area(
344        &self,
345        node_ref: &DioxusNode,
346        transform_state: &TransformState,
347        style_state: &StyleState,
348    ) -> bool {
349        match self {
350            Self::Rect(el) => el.needs_cached_area(node_ref, transform_state, style_state),
351            Self::Svg(el) => el.needs_cached_area(node_ref, transform_state, style_state),
352            Self::Paragraph(el) => el.needs_cached_area(node_ref, transform_state, style_state),
353            Self::Image(el) => el.needs_cached_area(node_ref, transform_state, style_state),
354            Self::Label(el) => el.needs_cached_area(node_ref, transform_state, style_state),
355        }
356    }
357}