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, 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 rect_for_text_range(
77 &self,
78 _: Range<usize>,
79 _: RectF,
80 _: RectF,
81 _: &Self::LayoutState,
82 _: &Self::PaintState,
83 _: &MeasurementContext,
84 ) -> Option<RectF> {
85 None
86 }
87
88 fn debug(
89 &self,
90 bounds: RectF,
91 _: &Self::LayoutState,
92 _: &Self::PaintState,
93 _: &DebugContext,
94 ) -> serde_json::Value {
95 json!({
96 "type": "Svg",
97 "bounds": bounds.to_json(),
98 "path": self.path,
99 "color": self.color.to_json(),
100 })
101 }
102}
103
104use crate::json::ToJson;
105
106use super::constrain_size_preserving_aspect_ratio;
107
108fn from_usvg_rect(rect: usvg::Rect) -> RectF {
109 RectF::new(
110 vec2f(rect.x() as f32, rect.y() as f32),
111 vec2f(rect.width() as f32, rect.height() as f32),
112 )
113}