vizia_core/context/
draw.rs

1use skia_safe::canvas::SaveLayerRec;
2use skia_safe::gradient_shader::GradientShaderColors;
3use skia_safe::path::ArcSize;
4use skia_safe::rrect::Corner;
5use skia_safe::wrapper::PointerWrapper;
6use skia_safe::{
7    BlurStyle, ClipOp, MaskFilter, Matrix, Paint, PaintStyle, Path, PathDirection, PathEffect,
8    Point, RRect, Rect, SamplingOptions, Shader, TileMode,
9};
10use std::any::{Any, TypeId};
11use std::f32::consts::SQRT_2;
12use vizia_style::LengthPercentageOrAuto;
13
14use hashbrown::HashMap;
15
16use crate::animation::Interpolator;
17use crate::cache::CachedData;
18use crate::events::ViewHandler;
19use crate::prelude::*;
20use crate::resource::{ImageOrSvg, ResourceManager};
21use crate::text::TextContext;
22use vizia_input::MouseState;
23
24use super::ModelData;
25
26/// A context used when drawing a view.
27///
28/// The `DrawContext` is provided by the [`draw`](crate::view::View::draw) method in [`View`] and can be used to immutably access the
29/// computed style and layout properties of the current view.
30///
31/// # Example
32/// ```
33/// # use vizia_core::prelude::*;
34/// # use vizia_core::vg;
35/// # let cx = &mut Context::default();
36///
37/// pub struct CustomView {}
38///
39/// impl CustomView {
40///     pub fn new(cx: &mut Context) -> Handle<Self> {
41///         Self{}.build(cx, |_|{})
42///     }
43/// }
44///
45/// impl View for CustomView {
46///     fn draw(&self, cx: &mut DrawContext, canvas: &Canvas) {
47///         // Get the computed bounds after layout of the current view
48///         let bounds = cx.bounds();
49///         // Draw to the canvas using the bounds of the current view
50///         let path = vg::Path::new();
51///         path.rect(bounds.x, bounds.y, bounds.w, bounds.h);
52///         let mut paint = vg::Paint::default();
53///         paint.set_color(Color::rgb(200, 100, 100));
54///         canvas.draw_path(&path, &paint);
55///     }
56/// }
57/// ```
58pub struct DrawContext<'a> {
59    pub(crate) current: Entity,
60    pub(crate) style: &'a Style,
61    pub(crate) cache: &'a mut CachedData,
62    pub(crate) tree: &'a Tree<Entity>,
63    pub(crate) models: &'a HashMap<Entity, HashMap<TypeId, Box<dyn ModelData>>>,
64    pub(crate) views: &'a mut HashMap<Entity, Box<dyn ViewHandler>>,
65    pub(crate) resource_manager: &'a ResourceManager,
66    pub(crate) text_context: &'a mut TextContext,
67    pub(crate) modifiers: &'a Modifiers,
68    pub(crate) mouse: &'a MouseState<Entity>,
69    pub(crate) windows: &'a mut HashMap<Entity, WindowState>,
70}
71
72macro_rules! get_units_property {
73    (
74        $(#[$meta:meta])*
75        $name:ident
76    ) => {
77        $(#[$meta])*
78        pub fn $name(&self) -> Units {
79            let result = self.style.$name.get(self.current);
80            if let Some(Units::Pixels(p)) = result {
81                Units::Pixels(self.logical_to_physical(*p))
82            } else {
83                result.copied().unwrap_or_default()
84            }
85        }
86    };
87}
88
89macro_rules! get_color_property {
90    (
91        $(#[$meta:meta])*
92        $name:ident
93    ) => {
94        $(#[$meta])*
95        pub fn $name(&self) -> Color {
96            if let Some(col) = self.style.$name.get(self.current) {
97                Color::rgba(col.r(), col.g(), col.b(), col.a())
98            } else {
99                Color::rgba(0, 0, 0, 0)
100            }
101        }
102    };
103}
104
105macro_rules! get_length_property {
106    (
107        $(#[$meta:meta])*
108        $name:ident
109    ) => {
110        $(#[$meta])*
111        pub fn $name(&self) -> f32 {
112            if let Some(length) = self.style.$name.get(self.current) {
113                let bounds = self.bounds();
114
115                let px = length.to_pixels(bounds.w.min(bounds.h), self.scale_factor());
116                return px.round();
117            }
118
119            0.0
120        }
121    };
122}
123
124impl DrawContext<'_> {
125    /// Returns the bounds of the current view.
126    pub fn bounds(&self) -> BoundingBox {
127        self.cache.get_bounds(self.current)
128    }
129
130    /// Marks the current view as needing to be redrawn.
131    pub fn needs_redraw(&mut self) {
132        let parent_window = self.tree.get_parent_window(self.current).unwrap_or(Entity::root());
133        if let Some(window_state) = self.windows.get_mut(&parent_window) {
134            window_state.redraw_list.insert(self.current);
135        }
136    }
137
138    /// Returns the z-index of the current view.
139    pub fn z_index(&self) -> i32 {
140        self.style.z_index.get(self.current).copied().unwrap_or_default()
141    }
142
143    /// Returns the scale factor.
144    pub fn scale_factor(&self) -> f32 {
145        self.style.dpi_factor as f32
146    }
147
148    /// Returns a reference to the keyboard modifiers state.
149    pub fn modifiers(&self) -> &Modifiers {
150        self.modifiers
151    }
152
153    /// Returns a reference to the mouse state.
154    pub fn mouse(&self) -> &MouseState<Entity> {
155        self.mouse
156    }
157
158    /// Returns the clip path of the current view.
159    pub fn clip_path(&self) -> Option<skia_safe::Path> {
160        let bounds = self.bounds();
161        let overflowx = self.style.overflowx.get(self.current).copied().unwrap_or_default();
162        let overflowy = self.style.overflowy.get(self.current).copied().unwrap_or_default();
163
164        let scale = self.scale_factor();
165
166        let clip_bounds = self
167            .style
168            .clip_path
169            .get(self.current)
170            .map(|clip| match clip {
171                ClipPath::Auto => bounds,
172                ClipPath::Shape(rect) => bounds.shrink_sides(
173                    rect.3.to_pixels(bounds.w, scale),
174                    rect.0.to_pixels(bounds.h, scale),
175                    rect.1.to_pixels(bounds.w, scale),
176                    rect.2.to_pixels(bounds.h, scale),
177                ),
178            })
179            .unwrap_or(bounds);
180
181        let root_bounds = self.cache.get_bounds(Entity::root());
182
183        let clip_bounds = match (overflowx, overflowy) {
184            (Overflow::Visible, Overflow::Visible) => return None,
185            (Overflow::Hidden, Overflow::Visible) => {
186                let left = clip_bounds.left();
187                let right = clip_bounds.right();
188                let top = root_bounds.top();
189                let bottom = root_bounds.bottom();
190                BoundingBox::from_min_max(left, top, right, bottom)
191            }
192            (Overflow::Visible, Overflow::Hidden) => {
193                let left = root_bounds.left();
194                let right = root_bounds.right();
195                let top = clip_bounds.top();
196                let bottom = clip_bounds.bottom();
197                BoundingBox::from_min_max(left, top, right, bottom)
198            }
199            (Overflow::Hidden, Overflow::Hidden) => clip_bounds,
200        };
201
202        let mut clip_path = self.build_path(clip_bounds, (0.0, 0.0));
203        clip_path.offset(clip_bounds.top_left());
204
205        Some(clip_path)
206    }
207
208    /// Returns the 2D transform of the current view.
209    pub fn transform(&self) -> Matrix {
210        let bounds = self.bounds();
211        let scale_factor = self.scale_factor();
212
213        // Apply transform origin.
214        let mut origin = self
215            .style
216            .transform_origin
217            .get(self.current)
218            .map(|transform_origin| {
219                let mut origin = Matrix::translate(bounds.top_left());
220                let offset = transform_origin.as_transform(bounds, scale_factor);
221                origin = offset * origin;
222                origin
223            })
224            .unwrap_or(Matrix::translate(bounds.center()));
225
226        let mut transform = origin;
227        origin = origin.invert().unwrap();
228
229        // Apply translation.
230        if let Some(translate) = self.style.translate.get(self.current) {
231            transform = transform * translate.as_transform(bounds, scale_factor);
232        }
233
234        // Apply rotation.
235        if let Some(rotate) = self.style.rotate.get(self.current) {
236            transform = transform * rotate.as_transform(bounds, scale_factor);
237        }
238
239        // Apply scaling.
240        if let Some(scale) = self.style.scale.get(self.current) {
241            transform = transform * scale.as_transform(bounds, scale_factor);
242        }
243
244        // Apply transform functions.
245        if let Some(transforms) = self.style.transform.get(self.current) {
246            // Check if the transform is currently animating
247            // Get the animation state
248            // Manually interpolate the value to get the overall transform for the current frame
249            if let Some(animation_state) = self.style.transform.get_active_animation(self.current) {
250                if let Some(start) = animation_state.keyframes.first() {
251                    if let Some(end) = animation_state.keyframes.last() {
252                        let start_transform = start.value.as_transform(bounds, scale_factor);
253                        let end_transform = end.value.as_transform(bounds, scale_factor);
254                        let t = animation_state.t;
255                        let animated_transform =
256                            Matrix::interpolate(&start_transform, &end_transform, t);
257                        transform = transform * animated_transform;
258                    }
259                }
260            } else {
261                transform = transform * transforms.as_transform(bounds, scale_factor);
262            }
263        }
264
265        transform = transform * origin;
266
267        transform
268    }
269
270    /// Returns the visibility of the current view.
271    pub fn visibility(&self) -> Option<Visibility> {
272        self.style.visibility.get(self.current).copied()
273    }
274
275    /// Returns the display of the current view.
276    pub fn display(&self) -> Display {
277        self.style.display.get(self.current).copied().unwrap_or(Display::Flex)
278    }
279
280    /// Returns the opacity of the current view.
281    pub fn opacity(&self) -> f32 {
282        self.style.opacity.get(self.current).copied().unwrap_or(Opacity(1.0)).0
283    }
284
285    /// Returns the lookup pattern to pick the default font.
286    pub fn default_font(&self) -> &[FamilyOwned] {
287        &self.style.default_font
288    }
289
290    /// Returns the font-size of the current view in physical pixels.
291    pub fn font_size(&self) -> f32 {
292        self.logical_to_physical(
293            self.style
294                .font_size
295                .get(self.current)
296                .cloned()
297                .map(|f| f.0.to_px().unwrap())
298                .unwrap_or(16.0),
299        )
300    }
301
302    /// Returns the font-weight of the current view.
303    pub fn font_weight(&self) -> FontWeight {
304        self.style.font_weight.get(self.current).copied().unwrap_or_default()
305    }
306
307    /// Returns the font-width of the current view.
308    pub fn font_width(&self) -> FontWidth {
309        self.style.font_width.get(self.current).copied().unwrap_or_default()
310    }
311
312    /// Returns the font-slant of the current view.
313    pub fn font_slant(&self) -> FontSlant {
314        self.style.font_slant.get(self.current).copied().unwrap_or_default()
315    }
316
317    /// Returns the font variation settings of the current view.
318    pub fn font_variation_settings(&self) -> &[FontVariation] {
319        self.style.font_variation_settings.get(self.current).map(Vec::as_slice).unwrap_or_default()
320    }
321
322    /// Function to convert logical points to physical pixels.
323    pub fn logical_to_physical(&self, logical: f32) -> f32 {
324        self.style.logical_to_physical(logical)
325    }
326
327    /// Function to convert physical pixels to logical points.
328    pub fn physical_to_logical(&self, physical: f32) -> f32 {
329        self.style.physical_to_logical(physical)
330    }
331
332    get_length_property!(
333        /// Returns the border width of the current view in physical pixels.
334        border_width
335    );
336
337    get_color_property!(
338        /// Returns the outline color of the current view.
339        outline_color
340    );
341
342    get_length_property!(
343        /// Returns the outline width of the current view in physical pixels.
344        outline_width
345    );
346
347    get_length_property!(
348        /// Returns the outline offset of the current view in physcial pixels.
349        outline_offset
350    );
351
352    get_length_property!(
353        /// Returns the corner radius for the top-left corner of the current view.
354        corner_top_left_radius
355    );
356
357    get_length_property!(
358        /// Returns the corner radius for the top-right corner of the current view.
359        corner_top_right_radius
360    );
361
362    get_length_property!(
363        /// Returns the corner radius for the bottom-left corner of the current view.
364        corner_bottom_left_radius
365    );
366
367    get_length_property!(
368        /// Returns the corner radius for the bottom-right corner of the current view.
369        corner_bottom_right_radius
370    );
371
372    /// Returns the corner shape for the top-left corner of the current view.
373    pub fn corner_top_left_shape(&self) -> CornerShape {
374        self.style.corner_top_left_shape.get(self.current).copied().unwrap_or_default()
375    }
376
377    /// Returns the corner shape for the top-left corner of the current view.
378    pub fn corner_top_right_shape(&self) -> CornerShape {
379        self.style.corner_top_right_shape.get(self.current).copied().unwrap_or_default()
380    }
381
382    /// Returns the corner shape for the top-left corner of the current view.
383    pub fn corner_bottom_left_shape(&self) -> CornerShape {
384        self.style.corner_bottom_left_shape.get(self.current).copied().unwrap_or_default()
385    }
386
387    /// Returns the corner shape for the top-left corner of the current view.
388    pub fn corner_bottom_right_shape(&self) -> CornerShape {
389        self.style.corner_bottom_right_shape.get(self.current).copied().unwrap_or_default()
390    }
391
392    /// Returns the corner smoothing for the top-left corner of the current view.
393    pub fn corner_top_left_smoothing(&self) -> f32 {
394        self.style.corner_top_left_smoothing.get(self.current).copied().unwrap_or_default()
395    }
396
397    /// Returns the corner shape for the top-left corner of the current view.
398    pub fn corner_top_right_smoothing(&self) -> f32 {
399        self.style.corner_top_right_smoothing.get(self.current).copied().unwrap_or_default()
400    }
401
402    /// Returns the corner shape for the top-left corner of the current view.
403    pub fn corner_bottom_left_smoothing(&self) -> f32 {
404        self.style.corner_bottom_left_smoothing.get(self.current).copied().unwrap_or_default()
405    }
406
407    /// Returns the corner shape for the top-left corner of the current view.
408    pub fn corner_bottom_right_smoothing(&self) -> f32 {
409        self.style.corner_bottom_right_smoothing.get(self.current).copied().unwrap_or_default()
410    }
411
412    get_units_property!(
413        /// Returns the padding-left space of the current view.
414        padding_left
415    );
416
417    get_units_property!(
418        /// Returns the padding-right space of the current view.
419        padding_right
420    );
421
422    get_units_property!(
423        /// Returns the padding-top space of the current view.
424        padding_top
425    );
426
427    get_units_property!(
428        /// Returns the padding-bottom space of the current view.
429        padding_bottom
430    );
431
432    /// Returns the alignment of the current view.
433    pub fn alignment(&self) -> Alignment {
434        self.style.alignment.get(self.current).copied().unwrap_or_default()
435    }
436
437    get_color_property!(
438        /// Returns the background color of the current view.
439        background_color
440    );
441
442    get_color_property!(
443        /// Returns the border color of the current view.
444        border_color
445    );
446
447    /// Returns the border style of the current view.
448    pub fn border_style(&self) -> BorderStyleKeyword {
449        self.style.border_style.get(self.current).copied().unwrap_or_default()
450    }
451
452    get_color_property!(
453        /// Returns the text selection color for the current view.
454        selection_color
455    );
456
457    get_color_property!(
458        /// Returns the text caret color for the current view.
459        caret_color
460    );
461
462    get_color_property!(
463        /// Returns the font color for the current view.
464        font_color
465    );
466
467    /// Returns whether the current view should have its text wrapped.
468    pub fn text_wrap(&self) -> bool {
469        self.style.text_wrap.get(self.current).copied().unwrap_or(true)
470    }
471
472    /// Returns the text alignment of the current view.
473    pub fn text_align(&self) -> TextAlign {
474        self.style.text_align.get(self.current).copied().unwrap_or_default()
475    }
476
477    /// Returns the text overflow preference of the current view.
478    pub fn text_overflow(&self) -> TextOverflow {
479        self.style.text_overflow.get(self.current).copied().unwrap_or_default()
480    }
481
482    /// Returns the line clamp Of the current view.
483    pub fn line_clamp(&self) -> Option<usize> {
484        self.style.line_clamp.get(self.current).copied().map(|lc| lc.0 as usize)
485    }
486
487    /// Returns a reference to any shadows of the current view.
488    pub fn shadows(&self) -> Option<&Vec<Shadow>> {
489        self.style.shadow.get(self.current)
490    }
491
492    /// Return to reference to any filter applied to the current view.
493    pub fn backdrop_filter(&self) -> Option<&Filter> {
494        self.style.backdrop_filter.get(self.current)
495    }
496
497    /// Returns a reference to any images of the current view.
498    pub fn background_images(&self) -> Option<&Vec<ImageOrGradient>> {
499        self.style.background_image.get(self.current)
500    }
501
502    ///  Returns a list of background sizes for the current view.
503    pub fn background_size(&self) -> Vec<BackgroundSize> {
504        self.style.background_size.get(self.current).cloned().unwrap_or_default()
505    }
506
507    pub fn path(&mut self) -> Path {
508        let border_width = self.border_width();
509        if self.cache.path.get(self.current).is_none() {
510            self.cache.path.insert(
511                self.current,
512                self.build_path(self.bounds(), (-border_width / 2.0, -border_width / 2.0)),
513            );
514        }
515        let bounds = self.bounds();
516        let mut path = self.cache.path.get(self.current).unwrap().clone();
517
518        path.offset(bounds.top_left());
519
520        path
521    }
522
523    /// Get the vector path of the current view.
524    pub fn build_path(&self, bounds: BoundingBox, outset: (f32, f32)) -> Path {
525        let corner_top_left_radius = self.corner_top_left_radius();
526        let corner_top_right_radius = self.corner_top_right_radius();
527        let corner_bottom_right_radius = self.corner_bottom_right_radius();
528        let corner_bottom_left_radius = self.corner_bottom_left_radius();
529
530        let corner_top_left_shape = self.corner_top_left_shape();
531        let corner_top_right_shape = self.corner_top_right_shape();
532        let corner_bottom_right_shape = self.corner_bottom_right_shape();
533        let corner_bottom_left_shape = self.corner_bottom_left_shape();
534
535        let corner_top_left_smoothing = self.corner_top_left_smoothing();
536        let corner_top_right_smoothing = self.corner_top_right_smoothing();
537        let corner_bottom_right_smoothing = self.corner_bottom_right_smoothing();
538        let corner_bottom_left_smoothing = self.corner_bottom_left_smoothing();
539
540        let bounds = BoundingBox::from_min_max(0.0, 0.0, bounds.w, bounds.h);
541
542        let rect: Rect = bounds.into();
543
544        let mut rr = RRect::new_rect_radii(
545            rect,
546            &[
547                Point::new(corner_top_left_radius, corner_top_left_radius),
548                Point::new(corner_top_right_radius, corner_top_right_radius),
549                Point::new(corner_bottom_right_radius, corner_bottom_right_radius),
550                Point::new(corner_bottom_left_radius, corner_bottom_left_radius),
551            ],
552        );
553
554        rr = rr.with_outset(outset);
555
556        let x = rr.bounds().x();
557        let y = rr.bounds().y();
558        let width = rr.width();
559        let height = rr.height();
560
561        //TODO: Cache the path and regenerate if the bounds change
562        let mut path = Path::new();
563
564        if width == height
565            && corner_bottom_left_radius == width / 2.0
566            && corner_bottom_right_radius == width / 2.0
567            && corner_top_left_radius == height / 2.0
568            && corner_top_right_radius == height / 2.0
569        {
570            path.add_circle((width / 2.0, bounds.h / 2.0), width / 2.0, PathDirection::CW);
571        } else if corner_top_left_radius == corner_top_right_radius
572            && corner_top_right_radius == corner_bottom_right_radius
573            && corner_bottom_right_radius == corner_bottom_left_radius
574            && corner_top_left_smoothing == 0.0
575            && corner_top_left_smoothing == corner_top_right_smoothing
576            && corner_top_right_smoothing == corner_bottom_right_smoothing
577            && corner_bottom_right_smoothing == corner_bottom_left_smoothing
578            && corner_top_left_shape == CornerShape::Round
579            && corner_top_left_shape == corner_top_right_shape
580            && corner_top_right_shape == corner_bottom_right_shape
581            && corner_bottom_right_shape == corner_bottom_left_shape
582        {
583            path.add_rrect(rr, None);
584        } else {
585            let top_right = rr.radii(Corner::UpperRight).x;
586
587            if top_right > 0.0 {
588                let (a, b, c, d, l, p, radius) = compute_smooth_corner(
589                    top_right,
590                    corner_top_right_smoothing,
591                    bounds.width(),
592                    bounds.height(),
593                );
594
595                path.move_to((f32::max(width / 2.0, width - p), 0.0));
596                if corner_top_right_shape == CornerShape::Round {
597                    path.cubic_to(
598                        (width - (p - a), 0.0),
599                        (width - (p - a - b), 0.0),
600                        (width - (p - a - b - c), d),
601                    )
602                    .r_arc_to_rotated(
603                        (radius, radius),
604                        0.0,
605                        ArcSize::Small,
606                        PathDirection::CW,
607                        (l, l),
608                    )
609                    .cubic_to(
610                        (width, p - a - b),
611                        (width, p - a),
612                        (width, f32::min(height / 2.0, p)),
613                    );
614                } else {
615                    path.line_to((width, f32::min(height / 2.0, p)));
616                }
617            } else {
618                path.move_to((width / 2.0, 0.0))
619                    .line_to((width, 0.0))
620                    .line_to((width, height / 2.0));
621            }
622
623            let bottom_right = rr.radii(Corner::LowerRight).x;
624            if bottom_right > 0.0 {
625                let (a, b, c, d, l, p, radius) = compute_smooth_corner(
626                    bottom_right,
627                    corner_bottom_right_smoothing,
628                    width,
629                    height,
630                );
631
632                path.line_to((width, f32::max(height / 2.0, height - p)));
633                if corner_bottom_right_shape == CornerShape::Round {
634                    path.cubic_to(
635                        (width, height - (p - a)),
636                        (width, height - (p - a - b)),
637                        (width - d, height - (p - a - b - c)),
638                    )
639                    .r_arc_to_rotated(
640                        (radius, radius),
641                        0.0,
642                        ArcSize::Small,
643                        PathDirection::CW,
644                        (-l, l),
645                    )
646                    .cubic_to(
647                        (width - (p - a - b), height),
648                        (width - (p - a), height),
649                        (f32::max(width / 2.0, width - p), height),
650                    );
651                } else {
652                    path.line_to((f32::max(width / 2.0, width - p), height));
653                }
654            } else {
655                path.line_to((width, height)).line_to((width / 2.0, height));
656            }
657
658            let bottom_left = rr.radii(Corner::LowerLeft).x;
659            if bottom_left > 0.0 {
660                let (a, b, c, d, l, p, radius) =
661                    compute_smooth_corner(bottom_left, corner_bottom_left_smoothing, width, height);
662
663                path.line_to((f32::min(width / 2.0, p), height));
664                if corner_bottom_left_shape == CornerShape::Round {
665                    path.cubic_to(
666                        (p - a, height),
667                        (p - a - b, height),
668                        (p - a - b - c, height - d),
669                    )
670                    .r_arc_to_rotated(
671                        (radius, radius),
672                        0.0,
673                        ArcSize::Small,
674                        PathDirection::CW,
675                        (-l, -l),
676                    )
677                    .cubic_to(
678                        (0.0, height - (p - a - b)),
679                        (0.0, height - (p - a)),
680                        (0.0, f32::max(height / 2.0, height - p)),
681                    );
682                } else {
683                    path.line_to((0.0, f32::max(height / 2.0, height - p)));
684                }
685            } else {
686                path.line_to((0.0, height)).line_to((0.0, height / 2.0));
687            }
688
689            let top_left = rr.radii(Corner::UpperLeft).x;
690            if top_left > 0.0 {
691                let (a, b, c, d, l, p, radius) =
692                    compute_smooth_corner(top_left, corner_top_left_smoothing, width, height);
693
694                path.line_to((0.0, f32::min(height / 2.0, p)));
695                if corner_top_left_shape == CornerShape::Round {
696                    path.cubic_to((0.0, p - a), (0.0, p - a - b), (d, p - a - b - c))
697                        .r_arc_to_rotated(
698                            (radius, radius),
699                            0.0,
700                            ArcSize::Small,
701                            PathDirection::CW,
702                            (l, -l),
703                        )
704                        .cubic_to((p - a - b, 0.0), (p - a, 0.0), (f32::min(width / 2.0, p), 0.0));
705                } else {
706                    path.line_to((f32::min(width / 2.0, p), 0.0));
707                }
708            } else {
709                path.line_to((0.0, 0.0));
710            }
711
712            path.close();
713
714            path.offset((x, y));
715        }
716
717        path
718    }
719
720    /// Draw background color or background image (including gradients) for the current view.
721    pub fn draw_background(&mut self, canvas: &Canvas) {
722        let background_color = self.background_color();
723        if background_color.a() > 0 {
724            let path = self.path();
725
726            let mut paint = Paint::default();
727            paint.set_color(skia_safe::Color::from_argb(
728                background_color.a(),
729                background_color.r(),
730                background_color.g(),
731                background_color.b(),
732            ));
733            paint.set_anti_alias(true);
734            canvas.draw_path(&path, &paint);
735        }
736
737        self.draw_background_images(canvas);
738    }
739
740    /// Draw the border of the current view.
741    pub fn draw_border(&mut self, canvas: &Canvas) {
742        let border_color = self.border_color();
743        let border_width = self.border_width();
744        let border_style = self.border_style();
745
746        if border_width > 0.0 && border_color.a() > 0 && border_style != BorderStyleKeyword::None {
747            let path = self.path();
748            let mut paint = Paint::default();
749            paint.set_style(PaintStyle::Stroke);
750            paint.set_color(border_color);
751            paint.set_stroke_width(border_width);
752            match border_style {
753                BorderStyleKeyword::Dashed => {
754                    paint.set_path_effect(PathEffect::dash(
755                        &[border_width * 2.0, border_width],
756                        0.0,
757                    ));
758                }
759
760                BorderStyleKeyword::Dotted => {
761                    paint.set_path_effect(PathEffect::dash(&[0.0, border_width * 2.0], 0.0));
762                    paint.set_stroke_cap(skia_safe::PaintCap::Round);
763                }
764
765                _ => {}
766            }
767
768            paint.set_anti_alias(true);
769            canvas.draw_path(&path, &paint);
770        }
771    }
772
773    /// Draw the outline of the current view.
774    pub fn draw_outline(&mut self, canvas: &Canvas) {
775        let outline_width = self.outline_width();
776        let outline_color = self.outline_color();
777
778        if outline_width > 0.0 && outline_color.a() != 0 {
779            let outline_offset = self.outline_offset();
780
781            let bounds = self.bounds();
782
783            let half_outline_width = outline_width / 2.0;
784            let mut outline_path = self.build_path(
785                bounds,
786                (half_outline_width + outline_offset, half_outline_width + outline_offset),
787            );
788
789            outline_path.offset(self.bounds().top_left());
790
791            let mut outline_paint = Paint::default();
792            outline_paint.set_color(outline_color);
793            outline_paint.set_stroke_width(outline_width);
794            outline_paint.set_style(PaintStyle::Stroke);
795            outline_paint.set_anti_alias(true);
796            canvas.draw_path(&outline_path, &outline_paint);
797        }
798    }
799
800    /// Draw shadows for the current view.
801    pub fn draw_shadows(&mut self, canvas: &Canvas) {
802        if let Some(shadows) = self.shadows() {
803            if shadows.is_empty() {
804                return;
805            }
806
807            let bounds = self.bounds();
808
809            let mut path = self.build_path(bounds, (0.0, 0.0));
810
811            path.offset(bounds.top_left());
812
813            for shadow in shadows.iter().rev() {
814                let shadow_color = shadow.color.unwrap_or_default();
815
816                let shadow_x_offset = shadow.x_offset.to_px().unwrap_or(0.0) * self.scale_factor();
817                let shadow_y_offset = shadow.y_offset.to_px().unwrap_or(0.0) * self.scale_factor();
818                let spread_radius =
819                    shadow.spread_radius.as_ref().and_then(|l| l.to_px()).unwrap_or(0.0)
820                        * self.scale_factor();
821
822                let blur_radius =
823                    shadow.blur_radius.as_ref().and_then(|br| br.to_px()).unwrap_or(0.0);
824
825                if shadow_color.a() == 0
826                    || (shadow_x_offset == 0.0
827                        && shadow_y_offset == 0.0
828                        && spread_radius == 0.0
829                        && blur_radius == 0.0)
830                {
831                    continue;
832                }
833
834                let mut shadow_paint = Paint::default();
835
836                let outset = if shadow.inset { -spread_radius } else { spread_radius };
837
838                shadow_paint.set_style(PaintStyle::Fill);
839
840                let mut shadow_path = self.build_path(bounds, (outset, outset));
841                shadow_path.offset(bounds.top_left());
842
843                shadow_paint.set_color(shadow_color);
844
845                if blur_radius > 0.0 {
846                    shadow_paint.set_mask_filter(MaskFilter::blur(
847                        BlurStyle::Normal,
848                        blur_radius / 2.0,
849                        false,
850                    ));
851                }
852
853                shadow_path.offset((shadow_x_offset, shadow_y_offset));
854
855                if shadow.inset {
856                    shadow_path = path.op(&shadow_path, skia_safe::PathOp::Difference).unwrap();
857                }
858
859                canvas.save();
860                canvas.clip_path(
861                    &path,
862                    if shadow.inset { ClipOp::Intersect } else { ClipOp::Difference },
863                    true,
864                );
865                canvas.draw_path(&shadow_path, &shadow_paint);
866                canvas.restore();
867            }
868        }
869    }
870
871    /// Draw background images (including gradients) for the current view.
872    fn draw_background_images(&mut self, canvas: &Canvas) {
873        let bounds = self.bounds();
874
875        if self.background_images().is_some() {
876            let path = self.path();
877            if let Some(images) = self.background_images() {
878                let image_sizes = self.background_size();
879
880                for (index, image) in images.iter().enumerate() {
881                    match image {
882                        ImageOrGradient::Gradient(gradient) => match gradient {
883                            Gradient::Linear(linear_gradient) => {
884                                let (start, end, parent_length) = match linear_gradient.direction {
885                                    LineDirection::Horizontal(horizontal_keyword) => {
886                                        match horizontal_keyword {
887                                            HorizontalPositionKeyword::Left => (
888                                                bounds.center_right(),
889                                                bounds.center_left(),
890                                                bounds.width(),
891                                            ),
892
893                                            HorizontalPositionKeyword::Right => (
894                                                bounds.center_left(),
895                                                bounds.center_right(),
896                                                bounds.width(),
897                                            ),
898                                        }
899                                    }
900
901                                    LineDirection::Vertical(vertical_keyword) => {
902                                        match vertical_keyword {
903                                            VerticalPositionKeyword::Top => (
904                                                bounds.center_bottom(),
905                                                bounds.center_top(),
906                                                bounds.height(),
907                                            ),
908
909                                            VerticalPositionKeyword::Bottom => (
910                                                bounds.center_top(),
911                                                bounds.center_bottom(),
912                                                bounds.height(),
913                                            ),
914                                        }
915                                    }
916
917                                    LineDirection::Corner { horizontal, vertical } => {
918                                        match (horizontal, vertical) {
919                                            (
920                                                HorizontalPositionKeyword::Right,
921                                                VerticalPositionKeyword::Bottom,
922                                            ) => (
923                                                bounds.top_left(),
924                                                bounds.bottom_right(),
925                                                bounds.diagonal(),
926                                            ),
927
928                                            (
929                                                HorizontalPositionKeyword::Right,
930                                                VerticalPositionKeyword::Top,
931                                            ) => (
932                                                bounds.bottom_left(),
933                                                bounds.top_right(),
934                                                bounds.diagonal(),
935                                            ),
936
937                                            _ => (bounds.top_left(), bounds.bottom_right(), 0.0),
938                                        }
939                                    }
940
941                                    LineDirection::Angle(angle) => {
942                                        let angle_rad = angle.to_radians();
943                                        let start_x = bounds.x
944                                            + ((angle_rad.sin() * bounds.w) - bounds.w) / -2.0;
945                                        let end_x = bounds.x
946                                            + ((angle_rad.sin() * bounds.w) + bounds.w) / 2.0;
947                                        let start_y = bounds.y
948                                            + ((angle_rad.cos() * bounds.h) + bounds.h) / 2.0;
949                                        let end_y = bounds.y
950                                            + ((angle_rad.cos() * bounds.h) - bounds.h) / -2.0;
951
952                                        let x = (end_x - start_x).abs();
953                                        let y = (end_y - start_y).abs();
954
955                                        let dist = (x * x + y * y).sqrt();
956
957                                        ((start_x, start_y), (end_x, end_y), dist)
958                                    }
959                                };
960
961                                let num_stops = linear_gradient.stops.len();
962
963                                let mut stops = linear_gradient
964                                    .stops
965                                    .iter()
966                                    .enumerate()
967                                    .map(|(index, stop)| {
968                                        let pos = if let Some(pos) = &stop.position {
969                                            pos.to_pixels(parent_length, self.scale_factor())
970                                                / parent_length
971                                        } else {
972                                            index as f32 / (num_stops - 1) as f32
973                                        };
974                                        (pos, skia_safe::Color::from(stop.color))
975                                    })
976                                    .collect::<Vec<_>>();
977
978                                // Insert a stop at the front if the first stop is not at 0.
979                                if let Some(first) = stops.first() {
980                                    if first.0 != 0.0 {
981                                        stops.insert(0, (0.0, first.1));
982                                    }
983                                }
984
985                                // Insert a stop at the end if the last stop is not at 1.0.
986                                if let Some(last) = stops.last() {
987                                    if last.0 != 1.0 {
988                                        stops.push((1.0, last.1));
989                                    }
990                                }
991
992                                let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
993                                    stops.into_iter().unzip();
994
995                                let shader = Shader::linear_gradient(
996                                    (Point::from(start), Point::from(end)),
997                                    GradientShaderColors::Colors(&colors[..]),
998                                    Some(&offsets[..]),
999                                    TileMode::Clamp,
1000                                    None,
1001                                    None,
1002                                );
1003
1004                                let mut paint = Paint::default();
1005                                paint.set_shader(shader);
1006
1007                                canvas.draw_path(&path, &paint);
1008                            }
1009
1010                            Gradient::Radial(radial_gradient) => {
1011                                let num_stops = radial_gradient.stops.len();
1012
1013                                let mut stops = radial_gradient
1014                                    .stops
1015                                    .iter()
1016                                    .enumerate()
1017                                    .map(|(index, stop)| {
1018                                        let pos = if let Some(pos) = &stop.position {
1019                                            pos.to_pixels(bounds.width(), self.scale_factor())
1020                                                / bounds.width()
1021                                        } else {
1022                                            index as f32 / (num_stops - 1) as f32
1023                                        };
1024
1025                                        (pos, skia_safe::Color::from(stop.color))
1026                                    })
1027                                    .collect::<Vec<_>>();
1028
1029                                // Insert a stop at the front if the first stop is not at 0.
1030                                if let Some(first) = stops.first() {
1031                                    if first.0 != 0.0 {
1032                                        stops.insert(0, (0.0, first.1));
1033                                    }
1034                                }
1035
1036                                // Insert a stop at the end if the last stop is not at 1.0.
1037                                if let Some(last) = stops.last() {
1038                                    if last.0 != 1.0 {
1039                                        stops.push((1.0, last.1));
1040                                    }
1041                                }
1042
1043                                let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
1044                                    stops.into_iter().unzip();
1045
1046                                let shader = Shader::radial_gradient(
1047                                    Point::from(bounds.center()),
1048                                    bounds.w.max(bounds.h),
1049                                    GradientShaderColors::Colors(&colors[..]),
1050                                    Some(&offsets[..]),
1051                                    TileMode::Clamp,
1052                                    None,
1053                                    None,
1054                                );
1055
1056                                let mut paint = Paint::default();
1057                                paint.set_shader(shader);
1058                                canvas.draw_path(&path, &paint);
1059                            }
1060
1061                            _ => {}
1062                        },
1063
1064                        ImageOrGradient::Image(image_name) => {
1065                            if let Some(image_id) = self.resource_manager.image_ids.get(image_name)
1066                            {
1067                                if let Some(image) = self.resource_manager.images.get(image_id) {
1068                                    match &image.image {
1069                                        ImageOrSvg::Image(image) => {
1070                                            let image_width = image.width();
1071                                            let image_height = image.height();
1072                                            let (width, height) = if let Some(background_size) =
1073                                                image_sizes.get(index)
1074                                            {
1075                                                match background_size {
1076                                                    BackgroundSize::Explicit { width, height } => {
1077                                                        let w = match width {
1078                                                LengthPercentageOrAuto::LengthPercentage(
1079                                                    length,
1080                                                ) => {
1081                                                    length.to_pixels(bounds.w, self.scale_factor())
1082                                                }
1083                                                LengthPercentageOrAuto::Auto => image_width as f32,
1084                                            };
1085
1086                                                        let h = match height {
1087                                                LengthPercentageOrAuto::LengthPercentage(
1088                                                    length,
1089                                                ) => {
1090                                                    length.to_pixels(bounds.h, self.scale_factor())
1091                                                }
1092                                                LengthPercentageOrAuto::Auto => image_height as f32,
1093                                            };
1094
1095                                                        (w, h)
1096                                                    }
1097
1098                                                    BackgroundSize::Contain => {
1099                                                        let image_ratio = image_width as f32
1100                                                            / image_height as f32;
1101                                                        let container_ratio = bounds.w / bounds.h;
1102
1103                                                        let (w, h) =
1104                                                            if image_ratio > container_ratio {
1105                                                                (bounds.w, bounds.w / image_ratio)
1106                                                            } else {
1107                                                                (bounds.h * image_ratio, bounds.h)
1108                                                            };
1109
1110                                                        (w, h)
1111                                                    }
1112
1113                                                    BackgroundSize::Cover => {
1114                                                        let image_ratio = image_width as f32
1115                                                            / image_height as f32;
1116                                                        let container_ratio = bounds.w / bounds.h;
1117
1118                                                        let (w, h) =
1119                                                            if image_ratio < container_ratio {
1120                                                                (bounds.w, bounds.w / image_ratio)
1121                                                            } else {
1122                                                                (bounds.h * image_ratio, bounds.h)
1123                                                            };
1124
1125                                                        (w, h)
1126                                                    }
1127                                                }
1128                                            } else {
1129                                                (image_width as f32, image_height as f32)
1130                                            };
1131
1132                                            let matrix = Matrix::rect_to_rect(
1133                                                Rect::new(
1134                                                    0.0,
1135                                                    0.0,
1136                                                    image.width() as f32,
1137                                                    image.height() as f32,
1138                                                ),
1139                                                Rect::new(
1140                                                    bounds.left(),
1141                                                    bounds.top(),
1142                                                    bounds.left() + width,
1143                                                    bounds.top() + height,
1144                                                ),
1145                                                None,
1146                                            );
1147
1148                                            let mut paint = Paint::default();
1149                                            paint.set_anti_alias(true);
1150                                            paint.set_shader(image.to_shader(
1151                                                (TileMode::Repeat, TileMode::Repeat),
1152                                                SamplingOptions::default(),
1153                                                &matrix,
1154                                            ));
1155
1156                                            canvas.draw_path(&path, &paint);
1157                                        }
1158
1159                                        ImageOrSvg::Svg(svg) => {
1160                                            canvas.save_layer(&SaveLayerRec::default());
1161                                            canvas.translate((bounds.x, bounds.y));
1162                                            let (scale_x, scale_y) = (
1163                                                bounds.width() / svg.inner().fContainerSize.fWidth,
1164                                                bounds.height()
1165                                                    / svg.inner().fContainerSize.fHeight,
1166                                            );
1167
1168                                            if scale_x.is_finite() && scale_y.is_finite() {
1169                                                canvas.scale((scale_x, scale_y));
1170                                            } else {
1171                                                svg.clone().set_container_size((
1172                                                    bounds.width(),
1173                                                    bounds.height(),
1174                                                ));
1175                                            }
1176
1177                                            svg.render(canvas);
1178
1179                                            if let Some(color) =
1180                                                self.style.fill.get(self.current).copied()
1181                                            {
1182                                                let mut paint = Paint::default();
1183
1184                                                paint.set_anti_alias(true);
1185                                                paint.set_blend_mode(skia_safe::BlendMode::SrcIn);
1186                                                paint.set_color(color);
1187                                                canvas.draw_paint(&paint);
1188                                            }
1189                                            canvas.restore();
1190                                        }
1191                                    }
1192                                }
1193                            }
1194                        }
1195                    }
1196                }
1197            }
1198        }
1199    }
1200
1201    /// Draw any text for the current view.
1202    pub fn draw_text(&mut self, canvas: &Canvas) {
1203        if let Some(paragraph) = self.text_context.text_paragraphs.get(self.current) {
1204            let bounds = self.bounds();
1205
1206            let alignment = self.alignment();
1207
1208            let (mut top, _) = match alignment {
1209                Alignment::TopLeft => (0.0, 0.0),
1210                Alignment::TopCenter => (0.0, 0.5),
1211                Alignment::TopRight => (0.0, 1.0),
1212                Alignment::Left => (0.5, 0.0),
1213                Alignment::Center => (0.5, 0.5),
1214                Alignment::Right => (0.5, 1.0),
1215                Alignment::BottomLeft => (1.0, 0.0),
1216                Alignment::BottomCenter => (1.0, 0.5),
1217                Alignment::BottomRight => (1.0, 1.0),
1218            };
1219
1220            let padding_top = match self.padding_top() {
1221                Units::Pixels(val) => val,
1222                _ => 0.0,
1223            };
1224
1225            let padding_bottom = match self.padding_bottom() {
1226                Units::Pixels(val) => val,
1227                _ => 0.0,
1228            };
1229
1230            top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
1231
1232            let padding_left = match self.padding_left() {
1233                Units::Pixels(val) => val,
1234                _ => 0.0,
1235            };
1236
1237            paragraph.paint(
1238                canvas,
1239                ((bounds.x + padding_left).round(), (bounds.y + padding_top + top).round()),
1240            );
1241        }
1242    }
1243}
1244
1245impl DataContext for DrawContext<'_> {
1246    fn data<T: 'static>(&self) -> Option<&T> {
1247        // Return data for the static model.
1248        if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1249            return Some(t);
1250        }
1251
1252        for entity in self.current.parent_iter(self.tree) {
1253            // Return model data.
1254            if let Some(models) = self.models.get(&entity) {
1255                if let Some(model) = models.get(&TypeId::of::<T>()) {
1256                    return model.downcast_ref::<T>();
1257                }
1258            }
1259
1260            // Return view data.
1261            if let Some(view_handler) = self.views.get(&entity) {
1262                if let Some(data) = view_handler.downcast_ref::<T>() {
1263                    return Some(data);
1264                }
1265            }
1266        }
1267
1268        None
1269    }
1270}
1271
1272// Helper function for computing a rounded corner with variable smoothing
1273fn compute_smooth_corner(
1274    corner_radius: f32,
1275    smoothing: f32,
1276    width: f32,
1277    height: f32,
1278) -> (f32, f32, f32, f32, f32, f32, f32) {
1279    let max_p = f32::min(width, height) / 2.0;
1280    let corner_radius = f32::min(corner_radius, max_p);
1281
1282    let p = f32::min((1.0 + smoothing) * corner_radius, max_p);
1283
1284    let angle_alpha: f32;
1285    let angle_beta: f32;
1286
1287    if corner_radius <= max_p / 2.0 {
1288        angle_alpha = 45.0 * smoothing;
1289        angle_beta = 90.0 * (1.0 - smoothing);
1290    } else {
1291        let diff_ratio = (corner_radius - max_p / 2.0) / (max_p / 2.0);
1292
1293        angle_alpha = 45.0 * smoothing * (1.0 - diff_ratio);
1294        angle_beta = 90.0 * (1.0 - smoothing * (1.0 - diff_ratio));
1295    }
1296
1297    let angle_theta = (90.0 - angle_beta) / 2.0;
1298    let dist_p3_p4 = corner_radius * (angle_theta / 2.0).to_radians().tan();
1299
1300    let l = (angle_beta / 2.0).to_radians().sin() * corner_radius * SQRT_2;
1301    let c = dist_p3_p4 * angle_alpha.to_radians().cos();
1302    let d = c * angle_alpha.to_radians().tan();
1303    let b = (p - l - c - d) / 3.0;
1304    let a = 2.0 * b;
1305
1306    (a, b, c, d, l, p, corner_radius)
1307}