276 lines · 7.9 KB · Rust
RawDownload
use std::{fs, path::Path, sync::Arc}; use crate::{    App, Asset, Bounds, Element, GlobalElementId, Hitbox, InspectorElementId, InteractiveElement,    Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString, Size,    StyleRefinement, Styled, TransformationMatrix, Window, point, px, radians, size,};use gpui_util::ResultExt; /// An SVG element.pub struct Svg {    interactivity: Interactivity,    transformation: Option<Transformation>,    path: Option<SharedString>,    external_path: Option<SharedString>,} /// Create a new SVG element.#[track_caller]pub fn svg() -> Svg {    Svg {        interactivity: Interactivity::new(),        transformation: None,        path: None,        external_path: None,    }} impl Svg {    /// Set the path to the SVG file for this element.    pub fn path(mut self, path: impl Into<SharedString>) -> Self {        self.path = Some(path.into());        self    }     /// Set the path to the SVG file for this element.    pub fn external_path(mut self, path: impl Into<SharedString>) -> Self {        self.external_path = Some(path.into());        self    }     /// Transform the SVG element with the given transformation.    /// Note that this won't effect the hitbox or layout of the element, only the rendering.    pub fn with_transformation(mut self, transformation: Transformation) -> Self {        self.transformation = Some(transformation);        self    }} impl Element for Svg {    type RequestLayoutState = ();    type PrepaintState = Option<Hitbox>;     fn id(&self) -> Option<crate::ElementId> {        self.interactivity.element_id.clone()    }     fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {        self.interactivity.source_location()    }     fn request_layout(        &mut self,        global_id: Option<&GlobalElementId>,        inspector_id: Option<&InspectorElementId>,        window: &mut Window,        cx: &mut App,    ) -> (LayoutId, Self::RequestLayoutState) {        let layout_id = self.interactivity.request_layout(            global_id,            inspector_id,            window,            cx,            |style, window, cx| window.request_layout(style, None, cx),        );        (layout_id, ())    }     fn prepaint(        &mut self,        global_id: Option<&GlobalElementId>,        inspector_id: Option<&InspectorElementId>,        bounds: Bounds<Pixels>,        _request_layout: &mut Self::RequestLayoutState,        window: &mut Window,        cx: &mut App,    ) -> Option<Hitbox> {        self.interactivity.prepaint(            global_id,            inspector_id,            bounds,            bounds.size,            window,            cx,            |_, _, hitbox, _, _| hitbox,        )    }     fn paint(        &mut self,        global_id: Option<&GlobalElementId>,        inspector_id: Option<&InspectorElementId>,        bounds: Bounds<Pixels>,        _request_layout: &mut Self::RequestLayoutState,        hitbox: &mut Option<Hitbox>,        window: &mut Window,        cx: &mut App,    ) where        Self: Sized,    {        self.interactivity.paint(            global_id,            inspector_id,            bounds,            hitbox.as_ref(),            window,            cx,            |style, window, cx| {                if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {                    let transformation = self                        .transformation                        .as_ref()                        .map(|transformation| {                            transformation.into_matrix(bounds.center(), window.scale_factor())                        })                        .unwrap_or_default();                     window                        .paint_svg(bounds, path.clone(), None, transformation, color, cx)                        .log_err();                } else if let Some((path, color)) =                    self.external_path.as_ref().zip(style.text.color)                {                    let Some(bytes) = window                        .use_asset::<SvgAsset>(path, cx)                        .and_then(|asset| asset.log_err())                    else {                        return;                    };                     let transformation = self                        .transformation                        .as_ref()                        .map(|transformation| {                            transformation.into_matrix(bounds.center(), window.scale_factor())                        })                        .unwrap_or_default();                     window                        .paint_svg(                            bounds,                            path.clone(),                            Some(&bytes),                            transformation,                            color,                            cx,                        )                        .log_err();                }            },        )    }} impl IntoElement for Svg {    type Element = Self;     fn into_element(self) -> Self::Element {        self    }} impl Styled for Svg {    fn style(&mut self) -> &mut StyleRefinement {        &mut self.interactivity.base_style    }} impl InteractiveElement for Svg {    fn interactivity(&mut self) -> &mut Interactivity {        &mut self.interactivity    }} /// A transformation to apply to an SVG element.#[derive(Clone, Copy, Debug, PartialEq)]pub struct Transformation {    scale: Size<f32>,    translate: Point<Pixels>,    rotate: Radians,} impl Default for Transformation {    fn default() -> Self {        Self {            scale: size(1.0, 1.0),            translate: point(px(0.0), px(0.0)),            rotate: radians(0.0),        }    }} impl Transformation {    /// Create a new Transformation with the specified scale along each axis.    pub fn scale(scale: Size<f32>) -> Self {        Self {            scale,            translate: point(px(0.0), px(0.0)),            rotate: radians(0.0),        }    }     /// Create a new Transformation with the specified translation.    pub fn translate(translate: Point<Pixels>) -> Self {        Self {            scale: size(1.0, 1.0),            translate,            rotate: radians(0.0),        }    }     /// Create a new Transformation with the specified rotation in radians.    pub fn rotate(rotate: impl Into<Radians>) -> Self {        let rotate = rotate.into();        Self {            scale: size(1.0, 1.0),            translate: point(px(0.0), px(0.0)),            rotate,        }    }     /// Update the scaling factor of this transformation.    pub fn with_scaling(mut self, scale: Size<f32>) -> Self {        self.scale = scale;        self    }     /// Update the translation value of this transformation.    pub fn with_translation(mut self, translate: Point<Pixels>) -> Self {        self.translate = translate;        self    }     /// Update the rotation angle of this transformation.    pub fn with_rotation(mut self, rotate: impl Into<Radians>) -> Self {        self.rotate = rotate.into();        self    }     fn into_matrix(self, center: Point<Pixels>, scale_factor: f32) -> TransformationMatrix {        //Note: if you read this as a sequence of matrix multiplications, start from the bottom        TransformationMatrix::unit()            .translate(center.scale(scale_factor) + self.translate.scale(scale_factor))            .rotate(self.rotate)            .scale(self.scale)            .translate(center.scale(-scale_factor))    }} enum SvgAsset {} impl Asset for SvgAsset {    type Source = SharedString;    type Output = Result<Arc<[u8]>, Arc<std::io::Error>>;     fn load(        source: Self::Source,        _cx: &mut App,    ) -> impl Future<Output = Self::Output> + Send + 'static {        async move {            let bytes = fs::read(Path::new(source.as_ref())).map_err(|e| Arc::new(e))?;            let bytes = Arc::from(bytes);            Ok(bytes)        }    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t