1use super::constrain_size_preserving_aspect_ratio;
2use crate::json::ToJson;
3use crate::{
4 color::Color,
5 geometry::{
6 rect::RectF,
7 vector::{vec2f, Vector2F},
8 },
9 scene, Element, SizeConstraint, ViewContext,
10};
11use schemars::JsonSchema;
12use serde_derive::Deserialize;
13use serde_json::json;
14use std::{borrow::Cow, ops::Range};
15
16pub struct Svg {
17 path: Cow<'static, str>,
18 color: Color,
19}
20
21impl Svg {
22 pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
23 Self {
24 path: path.into(),
25 color: Color::black(),
26 }
27 }
28
29 pub fn for_style<V: 'static>(style: SvgStyle) -> impl Element<V> {
30 Self::new(style.asset)
31 .with_color(style.color)
32 .constrained()
33 .with_width(style.dimensions.width)
34 .with_height(style.dimensions.height)
35 }
36
37 pub fn with_color(mut self, color: Color) -> Self {
38 self.color = color;
39 self
40 }
41}
42
43impl<V: 'static> Element<V> for Svg {
44 type LayoutState = Option<usvg::Tree>;
45 type PaintState = ();
46
47 fn layout(
48 &mut self,
49 constraint: SizeConstraint,
50 _: &mut V,
51 cx: &mut ViewContext<V>,
52 ) -> (Vector2F, Self::LayoutState) {
53 match cx.asset_cache.svg(&self.path) {
54 Ok(tree) => {
55 let size = constrain_size_preserving_aspect_ratio(
56 constraint.max,
57 from_usvg_rect(tree.svg_node().view_box.rect).size(),
58 );
59 (size, Some(tree))
60 }
61 Err(_error) => {
62 #[cfg(not(any(test, feature = "test-support")))]
63 log::error!("{}", _error);
64 (constraint.min, None)
65 }
66 }
67 }
68
69 fn paint(
70 &mut self,
71 bounds: RectF,
72 _visible_bounds: RectF,
73 svg: &mut Self::LayoutState,
74 _: &mut V,
75 cx: &mut ViewContext<V>,
76 ) {
77 if let Some(svg) = svg.clone() {
78 cx.scene().push_icon(scene::Icon {
79 bounds,
80 svg,
81 path: self.path.clone(),
82 color: self.color,
83 });
84 }
85 }
86
87 fn rect_for_text_range(
88 &self,
89 _: Range<usize>,
90 _: RectF,
91 _: RectF,
92 _: &Self::LayoutState,
93 _: &Self::PaintState,
94 _: &V,
95 _: &ViewContext<V>,
96 ) -> Option<RectF> {
97 None
98 }
99
100 fn debug(
101 &self,
102 bounds: RectF,
103 _: &Self::LayoutState,
104 _: &Self::PaintState,
105 _: &V,
106 _: &ViewContext<V>,
107 ) -> serde_json::Value {
108 json!({
109 "type": "Svg",
110 "bounds": bounds.to_json(),
111 "path": self.path,
112 "color": self.color.to_json(),
113 })
114 }
115}
116
117#[derive(Clone, Deserialize, Default, JsonSchema)]
118pub struct SvgStyle {
119 pub color: Color,
120 pub asset: String,
121 pub dimensions: Dimensions,
122}
123
124#[derive(Clone, Deserialize, Default, JsonSchema)]
125pub struct Dimensions {
126 pub width: f32,
127 pub height: f32,
128}
129
130impl Dimensions {
131 pub fn to_vec(&self) -> Vector2F {
132 vec2f(self.width, self.height)
133 }
134}
135
136fn from_usvg_rect(rect: usvg::Rect) -> RectF {
137 RectF::new(
138 vec2f(rect.x() as f32, rect.y() as f32),
139 vec2f(rect.width() as f32, rect.height() as f32),
140 )
141}