Release Notes:
- N/A
Currently, the `img` element provided by GPUI only supports FilePath or
URL, but in actual applications we need to let `img` load an image
embedded in Assets.
The `svg` element can currently support this, but `img` cannot.
For example:
We have such an Assets directory:
```
assets
|- icons
|- images
|--- foo.png
```
```rs
// If give a path, considered an Asset
img("images/foo.png");
// If give a URI, considered a Remote image
img("https://foo.bar/images/foo.png");
// If give a PathBuf, considered a Local file
img(PathBuf::from("path/to/foo.png"));
```
## Example test
```
cargo run -p gpui --example image
```
<img width="827" alt="image"
src="https://github.com/user-attachments/assets/e45dcf7f-4626-4fb0-aca9-9b6e1045a952">
---------
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
89 lines
2.3 KiB
Rust
89 lines
2.3 KiB
Rust
use crate::{SharedString, SharedUri, WindowContext};
|
|
use collections::FxHashMap;
|
|
use futures::Future;
|
|
use parking_lot::Mutex;
|
|
use std::any::TypeId;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::sync::Arc;
|
|
use std::{any::Any, path::PathBuf};
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
|
pub(crate) enum UriOrPath {
|
|
Uri(SharedUri),
|
|
Path(Arc<PathBuf>),
|
|
Asset(SharedString),
|
|
}
|
|
|
|
impl From<SharedUri> for UriOrPath {
|
|
fn from(value: SharedUri) -> Self {
|
|
Self::Uri(value)
|
|
}
|
|
}
|
|
|
|
impl From<Arc<PathBuf>> for UriOrPath {
|
|
fn from(value: Arc<PathBuf>) -> Self {
|
|
Self::Path(value)
|
|
}
|
|
}
|
|
|
|
/// A trait for asynchronous asset loading.
|
|
pub trait Asset {
|
|
/// 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 WindowContext,
|
|
) -> impl Future<Output = Self::Output> + Send + 'static;
|
|
}
|
|
|
|
/// 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()
|
|
}
|
|
|
|
/// A cache for assets.
|
|
#[derive(Clone)]
|
|
pub struct AssetCache {
|
|
assets: Arc<Mutex<FxHashMap<(TypeId, u64), Box<dyn Any + Send>>>>,
|
|
}
|
|
|
|
impl AssetCache {
|
|
pub(crate) fn new() -> Self {
|
|
Self {
|
|
assets: Default::default(),
|
|
}
|
|
}
|
|
|
|
/// Get the asset from the cache, if it exists.
|
|
pub fn get<A: Asset + 'static>(&self, source: &A::Source) -> Option<A::Output> {
|
|
self.assets
|
|
.lock()
|
|
.get(&(TypeId::of::<A>(), hash(&source)))
|
|
.and_then(|task| task.downcast_ref::<A::Output>())
|
|
.cloned()
|
|
}
|
|
|
|
/// Insert the asset into the cache.
|
|
pub fn insert<A: Asset + 'static>(&mut self, source: A::Source, output: A::Output) {
|
|
self.assets
|
|
.lock()
|
|
.insert((TypeId::of::<A>(), hash(&source)), Box::new(output));
|
|
}
|
|
|
|
/// Remove an entry from the asset cache
|
|
pub fn remove<A: Asset + 'static>(&mut self, source: &A::Source) -> Option<A::Output> {
|
|
self.assets
|
|
.lock()
|
|
.remove(&(TypeId::of::<A>(), hash(&source)))
|
|
.and_then(|any| any.downcast::<A::Output>().ok())
|
|
.map(|boxed| *boxed)
|
|
}
|
|
}
|