carousel album covers

This commit is contained in:
Erica Z 2024-12-01 00:40:15 +01:00
parent afa27dbb6a
commit abfd5ab568
4 changed files with 41 additions and 5 deletions

View file

@ -12,7 +12,7 @@ template $AudreyUiAlbumCarouselAlbum: Adw.Bin {
spacing: 6; spacing: 6;
Image { Image {
icon-name: "media-optical-cd"; paintable: bind template.cover_art;
pixel-size: 160; pixel-size: 160;
halign: center; halign: center;
hexpand: false; hexpand: false;

View file

@ -93,7 +93,6 @@ gridview.albums child image {
.album-carousel listview row image { .album-carousel listview row image {
border-radius: 3px; border-radius: 3px;
background-color: oklch(0 0 0);
} }
.album-carousel .labels { .album-carousel .labels {

View file

@ -157,7 +157,7 @@ mod imp {
pub fn on_bind(&self, item: &gtk::ListItem, _factory: &gtk::SignalListItemFactory) { pub fn on_bind(&self, item: &gtk::ListItem, _factory: &gtk::SignalListItemFactory) {
let child: super::Album = item.child().and_downcast().unwrap(); let child: super::Album = item.child().and_downcast().unwrap();
let album: model::Album = item.item().and_downcast().unwrap(); let album: model::Album = item.item().and_downcast().unwrap();
child.bind(&album); child.bind(self.client.borrow().as_ref().unwrap(), &album);
} }
#[template_callback] #[template_callback]

View file

@ -1,7 +1,10 @@
use crate::model; use crate::model;
use adw::gdk;
use adw::{prelude::*, subclass::prelude::*}; use adw::{prelude::*, subclass::prelude::*};
use glib::subclass::InitializingObject; use glib::subclass::InitializingObject;
use std::cell::RefCell; use std::cell::{Cell, RefCell};
use tokio::sync::Semaphore;
use tracing::{event, Level};
mod imp { mod imp {
use super::*; use super::*;
@ -12,6 +15,11 @@ mod imp {
pub struct Album { pub struct Album {
#[property(get, set, nullable)] #[property(get, set, nullable)]
item: RefCell<Option<model::Album>>, item: RefCell<Option<model::Album>>,
#[property(get, set, nullable)]
cover_art: RefCell<Option<gdk::Paintable>>,
pub cover_art_loading: Cell<Option<glib::JoinHandle<()>>>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -37,6 +45,9 @@ mod imp {
impl BinImpl for Album {} impl BinImpl for Album {}
} }
// only fetch 10 thumbnails at a time
static SEM: Semaphore = Semaphore::const_new(10);
glib::wrapper! { glib::wrapper! {
pub struct Album(ObjectSubclass<imp::Album>) pub struct Album(ObjectSubclass<imp::Album>)
@extends adw::Bin, gtk::Widget, @extends adw::Bin, gtk::Widget,
@ -44,11 +55,37 @@ glib::wrapper! {
} }
impl Album { impl Album {
pub fn bind(&self, album: &model::Album) { pub fn bind(&self, client: &crate::wrappers::Client, album: &model::Album) {
self.set_item(Some(album)); self.set_item(Some(album));
let client = client.clone();
let id = album.id();
let this = self.downgrade();
let scale_factor = self.scale_factor() as u32;
let handle = glib::spawn_future_local(async move {
let _permit = SEM.acquire().await.unwrap();
// see pixel-size in album_carousel_album.blp
let pixbuf = match client.cover_art(&id, Some(160 * scale_factor)).await {
Ok(image) => audrey::util::image_to_pixbuf(image),
Err(err) => {
event!(Level::ERROR, "could not fetch cover for album {id}: {err}");
return;
}
};
let this = this.upgrade().unwrap();
this.set_cover_art(Some(gdk::Texture::for_pixbuf(&pixbuf)));
});
self.imp().cover_art_loading.replace(Some(handle));
} }
pub fn unbind(&self) { pub fn unbind(&self) {
self.set_item(None::<model::Album>); self.set_item(None::<model::Album>);
if let Some(handle) = self.imp().cover_art_loading.take() {
handle.abort();
}
self.set_cover_art(None::<gdk::Paintable>);
} }
} }