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