Handle current item on playlist page

This commit is contained in:
Elias Projahn 2023-10-26 11:48:42 +02:00
parent 7d21617e9a
commit 7110401f61
8 changed files with 83 additions and 32 deletions

View file

@ -7,7 +7,7 @@ edition = "2021"
adw = { package = "libadwaita", version = "0.5", features = ["v1_4"] } adw = { package = "libadwaita", version = "0.5", features = ["v1_4"] }
chrono = "0.4" chrono = "0.4"
gettext-rs = { version = "0.7", features = ["gettext-system"] } gettext-rs = { version = "0.7", features = ["gettext-system"] }
gtk = { package = "gtk4", version = "0.7", features = ["v4_10", "blueprint"] } gtk = { package = "gtk4", version = "0.7", features = ["v4_12", "blueprint"] }
log = "0.4" log = "0.4"
once_cell = "1" once_cell = "1"
rand = "0.8" rand = "0.8"

View file

@ -19,7 +19,7 @@ template $MusicusPlaylistPage : Adw.Bin {
Gtk.ScrolledWindow { Gtk.ScrolledWindow {
hscrollbar-policy: never; hscrollbar-policy: never;
Adw.Clamp { Adw.ClampScrollable {
maximum-size: 1000; maximum-size: 1000;
tightening-threshold: 600; tightening-threshold: 600;
@ -30,6 +30,7 @@ template $MusicusPlaylistPage : Adw.Bin {
margin-start: 12; margin-start: 12;
margin-end: 12; margin-end: 12;
single-click-activate: true; single-click-activate: true;
activate => $select_item() swapped;
} }
} }
} }

View file

@ -5,6 +5,8 @@ template $MusicusPlaylistTile : Gtk.Box {
styles ["playlisttile"] styles ["playlisttile"]
Adw.Bin { Adw.Bin {
valign: end;
margin-bottom: 12;
width-request: 48; width-request: 48;
Gtk.Image playing_icon { Gtk.Image playing_icon {

View file

@ -14,10 +14,26 @@ mod imp {
pub playing: Cell<bool>, pub playing: Cell<bool>,
#[property(get, construct_only)] #[property(get, construct_only)]
pub playlist: OnceCell<gio::ListStore>, pub playlist: OnceCell<gio::ListStore>,
#[property(get, set)] #[property(get, set = Self::set_current_index)]
pub current_index: Cell<u32>, pub current_index: Cell<u32>,
} }
impl MusicusPlayer {
pub fn set_current_index(&self, index: u32) {
let playlist = self.playlist.get().unwrap();
if let Some(item) = playlist.item(self.current_index.get()) {
item.downcast::<PlaylistItem>().unwrap().set_is_playing(false);
}
self.current_index.set(index);
if let Some(item) = playlist.item(index) {
item.downcast::<PlaylistItem>().unwrap().set_is_playing(true);
}
}
}
#[glib::object_subclass] #[glib::object_subclass]
impl ObjectSubclass for MusicusPlayer { impl ObjectSubclass for MusicusPlayer {
const NAME: &'static str = "MusicusPlayer"; const NAME: &'static str = "MusicusPlayer";

View file

@ -1,6 +1,6 @@
use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*}; use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*};
use std::{ use std::{
cell::OnceCell, cell::{Cell, OnceCell},
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -10,6 +10,9 @@ mod imp {
#[derive(Properties, Default)] #[derive(Properties, Default)]
#[properties(wrapper_type = super::PlaylistItem)] #[properties(wrapper_type = super::PlaylistItem)]
pub struct PlaylistItem { pub struct PlaylistItem {
#[property(get, set)]
pub is_playing: Cell<bool>,
#[property(get, construct_only)] #[property(get, construct_only)]
pub is_title: OnceCell<bool>, pub is_title: OnceCell<bool>,

View file

@ -1,6 +1,6 @@
use crate::{player::MusicusPlayer, playlist_tile::PlaylistTile}; use crate::{player::MusicusPlayer, playlist_tile::PlaylistTile};
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gtk::{glib, glib::subclass::Signal, glib::Properties, prelude::*}; use gtk::{glib, glib::subclass::Signal, glib::Properties, prelude::*, ListScrollFlags};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::cell::OnceCell; use std::cell::OnceCell;
@ -10,7 +10,7 @@ mod imp {
use super::*; use super::*;
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)] #[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
#[properties(wrapper_type = super::MusicusPlayer)] #[properties(wrapper_type = super::MusicusPlaylistPage)]
#[template(file = "data/ui/playlist_page.blp")] #[template(file = "data/ui/playlist_page.blp")]
pub struct MusicusPlaylistPage { pub struct MusicusPlaylistPage {
#[property(get, construct_only)] #[property(get, construct_only)]
@ -63,7 +63,13 @@ mod imp {
let item = item.downcast_ref::<gtk::ListItem>().unwrap(); let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let tile = item.child().and_downcast::<PlaylistTile>().unwrap(); let tile = item.child().and_downcast::<PlaylistTile>().unwrap();
let playlist_item = item.item().and_downcast::<PlaylistItem>().unwrap(); let playlist_item = item.item().and_downcast::<PlaylistItem>().unwrap();
tile.set_item(&playlist_item); tile.set_item(Some(&playlist_item));
});
factory.connect_unbind(|_, item| {
let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let tile = item.child().and_downcast::<PlaylistTile>().unwrap();
tile.set_item(None);
}); });
self.playlist.set_factory(Some(&factory)); self.playlist.set_factory(Some(&factory));
@ -93,6 +99,15 @@ impl MusicusPlaylistPage {
}) })
} }
pub fn scroll_to_current(&self) {
self.imp().playlist.scroll_to(self.player().current_index(), ListScrollFlags::NONE, None);
}
#[template_callback]
fn select_item(&self, index: u32, _: &gtk::ListView) {
self.player().set_current_index(index);
}
#[template_callback] #[template_callback]
fn close(&self, _: &gtk::Button) { fn close(&self, _: &gtk::Button) {
self.emit_by_name::<()>("close", &[]); self.emit_by_name::<()>("close", &[]);

View file

@ -1,5 +1,6 @@
use crate::playlist_item::PlaylistItem; use crate::playlist_item::PlaylistItem;
use gtk::{glib, prelude::*, subclass::prelude::*}; use gtk::{glib, prelude::*, subclass::prelude::*};
use std::cell::RefCell;
mod imp { mod imp {
use super::*; use super::*;
@ -15,6 +16,8 @@ mod imp {
pub performances_label: TemplateChild<gtk::Label>, pub performances_label: TemplateChild<gtk::Label>,
#[template_child] #[template_child]
pub part_title_label: TemplateChild<gtk::Label>, pub part_title_label: TemplateChild<gtk::Label>,
pub binding: RefCell<Option<glib::Binding>>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -47,9 +50,14 @@ impl PlaylistTile {
glib::Object::new() glib::Object::new()
} }
pub fn set_item(&self, item: &PlaylistItem) { pub fn set_item(&self, item: Option<&PlaylistItem>) {
let imp = self.imp(); let imp = self.imp();
if let Some(binding) = &*imp.binding.borrow() {
binding.unbind();
}
if let Some(item) = item {
if let Some(title) = item.title() { if let Some(title) = item.title() {
imp.title_label.set_label(&title); imp.title_label.set_label(&title);
imp.title_label.set_visible(true); imp.title_label.set_visible(true);
@ -66,9 +74,12 @@ impl PlaylistTile {
} else { } else {
imp.obj().set_margin_bottom(24); imp.obj().set_margin_bottom(24);
} }
}
pub fn set_playing(&self, playing: bool) { imp.binding.replace(Some(
self.imp().playing_icon.set_visible(playing); item.bind_property("is-playing", &imp.playing_icon.get(), "visible")
.sync_create()
.build(),
));
}
} }
} }

View file

@ -2,9 +2,9 @@ use crate::{
home_page::MusicusHomePage, library::MusicusLibrary, player::MusicusPlayer, home_page::MusicusHomePage, library::MusicusLibrary, player::MusicusPlayer,
playlist_page::MusicusPlaylistPage, welcome_page::MusicusWelcomePage, playlist_page::MusicusPlaylistPage, welcome_page::MusicusWelcomePage,
}; };
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gtk::{gio, glib, glib::clone, prelude::*}; use gtk::{gio, glib, glib::clone, prelude::*};
use std::cell::OnceCell;
mod imp { mod imp {
use super::*; use super::*;
@ -13,6 +13,7 @@ mod imp {
#[template(file = "data/ui/window.blp")] #[template(file = "data/ui/window.blp")]
pub struct MusicusWindow { pub struct MusicusWindow {
pub player: MusicusPlayer, pub player: MusicusPlayer,
pub playlist_page: OnceCell<MusicusPlaylistPage>,
#[template_child] #[template_child]
pub stack: TemplateChild<gtk::Stack>, pub stack: TemplateChild<gtk::Stack>,
@ -79,6 +80,7 @@ mod imp {
}); });
self.stack.add_named(&playlist_page, Some("playlist")); self.stack.add_named(&playlist_page, Some("playlist"));
self.playlist_page.set(playlist_page).unwrap();
} }
} }
@ -140,12 +142,13 @@ impl MusicusWindow {
#[template_callback] #[template_callback]
fn show_playlist(&self, button: &gtk::ToggleButton) { fn show_playlist(&self, button: &gtk::ToggleButton) {
self.imp() let imp = self.imp();
.stack
.set_visible_child_name(if button.is_active() { if button.is_active() {
"playlist" imp.playlist_page.get().unwrap().scroll_to_current();
imp.stack.set_visible_child_name("playlist");
} else { } else {
"navigation" imp.stack.set_visible_child_name("navigation");
}); };
} }
} }