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