Fixes: https://github.com/longbridge/gpui-component/issues/994
1. When SVG is rotated, incorrect graphics are drawn.
For example: the original aspect ratio of the SVG is 1:1, if the bounds
used to render the SVG are 400x200 (aspect ratio 2:1),
[here](21f985a018/crates/gpui/src/svg_renderer.rs (L91))
the width is used as the scaling factor, causing the rendered SVG to
only have half the height. This PR ensures the complete SVG image is
always rendered.
2. The clipping region has no transformation applied, I added a function
called `distance_from_clip_rect_transformed` in the shader.
3. Fixed `monochrome_sprite_fragment` in `shader.metal` not applying
clipping region.
### Before:
https://github.com/user-attachments/assets/8f93ac36-281e-4837-96cd-c308bfbf92d1
### After:
https://github.com/user-attachments/assets/f52b67a6-4cb9-4d6c-b759-bbb91b59c1cf
Release Notes:
- N/A
---------
Co-authored-by: Jason Lee <huacnlee@gmail.com>
122 lines
4.1 KiB
Rust
122 lines
4.1 KiB
Rust
use std::time::Duration;
|
|
|
|
use anyhow::Result;
|
|
use gpui::{
|
|
Animation, AnimationExt as _, App, Application, AssetSource, Bounds, Context, SharedString,
|
|
Transformation, Window, WindowBounds, WindowOptions, bounce, div, ease_in_out, percentage,
|
|
prelude::*, px, size, svg,
|
|
};
|
|
|
|
struct Assets {}
|
|
|
|
impl AssetSource for Assets {
|
|
fn load(&self, path: &str) -> Result<Option<std::borrow::Cow<'static, [u8]>>> {
|
|
std::fs::read(path)
|
|
.map(Into::into)
|
|
.map_err(Into::into)
|
|
.map(Some)
|
|
}
|
|
|
|
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
|
|
Ok(std::fs::read_dir(path)?
|
|
.filter_map(|entry| {
|
|
Some(SharedString::from(
|
|
entry.ok()?.path().to_string_lossy().into_owned(),
|
|
))
|
|
})
|
|
.collect::<Vec<_>>())
|
|
}
|
|
}
|
|
|
|
const ARROW_CIRCLE_SVG: &str = concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/examples/image/arrow_circle.svg"
|
|
);
|
|
|
|
struct AnimationExample {}
|
|
|
|
impl Render for AnimationExample {
|
|
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
|
div()
|
|
.flex()
|
|
.flex_col()
|
|
.size_full()
|
|
.bg(gpui::white())
|
|
.text_color(gpui::black())
|
|
.justify_around()
|
|
.child(
|
|
div()
|
|
.flex()
|
|
.flex_col()
|
|
.size_full()
|
|
.justify_around()
|
|
.child(
|
|
div()
|
|
.id("content")
|
|
.flex()
|
|
.flex_col()
|
|
.h(px(150.))
|
|
.overflow_y_scroll()
|
|
.w_full()
|
|
.flex_1()
|
|
.justify_center()
|
|
.items_center()
|
|
.text_xl()
|
|
.gap_4()
|
|
.child("Hello Animation")
|
|
.child(
|
|
svg()
|
|
.size_20()
|
|
.overflow_hidden()
|
|
.path(ARROW_CIRCLE_SVG)
|
|
.text_color(gpui::black())
|
|
.with_animation(
|
|
"image_circle",
|
|
Animation::new(Duration::from_secs(2))
|
|
.repeat()
|
|
.with_easing(bounce(ease_in_out)),
|
|
|svg, delta| {
|
|
svg.with_transformation(Transformation::rotate(
|
|
percentage(delta),
|
|
))
|
|
},
|
|
),
|
|
),
|
|
)
|
|
.child(
|
|
div()
|
|
.flex()
|
|
.h(px(64.))
|
|
.w_full()
|
|
.p_2()
|
|
.justify_center()
|
|
.items_center()
|
|
.border_t_1()
|
|
.border_color(gpui::black().opacity(0.1))
|
|
.bg(gpui::black().opacity(0.05))
|
|
.child("Other Panel"),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
Application::new()
|
|
.with_assets(Assets {})
|
|
.run(|cx: &mut App| {
|
|
let options = WindowOptions {
|
|
window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
|
|
None,
|
|
size(px(300.), px(300.)),
|
|
cx,
|
|
))),
|
|
..Default::default()
|
|
};
|
|
cx.open_window(options, |_, cx| {
|
|
cx.activate(false);
|
|
cx.new(|_| AnimationExample {})
|
|
})
|
|
.unwrap();
|
|
});
|
|
}
|