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 = if constraint.max.x().is_infinite() && constraint.max.y().is_infinite() {
45 let rect = from_usvg_rect(tree.svg_node().view_box.rect);
46 rect.size()
47 } else {
48 let max_size = constraint.max;
49 let svg_size = from_usvg_rect(tree.svg_node().view_box.rect).size();
50
51 if max_size.x().is_infinite()
52 || max_size.x() / max_size.y() > svg_size.x() / svg_size.y()
53 {
54 vec2f(svg_size.x() * max_size.y() / svg_size.y(), max_size.y())
55 } else {
56 vec2f(max_size.x(), svg_size.y() * max_size.x() / svg_size.x())
57 }
58 };
59 (size, Some(tree))
60 }
61 Err(error) => {
62 log::error!("{}", error);
63 (constraint.min, None)
64 }
65 }
66 }
67
68 fn paint(
69 &mut self,
70 bounds: RectF,
71 _visible_bounds: RectF,
72 svg: &mut Self::LayoutState,
73 cx: &mut PaintContext,
74 ) {
75 if let Some(svg) = svg.clone() {
76 cx.scene.push_icon(scene::Icon {
77 bounds,
78 svg,
79 path: self.path.clone(),
80 color: self.color,
81 });
82 }
83 }
84
85 fn dispatch_event(
86 &mut self,
87 _: &Event,
88 _: RectF,
89 _: &mut Self::LayoutState,
90 _: &mut Self::PaintState,
91 _: &mut EventContext,
92 ) -> bool {
93 false
94 }
95
96 fn debug(
97 &self,
98 bounds: RectF,
99 _: &Self::LayoutState,
100 _: &Self::PaintState,
101 _: &DebugContext,
102 ) -> serde_json::Value {
103 json!({
104 "type": "Svg",
105 "bounds": bounds.to_json(),
106 "path": self.path,
107 "color": self.color.to_json(),
108 })
109 }
110}
111
112use crate::json::ToJson;
113
114fn from_usvg_rect(rect: usvg::Rect) -> RectF {
115 RectF::new(
116 vec2f(rect.x() as f32, rect.y() as f32),
117 vec2f(rect.width() as f32, rect.height() as f32),
118 )
119}