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
24pub 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 pub fn bounds(&self) -> BoundingBox {
98 self.cache.get_bounds(self.current)
99 }
100
101 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 pub fn z_index(&self) -> i32 {
111 self.style.z_index.get(self.current).copied().unwrap_or_default()
112 }
113
114 pub fn scale_factor(&self) -> f32 {
116 self.style.dpi_factor as f32
117 }
118
119 pub fn modifiers(&self) -> &Modifiers {
121 self.modifiers
122 }
123
124 pub fn mouse(&self) -> &MouseState<Entity> {
126 self.mouse
127 }
128
129 pub fn clip_path(&self) -> Option<skia_safe::Path> {
131 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 let mut current = self.current;
142 while let Some(parent) = self.tree.get_parent(current) {
143 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 pub fn transform(&self) -> Matrix {
159 self.cache.transform.get(self.current).copied().unwrap_or_default()
160 }
161
162 pub fn visibility(&self) -> Option<Visibility> {
164 self.style.visibility.get(self.current).copied()
165 }
166
167 pub fn display(&self) -> Display {
169 self.style.display.get(self.current).copied().unwrap_or(Display::Flex)
170 }
171
172 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 pub fn default_font(&self) -> &[FamilyOwned] {
183 &self.style.default_font
184 }
185
186 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 pub fn font_weight(&self) -> FontWeight {
199 self.style.font_weight.get(self.current).copied().unwrap_or_default()
200 }
201
202 pub fn font_width(&self) -> FontWidth {
204 self.style.font_width.get(self.current).copied().unwrap_or_default()
205 }
206
207 pub fn font_slant(&self) -> FontSlant {
209 self.style.font_slant.get(self.current).copied().unwrap_or_default()
210 }
211
212 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 pub fn logical_to_physical(&self, logical: f32) -> f32 {
219 self.style.logical_to_physical(logical)
220 }
221
222 pub fn physical_to_logical(&self, physical: f32) -> f32 {
224 self.style.physical_to_logical(physical)
225 }
226
227 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 padding_left
358 );
359
360 get_units_property!(
361 padding_right
363 );
364
365 get_units_property!(
366 padding_top
368 );
369
370 get_units_property!(
371 padding_bottom
373 );
374
375 pub fn alignment(&self) -> Alignment {
377 self.style.alignment.get(self.current).copied().unwrap_or_default()
378 }
379
380 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 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 pub fn border_style(&self) -> BorderStyleKeyword {
404 self.style.border_style.get(self.current).copied().unwrap_or_default()
405 }
406
407 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 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 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 pub fn text_wrap(&self) -> bool {
442 self.style.text_wrap.get(self.current).copied().unwrap_or(true)
443 }
444
445 pub fn text_align(&self) -> TextAlign {
447 self.style.text_align.get(self.current).copied().unwrap_or_default()
448 }
449
450 pub fn text_overflow(&self) -> TextOverflow {
452 self.style.text_overflow.get(self.current).copied().unwrap_or_default()
453 }
454
455 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 pub fn shadows(&self) -> Option<Vec<Shadow>> {
462 self.style.shadow.get_resolved(self.current, &self.style.custom_shadow_props)
463 }
464
465 pub fn filter(&self) -> Option<&Filter> {
467 self.style.filter.get(self.current)
468 }
469
470 pub fn backdrop_filter(&self) -> Option<&Filter> {
472 self.style.backdrop_filter.get(self.current)
473 }
474
475 pub fn background_images(&self) -> Option<&Vec<ImageOrGradient>> {
477 self.style.background_image.get(self.current)
478 }
479
480 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 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 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 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 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 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 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 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 if let Some(first) = stops.first() {
924 if first.0 != 0.0 {
925 stops.insert(0, (0.0, first.1));
926 }
927 }
928
929 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 if let Some(first) = stops.first() {
986 if first.0 != 0.0 {
987 stops.insert(0, (0.0, first.1));
988 }
989 }
990
991 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 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 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 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 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 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
1264fn 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}