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(&mut self, bounds: RectF, svg: &mut Self::LayoutState, cx: &mut PaintContext) {
69 if let Some(svg) = svg.clone() {
70 cx.scene.push_icon(scene::Icon {
71 bounds,
72 svg,
73 path: self.path.clone(),
74 color: self.color,
75 });
76 }
77 }
78
79 fn dispatch_event(
80 &mut self,
81 _: &Event,
82 _: RectF,
83 _: &mut Self::LayoutState,
84 _: &mut Self::PaintState,
85 _: &mut EventContext,
86 ) -> bool {
87 false
88 }
89
90 fn debug(
91 &self,
92 bounds: RectF,
93 _: &Self::LayoutState,
94 _: &Self::PaintState,
95 _: &DebugContext,
96 ) -> serde_json::Value {
97 json!({
98 "type": "Svg",
99 "bounds": bounds.to_json(),
100 "path": self.path,
101 "color": self.color.to_json(),
102 })
103 }
104}
105
106use crate::json::ToJson;
107
108fn from_usvg_rect(rect: usvg::Rect) -> RectF {
109 RectF::new(
110 vec2f(rect.x() as f32, rect.y() as f32),
111 vec2f(rect.width() as f32, rect.height() as f32),
112 )
113}