Files
oak-gpui/crates/gpui/src/elements/svg.rs
T
Calin Martinconi 3751f67730 fix: Typos (#15313)
Fixed typos in the code base according with output from `codespell`
tool.

Release Notes:

- N/A
2024-07-26 17:52:37 -07:00

191 lines
5.4 KiB
Rust

use crate::{
geometry::Negate as _, point, px, radians, size, Bounds, Element, GlobalElementId, Hitbox,
InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString,
Size, StyleRefinement, Styled, TransformationMatrix, WindowContext,
};
use util::ResultExt;
/// An SVG element.
pub struct Svg {
interactivity: Interactivity,
transformation: Option<Transformation>,
path: Option<SharedString>,
}
/// Create a new SVG element.
pub fn svg() -> Svg {
Svg {
interactivity: Interactivity::default(),
transformation: None,
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
}
/// 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 request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self
.interactivity
.request_layout(global_id, cx, |style, cx| cx.request_layout(style, None));
(layout_id, ())
}
fn prepaint(
&mut self,
global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Option<Hitbox> {
self.interactivity
.prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
}
fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
cx: &mut WindowContext,
) where
Self: Sized,
{
self.interactivity
.paint(global_id, bounds, hitbox.as_ref(), cx, |style, 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(), cx.scale_factor())
})
.unwrap_or_default();
cx.paint_svg(bounds, path.clone(), transformation, color)
.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).negate())
}
}