From 7110401f61fea7cd9fb600d99e79c02d8e2f8cd8 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Thu, 26 Oct 2023 11:48:42 +0200 Subject: [PATCH] Handle current item on playlist page --- Cargo.toml | 2 +- data/ui/playlist_page.blp | 3 ++- data/ui/playlist_tile.blp | 2 ++ src/player.rs | 18 +++++++++++++++- src/playlist_item.rs | 5 ++++- src/playlist_page.rs | 21 +++++++++++++++--- src/playlist_tile.rs | 45 ++++++++++++++++++++++++--------------- src/window.rs | 19 ++++++++++------- 8 files changed, 83 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f942531..eb1c906 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" adw = { package = "libadwaita", version = "0.5", features = ["v1_4"] } chrono = "0.4" 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" once_cell = "1" rand = "0.8" diff --git a/data/ui/playlist_page.blp b/data/ui/playlist_page.blp index 636ccdd..db7e5d8 100644 --- a/data/ui/playlist_page.blp +++ b/data/ui/playlist_page.blp @@ -19,7 +19,7 @@ template $MusicusPlaylistPage : Adw.Bin { Gtk.ScrolledWindow { hscrollbar-policy: never; - Adw.Clamp { + Adw.ClampScrollable { maximum-size: 1000; tightening-threshold: 600; @@ -30,6 +30,7 @@ template $MusicusPlaylistPage : Adw.Bin { margin-start: 12; margin-end: 12; single-click-activate: true; + activate => $select_item() swapped; } } } diff --git a/data/ui/playlist_tile.blp b/data/ui/playlist_tile.blp index 559b2f4..972f4b1 100644 --- a/data/ui/playlist_tile.blp +++ b/data/ui/playlist_tile.blp @@ -5,6 +5,8 @@ template $MusicusPlaylistTile : Gtk.Box { styles ["playlisttile"] Adw.Bin { + valign: end; + margin-bottom: 12; width-request: 48; Gtk.Image playing_icon { diff --git a/src/player.rs b/src/player.rs index 00a5ae0..7b0d200 100644 --- a/src/player.rs +++ b/src/player.rs @@ -14,10 +14,26 @@ mod imp { pub playing: Cell, #[property(get, construct_only)] pub playlist: OnceCell, - #[property(get, set)] + #[property(get, set = Self::set_current_index)] pub current_index: Cell, } + 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::().unwrap().set_is_playing(false); + } + + self.current_index.set(index); + + if let Some(item) = playlist.item(index) { + item.downcast::().unwrap().set_is_playing(true); + } + } + } + #[glib::object_subclass] impl ObjectSubclass for MusicusPlayer { const NAME: &'static str = "MusicusPlayer"; diff --git a/src/playlist_item.rs b/src/playlist_item.rs index 762f8fb..301cc81 100644 --- a/src/playlist_item.rs +++ b/src/playlist_item.rs @@ -1,6 +1,6 @@ use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*}; use std::{ - cell::OnceCell, + cell::{Cell, OnceCell}, path::{Path, PathBuf}, }; @@ -10,6 +10,9 @@ mod imp { #[derive(Properties, Default)] #[properties(wrapper_type = super::PlaylistItem)] pub struct PlaylistItem { + #[property(get, set)] + pub is_playing: Cell, + #[property(get, construct_only)] pub is_title: OnceCell, diff --git a/src/playlist_page.rs b/src/playlist_page.rs index fe34f1b..eba8a16 100644 --- a/src/playlist_page.rs +++ b/src/playlist_page.rs @@ -1,6 +1,6 @@ use crate::{player::MusicusPlayer, playlist_tile::PlaylistTile}; 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 std::cell::OnceCell; @@ -10,7 +10,7 @@ mod imp { use super::*; #[derive(Properties, Debug, Default, gtk::CompositeTemplate)] - #[properties(wrapper_type = super::MusicusPlayer)] + #[properties(wrapper_type = super::MusicusPlaylistPage)] #[template(file = "data/ui/playlist_page.blp")] pub struct MusicusPlaylistPage { #[property(get, construct_only)] @@ -63,7 +63,13 @@ mod imp { let item = item.downcast_ref::().unwrap(); let tile = item.child().and_downcast::().unwrap(); let playlist_item = item.item().and_downcast::().unwrap(); - tile.set_item(&playlist_item); + tile.set_item(Some(&playlist_item)); + }); + + factory.connect_unbind(|_, item| { + let item = item.downcast_ref::().unwrap(); + let tile = item.child().and_downcast::().unwrap(); + tile.set_item(None); }); 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, _: >k::ListView) { + self.player().set_current_index(index); + } + #[template_callback] fn close(&self, _: >k::Button) { self.emit_by_name::<()>("close", &[]); diff --git a/src/playlist_tile.rs b/src/playlist_tile.rs index 411a643..c26d9da 100644 --- a/src/playlist_tile.rs +++ b/src/playlist_tile.rs @@ -1,5 +1,6 @@ use crate::playlist_item::PlaylistItem; use gtk::{glib, prelude::*, subclass::prelude::*}; +use std::cell::RefCell; mod imp { use super::*; @@ -15,6 +16,8 @@ mod imp { pub performances_label: TemplateChild, #[template_child] pub part_title_label: TemplateChild, + + pub binding: RefCell>, } #[glib::object_subclass] @@ -47,28 +50,36 @@ impl PlaylistTile { glib::Object::new() } - pub fn set_item(&self, item: &PlaylistItem) { + pub fn set_item(&self, item: Option<&PlaylistItem>) { let imp = self.imp(); - if let Some(title) = item.title() { - imp.title_label.set_label(&title); - imp.title_label.set_visible(true); + if let Some(binding) = &*imp.binding.borrow() { + binding.unbind(); } - if let Some(performances) = item.performers() { - imp.performances_label.set_label(&performances); - imp.performances_label.set_visible(true); - } + if let Some(item) = item { + if let Some(title) = item.title() { + imp.title_label.set_label(&title); + imp.title_label.set_visible(true); + } - if let Some(part_title) = item.part_title() { - imp.part_title_label.set_label(&part_title); - imp.part_title_label.set_visible(true); - } else { - imp.obj().set_margin_bottom(24); - } - } + if let Some(performances) = item.performers() { + imp.performances_label.set_label(&performances); + imp.performances_label.set_visible(true); + } - pub fn set_playing(&self, playing: bool) { - self.imp().playing_icon.set_visible(playing); + if let Some(part_title) = item.part_title() { + imp.part_title_label.set_label(&part_title); + imp.part_title_label.set_visible(true); + } else { + imp.obj().set_margin_bottom(24); + } + + imp.binding.replace(Some( + item.bind_property("is-playing", &imp.playing_icon.get(), "visible") + .sync_create() + .build(), + )); + } } } diff --git a/src/window.rs b/src/window.rs index 164c454..8d72a7a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -2,9 +2,9 @@ use crate::{ home_page::MusicusHomePage, library::MusicusLibrary, player::MusicusPlayer, playlist_page::MusicusPlaylistPage, welcome_page::MusicusWelcomePage, }; - use adw::subclass::prelude::*; use gtk::{gio, glib, glib::clone, prelude::*}; +use std::cell::OnceCell; mod imp { use super::*; @@ -13,6 +13,7 @@ mod imp { #[template(file = "data/ui/window.blp")] pub struct MusicusWindow { pub player: MusicusPlayer, + pub playlist_page: OnceCell, #[template_child] pub stack: TemplateChild, @@ -79,6 +80,7 @@ mod imp { }); self.stack.add_named(&playlist_page, Some("playlist")); + self.playlist_page.set(playlist_page).unwrap(); } } @@ -140,12 +142,13 @@ impl MusicusWindow { #[template_callback] fn show_playlist(&self, button: >k::ToggleButton) { - self.imp() - .stack - .set_visible_child_name(if button.is_active() { - "playlist" - } else { - "navigation" - }); + let imp = self.imp(); + + if button.is_active() { + imp.playlist_page.get().unwrap().scroll_to_current(); + imp.stack.set_visible_child_name("playlist"); + } else { + imp.stack.set_visible_child_name("navigation"); + }; } }