use super::constrain_size_preserving_aspect_ratio; use crate::json::ToJson; use crate::{ color::Color, geometry::{ rect::RectF, vector::{vec2f, Vector2F}, }, scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, }; use schemars::JsonSchema; use serde_derive::Deserialize; use serde_json::json; use std::{borrow::Cow, ops::Range}; pub struct Svg { path: Cow<'static, str>, color: Color, } impl Svg { pub fn new(path: impl Into>) -> Self { Self { path: path.into(), color: Color::black(), } } pub fn for_style(style: SvgStyle) -> impl Element { Self::new(style.asset) .with_color(style.color) .constrained() .with_width(style.dimensions.width) .with_height(style.dimensions.height) } pub fn with_color(mut self, color: Color) -> Self { self.color = color; self } } impl Element for Svg { type LayoutState = Option; type PaintState = (); fn layout( &mut self, constraint: SizeConstraint, _: &mut V, cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { match cx.asset_cache.svg(&self.path) { Ok(tree) => { let size = constrain_size_preserving_aspect_ratio( constraint.max, from_usvg_rect(tree.svg_node().view_box.rect).size(), ); (size, Some(tree)) } Err(_error) => { #[cfg(not(any(test, feature = "test-support")))] log::error!("{}", _error); (constraint.min, None) } } } fn paint( &mut self, scene: &mut SceneBuilder, bounds: RectF, _visible_bounds: RectF, svg: &mut Self::LayoutState, _: &mut V, _: &mut ViewContext, ) { if let Some(svg) = svg.clone() { scene.push_icon(scene::Icon { bounds, svg, path: self.path.clone(), color: self.color, }); } } fn rect_for_text_range( &self, _: Range, _: RectF, _: RectF, _: &Self::LayoutState, _: &Self::PaintState, _: &V, _: &ViewContext, ) -> Option { None } fn debug( &self, bounds: RectF, _: &Self::LayoutState, _: &Self::PaintState, _: &V, _: &ViewContext, ) -> serde_json::Value { json!({ "type": "Svg", "bounds": bounds.to_json(), "path": self.path, "color": self.color.to_json(), }) } } #[derive(Clone, Deserialize, Default, JsonSchema)] pub struct SvgStyle { pub color: Color, pub asset: String, pub dimensions: Dimensions, } #[derive(Clone, Deserialize, Default, JsonSchema)] pub struct Dimensions { pub width: f32, pub height: f32, } impl Dimensions { pub fn to_vec(&self) -> Vector2F { vec2f(self.width, self.height) } } fn from_usvg_rect(rect: usvg::Rect) -> RectF { RectF::new( vec2f(rect.x() as f32, rect.y() as f32), vec2f(rect.width() as f32, rect.height() as f32), ) }