svg.rs

  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}