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.font_size.get(self.current).copied().map(|f| f.0).unwrap_or(16.0),
294 )
295 }
296
297 pub fn font_weight(&self) -> FontWeight {
299 self.style.font_weight.get(self.current).copied().unwrap_or_default()
300 }
301
302 pub fn font_width(&self) -> FontWidth {
304 self.style.font_width.get(self.current).copied().unwrap_or_default()
305 }
306
307 pub fn font_slant(&self) -> FontSlant {
309 self.style.font_slant.get(self.current).copied().unwrap_or_default()
310 }
311
312 pub fn font_variation_settings(&self) -> &[FontVariation] {
314 self.style.font_variation_settings.get(self.current).map(Vec::as_slice).unwrap_or_default()
315 }
316
317 pub fn logical_to_physical(&self, logical: f32) -> f32 {
319 self.style.logical_to_physical(logical)
320 }
321
322 pub fn physical_to_logical(&self, physical: f32) -> f32 {
324 self.style.physical_to_logical(physical)
325 }
326
327 get_length_property!(
328 border_width
330 );
331
332 get_color_property!(
333 outline_color
335 );
336
337 get_length_property!(
338 outline_width
340 );
341
342 get_length_property!(
343 outline_offset
345 );
346
347 get_length_property!(
348 corner_top_left_radius
350 );
351
352 get_length_property!(
353 corner_top_right_radius
355 );
356
357 get_length_property!(
358 corner_bottom_left_radius
360 );
361
362 get_length_property!(
363 corner_bottom_right_radius
365 );
366
367 pub fn corner_top_left_shape(&self) -> CornerShape {
369 self.style.corner_top_left_shape.get(self.current).copied().unwrap_or_default()
370 }
371
372 pub fn corner_top_right_shape(&self) -> CornerShape {
374 self.style.corner_top_right_shape.get(self.current).copied().unwrap_or_default()
375 }
376
377 pub fn corner_bottom_left_shape(&self) -> CornerShape {
379 self.style.corner_bottom_left_shape.get(self.current).copied().unwrap_or_default()
380 }
381
382 pub fn corner_bottom_right_shape(&self) -> CornerShape {
384 self.style.corner_bottom_right_shape.get(self.current).copied().unwrap_or_default()
385 }
386
387 pub fn corner_top_left_smoothing(&self) -> f32 {
389 self.style.corner_top_left_smoothing.get(self.current).copied().unwrap_or_default()
390 }
391
392 pub fn corner_top_right_smoothing(&self) -> f32 {
394 self.style.corner_top_right_smoothing.get(self.current).copied().unwrap_or_default()
395 }
396
397 pub fn corner_bottom_left_smoothing(&self) -> f32 {
399 self.style.corner_bottom_left_smoothing.get(self.current).copied().unwrap_or_default()
400 }
401
402 pub fn corner_bottom_right_smoothing(&self) -> f32 {
404 self.style.corner_bottom_right_smoothing.get(self.current).copied().unwrap_or_default()
405 }
406
407 get_units_property!(
408 padding_left
410 );
411
412 get_units_property!(
413 padding_right
415 );
416
417 get_units_property!(
418 padding_top
420 );
421
422 get_units_property!(
423 padding_bottom
425 );
426
427 pub fn alignment(&self) -> Alignment {
429 self.style.alignment.get(self.current).copied().unwrap_or_default()
430 }
431
432 get_color_property!(
433 background_color
435 );
436
437 get_color_property!(
438 border_color
440 );
441
442 pub fn border_style(&self) -> BorderStyleKeyword {
444 self.style.border_style.get(self.current).copied().unwrap_or_default()
445 }
446
447 get_color_property!(
448 selection_color
450 );
451
452 get_color_property!(
453 caret_color
455 );
456
457 get_color_property!(
458 font_color
460 );
461
462 pub fn text_wrap(&self) -> bool {
464 self.style.text_wrap.get(self.current).copied().unwrap_or(true)
465 }
466
467 pub fn text_align(&self) -> TextAlign {
469 self.style.text_align.get(self.current).copied().unwrap_or_default()
470 }
471
472 pub fn text_overflow(&self) -> TextOverflow {
474 self.style.text_overflow.get(self.current).copied().unwrap_or_default()
475 }
476
477 pub fn line_clamp(&self) -> Option<usize> {
479 self.style.line_clamp.get(self.current).copied().map(|lc| lc.0 as usize)
480 }
481
482 pub fn shadows(&self) -> Option<&Vec<Shadow>> {
484 self.style.shadow.get(self.current)
485 }
486
487 pub fn backdrop_filter(&self) -> Option<&Filter> {
489 self.style.backdrop_filter.get(self.current)
490 }
491
492 pub fn background_images(&self) -> Option<&Vec<ImageOrGradient>> {
494 self.style.background_image.get(self.current)
495 }
496
497 pub fn background_size(&self) -> Vec<BackgroundSize> {
499 self.style.background_size.get(self.current).cloned().unwrap_or_default()
500 }
501
502 pub fn path(&mut self) -> Path {
503 let border_width = self.border_width();
504 if self.cache.path.get(self.current).is_none() {
505 self.cache.path.insert(
506 self.current,
507 self.build_path(self.bounds(), (-border_width / 2.0, -border_width / 2.0)),
508 );
509 }
510 let bounds = self.bounds();
511 let mut path = self.cache.path.get(self.current).unwrap().clone();
512
513 path.offset(bounds.top_left());
514
515 path
516 }
517
518 pub fn build_path(&self, bounds: BoundingBox, outset: (f32, f32)) -> Path {
520 let corner_top_left_radius = self.corner_top_left_radius();
521 let corner_top_right_radius = self.corner_top_right_radius();
522 let corner_bottom_right_radius = self.corner_bottom_right_radius();
523 let corner_bottom_left_radius = self.corner_bottom_left_radius();
524
525 let corner_top_left_shape = self.corner_top_left_shape();
526 let corner_top_right_shape = self.corner_top_right_shape();
527 let corner_bottom_right_shape = self.corner_bottom_right_shape();
528 let corner_bottom_left_shape = self.corner_bottom_left_shape();
529
530 let corner_top_left_smoothing = self.corner_top_left_smoothing();
531 let corner_top_right_smoothing = self.corner_top_right_smoothing();
532 let corner_bottom_right_smoothing = self.corner_bottom_right_smoothing();
533 let corner_bottom_left_smoothing = self.corner_bottom_left_smoothing();
534
535 let bounds = BoundingBox::from_min_max(0.0, 0.0, bounds.w, bounds.h);
536
537 let rect: Rect = bounds.into();
538
539 let mut rr = RRect::new_rect_radii(
540 rect,
541 &[
542 Point::new(corner_top_left_radius, corner_top_left_radius),
543 Point::new(corner_top_right_radius, corner_top_right_radius),
544 Point::new(corner_bottom_right_radius, corner_bottom_right_radius),
545 Point::new(corner_bottom_left_radius, corner_bottom_left_radius),
546 ],
547 );
548
549 rr = rr.with_outset(outset);
550
551 let x = rr.bounds().x();
552 let y = rr.bounds().y();
553 let width = rr.width();
554 let height = rr.height();
555
556 let mut path = Path::new();
558
559 if width == height
560 && corner_bottom_left_radius == width / 2.0
561 && corner_bottom_right_radius == width / 2.0
562 && corner_top_left_radius == height / 2.0
563 && corner_top_right_radius == height / 2.0
564 {
565 path.add_circle((width / 2.0, bounds.h / 2.0), width / 2.0, PathDirection::CW);
566 } else if corner_top_left_radius == corner_top_right_radius
567 && corner_top_right_radius == corner_bottom_right_radius
568 && corner_bottom_right_radius == corner_bottom_left_radius
569 && corner_top_left_smoothing == 0.0
570 && corner_top_left_smoothing == corner_top_right_smoothing
571 && corner_top_right_smoothing == corner_bottom_right_smoothing
572 && corner_bottom_right_smoothing == corner_bottom_left_smoothing
573 && corner_top_left_shape == CornerShape::Round
574 && corner_top_left_shape == corner_top_right_shape
575 && corner_top_right_shape == corner_bottom_right_shape
576 && corner_bottom_right_shape == corner_bottom_left_shape
577 {
578 path.add_rrect(rr, None);
579 } else {
580 let top_right = rr.radii(Corner::UpperRight).x;
581
582 if top_right > 0.0 {
583 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
584 top_right,
585 corner_top_right_smoothing,
586 bounds.width(),
587 bounds.height(),
588 );
589
590 path.move_to((f32::max(width / 2.0, width - p), 0.0));
591 if corner_top_right_shape == CornerShape::Round {
592 path.cubic_to(
593 (width - (p - a), 0.0),
594 (width - (p - a - b), 0.0),
595 (width - (p - a - b - c), d),
596 )
597 .r_arc_to_rotated(
598 (radius, radius),
599 0.0,
600 ArcSize::Small,
601 PathDirection::CW,
602 (l, l),
603 )
604 .cubic_to(
605 (width, p - a - b),
606 (width, p - a),
607 (width, f32::min(height / 2.0, p)),
608 );
609 } else {
610 path.line_to((width, f32::min(height / 2.0, p)));
611 }
612 } else {
613 path.move_to((width / 2.0, 0.0))
614 .line_to((width, 0.0))
615 .line_to((width, height / 2.0));
616 }
617
618 let bottom_right = rr.radii(Corner::LowerRight).x;
619 if bottom_right > 0.0 {
620 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
621 bottom_right,
622 corner_bottom_right_smoothing,
623 width,
624 height,
625 );
626
627 path.line_to((width, f32::max(height / 2.0, height - p)));
628 if corner_bottom_right_shape == CornerShape::Round {
629 path.cubic_to(
630 (width, height - (p - a)),
631 (width, height - (p - a - b)),
632 (width - d, height - (p - a - b - c)),
633 )
634 .r_arc_to_rotated(
635 (radius, radius),
636 0.0,
637 ArcSize::Small,
638 PathDirection::CW,
639 (-l, l),
640 )
641 .cubic_to(
642 (width - (p - a - b), height),
643 (width - (p - a), height),
644 (f32::max(width / 2.0, width - p), height),
645 );
646 } else {
647 path.line_to((f32::max(width / 2.0, width - p), height));
648 }
649 } else {
650 path.line_to((width, height)).line_to((width / 2.0, height));
651 }
652
653 let bottom_left = rr.radii(Corner::LowerLeft).x;
654 if bottom_left > 0.0 {
655 let (a, b, c, d, l, p, radius) =
656 compute_smooth_corner(bottom_left, corner_bottom_left_smoothing, width, height);
657
658 path.line_to((f32::min(width / 2.0, p), height));
659 if corner_bottom_left_shape == CornerShape::Round {
660 path.cubic_to(
661 (p - a, height),
662 (p - a - b, height),
663 (p - a - b - c, height - d),
664 )
665 .r_arc_to_rotated(
666 (radius, radius),
667 0.0,
668 ArcSize::Small,
669 PathDirection::CW,
670 (-l, -l),
671 )
672 .cubic_to(
673 (0.0, height - (p - a - b)),
674 (0.0, height - (p - a)),
675 (0.0, f32::max(height / 2.0, height - p)),
676 );
677 } else {
678 path.line_to((0.0, f32::max(height / 2.0, height - p)));
679 }
680 } else {
681 path.line_to((0.0, height)).line_to((0.0, height / 2.0));
682 }
683
684 let top_left = rr.radii(Corner::UpperLeft).x;
685 if top_left > 0.0 {
686 let (a, b, c, d, l, p, radius) =
687 compute_smooth_corner(top_left, corner_top_left_smoothing, width, height);
688
689 path.line_to((0.0, f32::min(height / 2.0, p)));
690 if corner_top_left_shape == CornerShape::Round {
691 path.cubic_to((0.0, p - a), (0.0, p - a - b), (d, p - a - b - c))
692 .r_arc_to_rotated(
693 (radius, radius),
694 0.0,
695 ArcSize::Small,
696 PathDirection::CW,
697 (l, -l),
698 )
699 .cubic_to((p - a - b, 0.0), (p - a, 0.0), (f32::min(width / 2.0, p), 0.0));
700 } else {
701 path.line_to((f32::min(width / 2.0, p), 0.0));
702 }
703 } else {
704 path.line_to((0.0, 0.0));
705 }
706
707 path.close();
708
709 path.offset((x, y));
710 }
711
712 path
713 }
714
715 pub fn draw_background(&mut self, canvas: &Canvas) {
717 let background_color = self.background_color();
718 if background_color.a() > 0 {
719 let path = self.path();
720
721 let mut paint = Paint::default();
722 paint.set_color(skia_safe::Color::from_argb(
723 background_color.a(),
724 background_color.r(),
725 background_color.g(),
726 background_color.b(),
727 ));
728 paint.set_anti_alias(true);
729 canvas.draw_path(&path, &paint);
730 }
731
732 self.draw_background_images(canvas);
733 }
734
735 pub fn draw_border(&mut self, canvas: &Canvas) {
737 let border_color = self.border_color();
738 let border_width = self.border_width();
739 let border_style = self.border_style();
740
741 if border_width > 0.0 && border_color.a() > 0 && border_style != BorderStyleKeyword::None {
742 let path = self.path();
743 let mut paint = Paint::default();
744 paint.set_style(PaintStyle::Stroke);
745 paint.set_color(border_color);
746 paint.set_stroke_width(border_width);
747 match border_style {
748 BorderStyleKeyword::Dashed => {
749 paint.set_path_effect(PathEffect::dash(
750 &[border_width * 2.0, border_width],
751 0.0,
752 ));
753 }
754
755 BorderStyleKeyword::Dotted => {
756 paint.set_path_effect(PathEffect::dash(&[0.0, border_width * 2.0], 0.0));
757 paint.set_stroke_cap(skia_safe::PaintCap::Round);
758 }
759
760 _ => {}
761 }
762
763 paint.set_anti_alias(true);
764 canvas.draw_path(&path, &paint);
765 }
766 }
767
768 pub fn draw_outline(&mut self, canvas: &Canvas) {
770 let outline_width = self.outline_width();
771 let outline_color = self.outline_color();
772
773 if outline_width > 0.0 && outline_color.a() != 0 {
774 let outline_offset = self.outline_offset();
775
776 let bounds = self.bounds();
777
778 let half_outline_width = outline_width / 2.0;
779 let mut outline_path = self.build_path(
780 bounds,
781 (half_outline_width + outline_offset, half_outline_width + outline_offset),
782 );
783
784 outline_path.offset(self.bounds().top_left());
785
786 let mut outline_paint = Paint::default();
787 outline_paint.set_color(outline_color);
788 outline_paint.set_stroke_width(outline_width);
789 outline_paint.set_style(PaintStyle::Stroke);
790 outline_paint.set_anti_alias(true);
791 canvas.draw_path(&outline_path, &outline_paint);
792 }
793 }
794
795 pub fn draw_shadows(&mut self, canvas: &Canvas) {
797 if let Some(shadows) = self.shadows() {
798 if shadows.is_empty() {
799 return;
800 }
801
802 let bounds = self.bounds();
803
804 let mut path = self.build_path(bounds, (0.0, 0.0));
805
806 path.offset(bounds.top_left());
807
808 for shadow in shadows.iter().rev() {
809 let shadow_color = shadow.color.unwrap_or_default();
810
811 let shadow_x_offset = shadow.x_offset.to_px().unwrap_or(0.0) * self.scale_factor();
812 let shadow_y_offset = shadow.y_offset.to_px().unwrap_or(0.0) * self.scale_factor();
813 let spread_radius =
814 shadow.spread_radius.as_ref().and_then(|l| l.to_px()).unwrap_or(0.0)
815 * self.scale_factor();
816
817 let blur_radius =
818 shadow.blur_radius.as_ref().and_then(|br| br.to_px()).unwrap_or(0.0);
819
820 if shadow_color.a() == 0
821 || (shadow_x_offset == 0.0
822 && shadow_y_offset == 0.0
823 && spread_radius == 0.0
824 && blur_radius == 0.0)
825 {
826 continue;
827 }
828
829 let mut shadow_paint = Paint::default();
830
831 let outset = if shadow.inset { -spread_radius } else { spread_radius };
832
833 shadow_paint.set_style(PaintStyle::Fill);
834
835 let mut shadow_path = self.build_path(bounds, (outset, outset));
836 shadow_path.offset(bounds.top_left());
837
838 shadow_paint.set_color(shadow_color);
839
840 if blur_radius > 0.0 {
841 shadow_paint.set_mask_filter(MaskFilter::blur(
842 BlurStyle::Normal,
843 blur_radius / 2.0,
844 false,
845 ));
846 }
847
848 shadow_path.offset((shadow_x_offset, shadow_y_offset));
849
850 if shadow.inset {
851 shadow_path = path.op(&shadow_path, skia_safe::PathOp::Difference).unwrap();
852 }
853
854 canvas.save();
855 canvas.clip_path(
856 &path,
857 if shadow.inset { ClipOp::Intersect } else { ClipOp::Difference },
858 true,
859 );
860 canvas.draw_path(&shadow_path, &shadow_paint);
861 canvas.restore();
862 }
863 }
864 }
865
866 fn draw_background_images(&mut self, canvas: &Canvas) {
868 let bounds = self.bounds();
869
870 if self.background_images().is_some() {
871 let path = self.path();
872 if let Some(images) = self.background_images() {
873 let image_sizes = self.background_size();
874
875 for (index, image) in images.iter().enumerate() {
876 match image {
877 ImageOrGradient::Gradient(gradient) => match gradient {
878 Gradient::Linear(linear_gradient) => {
879 let (start, end, parent_length) = match linear_gradient.direction {
880 LineDirection::Horizontal(horizontal_keyword) => {
881 match horizontal_keyword {
882 HorizontalPositionKeyword::Left => (
883 bounds.center_right(),
884 bounds.center_left(),
885 bounds.width(),
886 ),
887
888 HorizontalPositionKeyword::Right => (
889 bounds.center_left(),
890 bounds.center_right(),
891 bounds.width(),
892 ),
893 }
894 }
895
896 LineDirection::Vertical(vertical_keyword) => {
897 match vertical_keyword {
898 VerticalPositionKeyword::Top => (
899 bounds.center_bottom(),
900 bounds.center_top(),
901 bounds.height(),
902 ),
903
904 VerticalPositionKeyword::Bottom => (
905 bounds.center_top(),
906 bounds.center_bottom(),
907 bounds.height(),
908 ),
909 }
910 }
911
912 LineDirection::Corner { horizontal, vertical } => {
913 match (horizontal, vertical) {
914 (
915 HorizontalPositionKeyword::Right,
916 VerticalPositionKeyword::Bottom,
917 ) => (
918 bounds.top_left(),
919 bounds.bottom_right(),
920 bounds.diagonal(),
921 ),
922
923 (
924 HorizontalPositionKeyword::Right,
925 VerticalPositionKeyword::Top,
926 ) => (
927 bounds.bottom_left(),
928 bounds.top_right(),
929 bounds.diagonal(),
930 ),
931
932 _ => (bounds.top_left(), bounds.bottom_right(), 0.0),
933 }
934 }
935
936 LineDirection::Angle(angle) => {
937 let angle_rad = angle.to_radians();
938 let start_x = bounds.x
939 + ((angle_rad.sin() * bounds.w) - bounds.w) / -2.0;
940 let end_x = bounds.x
941 + ((angle_rad.sin() * bounds.w) + bounds.w) / 2.0;
942 let start_y = bounds.y
943 + ((angle_rad.cos() * bounds.h) + bounds.h) / 2.0;
944 let end_y = bounds.y
945 + ((angle_rad.cos() * bounds.h) - bounds.h) / -2.0;
946
947 let x = (end_x - start_x).abs();
948 let y = (end_y - start_y).abs();
949
950 let dist = (x * x + y * y).sqrt();
951
952 ((start_x, start_y), (end_x, end_y), dist)
953 }
954 };
955
956 let num_stops = linear_gradient.stops.len();
957
958 let mut stops = linear_gradient
959 .stops
960 .iter()
961 .enumerate()
962 .map(|(index, stop)| {
963 let pos = if let Some(pos) = &stop.position {
964 pos.to_pixels(parent_length, self.scale_factor())
965 / parent_length
966 } else {
967 index as f32 / (num_stops - 1) as f32
968 };
969 (pos, skia_safe::Color::from(stop.color))
970 })
971 .collect::<Vec<_>>();
972
973 if let Some(first) = stops.first() {
975 if first.0 != 0.0 {
976 stops.insert(0, (0.0, first.1));
977 }
978 }
979
980 if let Some(last) = stops.last() {
982 if last.0 != 1.0 {
983 stops.push((1.0, last.1));
984 }
985 }
986
987 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
988 stops.into_iter().unzip();
989
990 let shader = Shader::linear_gradient(
991 (Point::from(start), Point::from(end)),
992 GradientShaderColors::Colors(&colors[..]),
993 Some(&offsets[..]),
994 TileMode::Clamp,
995 None,
996 None,
997 );
998
999 let mut paint = Paint::default();
1000 paint.set_shader(shader);
1001
1002 canvas.draw_path(&path, &paint);
1003 }
1004
1005 Gradient::Radial(radial_gradient) => {
1006 let num_stops = radial_gradient.stops.len();
1007
1008 let mut stops = radial_gradient
1009 .stops
1010 .iter()
1011 .enumerate()
1012 .map(|(index, stop)| {
1013 let pos = if let Some(pos) = &stop.position {
1014 pos.to_pixels(bounds.width(), self.scale_factor())
1015 / bounds.width()
1016 } else {
1017 index as f32 / (num_stops - 1) as f32
1018 };
1019
1020 (pos, skia_safe::Color::from(stop.color))
1021 })
1022 .collect::<Vec<_>>();
1023
1024 if let Some(first) = stops.first() {
1026 if first.0 != 0.0 {
1027 stops.insert(0, (0.0, first.1));
1028 }
1029 }
1030
1031 if let Some(last) = stops.last() {
1033 if last.0 != 1.0 {
1034 stops.push((1.0, last.1));
1035 }
1036 }
1037
1038 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
1039 stops.into_iter().unzip();
1040
1041 let shader = Shader::radial_gradient(
1042 Point::from(bounds.center()),
1043 bounds.w.max(bounds.h),
1044 GradientShaderColors::Colors(&colors[..]),
1045 Some(&offsets[..]),
1046 TileMode::Clamp,
1047 None,
1048 None,
1049 );
1050
1051 let mut paint = Paint::default();
1052 paint.set_shader(shader);
1053 canvas.draw_path(&path, &paint);
1054 }
1055
1056 _ => {}
1057 },
1058
1059 ImageOrGradient::Image(image_name) => {
1060 if let Some(image_id) = self.resource_manager.image_ids.get(image_name)
1061 {
1062 if let Some(image) = self.resource_manager.images.get(image_id) {
1063 match &image.image {
1064 ImageOrSvg::Image(image) => {
1065 let image_width = image.width();
1066 let image_height = image.height();
1067 let (width, height) = if let Some(background_size) =
1068 image_sizes.get(index)
1069 {
1070 match background_size {
1071 BackgroundSize::Explicit { width, height } => {
1072 let w = match width {
1073 LengthPercentageOrAuto::LengthPercentage(
1074 length,
1075 ) => {
1076 length.to_pixels(bounds.w, self.scale_factor())
1077 }
1078 LengthPercentageOrAuto::Auto => image_width as f32,
1079 };
1080
1081 let h = match height {
1082 LengthPercentageOrAuto::LengthPercentage(
1083 length,
1084 ) => {
1085 length.to_pixels(bounds.h, self.scale_factor())
1086 }
1087 LengthPercentageOrAuto::Auto => image_height as f32,
1088 };
1089
1090 (w, h)
1091 }
1092
1093 BackgroundSize::Contain => {
1094 let image_ratio = image_width as f32
1095 / image_height as f32;
1096 let container_ratio = bounds.w / bounds.h;
1097
1098 let (w, h) =
1099 if image_ratio > container_ratio {
1100 (bounds.w, bounds.w / image_ratio)
1101 } else {
1102 (bounds.h * image_ratio, bounds.h)
1103 };
1104
1105 (w, h)
1106 }
1107
1108 BackgroundSize::Cover => {
1109 let image_ratio = image_width as f32
1110 / image_height as f32;
1111 let container_ratio = bounds.w / bounds.h;
1112
1113 let (w, h) =
1114 if image_ratio < container_ratio {
1115 (bounds.w, bounds.w / image_ratio)
1116 } else {
1117 (bounds.h * image_ratio, bounds.h)
1118 };
1119
1120 (w, h)
1121 }
1122 }
1123 } else {
1124 (image_width as f32, image_height as f32)
1125 };
1126
1127 let matrix = Matrix::rect_to_rect(
1128 Rect::new(
1129 0.0,
1130 0.0,
1131 image.width() as f32,
1132 image.height() as f32,
1133 ),
1134 Rect::new(
1135 bounds.left(),
1136 bounds.top(),
1137 bounds.left() + width,
1138 bounds.top() + height,
1139 ),
1140 None,
1141 );
1142
1143 let mut paint = Paint::default();
1144 paint.set_anti_alias(true);
1145 paint.set_shader(image.to_shader(
1146 (TileMode::Repeat, TileMode::Repeat),
1147 SamplingOptions::default(),
1148 &matrix,
1149 ));
1150
1151 canvas.draw_path(&path, &paint);
1152 }
1153
1154 ImageOrSvg::Svg(svg) => {
1155 canvas.save_layer(&SaveLayerRec::default());
1156 canvas.translate((bounds.x, bounds.y));
1157 let (scale_x, scale_y) = (
1158 bounds.width() / svg.inner().fContainerSize.fWidth,
1159 bounds.height()
1160 / svg.inner().fContainerSize.fHeight,
1161 );
1162
1163 if scale_x.is_finite() && scale_y.is_finite() {
1164 canvas.scale((scale_x, scale_y));
1165 } else {
1166 svg.clone().set_container_size((
1167 bounds.width(),
1168 bounds.height(),
1169 ));
1170 }
1171
1172 svg.render(canvas);
1173
1174 if let Some(color) =
1175 self.style.fill.get(self.current).copied()
1176 {
1177 let mut paint = Paint::default();
1178
1179 paint.set_anti_alias(true);
1180 paint.set_blend_mode(skia_safe::BlendMode::SrcIn);
1181 paint.set_color(color);
1182 canvas.draw_paint(&paint);
1183 }
1184 canvas.restore();
1185 }
1186 }
1187 }
1188 }
1189 }
1190 }
1191 }
1192 }
1193 }
1194 }
1195
1196 pub fn draw_text(&mut self, canvas: &Canvas) {
1198 if let Some(paragraph) = self.text_context.text_paragraphs.get(self.current) {
1199 let bounds = self.bounds();
1200
1201 let alignment = self.alignment();
1202
1203 let (mut top, _) = match alignment {
1204 Alignment::TopLeft => (0.0, 0.0),
1205 Alignment::TopCenter => (0.0, 0.5),
1206 Alignment::TopRight => (0.0, 1.0),
1207 Alignment::Left => (0.5, 0.0),
1208 Alignment::Center => (0.5, 0.5),
1209 Alignment::Right => (0.5, 1.0),
1210 Alignment::BottomLeft => (1.0, 0.0),
1211 Alignment::BottomCenter => (1.0, 0.5),
1212 Alignment::BottomRight => (1.0, 1.0),
1213 };
1214
1215 let padding_top = match self.padding_top() {
1216 Units::Pixels(val) => val,
1217 _ => 0.0,
1218 };
1219
1220 let padding_bottom = match self.padding_bottom() {
1221 Units::Pixels(val) => val,
1222 _ => 0.0,
1223 };
1224
1225 top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
1226
1227 let padding_left = match self.padding_left() {
1228 Units::Pixels(val) => val,
1229 _ => 0.0,
1230 };
1231
1232 paragraph.paint(
1233 canvas,
1234 ((bounds.x + padding_left).round(), (bounds.y + padding_top + top).round()),
1235 );
1236 }
1237 }
1238}
1239
1240impl DataContext for DrawContext<'_> {
1241 fn data<T: 'static>(&self) -> Option<&T> {
1242 if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1244 return Some(t);
1245 }
1246
1247 for entity in self.current.parent_iter(self.tree) {
1248 if let Some(models) = self.models.get(&entity) {
1250 if let Some(model) = models.get(&TypeId::of::<T>()) {
1251 return model.downcast_ref::<T>();
1252 }
1253 }
1254
1255 if let Some(view_handler) = self.views.get(&entity) {
1257 if let Some(data) = view_handler.downcast_ref::<T>() {
1258 return Some(data);
1259 }
1260 }
1261 }
1262
1263 None
1264 }
1265}
1266
1267fn compute_smooth_corner(
1269 corner_radius: f32,
1270 smoothing: f32,
1271 width: f32,
1272 height: f32,
1273) -> (f32, f32, f32, f32, f32, f32, f32) {
1274 let max_p = f32::min(width, height) / 2.0;
1275 let corner_radius = f32::min(corner_radius, max_p);
1276
1277 let p = f32::min((1.0 + smoothing) * corner_radius, max_p);
1278
1279 let angle_alpha: f32;
1280 let angle_beta: f32;
1281
1282 if corner_radius <= max_p / 2.0 {
1283 angle_alpha = 45.0 * smoothing;
1284 angle_beta = 90.0 * (1.0 - smoothing);
1285 } else {
1286 let diff_ratio = (corner_radius - max_p / 2.0) / (max_p / 2.0);
1287
1288 angle_alpha = 45.0 * smoothing * (1.0 - diff_ratio);
1289 angle_beta = 90.0 * (1.0 - smoothing * (1.0 - diff_ratio));
1290 }
1291
1292 let angle_theta = (90.0 - angle_beta) / 2.0;
1293 let dist_p3_p4 = corner_radius * (angle_theta / 2.0).to_radians().tan();
1294
1295 let l = (angle_beta / 2.0).to_radians().sin() * corner_radius * SQRT_2;
1296 let c = dist_p3_p4 * angle_alpha.to_radians().cos();
1297 let d = c * angle_alpha.to_radians().tan();
1298 let b = (p - l - c - d) / 3.0;
1299 let a = 2.0 * b;
1300
1301 (a, b, c, d, l, p, corner_radius)
1302}