diff --git a/src/widgets/image.rs b/src/widgets/image.rs index 94e9230..4ac058b 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -1,11 +1,15 @@ -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; -use cushy::{kludgine::{AnyTexture, LazyTexture}, value::{CallbackDisconnected, CallbackHandle, Destination, Dynamic, Source, Value}, widgets::Image}; +use cushy::{ + kludgine::{AnyTexture, LazyTexture}, + value::{CallbackDisconnected, CallbackHandle, Destination, Dynamic, Source, Value}, + widgets::Image, +}; use futures_util::lock::Mutex; use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions}; use image::imageops::FilterType; use reqwest::Client; -use reqwest_middleware::ClientBuilder; +use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use tokio::task::JoinHandle; use crate::rt::tokio_runtime; @@ -16,13 +20,24 @@ pub trait ImageExt { fn load_url(&mut self, url: Dynamic>) -> CallbackHandle; fn with_url(mut self, url: Dynamic>) -> Self - where Self: Sized + where + Self: Sized, { self.load_url(url).persist(); self } } +static CLIENT: LazyLock = LazyLock::new(|| { + ClientBuilder::new(Client::new()) + .with(Cache(HttpCache { + mode: CacheMode::Default, + manager: CACacheManager::default(), + options: HttpCacheOptions::default(), + })) + .build() +}); + impl ImageExt for Image { fn new_empty() -> Self { Image::new(Dynamic::new(get_empty_texture())) @@ -31,15 +46,6 @@ impl ImageExt for Image { /// Makes the image connected to a URL /// Calling this multiple times on a single image may cause memory leaks fn load_url(&mut self, url: Dynamic>) -> CallbackHandle { - let client = ClientBuilder::new(Client::new()) - .with(Cache(HttpCache { - mode: CacheMode::Default, - manager: CACacheManager::default(), - options: HttpCacheOptions::default(), - })) - .build(); - - // let texture = Dynamic::new(get_empty_texture()); match &mut self.contents { Value::Constant(_) => self.contents = Value::Dynamic(Dynamic::new(get_empty_texture())), @@ -47,9 +53,9 @@ impl ImageExt for Image { } let texture = match &self.contents { Value::Dynamic(dynamic) => dynamic, - _ => unreachable!() + _ => unreachable!(), }; - + let prev_request_join = Arc::new(Mutex::new(None::>)); url.for_each_try({ let texture = texture.clone(); @@ -58,16 +64,18 @@ impl ImageExt for Image { if texture_count <= 1 { return Err(CallbackDisconnected); } + println!("loading url {:?}", url); let guard = tokio_runtime().enter(); let url = url.clone(); let prev_request_join = prev_request_join.clone(); let texture = texture.clone(); - let client = client.clone(); + let client = CLIENT.clone(); tokio::spawn(async move { let mut prev_request_join = prev_request_join.lock().await; if let Some(prev_request_join) = prev_request_join.take() { prev_request_join.abort(); } + println!("loading url {:?}", url); if let Some(url) = url { let texture = texture.clone(); let client = client.clone(); @@ -75,8 +83,11 @@ impl ImageExt for Image { let response = client.get(url).send().await.unwrap(); let bytes = response.bytes().await.unwrap(); let image = image::load_from_memory(&bytes).unwrap(); - let image = image.resize(128, 128, FilterType::Lanczos3); - let image_texture = LazyTexture::from_image(image, cushy::kludgine::wgpu::FilterMode::Linear); + // let image = image.resize(128, 128, FilterType::Lanczos3); + let image_texture = LazyTexture::from_image( + image, + cushy::kludgine::wgpu::FilterMode::Linear, + ); let image_texture = AnyTexture::Lazy(image_texture); texture.set(image_texture); })); @@ -91,14 +102,9 @@ impl ImageExt for Image { } } - fn get_empty_texture() -> AnyTexture { - AnyTexture::Lazy( - LazyTexture::from_image( - image::DynamicImage::ImageRgba8( - image::ImageBuffer::new(1, 1) - ), - cushy::kludgine::wgpu::FilterMode::Linear - ) - ) -} \ No newline at end of file + AnyTexture::Lazy(LazyTexture::from_image( + image::DynamicImage::ImageRgba8(image::ImageBuffer::new(1, 1)), + cushy::kludgine::wgpu::FilterMode::Linear, + )) +} diff --git a/src/widgets/pages/liked.rs b/src/widgets/pages/liked.rs index 3dc3466..26e8d85 100644 --- a/src/widgets/pages/liked.rs +++ b/src/widgets/pages/liked.rs @@ -8,7 +8,10 @@ use cushy::{ styles::{Dimension, DimensionRange}, value::{Destination, Dynamic, Source}, widget::{MakeWidget, WidgetInstance}, - widgets::{label::LabelOverflow, Image, Label, Space, VirtualList}, + widgets::{ + label::{Displayable, LabelOverflow}, + Image, Label, Space, VirtualList, + }, }; use itertools::Itertools; use rspotify::model::SavedTrack; @@ -35,7 +38,6 @@ fn get_or_create_track_image( idx: usize, create: impl FnOnce() -> WidgetInstance, ) -> WidgetInstance { - println!("Getting image"); let mut locked = track_images.lock().unwrap(); if let Some(image) = locked.get(&idx) { image.clone() @@ -147,19 +149,43 @@ impl LikedSongsPage { .and(track.map_each(|track| { track .as_ref() - .map(|track| track.track.album.name.clone().make_widget()) + .map(|track| { + track + .track + .album + .name + .clone() + .into_label() + .overflow(LabelOverflow::Clip) + .make_widget() + }) .unwrap_or(Space::primary().make_widget()) })) .and(track.map_each(|track| { track .as_ref() - .map(|track| track.added_at.to_string().make_widget()) + .map(|track| { + track + .added_at + .to_string() + .into_label() + .overflow(LabelOverflow::Clip) + .make_widget() + }) .unwrap_or(Space::primary().make_widget()) })) .and(track.map_each(|track| { track .as_ref() - .map(|track| track.track.duration.to_string().make_widget()) + .map(|track| { + track + .track + .duration + .to_string() + .into_label() + .overflow(LabelOverflow::Clip) + .make_widget() + }) .unwrap_or(Space::primary().make_widget()) })) .into_columns()