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