svg.rs

  1use std::borrow::Cow;
  2
  3use serde_json::json;
  4
  5use crate::{
  6    color::Color,
  7    geometry::{
  8        rect::RectF,
  9        vector::{vec2f, Vector2F},
 10    },
 11    scene, DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
 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 Element for Svg {
 34    type LayoutState = Option<usvg::Tree>;
 35    type PaintState = ();
 36
 37    fn layout(
 38        &mut self,
 39        constraint: SizeConstraint,
 40        cx: &mut LayoutContext,
 41    ) -> (Vector2F, Self::LayoutState) {
 42        match cx.asset_cache.svg(&self.path) {
 43            Ok(tree) => {
 44                let size = constrain_size_preserving_aspect_ratio(
 45                    constraint.max,
 46                    from_usvg_rect(tree.svg_node().view_box.rect).size(),
 47                );
 48                (size, Some(tree))
 49            }
 50            Err(_error) => {
 51                #[cfg(not(any(test, feature = "test-support")))]
 52                log::error!("{}", _error);
 53                (constraint.min, None)
 54            }
 55        }
 56    }
 57
 58    fn paint(
 59        &mut self,
 60        bounds: RectF,
 61        _visible_bounds: RectF,
 62        svg: &mut Self::LayoutState,
 63        cx: &mut PaintContext,
 64    ) {
 65        if let Some(svg) = svg.clone() {
 66            cx.scene.push_icon(scene::Icon {
 67                bounds,
 68                svg,
 69                path: self.path.clone(),
 70                color: self.color,
 71            });
 72        }
 73    }
 74
 75    fn dispatch_event(
 76        &mut self,
 77        _: &Event,
 78        _: RectF,
 79        _: RectF,
 80        _: &mut Self::LayoutState,
 81        _: &mut Self::PaintState,
 82        _: &mut EventContext,
 83    ) -> bool {
 84        false
 85    }
 86
 87    fn debug(
 88        &self,
 89        bounds: RectF,
 90        _: &Self::LayoutState,
 91        _: &Self::PaintState,
 92        _: &DebugContext,
 93    ) -> serde_json::Value {
 94        json!({
 95            "type": "Svg",
 96            "bounds": bounds.to_json(),
 97            "path": self.path,
 98            "color": self.color.to_json(),
 99        })
100    }
101}
102
103use crate::json::ToJson;
104
105use super::constrain_size_preserving_aspect_ratio;
106
107fn from_usvg_rect(rect: usvg::Rect) -> RectF {
108    RectF::new(
109        vec2f(rect.x() as f32, rect.y() as f32),
110        vec2f(rect.width() as f32, rect.height() as f32),
111    )
112}