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(&mut self, bounds: RectF, svg: &mut Self::LayoutState, cx: &mut PaintContext) {
 69        if let Some(svg) = svg.clone() {
 70            cx.scene.push_icon(scene::Icon {
 71                bounds,
 72                svg,
 73                path: self.path.clone(),
 74                color: self.color,
 75            });
 76        }
 77    }
 78
 79    fn dispatch_event(
 80        &mut self,
 81        _: &Event,
 82        _: RectF,
 83        _: &mut Self::LayoutState,
 84        _: &mut Self::PaintState,
 85        _: &mut EventContext,
 86    ) -> bool {
 87        false
 88    }
 89
 90    fn debug(
 91        &self,
 92        bounds: RectF,
 93        _: &Self::LayoutState,
 94        _: &Self::PaintState,
 95        _: &DebugContext,
 96    ) -> serde_json::Value {
 97        json!({
 98            "type": "Svg",
 99            "bounds": bounds.to_json(),
100            "path": self.path,
101            "color": self.color.to_json(),
102        })
103    }
104}
105
106use crate::json::ToJson;
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}