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 = if constraint.max.x().is_infinite() && constraint.max.y().is_infinite() {
 45                    let rect = from_usvg_rect(tree.svg_node().view_box.rect);
 46                    rect.size()
 47                } else {
 48                    let max_size = constraint.max;
 49                    let svg_size = from_usvg_rect(tree.svg_node().view_box.rect).size();
 50
 51                    if max_size.x().is_infinite()
 52                        || max_size.x() / max_size.y() > svg_size.x() / svg_size.y()
 53                    {
 54                        vec2f(svg_size.x() * max_size.y() / svg_size.y(), max_size.y())
 55                    } else {
 56                        vec2f(max_size.x(), svg_size.y() * max_size.x() / svg_size.x())
 57                    }
 58                };
 59                (size, Some(tree))
 60            }
 61            Err(error) => {
 62                log::error!("{}", error);
 63                (constraint.min, None)
 64            }
 65        }
 66    }
 67
 68    fn paint(
 69        &mut self,
 70        bounds: RectF,
 71        _visible_bounds: RectF,
 72        svg: &mut Self::LayoutState,
 73        cx: &mut PaintContext,
 74    ) {
 75        if let Some(svg) = svg.clone() {
 76            cx.scene.push_icon(scene::Icon {
 77                bounds,
 78                svg,
 79                path: self.path.clone(),
 80                color: self.color,
 81            });
 82        }
 83    }
 84
 85    fn dispatch_event(
 86        &mut self,
 87        _: &Event,
 88        _: RectF,
 89        _: &mut Self::LayoutState,
 90        _: &mut Self::PaintState,
 91        _: &mut EventContext,
 92    ) -> bool {
 93        false
 94    }
 95
 96    fn debug(
 97        &self,
 98        bounds: RectF,
 99        _: &Self::LayoutState,
100        _: &Self::PaintState,
101        _: &DebugContext,
102    ) -> serde_json::Value {
103        json!({
104            "type": "Svg",
105            "bounds": bounds.to_json(),
106            "path": self.path,
107            "color": self.color.to_json(),
108        })
109    }
110}
111
112use crate::json::ToJson;
113
114fn from_usvg_rect(rect: usvg::Rect) -> RectF {
115    RectF::new(
116        vec2f(rect.x() as f32, rect.y() as f32),
117        vec2f(rect.width() as f32, rect.height() as f32),
118    )
119}