diff --git a/data/ui/home_page.blp b/data/ui/home_page.blp index e1df41f..645aa15 100644 --- a/data/ui/home_page.blp +++ b/data/ui/home_page.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template $MusicusHomePage : Adw.NavigationPage { +template $MusicusHomePage: Adw.NavigationPage { title: _("Musicus"); tag: "home"; @@ -21,14 +21,65 @@ template $MusicusHomePage : Adw.NavigationPage { maximum-size: 1000; tightening-threshold: 600; - $MusicusSearchEntry search_entry { - activate => $select() swapped; + Gtk.Box { + orientation: vertical; + + $MusicusSearchEntry search_entry { + activate => $select() swapped; + } + + Gtk.Box header_box { + visible: false; + spacing: 12; + margin-start: 12; + margin-end: 12; + margin-top: 24; + margin-bottom: 12; + + Gtk.Button { + styles [ + "flat" + ] + + valign: center; + icon-name: "go-previous-symbolic"; + clicked => $back_button_clicked() swapped; + } + + Gtk.Box { + orientation: vertical; + hexpand: true; + + Gtk.Label title_label { + styles [ + "title-1" + ] + + xalign: 0.0; + } + + Gtk.Label subtitle_label { + xalign: 0.0; + } + } + + Gtk.Button { + styles [ + "flat" + ] + + valign: center; + icon-name: "document-edit-symbolic"; + clicked => $edit_button_clicked() swapped; + } + } } } Gtk.Stack stack { Gtk.StackPage { name: "results"; + child: Gtk.ScrolledWindow { hscrollbar-policy: never; @@ -44,8 +95,11 @@ template $MusicusHomePage : Adw.NavigationPage { margin-bottom: 68; Gtk.Label { - styles ["heading"] - visible: bind composers_flow_box.visible; + styles [ + "heading" + ] + + visible: bind composers_flow_box.visible; halign: start; label: _("Composers"); } @@ -61,8 +115,11 @@ template $MusicusHomePage : Adw.NavigationPage { } Gtk.Label { - styles ["heading"] - visible: bind performers_flow_box.visible; + styles [ + "heading" + ] + + visible: bind performers_flow_box.visible; halign: start; label: _("Performers"); } @@ -78,8 +135,11 @@ template $MusicusHomePage : Adw.NavigationPage { } Gtk.Label { - styles ["heading"] - visible: bind ensembles_flow_box.visible; + styles [ + "heading" + ] + + visible: bind ensembles_flow_box.visible; halign: start; label: _("Ensembles"); } @@ -95,7 +155,10 @@ template $MusicusHomePage : Adw.NavigationPage { } Gtk.Label { - styles ["heading"] + styles [ + "heading" + ] + visible: bind works_flow_box.visible; halign: start; label: _("Works"); @@ -112,7 +175,10 @@ template $MusicusHomePage : Adw.NavigationPage { } Gtk.Label { - styles ["heading"] + styles [ + "heading" + ] + visible: bind recordings_flow_box.visible; halign: start; label: _("Recordings"); @@ -129,7 +195,10 @@ template $MusicusHomePage : Adw.NavigationPage { } Gtk.Label { - styles ["heading"] + styles [ + "heading" + ] + visible: bind albums_flow_box.visible; halign: start; label: _("Albums"); @@ -148,8 +217,10 @@ template $MusicusHomePage : Adw.NavigationPage { } }; } + Gtk.StackPage { name: "empty"; + child: Adw.StatusPage { icon-name: "system-search-symbolic"; title: _("Nothing Found"); @@ -161,7 +232,11 @@ template $MusicusHomePage : Adw.NavigationPage { [overlay] Gtk.Button play_button { - styles ["pill", "suggested-action"] + styles [ + "pill", + "suggested-action" + ] + halign: end; valign: end; margin-end: 24; @@ -177,12 +252,14 @@ menu primary_menu { label: _("_Library manager"); action: "win.library"; } + item { label: _("_Preferences"); action: "app.preferences"; } + item { label: _("_About Musicus"); action: "app.about"; } -} \ No newline at end of file +} diff --git a/data/ui/person_editor.blp b/data/ui/person_editor.blp index 59b2430..343444f 100644 --- a/data/ui/person_editor.blp +++ b/data/ui/person_editor.blp @@ -9,18 +9,22 @@ template $MusicusPersonEditor: Adw.NavigationPage { Adw.HeaderBar header_bar {} Adw.Clamp { - Gtk.Label { - label: _("Name"); - xalign: 0; - margin-top: 24; + Gtk.Box { + orientation: vertical; - styles [ - "heading" - ] - } + Gtk.Label { + label: _("Name"); + xalign: 0; + margin-top: 24; - $MusicusTranslationEditor name_editor { - margin-start: 12; + styles [ + "heading" + ] + } + + $MusicusTranslationEditor name_editor { + margin-top: 12; + } } } } diff --git a/data/ui/recording_tile.blp b/data/ui/recording_tile.blp index 84ce61b..00e3a76 100644 --- a/data/ui/recording_tile.blp +++ b/data/ui/recording_tile.blp @@ -35,5 +35,12 @@ template $MusicusRecordingTile : Gtk.FlowBoxChild { wrap: true; } } + + Gtk.Button { + styles ["flat"] + valign: start; + margin-top: 12; + icon-name: "view-more-symbolic"; + } } } \ No newline at end of file diff --git a/src/editor/person_editor.rs b/src/editor/person_editor.rs index 42c1f84..e31a1b9 100644 --- a/src/editor/person_editor.rs +++ b/src/editor/person_editor.rs @@ -1,14 +1,17 @@ use adw::{prelude::*, subclass::prelude::*}; use gtk::glib; -use crate::editor::translation_editor::MusicusTranslationEditor; +use crate::{db::models::Person, editor::translation_editor::MusicusTranslationEditor}; mod imp { use super::*; #[derive(Debug, Default, gtk::CompositeTemplate)] #[template(file = "data/ui/person_editor.blp")] - pub struct MusicusPersonEditor {} + pub struct MusicusPersonEditor { + #[template_child] + pub name_editor: TemplateChild, + } #[glib::object_subclass] impl ObjectSubclass for MusicusPersonEditor { @@ -39,7 +42,13 @@ glib::wrapper! { #[gtk::template_callbacks] impl MusicusPersonEditor { - pub fn new() -> Self { - glib::Object::new() + pub fn new(person: Option<&Person>) -> Self { + let obj: Self = glib::Object::new(); + + if let Some(person) = person { + obj.imp().name_editor.set_translation(&person.name); + } + + obj } } diff --git a/src/editor/translation_editor.rs b/src/editor/translation_editor.rs index d8e16e5..f2afc58 100644 --- a/src/editor/translation_editor.rs +++ b/src/editor/translation_editor.rs @@ -55,19 +55,20 @@ glib::wrapper! { #[gtk::template_callbacks] impl MusicusTranslationEditor { - pub fn new(translation: TranslatedString) -> Self { - let obj: Self = glib::Object::new(); - let mut translation = translation.0; + pub fn new() -> Self { + glib::Object::new() + } - obj.imp() + pub fn set_translation(&self, translation: &TranslatedString) { + let mut translation = translation.0.clone(); + + self.imp() .entry_row .set_text(&translation.remove("generic").unwrap_or_default()); for (lang, translation) in translation { - obj.add_entry(&lang, &translation); + self.add_entry(&lang, &translation); } - - obj } #[template_callback] @@ -92,11 +93,17 @@ impl MusicusTranslationEditor { let obj = self.clone(); entry.connect_remove(move |entry| { - obj.imp().translation_entries.borrow_mut().retain(|e| e != entry); + obj.imp() + .translation_entries + .borrow_mut() + .retain(|e| e != entry); obj.imp().list_box.remove(entry); }); - self.imp().list_box.insert(&entry, self.imp().translation_entries.borrow().len() as i32 + 1); + self.imp().list_box.insert( + &entry, + self.imp().translation_entries.borrow().len() as i32 + 1, + ); entry.grab_focus(); self.imp().translation_entries.borrow_mut().push(entry); diff --git a/src/editor/work_editor.rs b/src/editor/work_editor.rs index fe07050..07a3e44 100644 --- a/src/editor/work_editor.rs +++ b/src/editor/work_editor.rs @@ -1,7 +1,7 @@ use crate::{ db::{ self, - models::{Composer, Instrument, Person, WorkPart}, + models::{Composer, Instrument, Person, Work, WorkPart}, }, editor::{ instrument_selector_popover::MusicusInstrumentSelectorPopover, @@ -25,6 +25,9 @@ mod imp { #[properties(wrapper_type = super::MusicusWorkEditor)] #[template(file = "data/ui/work_editor.blp")] pub struct MusicusWorkEditor { + #[property(get, construct_only)] + pub navigation: OnceCell, + #[property(get, construct_only)] pub library: OnceCell, @@ -38,6 +41,8 @@ mod imp { pub persons_popover: OnceCell, pub instruments_popover: OnceCell, + #[template_child] + pub name_editor: TemplateChild, #[template_child] pub composer_list: TemplateChild, #[template_child] @@ -146,8 +151,21 @@ glib::wrapper! { #[gtk::template_callbacks] impl MusicusWorkEditor { - pub fn new(library: &MusicusLibrary) -> Self { - glib::Object::builder().property("library", library).build() + pub fn new( + navigation: &adw::NavigationView, + library: &MusicusLibrary, + work: Option<&Work>, + ) -> Self { + let obj: Self = glib::Object::builder() + .property("navigation", navigation) + .property("library", library) + .build(); + + if let Some(_work) = work { + // TODO: Initialize work data. + } + + obj } #[template_callback] diff --git a/src/home_page.rs b/src/home_page.rs index 4937d59..6c0ce29 100644 --- a/src/home_page.rs +++ b/src/home_page.rs @@ -1,6 +1,7 @@ use crate::{ album_tile::MusicusAlbumTile, db::models::*, + editor::{person_editor::MusicusPersonEditor, work_editor::MusicusWorkEditor}, library::{LibraryQuery, MusicusLibrary}, player::MusicusPlayer, playlist_item::PlaylistItem, @@ -25,6 +26,9 @@ mod imp { #[properties(wrapper_type = super::MusicusHomePage)] #[template(file = "data/ui/home_page.blp")] pub struct MusicusHomePage { + #[property(get, construct_only)] + pub navigation: OnceCell, + #[property(get, construct_only)] pub library: OnceCell, @@ -43,6 +47,12 @@ mod imp { #[template_child] pub stack: TemplateChild, #[template_child] + pub header_box: TemplateChild, + #[template_child] + pub title_label: TemplateChild, + #[template_child] + pub subtitle_label: TemplateChild, + #[template_child] pub composers_flow_box: TemplateChild, #[template_child] pub performers_flow_box: TemplateChild, @@ -109,13 +119,41 @@ glib::wrapper! { #[gtk::template_callbacks] impl MusicusHomePage { - pub fn new(library: &MusicusLibrary, player: &MusicusPlayer) -> Self { + pub fn new( + navigation: &adw::NavigationView, + library: &MusicusLibrary, + player: &MusicusPlayer, + ) -> Self { glib::Object::builder() + .property("navigation", navigation) .property("library", library) .property("player", player) .build() } + #[template_callback] + fn back_button_clicked(&self, _: >k::Button) { + self.imp().search_entry.reset(); + } + + #[template_callback] + fn edit_button_clicked(&self, _: >k::Button) { + if let Some(tag) = self.imp().search_entry.tags().first() { + match tag { + Tag::Composer(person) | Tag::Performer(person) => { + self.navigation() + .push(&MusicusPersonEditor::new(Some(person))); + } + Tag::Ensemble(_) => todo!(), + Tag::Work(work) => self.navigation().push(&MusicusWorkEditor::new( + &self.navigation(), + &self.library(), + Some(work), + )), + } + } + } + #[template_callback] fn play(&self, _: >k::Button) { log::info!("Play button clicked"); @@ -262,6 +300,28 @@ impl MusicusHomePage { } } + if let Some(tag) = imp.search_entry.tags().first() { + match tag { + Tag::Composer(person) | Tag::Performer(person) => { + imp.title_label.set_text(&person.name.get()); + imp.subtitle_label.set_visible(false); + } + Tag::Ensemble(ensemble) => { + imp.title_label.set_text(&ensemble.name.get()); + imp.subtitle_label.set_visible(false); + } + Tag::Work(work) => { + imp.title_label.set_text(&work.name.get()); + imp.subtitle_label.set_text(&work.composers_string()); + imp.subtitle_label.set_visible(true); + } + } + + imp.header_box.set_visible(true); + } else { + imp.header_box.set_visible(false); + } + if results.is_empty() { imp.stack.set_visible_child_name("empty"); } else { diff --git a/src/library_manager.rs b/src/library_manager.rs index 481a7a6..b5f0703 100644 --- a/src/library_manager.rs +++ b/src/library_manager.rs @@ -5,7 +5,6 @@ use adw::{ use gtk::glib::{self, Properties}; use std::cell::OnceCell; -use crate::editor::work_editor::MusicusWorkEditor; use crate::library::MusicusLibrary; mod imp { @@ -36,12 +35,7 @@ mod imp { } #[glib::derived_properties] - impl ObjectImpl for LibraryManager { - fn constructed(&self) { - self.parent_constructed(); - self.obj().set_child(Some(&MusicusWorkEditor::new(self.library.get().unwrap()))); - } - } + impl ObjectImpl for LibraryManager {} impl WidgetImpl for LibraryManager {} impl NavigationPageImpl for LibraryManager {} diff --git a/src/search_entry.rs b/src/search_entry.rs index 927b34b..76fba39 100644 --- a/src/search_entry.rs +++ b/src/search_entry.rs @@ -174,6 +174,15 @@ impl MusicusSearchEntry { self.emit_by_name::<()>("query-changed", &[]); } + pub fn tags(&self) -> Vec { + self.imp() + .tags + .borrow() + .iter() + .map(|t| t.tag().to_owned()) + .collect() + } + pub fn query(&self) -> LibraryQuery { let mut query = LibraryQuery { search: self.imp().text.text().to_string(), diff --git a/src/window.rs b/src/window.rs index 0354cf1..19bf774 100644 --- a/src/window.rs +++ b/src/window.rs @@ -155,12 +155,8 @@ impl MusicusWindow { fn load_library(&self, path: impl AsRef) { let library = MusicusLibrary::new(path); - self.imp() - .navigation_view - .replace(&[MusicusHomePage::new(&library, &self.imp().player).into()]); - - self.imp() - .navigation_view - .add(&LibraryManager::new(&library)); + let navigation = self.imp().navigation_view.get(); + navigation.replace(&[MusicusHomePage::new(&navigation, &library, &self.imp().player).into()]); + navigation.add(&LibraryManager::new(&library)); } }