From 4a33ed3fc5e4871cdeb1a8beebdd0e6b3ec16c00 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Sat, 8 Mar 2025 08:35:15 +0100 Subject: [PATCH] Implement add to playlist --- data/ui/album_page.blp | 32 +++++++++++++++++++++---- data/ui/search_page.blp | 30 ++++++++++++++++++++---- src/album_page.rs | 52 ++++++++++++++++++++++++++++++++++------- src/player.rs | 21 ++++++++++++++++- src/search_page.rs | 50 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 164 insertions(+), 21 deletions(-) diff --git a/data/ui/album_page.blp b/data/ui/album_page.blp index 6251197..64ea5be 100644 --- a/data/ui/album_page.blp +++ b/data/ui/album_page.blp @@ -25,6 +25,7 @@ template $MusicusAlbumPage: Adw.NavigationPage { Gtk.Box { orientation: vertical; hexpand: true; + valign: center; Gtk.Label title_label { wrap: true; @@ -41,10 +42,10 @@ template $MusicusAlbumPage: Adw.NavigationPage { } } - Gtk.Button { - icon-name: "document-edit-symbolic"; + Gtk.MenuButton { + icon-name: "view-more-symbolic"; + menu-model: album_menu; valign: center; - clicked => $edit_button_clicked() swapped; styles [ "flat", @@ -52,8 +53,8 @@ template $MusicusAlbumPage: Adw.NavigationPage { } Gtk.Button { - icon-name: "media-playback-start-symbolic"; - label: _("_Play album"); + label: _("_Play"); + tooltip-text: _("Play this album"); use-underline: true; valign: center; clicked => $play_button_clicked() swapped; @@ -88,3 +89,24 @@ template $MusicusAlbumPage: Adw.NavigationPage { } } } + +menu album_menu { + section { + item { + label: _("_Add to playlist"); + action: "album.add-to-playlist"; + } + } + + section { + item { + label: _("_Edit item"); + action: "album.edit"; + } + + item { + label: _("_Delete item"); + action: "album.delete"; + } + } +} diff --git a/data/ui/search_page.blp b/data/ui/search_page.blp index 1ddba4b..7d5cc3c 100644 --- a/data/ui/search_page.blp +++ b/data/ui/search_page.blp @@ -33,6 +33,7 @@ template $MusicusSearchPage: Adw.NavigationPage { Gtk.Box { orientation: vertical; hexpand: true; + valign: center; Gtk.Label title_label { wrap: true; @@ -49,10 +50,10 @@ template $MusicusSearchPage: Adw.NavigationPage { } } - Gtk.Button { - icon-name: "document-edit-symbolic"; + Gtk.MenuButton { + icon-name: "view-more-symbolic"; + menu-model: item_menu; valign: center; - clicked => $edit_button_clicked() swapped; styles [ "flat", @@ -60,8 +61,8 @@ template $MusicusSearchPage: Adw.NavigationPage { } Gtk.Button { - icon-name: "media-playback-start-symbolic"; label: _("_Play"); + tooltip-text: _("Play this as a program"); use-underline: true; valign: center; clicked => $play_button_clicked() swapped; @@ -276,3 +277,24 @@ menu primary_menu { action: "app.about"; } } + +menu item_menu { + section { + item { + label: _("_Add to playlist"); + action: "search.add-to-playlist"; + } + } + + section { + item { + label: _("_Edit item"); + action: "search.edit"; + } + + item { + label: _("_Delete item"); + action: "search.delete"; + } + } +} diff --git a/src/album_page.rs b/src/album_page.rs index f021812..ee4ce53 100644 --- a/src/album_page.rs +++ b/src/album_page.rs @@ -2,6 +2,7 @@ use std::cell::OnceCell; use adw::subclass::prelude::*; use gtk::{ + gio, glib::{self, Properties}, prelude::*, }; @@ -57,6 +58,48 @@ mod imp { impl ObjectImpl for AlbumPage { fn constructed(&self) { self.parent_constructed(); + + let obj = self.obj().to_owned(); + let add_to_playlist_action = gio::ActionEntry::builder("add-to-playlist") + .activate(move |_, _, _| { + let playlist = obj + .imp() + .album + .get() + .unwrap() + .recordings + .iter() + .map(|r| obj.player().recording_to_playlist(r)) + .flatten() + .collect::>(); + + if let Err(err) = obj.player().append(playlist) { + log::warn!("Failed to add album to the playlits: {err}"); + }; + }) + .build(); + + let obj = self.obj().to_owned(); + let edit_action = gio::ActionEntry::builder("edit") + .activate(move |_, _, _| { + obj.navigation().push(&AlbumEditor::new( + &obj.navigation(), + &obj.library(), + Some(&obj.imp().album.get().unwrap().clone()), + )); + }) + .build(); + + // let obj = self.obj().to_owned(); + let delete_action = gio::ActionEntry::builder("delete") + .activate(move |_, _, _| { + log::error!("Delete not implemented"); + }) + .build(); + + let actions = gio::SimpleActionGroup::new(); + actions.add_action_entries([add_to_playlist_action, edit_action, delete_action]); + self.obj().insert_action_group("album", Some(&actions)); } } @@ -99,15 +142,6 @@ impl AlbumPage { obj } - #[template_callback] - fn edit_button_clicked(&self) { - self.navigation().push(&AlbumEditor::new( - &self.navigation(), - &self.library(), - Some(&self.imp().album.get().unwrap().clone()), - )); - } - #[template_callback] fn play_button_clicked(&self) { let playlist = self diff --git a/src/player.rs b/src/player.rs index ec7bba7..46993c0 100644 --- a/src/player.rs +++ b/src/player.rs @@ -34,7 +34,7 @@ mod imp { pub active: Cell, #[property(get, set)] pub playing: Cell, - #[property(get, set)] + #[property(get, set = Self::set_program)] pub program: RefCell>, #[property(get, construct_only)] pub playlist: OnceCell, @@ -51,6 +51,25 @@ mod imp { } impl Player { + pub fn set_program(&self, program: Option<&Program>) { + self.program.replace(program.cloned()); + + if let Some(program) = program { + if !self.obj().active() { + match self.obj().generate_items(program) { + Ok(index) => { + self.obj().set_active(true); + self.obj().set_current_index(index); + self.obj().pause(); + } + Err(err) => { + log::warn!("Failed to play from program: {err}"); + } + } + } + } + } + pub fn set_current_index(&self, index: u32) { let playlist = self.playlist.get().unwrap(); diff --git a/src/search_page.rs b/src/search_page.rs index 4d47b78..f6b4f17 100644 --- a/src/search_page.rs +++ b/src/search_page.rs @@ -115,6 +115,32 @@ mod imp { obj.imp().scrolled_window.vadjustment().set_value(0.0); obj.search(&entry.text()); }); + + let obj = self.obj().to_owned(); + let add_to_playlist_action = gio::ActionEntry::builder("add-to-playlist") + .activate(move |_, _, _| { + let program = Program::from_query(obj.imp().query.get().unwrap().clone()); + obj.player().set_program(program); + }) + .build(); + + let obj = self.obj().to_owned(); + let edit_action = gio::ActionEntry::builder("edit") + .activate(move |_, _, _| { + obj.edit(); + }) + .build(); + + let obj = self.obj().to_owned(); + let delete_action = gio::ActionEntry::builder("delete") + .activate(move |_, _, _| { + obj.delete(); + }) + .build(); + + let actions = gio::SimpleActionGroup::new(); + actions.add_action_entries([add_to_playlist_action, edit_action, delete_action]); + self.obj().insert_action_group("search", Some(&actions)); } } @@ -171,8 +197,7 @@ impl SearchPage { obj } - #[template_callback] - fn edit_button_clicked(&self) { + fn edit(&self) { if let Some(highlight) = &*self.imp().highlight.borrow() { match highlight { Tag::Composer(person) | Tag::Performer(person) => { @@ -204,6 +229,27 @@ impl SearchPage { } } + fn delete(&self) { + log::warn!("Deletion not implemented"); + + // if let Some(highlight) = &*self.imp().highlight.borrow() { + // match highlight { + // Tag::Composer(person) | Tag::Performer(person) => { + // // TODO + // } + // Tag::Ensemble(ensemble) => { + // // TODO + // } + // Tag::Instrument(instrument) => { + // // TODO + // } + // Tag::Work(work) => { + // // TODO + // } + // } + // } + } + #[template_callback] fn play_button_clicked(&self) { let program = Program::from_query(self.imp().query.get().unwrap().clone());