carousel thing
This commit is contained in:
parent
c43adb66eb
commit
a33fe4ba63
6 changed files with 106 additions and 31 deletions
|
@ -80,7 +80,7 @@ template $AudreyUiAlbumCarousel: Adw.Bin {
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: bind template.item as <StringObject>.string;
|
label: bind template.item as <$AudreyModelAlbum>.name;
|
||||||
ellipsize: end;
|
ellipsize: end;
|
||||||
xalign: 0;
|
xalign: 0;
|
||||||
margin-start: 6;
|
margin-start: 6;
|
||||||
|
@ -91,7 +91,7 @@ template $AudreyUiAlbumCarousel: Adw.Bin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: bind template.item as <StringObject>.string;
|
label: bind template.item as <$AudreyModelAlbum>.artist;
|
||||||
ellipsize: end;
|
ellipsize: end;
|
||||||
xalign: 0;
|
xalign: 0;
|
||||||
margin-start: 6;
|
margin-start: 6;
|
||||||
|
|
|
@ -41,22 +41,22 @@ template $AudreyUiWindow: Adw.ApplicationWindow {
|
||||||
child: Box {
|
child: Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
|
|
||||||
$AudreyUiAlbumCarousel {
|
$AudreyUiAlbumCarousel carousel1 {
|
||||||
title: _("Explore from your library");
|
title: _("Explore from your library");
|
||||||
type: random;
|
type: random;
|
||||||
}
|
}
|
||||||
|
|
||||||
$AudreyUiAlbumCarousel {
|
$AudreyUiAlbumCarousel carousel2 {
|
||||||
title: _("Newly added releases");
|
title: _("Newly added releases");
|
||||||
type: newest;
|
type: newest;
|
||||||
}
|
}
|
||||||
|
|
||||||
$AudreyUiAlbumCarousel {
|
$AudreyUiAlbumCarousel carousel3 {
|
||||||
title: _("Recently played");
|
title: _("Recently played");
|
||||||
type: recent;
|
type: recent;
|
||||||
}
|
}
|
||||||
|
|
||||||
$AudreyUiAlbumCarousel {
|
$AudreyUiAlbumCarousel carousel4 {
|
||||||
title: _("Most played");
|
title: _("Most played");
|
||||||
type: frequent;
|
type: frequent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,3 +30,15 @@ mod imp {
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct Album(ObjectSubclass<imp::Album>);
|
pub struct Album(ObjectSubclass<imp::Album>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Album {
|
||||||
|
pub fn from_api(album: &crate::subsonic::schema::AlbumID3) -> Self {
|
||||||
|
let album: Self = glib::Object::builder()
|
||||||
|
.property("id", &album.id)
|
||||||
|
.property("name", &album.name)
|
||||||
|
.property("artist", &album.artist)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
album
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,21 +22,24 @@ impl Client {
|
||||||
size: u32,
|
size: u32,
|
||||||
offset: u32,
|
offset: u32,
|
||||||
) -> Result<Vec<schema::AlbumID3>, Error> {
|
) -> Result<Vec<schema::AlbumID3>, Error> {
|
||||||
match type_ {
|
let type_ = match type_ {
|
||||||
AlbumListType::Newest => {
|
AlbumListType::Newest => "newest",
|
||||||
self.get_subsonic::<schema::AlbumList2Outer>(self.url(
|
AlbumListType::Random => "random",
|
||||||
&["rest", "getAlbumList2"],
|
AlbumListType::Recent => "recent",
|
||||||
&[
|
AlbumListType::Frequent => "frequent",
|
||||||
("type", "newest"),
|
|
||||||
("size", &size.to_string()),
|
|
||||||
("offset", &offset.to_string()),
|
|
||||||
],
|
|
||||||
))
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => todo!("{type_:?}"),
|
_ => todo!("{type_:?}"),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
self.get_subsonic::<schema::AlbumList2Outer>(self.url(
|
||||||
|
&["rest", "getAlbumList2"],
|
||||||
|
&[
|
||||||
|
("type", type_),
|
||||||
|
("size", &size.to_string()),
|
||||||
|
("offset", &offset.to_string()),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
.await
|
||||||
.map(|response| response.album_list2.album.unwrap_or_default())
|
.map(|response| response.album_list2.album.unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
use crate::subsonic::AlbumListType;
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use adw::subclass::prelude::*;
|
use adw::subclass::prelude::*;
|
||||||
use adw::{gio, glib};
|
use adw::{gio, glib};
|
||||||
use glib::subclass::InitializingObject;
|
use glib::subclass::InitializingObject;
|
||||||
use std::cell::{Cell, OnceCell, RefCell};
|
use std::cell::{Cell, OnceCell, RefCell};
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
use tracing::{event, Level};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)]
|
||||||
#[enum_type(name = "AudreyUiAlbumCarouselType")]
|
#[enum_type(name = "AudreyUiAlbumCarouselType")]
|
||||||
|
@ -26,6 +29,7 @@ mod imp {
|
||||||
#[derive(gtk::CompositeTemplate, Default)]
|
#[derive(gtk::CompositeTemplate, Default)]
|
||||||
#[template(resource = "/eu/callcc/audrey/album_carousel.ui")]
|
#[template(resource = "/eu/callcc/audrey/album_carousel.ui")]
|
||||||
pub struct AlbumCarousel {
|
pub struct AlbumCarousel {
|
||||||
|
pub api: RefCell<Option<Rc<crate::subsonic::Client>>>,
|
||||||
title: RefCell<String>,
|
title: RefCell<String>,
|
||||||
model: OnceCell<gio::ListStore>,
|
model: OnceCell<gio::ListStore>,
|
||||||
r#type: Cell<Type>,
|
r#type: Cell<Type>,
|
||||||
|
@ -95,16 +99,43 @@ mod imp {
|
||||||
.get_or_init(gio::ListStore::new::<crate::model::Album>)
|
.get_or_init(gio::ListStore::new::<crate::model::Album>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_model(&self) {
|
pub fn update_model(&self) {
|
||||||
let window: crate::ui::Window = match self.obj().root() {
|
self.model().remove_all();
|
||||||
None => return,
|
|
||||||
Some(root) => root.dynamic_cast().unwrap(),
|
|
||||||
};
|
|
||||||
let _api = window.api();
|
|
||||||
|
|
||||||
match self.r#type.get() {
|
let api = self.api.borrow();
|
||||||
_ => todo!(),
|
let api = match api.as_ref() {
|
||||||
}
|
Some(api) => api,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let api = Rc::clone(&api);
|
||||||
|
let carousel = self.obj().downgrade();
|
||||||
|
|
||||||
|
let type_ = match self.r#type.get() {
|
||||||
|
Type::Random => AlbumListType::Random,
|
||||||
|
Type::Newest => AlbumListType::Newest,
|
||||||
|
Type::Recent => AlbumListType::Recent,
|
||||||
|
Type::Frequent => AlbumListType::Frequent,
|
||||||
|
};
|
||||||
|
|
||||||
|
glib::spawn_future_local(async move {
|
||||||
|
let album_list = match api.album_list(&type_, 50, 0).await {
|
||||||
|
Ok(album_list) => album_list,
|
||||||
|
Err(err) => {
|
||||||
|
event!(Level::ERROR, "could not fetch albums for carousel: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: handle to cancel on drop etc etc
|
||||||
|
let carousel = carousel.upgrade().unwrap();
|
||||||
|
for album in album_list {
|
||||||
|
carousel
|
||||||
|
.imp()
|
||||||
|
.model()
|
||||||
|
.append(&crate::model::Album::from_api(&album));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,3 +145,10 @@ glib::wrapper! {
|
||||||
@extends adw::Bin, gtk::Widget,
|
@extends adw::Bin, gtk::Widget,
|
||||||
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AlbumCarousel {
|
||||||
|
pub fn set_api(&self, api: Option<&Rc<crate::subsonic::Client>>) {
|
||||||
|
self.imp().api.replace(api.map(Rc::clone));
|
||||||
|
self.imp().update_model();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::model::Song;
|
use crate::model::Song;
|
||||||
|
use crate::ui::AlbumCarousel;
|
||||||
use crate::util::buffering::BufferingPulseController;
|
use crate::util::buffering::BufferingPulseController;
|
||||||
use crate::{mpris, mpv};
|
use crate::{mpris, mpv};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
@ -90,6 +91,15 @@ mod imp {
|
||||||
refreshing_albums: Cell<bool>,
|
refreshing_albums: Cell<bool>,
|
||||||
|
|
||||||
css_provider: gtk::CssProvider,
|
css_provider: gtk::CssProvider,
|
||||||
|
|
||||||
|
#[template_child]
|
||||||
|
pub carousel1: TemplateChild<AlbumCarousel>,
|
||||||
|
#[template_child]
|
||||||
|
pub carousel2: TemplateChild<AlbumCarousel>,
|
||||||
|
#[template_child]
|
||||||
|
pub carousel3: TemplateChild<AlbumCarousel>,
|
||||||
|
#[template_child]
|
||||||
|
pub carousel4: TemplateChild<AlbumCarousel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Window {
|
impl Default for Window {
|
||||||
|
@ -152,6 +162,11 @@ mod imp {
|
||||||
refreshing_albums: Cell::new(true),
|
refreshing_albums: Cell::new(true),
|
||||||
|
|
||||||
css_provider: gtk::CssProvider::new(),
|
css_provider: gtk::CssProvider::new(),
|
||||||
|
|
||||||
|
carousel1: Default::default(),
|
||||||
|
carousel2: Default::default(),
|
||||||
|
carousel3: Default::default(),
|
||||||
|
carousel4: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1011,11 +1026,18 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_connected(&self, api: crate::subsonic::Client) {
|
pub fn setup_connected(&self, api: crate::subsonic::Client) {
|
||||||
if self.imp().api.replace(Some(Rc::new(api))).is_some() {
|
let api = Rc::new(api);
|
||||||
unimplemented!("changing the api object");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.set_can_click_shuffle_all(true);
|
self.set_can_click_shuffle_all(true);
|
||||||
|
|
||||||
|
self.imp().carousel1.set_api(Some(&api));
|
||||||
|
self.imp().carousel2.set_api(Some(&api));
|
||||||
|
self.imp().carousel3.set_api(Some(&api));
|
||||||
|
self.imp().carousel4.set_api(Some(&api));
|
||||||
|
|
||||||
|
if self.imp().api.replace(Some(api)).is_some() {
|
||||||
|
unimplemented!("changing the api object");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn playlist_next(&self) {
|
pub fn playlist_next(&self) {
|
||||||
|
|
Loading…
Reference in a new issue