Add Loading and Fallback States to Image Elements (via StyledImage) (#20371)

@iamnbutler edit:

This pull request enhances the image element by introducing the ability
to display loading and fallback states.

Changes:

- Implemented the loading and fallback states for image elements using
`.with_loading` and `.with_fallback` respectively.
- Introduced the `StyledImage` trait and `ImageStyle` to enable a fluent
API for changing image styles across image types (`Img`,
`Stateful<Img>`, etc).

Example Usage:

```rust
fn loading_element() -> impl IntoElement {
    div().size_full().flex_none().p_0p5().rounded_sm().child(
        div().size_full().with_animation(
            "loading-bg",
            Animation::new(Duration::from_secs(3))
                .repeat()
                .with_easing(pulsating_between(0.04, 0.24)),
            move |this, delta| this.bg(black().opacity(delta)),
        ),
    )
}

fn fallback_element() -> impl IntoElement {
    let fallback_color: Hsla = black().opacity(0.5);

    div().size_full().flex_none().p_0p5().child(
        div()
            .size_full()
            .flex()
            .items_center()
            .justify_center()
            .rounded_sm()
            .text_sm()
            .text_color(fallback_color)
            .border_1()
            .border_color(fallback_color)
            .child("?"),
    )
}

impl Render for ImageLoadingExample {
    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
        img("some/image/path")
            .id("image-1")
            .with_fallback(|| Self::fallback_element().into_any_element())
            .with_loading(|| Self::loading_element().into_any_element())
    }
}
```

Note:

An `Img` must have an `id` to be able to add a loading state.

Release Notes:

- N/A

---------

Co-authored-by: nate <nate@zed.dev>
Co-authored-by: michael <michael@zed.dev>
Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
Mikayla Maki 2024-11-15 19:12:01 -08:00 committed by GitHub
parent f34877334e
commit 516f7b3642
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1700 additions and 1041 deletions

View file

@ -747,7 +747,7 @@ impl AppContext {
}
/// Returns the SVG renderer GPUI uses
pub(crate) fn svg_renderer(&self) -> SvgRenderer {
pub fn svg_renderer(&self) -> SvgRenderer {
self.svg_renderer.clone()
}
@ -1369,7 +1369,7 @@ impl AppContext {
}
/// Remove an asset from GPUI's cache
pub fn remove_cached_asset<A: Asset + 'static>(&mut self, source: &A::Source) {
pub fn remove_asset<A: Asset>(&mut self, source: &A::Source) {
let asset_id = (TypeId::of::<A>(), hash(source));
self.loading_assets.remove(&asset_id);
}
@ -1378,12 +1378,7 @@ impl AppContext {
///
/// Note that the multiple calls to this method will only result in one `Asset::load` call at a
/// time, and the results of this call will be cached
///
/// This asset will not be cached by default, see [Self::use_cached_asset]
pub fn fetch_asset<A: Asset + 'static>(
&mut self,
source: &A::Source,
) -> (Shared<Task<A::Output>>, bool) {
pub fn fetch_asset<A: Asset>(&mut self, source: &A::Source) -> (Shared<Task<A::Output>>, bool) {
let asset_id = (TypeId::of::<A>(), hash(source));
let mut is_first = false;
let task = self