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