mirror of
https://github.com/danbulant/despot
synced 2026-07-05 19:10:57 +00:00
update image loading
This commit is contained in:
parent
d08dd6dcfc
commit
f3760ec4e8
2 changed files with 65 additions and 33 deletions
|
|
@ -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 futures_util::lock::Mutex;
|
||||||
use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions};
|
use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions};
|
||||||
use image::imageops::FilterType;
|
use image::imageops::FilterType;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use reqwest_middleware::ClientBuilder;
|
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use crate::rt::tokio_runtime;
|
use crate::rt::tokio_runtime;
|
||||||
|
|
@ -16,13 +20,24 @@ pub trait ImageExt {
|
||||||
fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle;
|
fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle;
|
||||||
|
|
||||||
fn with_url(mut self, url: Dynamic<Option<String>>) -> Self
|
fn with_url(mut self, url: Dynamic<Option<String>>) -> Self
|
||||||
where Self: Sized
|
where
|
||||||
|
Self: Sized,
|
||||||
{
|
{
|
||||||
self.load_url(url).persist();
|
self.load_url(url).persist();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CLIENT: LazyLock<ClientWithMiddleware> = LazyLock::new(|| {
|
||||||
|
ClientBuilder::new(Client::new())
|
||||||
|
.with(Cache(HttpCache {
|
||||||
|
mode: CacheMode::Default,
|
||||||
|
manager: CACacheManager::default(),
|
||||||
|
options: HttpCacheOptions::default(),
|
||||||
|
}))
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
|
||||||
impl ImageExt for Image {
|
impl ImageExt for Image {
|
||||||
fn new_empty() -> Self {
|
fn new_empty() -> Self {
|
||||||
Image::new(Dynamic::new(get_empty_texture()))
|
Image::new(Dynamic::new(get_empty_texture()))
|
||||||
|
|
@ -31,15 +46,6 @@ impl ImageExt for Image {
|
||||||
/// Makes the image connected to a URL
|
/// Makes the image connected to a URL
|
||||||
/// Calling this multiple times on a single image may cause memory leaks
|
/// Calling this multiple times on a single image may cause memory leaks
|
||||||
fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle {
|
fn load_url(&mut self, url: Dynamic<Option<String>>) -> 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());
|
// let texture = Dynamic::new(get_empty_texture());
|
||||||
match &mut self.contents {
|
match &mut self.contents {
|
||||||
Value::Constant(_) => self.contents = Value::Dynamic(Dynamic::new(get_empty_texture())),
|
Value::Constant(_) => self.contents = Value::Dynamic(Dynamic::new(get_empty_texture())),
|
||||||
|
|
@ -47,9 +53,9 @@ impl ImageExt for Image {
|
||||||
}
|
}
|
||||||
let texture = match &self.contents {
|
let texture = match &self.contents {
|
||||||
Value::Dynamic(dynamic) => dynamic,
|
Value::Dynamic(dynamic) => dynamic,
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let prev_request_join = Arc::new(Mutex::new(None::<JoinHandle<()>>));
|
let prev_request_join = Arc::new(Mutex::new(None::<JoinHandle<()>>));
|
||||||
url.for_each_try({
|
url.for_each_try({
|
||||||
let texture = texture.clone();
|
let texture = texture.clone();
|
||||||
|
|
@ -58,16 +64,18 @@ impl ImageExt for Image {
|
||||||
if texture_count <= 1 {
|
if texture_count <= 1 {
|
||||||
return Err(CallbackDisconnected);
|
return Err(CallbackDisconnected);
|
||||||
}
|
}
|
||||||
|
println!("loading url {:?}", url);
|
||||||
let guard = tokio_runtime().enter();
|
let guard = tokio_runtime().enter();
|
||||||
let url = url.clone();
|
let url = url.clone();
|
||||||
let prev_request_join = prev_request_join.clone();
|
let prev_request_join = prev_request_join.clone();
|
||||||
let texture = texture.clone();
|
let texture = texture.clone();
|
||||||
let client = client.clone();
|
let client = CLIENT.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut prev_request_join = prev_request_join.lock().await;
|
let mut prev_request_join = prev_request_join.lock().await;
|
||||||
if let Some(prev_request_join) = prev_request_join.take() {
|
if let Some(prev_request_join) = prev_request_join.take() {
|
||||||
prev_request_join.abort();
|
prev_request_join.abort();
|
||||||
}
|
}
|
||||||
|
println!("loading url {:?}", url);
|
||||||
if let Some(url) = url {
|
if let Some(url) = url {
|
||||||
let texture = texture.clone();
|
let texture = texture.clone();
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
|
|
@ -75,8 +83,11 @@ impl ImageExt for Image {
|
||||||
let response = client.get(url).send().await.unwrap();
|
let response = client.get(url).send().await.unwrap();
|
||||||
let bytes = response.bytes().await.unwrap();
|
let bytes = response.bytes().await.unwrap();
|
||||||
let image = image::load_from_memory(&bytes).unwrap();
|
let image = image::load_from_memory(&bytes).unwrap();
|
||||||
let image = image.resize(128, 128, FilterType::Lanczos3);
|
// let image = image.resize(128, 128, FilterType::Lanczos3);
|
||||||
let image_texture = LazyTexture::from_image(image, cushy::kludgine::wgpu::FilterMode::Linear);
|
let image_texture = LazyTexture::from_image(
|
||||||
|
image,
|
||||||
|
cushy::kludgine::wgpu::FilterMode::Linear,
|
||||||
|
);
|
||||||
let image_texture = AnyTexture::Lazy(image_texture);
|
let image_texture = AnyTexture::Lazy(image_texture);
|
||||||
texture.set(image_texture);
|
texture.set(image_texture);
|
||||||
}));
|
}));
|
||||||
|
|
@ -91,14 +102,9 @@ impl ImageExt for Image {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn get_empty_texture() -> AnyTexture {
|
fn get_empty_texture() -> AnyTexture {
|
||||||
AnyTexture::Lazy(
|
AnyTexture::Lazy(LazyTexture::from_image(
|
||||||
LazyTexture::from_image(
|
image::DynamicImage::ImageRgba8(image::ImageBuffer::new(1, 1)),
|
||||||
image::DynamicImage::ImageRgba8(
|
cushy::kludgine::wgpu::FilterMode::Linear,
|
||||||
image::ImageBuffer::new(1, 1)
|
))
|
||||||
),
|
}
|
||||||
cushy::kludgine::wgpu::FilterMode::Linear
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ use cushy::{
|
||||||
styles::{Dimension, DimensionRange},
|
styles::{Dimension, DimensionRange},
|
||||||
value::{Destination, Dynamic, Source},
|
value::{Destination, Dynamic, Source},
|
||||||
widget::{MakeWidget, WidgetInstance},
|
widget::{MakeWidget, WidgetInstance},
|
||||||
widgets::{label::LabelOverflow, Image, Label, Space, VirtualList},
|
widgets::{
|
||||||
|
label::{Displayable, LabelOverflow},
|
||||||
|
Image, Label, Space, VirtualList,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rspotify::model::SavedTrack;
|
use rspotify::model::SavedTrack;
|
||||||
|
|
@ -35,7 +38,6 @@ fn get_or_create_track_image(
|
||||||
idx: usize,
|
idx: usize,
|
||||||
create: impl FnOnce() -> WidgetInstance,
|
create: impl FnOnce() -> WidgetInstance,
|
||||||
) -> WidgetInstance {
|
) -> WidgetInstance {
|
||||||
println!("Getting image");
|
|
||||||
let mut locked = track_images.lock().unwrap();
|
let mut locked = track_images.lock().unwrap();
|
||||||
if let Some(image) = locked.get(&idx) {
|
if let Some(image) = locked.get(&idx) {
|
||||||
image.clone()
|
image.clone()
|
||||||
|
|
@ -147,19 +149,43 @@ impl LikedSongsPage {
|
||||||
.and(track.map_each(|track| {
|
.and(track.map_each(|track| {
|
||||||
track
|
track
|
||||||
.as_ref()
|
.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())
|
.unwrap_or(Space::primary().make_widget())
|
||||||
}))
|
}))
|
||||||
.and(track.map_each(|track| {
|
.and(track.map_each(|track| {
|
||||||
track
|
track
|
||||||
.as_ref()
|
.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())
|
.unwrap_or(Space::primary().make_widget())
|
||||||
}))
|
}))
|
||||||
.and(track.map_each(|track| {
|
.and(track.map_each(|track| {
|
||||||
track
|
track
|
||||||
.as_ref()
|
.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())
|
.unwrap_or(Space::primary().make_widget())
|
||||||
}))
|
}))
|
||||||
.into_columns()
|
.into_columns()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue