vizia_core/context/
draw.rs

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