
@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>
84 lines
2.1 KiB
Rust
84 lines
2.1 KiB
Rust
use crate::{AppContext, SharedString, SharedUri};
|
|
use futures::Future;
|
|
|
|
use std::fmt::Debug;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::marker::PhantomData;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::Arc;
|
|
|
|
/// An enum representing
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
|
pub enum Resource {
|
|
/// This resource is at a given URI
|
|
Uri(SharedUri),
|
|
/// This resource is at a given path in the file system
|
|
Path(Arc<Path>),
|
|
/// This resource is embedded in the application binary
|
|
Embedded(SharedString),
|
|
}
|
|
|
|
impl From<SharedUri> for Resource {
|
|
fn from(value: SharedUri) -> Self {
|
|
Self::Uri(value)
|
|
}
|
|
}
|
|
|
|
impl From<PathBuf> for Resource {
|
|
fn from(value: PathBuf) -> Self {
|
|
Self::Path(value.into())
|
|
}
|
|
}
|
|
|
|
impl From<Arc<Path>> for Resource {
|
|
fn from(value: Arc<Path>) -> Self {
|
|
Self::Path(value)
|
|
}
|
|
}
|
|
|
|
/// A trait for asynchronous asset loading.
|
|
pub trait Asset: 'static {
|
|
/// The source of the asset.
|
|
type Source: Clone + Hash + Send;
|
|
|
|
/// The loaded asset
|
|
type Output: Clone + Send;
|
|
|
|
/// Load the asset asynchronously
|
|
fn load(
|
|
source: Self::Source,
|
|
cx: &mut AppContext,
|
|
) -> impl Future<Output = Self::Output> + Send + 'static;
|
|
}
|
|
|
|
/// An asset Loader that logs whatever passes through it
|
|
pub enum AssetLogger<T> {
|
|
#[doc(hidden)]
|
|
_Phantom(PhantomData<T>, &'static dyn crate::seal::Sealed),
|
|
}
|
|
|
|
impl<R: Clone + Send, E: Clone + Send + std::error::Error, T: Asset<Output = Result<R, E>>> Asset
|
|
for AssetLogger<T>
|
|
{
|
|
type Source = T::Source;
|
|
|
|
type Output = T::Output;
|
|
|
|
fn load(
|
|
source: Self::Source,
|
|
cx: &mut AppContext,
|
|
) -> impl Future<Output = Self::Output> + Send + 'static {
|
|
let load = T::load(source, cx);
|
|
async {
|
|
load.await
|
|
.inspect_err(|e| log::error!("Failed to load asset: {}", e))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Use a quick, non-cryptographically secure hash function to get an identifier from data
|
|
pub fn hash<T: Hash>(data: &T) -> u64 {
|
|
let mut hasher = collections::FxHasher::default();
|
|
data.hash(&mut hasher);
|
|
hasher.finish()
|
|
}
|