From 99ad60460a501428d896df37fecb17f600f160e5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 6 Sep 2023 17:13:38 -0600 Subject: [PATCH] Add support for fetching/rendering images --- Cargo.lock | 1 + crates/gpui/src/image_cache.rs | 32 +++----- crates/gpui2/Cargo.toml | 1 + crates/gpui2/src/elements.rs | 2 + crates/gpui2/src/elements/img.rs | 105 +++++++++++++++++++++++++++ crates/storybook/src/collab_panel.rs | 39 +++++++--- crates/util/src/arc_cow.rs | 13 +++- 7 files changed, 161 insertions(+), 32 deletions(-) create mode 100644 crates/gpui2/src/elements/img.rs diff --git a/Cargo.lock b/Cargo.lock index cf14fcf989..2a1236bda3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3177,6 +3177,7 @@ version = "0.1.0" dependencies = [ "anyhow", "derive_more", + "futures 0.3.28", "gpui", "gpui2_macros", "log", diff --git a/crates/gpui/src/image_cache.rs b/crates/gpui/src/image_cache.rs index 77195c493f..d7682a43fd 100644 --- a/crates/gpui/src/image_cache.rs +++ b/crates/gpui/src/image_cache.rs @@ -11,7 +11,6 @@ use parking_lot::Mutex; use thiserror::Error; use util::{ arc_cow::ArcCow, - defer, http::{self, HttpClient}, }; @@ -44,16 +43,11 @@ impl From for Error { pub struct ImageCache { client: Arc, - images: Arc< - Mutex< - HashMap< - ArcCow<'static, str>, - Shared, Error>>>, - >, - >, - >, + images: Arc, FetchImageFuture>>>, } +type FetchImageFuture = Shared, Error>>>; + impl ImageCache { pub fn new(client: Arc) -> Self { ImageCache { @@ -64,24 +58,18 @@ impl ImageCache { pub fn get( &self, - uri: ArcCow<'static, str>, + uri: impl Into>, ) -> Shared, Error>>> { - match self.images.lock().get(uri.as_ref()) { + let uri = uri.into(); + let mut images = self.images.lock(); + + match images.get(uri.as_ref()) { Some(future) => future.clone(), None => { let client = self.client.clone(); - let images = self.images.clone(); let future = { let uri = uri.clone(); async move { - // If we error, remove the cached future. Otherwise we cancel before returning. - let remove_cached_future = defer({ - let uri = uri.clone(); - move || { - images.lock().remove(uri.as_ref()); - } - }); - let mut response = client.get(uri.as_ref(), ().into(), true).await?; let mut body = Vec::new(); response.body_mut().read_to_end(&mut body).await?; @@ -97,13 +85,13 @@ impl ImageCache { let image = image::load_from_memory_with_format(&body, format)?.into_bgra8(); - remove_cached_future.cancel(); Ok(ImageData::new(image)) } } .boxed() .shared(); - self.images.lock().insert(uri.clone(), future.clone()); + + images.insert(uri, future.clone()); future } } diff --git a/crates/gpui2/Cargo.toml b/crates/gpui2/Cargo.toml index 7d95c1e765..093ab1e347 100644 --- a/crates/gpui2/Cargo.toml +++ b/crates/gpui2/Cargo.toml @@ -16,6 +16,7 @@ anyhow.workspace = true derive_more.workspace = true gpui = { path = "../gpui" } log.workspace = true +futures.workspace = true gpui2_macros = { path = "../gpui2_macros" } parking_lot.workspace = true refineable.workspace = true diff --git a/crates/gpui2/src/elements.rs b/crates/gpui2/src/elements.rs index 65b8c7119f..5b4942fc46 100644 --- a/crates/gpui2/src/elements.rs +++ b/crates/gpui2/src/elements.rs @@ -1,8 +1,10 @@ pub mod div; pub mod hoverable; +mod img; pub mod pressable; pub mod svg; pub mod text; pub use div::div; +pub use img::img; pub use svg::svg; diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs new file mode 100644 index 0000000000..cf22f29869 --- /dev/null +++ b/crates/gpui2/src/elements/img.rs @@ -0,0 +1,105 @@ +use crate as gpui2; +use crate::style::{StyleHelpers, Styleable}; +use crate::{style::Style, Element}; +use futures::FutureExt; +use gpui::scene; +use gpui2_macros::IntoElement; +use refineable::RefinementCascade; +use util::arc_cow::ArcCow; +use util::ResultExt; + +#[derive(IntoElement)] +pub struct Img { + style: RefinementCascade