1use std::borrow::Cow;
2
3use serde_json::json;
4
5use crate::{
6 color::Color,
7 geometry::{
8 rect::RectF,
9 vector::{vec2f, Vector2F},
10 },
11 scene, DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
12};
13
14pub struct Svg {
15 path: Cow<'static, str>,
16 color: Color,
17}
18
19impl Svg {
20 pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
21 Self {
22 path: path.into(),
23 color: Color::black(),
24 }
25 }
26
27 pub fn with_color(mut self, color: Color) -> Self {
28 self.color = color;
29 self
30 }
31}
32
33impl Element for Svg {
34 type LayoutState = Option<usvg::Tree>;
35 type PaintState = ();
36
37 fn layout(
38 &mut self,
39 constraint: SizeConstraint,
40 cx: &mut LayoutContext,
41 ) -> (Vector2F, Self::LayoutState) {
42 match cx.asset_cache.svg(&self.path) {
43 Ok(tree) => {
44 let size = constrain_size_preserving_aspect_ratio(
45 constraint.max,
46 from_usvg_rect(tree.svg_node().view_box.rect).size(),
47 );
48 (size, Some(tree))
49 }
50 Err(error) => {
51 log::error!("{}", error);
52 (constraint.min, None)
53 }
54 }
55 }
56
57 fn paint(
58 &mut self,
59 bounds: RectF,
60 _visible_bounds: RectF,
61 svg: &mut Self::LayoutState,
62 cx: &mut PaintContext,
63 ) {
64 if let Some(svg) = svg.clone() {
65 cx.scene.push_icon(scene::Icon {
66 bounds,
67 svg,
68 path: self.path.clone(),
69 color: self.color,
70 });
71 }
72 }
73
74 fn dispatch_event(
75 &mut self,
76 _: &Event,
77 _: RectF,
78 _: &mut Self::LayoutState,
79 _: &mut Self::PaintState,
80 _: &mut EventContext,
81 ) -> bool {
82 false
83 }
84
85 fn debug(
86 &self,
87 bounds: RectF,
88 _: &Self::LayoutState,
89 _: &Self::PaintState,
90 _: &DebugContext,
91 ) -> serde_json::Value {
92 json!({
93 "type": "Svg",
94 "bounds": bounds.to_json(),
95 "path": self.path,
96 "color": self.color.to_json(),
97 })
98 }
99}
100
101use crate::json::ToJson;
102
103use super::constrain_size_preserving_aspect_ratio;
104
105fn from_usvg_rect(rect: usvg::Rect) -> RectF {
106 RectF::new(
107 vec2f(rect.x() as f32, rect.y() as f32),
108 vec2f(rect.width() as f32, rect.height() as f32),
109 )
110}