svg.rs

  1use std::borrow::Cow;
  2
  3use serde_json::json;
  4
  5use crate::{
  6    color::ColorU,
  7    geometry::{
  8        rect::RectF,
  9        vector::{vec2f, Vector2F},
 10    },
 11    scene, AfterLayoutContext, DebugContext, Element, Event, EventContext, LayoutContext,
 12    PaintContext, SizeConstraint,
 13};
 14
 15pub struct Svg {
 16    path: Cow<'static, str>,
 17    color: ColorU,
 18}
 19
 20impl Svg {
 21    pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
 22        Self {
 23            path: path.into(),
 24            color: ColorU::black(),
 25        }
 26    }
 27
 28    pub fn with_color(mut self, color: ColorU) -> Self {
 29        self.color = color;
 30        self
 31    }
 32}
 33
 34impl Element for Svg {
 35    type LayoutState = Option<usvg::Tree>;
 36    type PaintState = ();
 37
 38    fn layout(
 39        &mut self,
 40        constraint: SizeConstraint,
 41        cx: &mut LayoutContext,
 42    ) -> (Vector2F, Self::LayoutState) {
 43        match cx.asset_cache.svg(&self.path) {
 44            Ok(tree) => {
 45                let size = if constraint.max.x().is_infinite() && constraint.max.y().is_infinite() {
 46                    let rect = from_usvg_rect(tree.svg_node().view_box.rect);
 47                    rect.size()
 48                } else {
 49                    let max_size = constraint.max;
 50                    let svg_size = from_usvg_rect(tree.svg_node().view_box.rect).size();
 51
 52                    if max_size.x().is_infinite()
 53                        || max_size.x() / max_size.y() > svg_size.x() / svg_size.y()
 54                    {
 55                        vec2f(svg_size.x() * max_size.y() / svg_size.y(), max_size.y())
 56                    } else {
 57                        vec2f(max_size.x(), svg_size.y() * max_size.x() / svg_size.x())
 58                    }
 59                };
 60                (size, Some(tree))
 61            }
 62            Err(error) => {
 63                log::error!("{}", error);
 64                (constraint.min, None)
 65            }
 66        }
 67    }
 68
 69    fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
 70    }
 71
 72    fn paint(&mut self, bounds: RectF, svg: &mut Self::LayoutState, cx: &mut PaintContext) {
 73        if let Some(svg) = svg.clone() {
 74            cx.scene.push_icon(scene::Icon {
 75                bounds,
 76                svg,
 77                path: self.path.clone(),
 78                color: self.color,
 79            });
 80        }
 81    }
 82
 83    fn dispatch_event(
 84        &mut self,
 85        _: &Event,
 86        _: RectF,
 87        _: &mut Self::LayoutState,
 88        _: &mut Self::PaintState,
 89        _: &mut EventContext,
 90    ) -> bool {
 91        false
 92    }
 93
 94    fn debug(
 95        &self,
 96        bounds: RectF,
 97        _: &Self::LayoutState,
 98        _: &Self::PaintState,
 99        _: &DebugContext,
100    ) -> serde_json::Value {
101        json!({
102            "type": "Svg",
103            "bounds": bounds.to_json(),
104            "path": self.path,
105            "color": self.color.to_json(),
106        })
107    }
108}
109
110use crate::json::ToJson;
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}