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 scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
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<V: View> Element<V> for Svg {
34 type LayoutState = Option<usvg::Tree>;
35 type PaintState = ();
36
37 fn layout(
38 &mut self,
39 constraint: SizeConstraint,
40 _: &mut V,
41 cx: &mut LayoutContext<V>,
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 scene: &mut SceneBuilder,
62 bounds: RectF,
63 _visible_bounds: RectF,
64 svg: &mut Self::LayoutState,
65 _: &mut V,
66 _: &mut ViewContext<V>,
67 ) {
68 if let Some(svg) = svg.clone() {
69 scene.push_icon(scene::Icon {
70 bounds,
71 svg,
72 path: self.path.clone(),
73 color: self.color,
74 });
75 }
76 }
77
78 fn rect_for_text_range(
79 &self,
80 _: Range<usize>,
81 _: RectF,
82 _: RectF,
83 _: &Self::LayoutState,
84 _: &Self::PaintState,
85 _: &V,
86 _: &ViewContext<V>,
87 ) -> Option<RectF> {
88 None
89 }
90
91 fn debug(
92 &self,
93 bounds: RectF,
94 _: &Self::LayoutState,
95 _: &Self::PaintState,
96 _: &V,
97 _: &ViewContext<V>,
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
110use super::constrain_size_preserving_aspect_ratio;
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}