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