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 #[cfg(not(any(test, feature = "test-support")))]
52 log::error!("{}", _error);
53 (constraint.min, None)
54 }
55 }
56 }
57
58 fn paint(
59 &mut self,
60 bounds: RectF,
61 _visible_bounds: RectF,
62 svg: &mut Self::LayoutState,
63 cx: &mut PaintContext,
64 ) {
65 if let Some(svg) = svg.clone() {
66 cx.scene.push_icon(scene::Icon {
67 bounds,
68 svg,
69 path: self.path.clone(),
70 color: self.color,
71 });
72 }
73 }
74
75 fn dispatch_event(
76 &mut self,
77 _: &Event,
78 _: RectF,
79 _: RectF,
80 _: &mut Self::LayoutState,
81 _: &mut Self::PaintState,
82 _: &mut EventContext,
83 ) -> bool {
84 false
85 }
86
87 fn debug(
88 &self,
89 bounds: RectF,
90 _: &Self::LayoutState,
91 _: &Self::PaintState,
92 _: &DebugContext,
93 ) -> serde_json::Value {
94 json!({
95 "type": "Svg",
96 "bounds": bounds.to_json(),
97 "path": self.path,
98 "color": self.color.to_json(),
99 })
100 }
101}
102
103use crate::json::ToJson;
104
105use super::constrain_size_preserving_aspect_ratio;
106
107fn from_usvg_rect(rect: usvg::Rect) -> RectF {
108 RectF::new(
109 vec2f(rect.x() as f32, rect.y() as f32),
110 vec2f(rect.width() as f32, rect.height() as f32),
111 )
112}