Load images in the background

Antonio Scandurra created

Change summary

crates/gpui/src/elements/img.rs |  2 
crates/gpui/src/image_cache.rs  | 75 ++++++++++++++++------------------
2 files changed, 37 insertions(+), 40 deletions(-)

Detailed changes

crates/gpui/src/elements/img.rs 🔗

@@ -104,7 +104,7 @@ impl Element for Img {
                 cx.with_z_index(1, |cx| {
                     match source {
                         ImageSource::Uri(uri) => {
-                            let image_future = cx.image_cache.get(uri.clone());
+                            let image_future = cx.image_cache.get(uri.clone(), cx);
                             if let Some(data) = image_future
                                 .clone()
                                 .now_or_never()

crates/gpui/src/image_cache.rs 🔗

@@ -1,9 +1,6 @@
-use crate::{ImageData, ImageId, SharedUrl};
+use crate::{AppContext, ImageData, ImageId, SharedUrl, Task};
 use collections::HashMap;
-use futures::{
-    future::{BoxFuture, Shared},
-    AsyncReadExt, FutureExt, TryFutureExt,
-};
+use futures::{future::Shared, AsyncReadExt, FutureExt, TryFutureExt};
 use image::ImageError;
 use parking_lot::Mutex;
 use std::sync::Arc;
@@ -44,10 +41,10 @@ impl From<ImageError> for Error {
 
 pub(crate) struct ImageCache {
     client: Arc<dyn HttpClient>,
-    images: Arc<Mutex<HashMap<SharedUrl, FetchImageFuture>>>,
+    images: Arc<Mutex<HashMap<SharedUrl, FetchImageTask>>>,
 }
 
-type FetchImageFuture = Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>>;
+type FetchImageTask = Shared<Task<Result<Arc<ImageData>, Error>>>;
 
 impl ImageCache {
     pub fn new(client: Arc<dyn HttpClient>) -> Self {
@@ -57,10 +54,7 @@ impl ImageCache {
         }
     }
 
-    pub fn get(
-        &self,
-        uri: impl Into<SharedUrl>,
-    ) -> Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>> {
+    pub fn get(&self, uri: impl Into<SharedUrl>, cx: &AppContext) -> FetchImageTask {
         let uri = uri.into();
         let mut images = self.images.lock();
 
@@ -68,36 +62,39 @@ impl ImageCache {
             Some(future) => future.clone(),
             None => {
                 let client = self.client.clone();
-                let future = {
-                    let uri = uri.clone();
-                    async move {
-                        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?;
+                let future = cx
+                    .background_executor()
+                    .spawn(
+                        {
+                            let uri = uri.clone();
+                            async move {
+                                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?;
 
-                        if !response.status().is_success() {
-                            return Err(Error::BadStatus {
-                                status: response.status(),
-                                body: String::from_utf8_lossy(&body).into_owned(),
-                            });
-                        }
-
-                        let format = image::guess_format(&body)?;
-                        let image =
-                            image::load_from_memory_with_format(&body, format)?.into_bgra8();
-                        Ok(Arc::new(ImageData::new(image)))
-                    }
-                }
-                .map_err({
-                    let uri = uri.clone();
+                                if !response.status().is_success() {
+                                    return Err(Error::BadStatus {
+                                        status: response.status(),
+                                        body: String::from_utf8_lossy(&body).into_owned(),
+                                    });
+                                }
 
-                    move |error| {
-                        log::log!(log::Level::Error, "{:?} {:?}", &uri, &error);
-                        error
-                    }
-                })
-                .boxed()
-                .shared();
+                                let format = image::guess_format(&body)?;
+                                let image = image::load_from_memory_with_format(&body, format)?
+                                    .into_bgra8();
+                                Ok(Arc::new(ImageData::new(image)))
+                            }
+                        }
+                        .map_err({
+                            let uri = uri.clone();
+                            move |error| {
+                                log::log!(log::Level::Error, "{:?} {:?}", &uri, &error);
+                                error
+                            }
+                        }),
+                    )
+                    .shared();
 
                 images.insert(uri, future.clone());
                 future