1use std::{borrow::Cow, ops::Range};
2
3use serde_json::json;
4
5use crate::{
6 color::Color,
7 geometry::{
8 rect::RectF,
9 vector::{vec2f, Vector2F},
10 },
11 presenter::MeasurementContext,
12 scene, DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
13};
14
15pub struct Svg {
16 path: Cow<'static, str>,
17 color: Color,
18}
19
20impl Svg {
21 pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
22 Self {
23 path: path.into(),
24 color: Color::black(),
25 }
26 }
27
28 pub fn with_color(mut self, color: Color) -> 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 = constrain_size_preserving_aspect_ratio(
46 constraint.max,
47 from_usvg_rect(tree.svg_node().view_box.rect).size(),
48 );
49 (size, Some(tree))
50 }
51 Err(_error) => {
52 #[cfg(not(any(test, feature = "test-support")))]
53 log::error!("{}", _error);
54 (constraint.min, None)
55 }
56 }
57 }
58
59 fn paint(
60 &mut self,
61 bounds: RectF,
62 _visible_bounds: RectF,
63 svg: &mut Self::LayoutState,
64 cx: &mut PaintContext,
65 ) {
66 if let Some(svg) = svg.clone() {
67 cx.scene.push_icon(scene::Icon {
68 bounds,
69 svg,
70 path: self.path.clone(),
71 color: self.color,
72 });
73 }
74 }
75
76 fn dispatch_event(
77 &mut self,
78 _: &Event,
79 _: RectF,
80 _: RectF,
81 _: &mut Self::LayoutState,
82 _: &mut Self::PaintState,
83 _: &mut EventContext,
84 ) -> bool {
85 false
86 }
87
88 fn rect_for_text_range(
89 &self,
90 _: Range<usize>,
91 _: RectF,
92 _: RectF,
93 _: &Self::LayoutState,
94 _: &Self::PaintState,
95 _: &MeasurementContext,
96 ) -> Option<RectF> {
97 None
98 }
99
100 fn debug(
101 &self,
102 bounds: RectF,
103 _: &Self::LayoutState,
104 _: &Self::PaintState,
105 _: &DebugContext,
106 ) -> serde_json::Value {
107 json!({
108 "type": "Svg",
109 "bounds": bounds.to_json(),
110 "path": self.path,
111 "color": self.color.to_json(),
112 })
113 }
114}
115
116use crate::json::ToJson;
117
118use super::constrain_size_preserving_aspect_ratio;
119
120fn from_usvg_rect(rect: usvg::Rect) -> RectF {
121 RectF::new(
122 vec2f(rect.x() as f32, rect.y() as f32),
123 vec2f(rect.width() as f32, rect.height() as f32),
124 )
125}