chore: Remove SvgImg element. (#1187)
## Break Change - The `SvgImg` element has been removed. This is a special implementation and should not be included in the GPUI Component.
This commit is contained in:
parent
7fb6000189
commit
785df6a5ff
6 changed files with 20 additions and 423 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -3156,11 +3156,9 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"futures-util",
|
|
||||||
"gpui",
|
"gpui",
|
||||||
"gpui-component-macros",
|
"gpui-component-macros",
|
||||||
"html5ever 0.27.0",
|
"html5ever 0.27.0",
|
||||||
"image",
|
|
||||||
"indexset",
|
"indexset",
|
||||||
"indoc",
|
"indoc",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
|
|
@ -3171,7 +3169,6 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"paste",
|
"paste",
|
||||||
"regex",
|
"regex",
|
||||||
"resvg",
|
|
||||||
"rust-i18n",
|
"rust-i18n",
|
||||||
"rust_decimal",
|
"rust_decimal",
|
||||||
"schemars",
|
"schemars",
|
||||||
|
|
@ -3212,7 +3209,6 @@ dependencies = [
|
||||||
"tree-sitter-yaml",
|
"tree-sitter-yaml",
|
||||||
"tree-sitter-zig",
|
"tree-sitter-zig",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"usvg",
|
|
||||||
"uuid",
|
"uuid",
|
||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, path::PathBuf};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
|
|
@ -19,6 +19,14 @@ impl AssetSource for Assets {
|
||||||
|
|
||||||
Self::get(path)
|
Self::get(path)
|
||||||
.map(|f| Some(f.data))
|
.map(|f| Some(f.data))
|
||||||
|
.or_else(|| {
|
||||||
|
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
let path = manifest_dir.join(path);
|
||||||
|
|
||||||
|
std::fs::read(path)
|
||||||
|
.map(|data| Some(std::borrow::Cow::Owned(data)))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,11 @@
|
||||||
use gpui::{
|
|
||||||
img, App, AppContext, ClickEvent, ElementId, Entity, FocusHandle, Focusable,
|
|
||||||
ParentElement as _, Render, Styled, Window,
|
|
||||||
};
|
|
||||||
use gpui_component::{button::Button, dock::PanelControl, v_flex, SvgImg};
|
|
||||||
|
|
||||||
use crate::section;
|
use crate::section;
|
||||||
|
use gpui::{
|
||||||
const SVG_ITEMS: &[&str] = &[
|
img, App, AppContext, Context, Entity, FocusHandle, Focusable, IntoElement, ParentElement as _,
|
||||||
include_str!("./fixtures/google.svg"),
|
Render, Styled, Window,
|
||||||
include_str!("./fixtures/color-wheel.svg"),
|
};
|
||||||
];
|
use gpui_component::{dock::PanelControl, v_flex};
|
||||||
|
|
||||||
pub struct ImageStory {
|
pub struct ImageStory {
|
||||||
svg_index: usize,
|
|
||||||
focus_handle: gpui::FocusHandle,
|
focus_handle: gpui::FocusHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,7 +30,6 @@ impl super::Story for ImageStory {
|
||||||
impl ImageStory {
|
impl ImageStory {
|
||||||
pub fn new(_: &mut Window, cx: &mut App) -> Self {
|
pub fn new(_: &mut Window, cx: &mut App) -> Self {
|
||||||
Self {
|
Self {
|
||||||
svg_index: 0,
|
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,10 +37,6 @@ impl ImageStory {
|
||||||
pub fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
pub fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||||
cx.new(|cx| Self::new(window, cx))
|
cx.new(|cx| Self::new(window, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn svg_img(&self, id: impl Into<ElementId>) -> SvgImg {
|
|
||||||
SvgImg::new(id, SVG_ITEMS[self.svg_index].as_bytes())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Focusable for ImageStory {
|
impl Focusable for ImageStory {
|
||||||
|
|
@ -58,29 +46,17 @@ impl Focusable for ImageStory {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for ImageStory {
|
impl Render for ImageStory {
|
||||||
fn render(
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||||
&mut self,
|
// The svg file are from Assets
|
||||||
_window: &mut gpui::Window,
|
// See: crates/story/src/assets.rs#L21
|
||||||
cx: &mut gpui::Context<Self>,
|
|
||||||
) -> impl gpui::IntoElement {
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
.size_full()
|
.size_full()
|
||||||
|
.child(section("SVG 160px").child(img("src/fixtures/google.svg").size_40().flex_grow()))
|
||||||
.child(
|
.child(
|
||||||
Button::new("switch")
|
section("SVG 80px")
|
||||||
.outline()
|
.child(img("src/fixtures/color-wheel.svg").size_20().flex_grow()),
|
||||||
.label("Switch SVG")
|
|
||||||
.on_click(cx.listener(|this, _: &ClickEvent, _, cx| {
|
|
||||||
this.svg_index += 1;
|
|
||||||
if this.svg_index >= SVG_ITEMS.len() {
|
|
||||||
this.svg_index = 0;
|
|
||||||
}
|
|
||||||
cx.notify();
|
|
||||||
})),
|
|
||||||
)
|
)
|
||||||
.child(section("SVG 160px").child(self.svg_img("logo1").size_40().flex_grow()))
|
|
||||||
.child(section("SVG 80px").child(self.svg_img("logo3").size_20().flex_grow()))
|
|
||||||
.child(section("SVG 48px").child(self.svg_img("logo4").size_12().flex_grow()))
|
|
||||||
.child(
|
.child(
|
||||||
section("SVG from img 40px").child(
|
section("SVG from img 40px").child(
|
||||||
img("https://pub.lbkrs.com/files/202503/vEnnmgUM6bo362ya/sdk.svg").h_24(),
|
img("https://pub.lbkrs.com/files/202503/vEnnmgUM6bo362ya/sdk.svg").h_24(),
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ anyhow.workspace = true
|
||||||
notify.workspace = true
|
notify.workspace = true
|
||||||
|
|
||||||
enum-iterator = "2.1.0"
|
enum-iterator = "2.1.0"
|
||||||
futures-util = "0.3.31"
|
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
|
|
@ -73,11 +72,6 @@ uuid = "1.10"
|
||||||
# WebView
|
# WebView
|
||||||
wry = { version = "0.48.0", optional = true }
|
wry = { version = "0.48.0", optional = true }
|
||||||
|
|
||||||
# SvgImg
|
|
||||||
image = { version = "0.25.1", default-features = false, features = ["png"] }
|
|
||||||
resvg = { version = "0.45.0", default-features = false, features = ["text"] }
|
|
||||||
usvg = { version = "0.45.0", default-features = false, features = ["text"] }
|
|
||||||
|
|
||||||
# Chart
|
# Chart
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
rust_decimal = { version = "1.37.0", optional = true }
|
rust_decimal = { version = "1.37.0", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ mod kbd;
|
||||||
mod menu;
|
mod menu;
|
||||||
mod root;
|
mod root;
|
||||||
mod styled;
|
mod styled;
|
||||||
mod svg_img;
|
|
||||||
mod time;
|
mod time;
|
||||||
mod title_bar;
|
mod title_bar;
|
||||||
mod virtual_list;
|
mod virtual_list;
|
||||||
|
|
@ -85,7 +84,6 @@ pub use window_border::{window_border, window_paddings, WindowBorder};
|
||||||
|
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
pub use kbd::*;
|
pub use kbd::*;
|
||||||
pub use svg_img::*;
|
|
||||||
pub use theme::*;
|
pub use theme::*;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
|
||||||
|
|
@ -1,375 +0,0 @@
|
||||||
use std::{
|
|
||||||
hash::Hash,
|
|
||||||
ops::Deref,
|
|
||||||
sync::{Arc, LazyLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use gpui::{
|
|
||||||
hash, px, App, Asset, AssetLogger, Bounds, Element, ElementId, GlobalElementId, Hitbox,
|
|
||||||
ImageCacheError, InteractiveElement, Interactivity, IntoElement, Pixels, RenderImage,
|
|
||||||
SharedString, StyleRefinement, Styled, Task, Window,
|
|
||||||
};
|
|
||||||
use image::Frame;
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
use futures_util::{future::Shared, FutureExt};
|
|
||||||
use image::ImageBuffer;
|
|
||||||
|
|
||||||
const SCALE: f32 = 2.;
|
|
||||||
|
|
||||||
static OPTIONS: LazyLock<usvg::Options> = LazyLock::new(|| {
|
|
||||||
let mut options = usvg::Options::default();
|
|
||||||
options.fontdb_mut().load_system_fonts();
|
|
||||||
options
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
|
||||||
pub enum SvgSource {
|
|
||||||
/// A svg bytes
|
|
||||||
Data(Arc<[u8]>),
|
|
||||||
/// An asset path
|
|
||||||
Path(SharedString),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&[u8]> for SvgSource {
|
|
||||||
fn from(data: &[u8]) -> Self {
|
|
||||||
Self::Data(data.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Arc<[u8]>> for SvgSource {
|
|
||||||
fn from(data: Arc<[u8]>) -> Self {
|
|
||||||
Self::Data(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SharedString> for SvgSource {
|
|
||||||
fn from(path: SharedString) -> Self {
|
|
||||||
Self::Path(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&'static str> for SvgSource {
|
|
||||||
fn from(path: &'static str) -> Self {
|
|
||||||
Self::Path(path.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for SvgImg {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
id: self.id.clone(),
|
|
||||||
interactivity: Interactivity::default(),
|
|
||||||
source: self.source.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SvgImageLoader {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ImageSource {
|
|
||||||
source: SvgSource,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for ImageSource {
|
|
||||||
/// Hash to to control the Asset cache
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.source.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asset for SvgImageLoader {
|
|
||||||
type Source = ImageSource;
|
|
||||||
type Output = Result<Arc<RenderImage>, ImageCacheError>;
|
|
||||||
|
|
||||||
fn load(
|
|
||||||
source: Self::Source,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> impl std::future::Future<Output = Self::Output> + Send + 'static {
|
|
||||||
let asset_source = cx.asset_source().clone();
|
|
||||||
|
|
||||||
async move {
|
|
||||||
let bytes = match source.source.clone() {
|
|
||||||
SvgSource::Data(data) => data,
|
|
||||||
SvgSource::Path(path) => {
|
|
||||||
if let Ok(Some(data)) = asset_source.load(&path) {
|
|
||||||
data.deref().to_vec().into()
|
|
||||||
} else {
|
|
||||||
Err(std::io::Error::other(format!(
|
|
||||||
"failed to load svg image from path: {}",
|
|
||||||
path
|
|
||||||
)))
|
|
||||||
.map_err(|e| ImageCacheError::Io(Arc::new(e)))?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let tree = usvg::Tree::from_data(&bytes, &OPTIONS)?;
|
|
||||||
|
|
||||||
// Get svg size
|
|
||||||
let svg_size = tree.size();
|
|
||||||
let mut pixmap = resvg::tiny_skia::Pixmap::new(
|
|
||||||
(svg_size.width() * SCALE) as u32,
|
|
||||||
(svg_size.height() * SCALE) as u32,
|
|
||||||
)
|
|
||||||
.ok_or(usvg::Error::InvalidSize)?;
|
|
||||||
|
|
||||||
let transform = resvg::tiny_skia::Transform::from_scale(SCALE, SCALE);
|
|
||||||
|
|
||||||
resvg::render(&tree, transform, &mut pixmap.as_mut());
|
|
||||||
|
|
||||||
let mut buffer = ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take())
|
|
||||||
.expect("invalid svg image buffer");
|
|
||||||
|
|
||||||
// Convert from RGBA with premultiplied alpha to BGRA with straight alpha.
|
|
||||||
for pixel in buffer.chunks_exact_mut(4) {
|
|
||||||
pixel.swap(0, 2);
|
|
||||||
if pixel[3] > 0 {
|
|
||||||
let a = pixel[3] as f32 / 255.;
|
|
||||||
pixel[0] = (pixel[0] as f32 / a) as u8;
|
|
||||||
pixel[1] = (pixel[1] as f32 / a) as u8;
|
|
||||||
pixel[2] = (pixel[2] as f32 / a) as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = Arc::new(RenderImage::new(SmallVec::from_elem(Frame::new(buffer), 1)));
|
|
||||||
Ok(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SvgImg {
|
|
||||||
id: ElementId,
|
|
||||||
interactivity: Interactivity,
|
|
||||||
source: ImageSource,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SvgImg {
|
|
||||||
/// Create a new svg image element.
|
|
||||||
///
|
|
||||||
/// The `source` can be a string of SVG XML data or a Asset Path.
|
|
||||||
pub fn new(id: impl Into<ElementId>, source: impl Into<SvgSource>) -> Self {
|
|
||||||
Self {
|
|
||||||
id: id.into(),
|
|
||||||
interactivity: Interactivity::default(),
|
|
||||||
source: ImageSource {
|
|
||||||
source: source.into(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the source of the svg image.
|
|
||||||
pub fn source(&self) -> &ImageSource {
|
|
||||||
&self.source
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoElement for SvgImg {
|
|
||||||
type Element = Self;
|
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_svg(
|
|
||||||
source: &ImageSource,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> Shared<Task<Result<Arc<RenderImage>, ImageCacheError>>> {
|
|
||||||
let fut = AssetLogger::<SvgImageLoader>::load(source.clone(), cx);
|
|
||||||
let task = cx.background_executor().spawn(fut).shared();
|
|
||||||
|
|
||||||
let entity = window.current_view();
|
|
||||||
window
|
|
||||||
.spawn(cx, {
|
|
||||||
let task = task.clone();
|
|
||||||
async move |cx| {
|
|
||||||
_ = task.await;
|
|
||||||
cx.on_next_frame(move |_, cx| {
|
|
||||||
cx.notify(entity);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
task
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SvgImgState {
|
|
||||||
hash: u64,
|
|
||||||
image: Option<Arc<RenderImage>>,
|
|
||||||
task: Shared<Task<Result<Arc<RenderImage>, ImageCacheError>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element for SvgImg {
|
|
||||||
type RequestLayoutState = Option<Arc<RenderImage>>;
|
|
||||||
type PrepaintState = (Option<Hitbox>, Option<Arc<RenderImage>>);
|
|
||||||
|
|
||||||
fn id(&self) -> Option<ElementId> {
|
|
||||||
Some(self.id.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn request_layout(
|
|
||||||
&mut self,
|
|
||||||
global_id: Option<&GlobalElementId>,
|
|
||||||
inspector_id: Option<&gpui::InspectorElementId>,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> (gpui::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),
|
|
||||||
);
|
|
||||||
|
|
||||||
let global_id = global_id.unwrap();
|
|
||||||
let source = &self.source;
|
|
||||||
let source_hash = hash(source);
|
|
||||||
|
|
||||||
window.with_element_state::<Option<SvgImgState>, _>(global_id, |state, window| {
|
|
||||||
match state {
|
|
||||||
Some(state) => {
|
|
||||||
// Try to keep the previous image if it's still loading.
|
|
||||||
let mut prev_image = None;
|
|
||||||
if let Some(mut state) = state {
|
|
||||||
prev_image = state.image.clone();
|
|
||||||
if source_hash == state.hash {
|
|
||||||
state.image = state
|
|
||||||
.task
|
|
||||||
.clone()
|
|
||||||
.now_or_never()
|
|
||||||
.transpose()
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.or(state.image);
|
|
||||||
|
|
||||||
return ((layout_id, state.image.clone()), Some(state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let task = load_svg(source, window, cx);
|
|
||||||
let mut image = task.clone().now_or_never().transpose().ok().flatten();
|
|
||||||
if let Some(new_image) = image.as_ref() {
|
|
||||||
_ = window.drop_image(new_image.clone());
|
|
||||||
} else {
|
|
||||||
image = prev_image;
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
(layout_id, image.clone()),
|
|
||||||
Some(SvgImgState {
|
|
||||||
hash: source_hash,
|
|
||||||
image,
|
|
||||||
task,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let task = load_svg(source, window, cx);
|
|
||||||
(
|
|
||||||
(layout_id, None),
|
|
||||||
Some(SvgImgState {
|
|
||||||
hash: source_hash,
|
|
||||||
image: None,
|
|
||||||
task,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepaint(
|
|
||||||
&mut self,
|
|
||||||
global_id: Option<&GlobalElementId>,
|
|
||||||
inspector_id: Option<&gpui::InspectorElementId>,
|
|
||||||
bounds: Bounds<Pixels>,
|
|
||||||
state: &mut Self::RequestLayoutState,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> Self::PrepaintState {
|
|
||||||
let hitbox = self.interactivity.prepaint(
|
|
||||||
global_id,
|
|
||||||
inspector_id,
|
|
||||||
bounds,
|
|
||||||
bounds.size,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
|_, _, hitbox, _, _| hitbox,
|
|
||||||
);
|
|
||||||
|
|
||||||
(hitbox, state.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
global_id: Option<&GlobalElementId>,
|
|
||||||
inspector_id: Option<&gpui::InspectorElementId>,
|
|
||||||
bounds: Bounds<Pixels>,
|
|
||||||
_: &mut Self::RequestLayoutState,
|
|
||||||
state: &mut Self::PrepaintState,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut App,
|
|
||||||
) {
|
|
||||||
let hitbox = state.0.as_ref();
|
|
||||||
let Some(image) = state.1.take() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let size = image.size(0).map(|x| x.0 as f32);
|
|
||||||
|
|
||||||
self.interactivity.paint(
|
|
||||||
global_id,
|
|
||||||
inspector_id,
|
|
||||||
bounds,
|
|
||||||
hitbox,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
|_, window, _| {
|
|
||||||
// To calculate the ratio of the original image size to the container bounds size.
|
|
||||||
// Scale by shortest side (width or height) to get a fit image.
|
|
||||||
// And center the image in the container bounds.
|
|
||||||
let ratio = if bounds.size.width < bounds.size.height {
|
|
||||||
bounds.size.width / size.width
|
|
||||||
} else {
|
|
||||||
bounds.size.height / size.height
|
|
||||||
};
|
|
||||||
|
|
||||||
let ratio = ratio.0.min(1.0);
|
|
||||||
let new_size = size.map(|dim| px(dim) * ratio);
|
|
||||||
|
|
||||||
let new_origin = gpui::Point {
|
|
||||||
x: bounds.origin.x + px(((bounds.size.width - new_size.width) / 2.).into()),
|
|
||||||
y: bounds.origin.y + px(((bounds.size.height - new_size.height) / 2.).into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let img_bounds = Bounds {
|
|
||||||
origin: new_origin.map(|origin| origin.floor()),
|
|
||||||
size: new_size.map(|size| size.ceil()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = window.paint_image(img_bounds, px(0.).into(), image, 0, false) {
|
|
||||||
eprintln!("failed to paint svg image: {:?}", err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Styled for SvgImg {
|
|
||||||
fn style(&mut self) -> &mut StyleRefinement {
|
|
||||||
&mut self.interactivity.base_style
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InteractiveElement for SvgImg {
|
|
||||||
fn interactivity(&mut self) -> &mut Interactivity {
|
|
||||||
&mut self.interactivity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue