Skip to main content

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