Add home page header and hook up editors

This commit is contained in:
Elias Projahn 2024-06-05 19:03:04 +02:00
parent 38613c0063
commit f49f23a501
10 changed files with 236 additions and 55 deletions

View file

@ -1,7 +1,7 @@
using Gtk 4.0; using Gtk 4.0;
using Adw 1; using Adw 1;
template $MusicusHomePage : Adw.NavigationPage { template $MusicusHomePage: Adw.NavigationPage {
title: _("Musicus"); title: _("Musicus");
tag: "home"; tag: "home";
@ -21,14 +21,65 @@ template $MusicusHomePage : Adw.NavigationPage {
maximum-size: 1000; maximum-size: 1000;
tightening-threshold: 600; tightening-threshold: 600;
$MusicusSearchEntry search_entry { Gtk.Box {
activate => $select() swapped; 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.Stack stack {
Gtk.StackPage { Gtk.StackPage {
name: "results"; name: "results";
child: Gtk.ScrolledWindow { child: Gtk.ScrolledWindow {
hscrollbar-policy: never; hscrollbar-policy: never;
@ -44,8 +95,11 @@ template $MusicusHomePage : Adw.NavigationPage {
margin-bottom: 68; margin-bottom: 68;
Gtk.Label { Gtk.Label {
styles ["heading"] styles [
visible: bind composers_flow_box.visible; "heading"
]
visible: bind composers_flow_box.visible;
halign: start; halign: start;
label: _("Composers"); label: _("Composers");
} }
@ -61,8 +115,11 @@ template $MusicusHomePage : Adw.NavigationPage {
} }
Gtk.Label { Gtk.Label {
styles ["heading"] styles [
visible: bind performers_flow_box.visible; "heading"
]
visible: bind performers_flow_box.visible;
halign: start; halign: start;
label: _("Performers"); label: _("Performers");
} }
@ -78,8 +135,11 @@ template $MusicusHomePage : Adw.NavigationPage {
} }
Gtk.Label { Gtk.Label {
styles ["heading"] styles [
visible: bind ensembles_flow_box.visible; "heading"
]
visible: bind ensembles_flow_box.visible;
halign: start; halign: start;
label: _("Ensembles"); label: _("Ensembles");
} }
@ -95,7 +155,10 @@ template $MusicusHomePage : Adw.NavigationPage {
} }
Gtk.Label { Gtk.Label {
styles ["heading"] styles [
"heading"
]
visible: bind works_flow_box.visible; visible: bind works_flow_box.visible;
halign: start; halign: start;
label: _("Works"); label: _("Works");
@ -112,7 +175,10 @@ template $MusicusHomePage : Adw.NavigationPage {
} }
Gtk.Label { Gtk.Label {
styles ["heading"] styles [
"heading"
]
visible: bind recordings_flow_box.visible; visible: bind recordings_flow_box.visible;
halign: start; halign: start;
label: _("Recordings"); label: _("Recordings");
@ -129,7 +195,10 @@ template $MusicusHomePage : Adw.NavigationPage {
} }
Gtk.Label { Gtk.Label {
styles ["heading"] styles [
"heading"
]
visible: bind albums_flow_box.visible; visible: bind albums_flow_box.visible;
halign: start; halign: start;
label: _("Albums"); label: _("Albums");
@ -148,8 +217,10 @@ template $MusicusHomePage : Adw.NavigationPage {
} }
}; };
} }
Gtk.StackPage { Gtk.StackPage {
name: "empty"; name: "empty";
child: Adw.StatusPage { child: Adw.StatusPage {
icon-name: "system-search-symbolic"; icon-name: "system-search-symbolic";
title: _("Nothing Found"); title: _("Nothing Found");
@ -161,7 +232,11 @@ template $MusicusHomePage : Adw.NavigationPage {
[overlay] [overlay]
Gtk.Button play_button { Gtk.Button play_button {
styles ["pill", "suggested-action"] styles [
"pill",
"suggested-action"
]
halign: end; halign: end;
valign: end; valign: end;
margin-end: 24; margin-end: 24;
@ -177,12 +252,14 @@ menu primary_menu {
label: _("_Library manager"); label: _("_Library manager");
action: "win.library"; action: "win.library";
} }
item { item {
label: _("_Preferences"); label: _("_Preferences");
action: "app.preferences"; action: "app.preferences";
} }
item { item {
label: _("_About Musicus"); label: _("_About Musicus");
action: "app.about"; action: "app.about";
} }
} }

View file

@ -9,18 +9,22 @@ template $MusicusPersonEditor: Adw.NavigationPage {
Adw.HeaderBar header_bar {} Adw.HeaderBar header_bar {}
Adw.Clamp { Adw.Clamp {
Gtk.Label { Gtk.Box {
label: _("Name"); orientation: vertical;
xalign: 0;
margin-top: 24;
styles [ Gtk.Label {
"heading" label: _("Name");
] xalign: 0;
} margin-top: 24;
$MusicusTranslationEditor name_editor { styles [
margin-start: 12; "heading"
]
}
$MusicusTranslationEditor name_editor {
margin-top: 12;
}
} }
} }
} }

View file

@ -35,5 +35,12 @@ template $MusicusRecordingTile : Gtk.FlowBoxChild {
wrap: true; wrap: true;
} }
} }
Gtk.Button {
styles ["flat"]
valign: start;
margin-top: 12;
icon-name: "view-more-symbolic";
}
} }
} }

View file

@ -1,14 +1,17 @@
use adw::{prelude::*, subclass::prelude::*}; use adw::{prelude::*, subclass::prelude::*};
use gtk::glib; use gtk::glib;
use crate::editor::translation_editor::MusicusTranslationEditor; use crate::{db::models::Person, editor::translation_editor::MusicusTranslationEditor};
mod imp { mod imp {
use super::*; use super::*;
#[derive(Debug, Default, gtk::CompositeTemplate)] #[derive(Debug, Default, gtk::CompositeTemplate)]
#[template(file = "data/ui/person_editor.blp")] #[template(file = "data/ui/person_editor.blp")]
pub struct MusicusPersonEditor {} pub struct MusicusPersonEditor {
#[template_child]
pub name_editor: TemplateChild<MusicusTranslationEditor>,
}
#[glib::object_subclass] #[glib::object_subclass]
impl ObjectSubclass for MusicusPersonEditor { impl ObjectSubclass for MusicusPersonEditor {
@ -39,7 +42,13 @@ glib::wrapper! {
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl MusicusPersonEditor { impl MusicusPersonEditor {
pub fn new() -> Self { pub fn new(person: Option<&Person>) -> Self {
glib::Object::new() let obj: Self = glib::Object::new();
if let Some(person) = person {
obj.imp().name_editor.set_translation(&person.name);
}
obj
} }
} }

View file

@ -55,19 +55,20 @@ glib::wrapper! {
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl MusicusTranslationEditor { impl MusicusTranslationEditor {
pub fn new(translation: TranslatedString) -> Self { pub fn new() -> Self {
let obj: Self = glib::Object::new(); glib::Object::new()
let mut translation = translation.0; }
obj.imp() pub fn set_translation(&self, translation: &TranslatedString) {
let mut translation = translation.0.clone();
self.imp()
.entry_row .entry_row
.set_text(&translation.remove("generic").unwrap_or_default()); .set_text(&translation.remove("generic").unwrap_or_default());
for (lang, translation) in translation { for (lang, translation) in translation {
obj.add_entry(&lang, &translation); self.add_entry(&lang, &translation);
} }
obj
} }
#[template_callback] #[template_callback]
@ -92,11 +93,17 @@ impl MusicusTranslationEditor {
let obj = self.clone(); let obj = self.clone();
entry.connect_remove(move |entry| { 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); 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(); entry.grab_focus();
self.imp().translation_entries.borrow_mut().push(entry); self.imp().translation_entries.borrow_mut().push(entry);

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
db::{ db::{
self, self,
models::{Composer, Instrument, Person, WorkPart}, models::{Composer, Instrument, Person, Work, WorkPart},
}, },
editor::{ editor::{
instrument_selector_popover::MusicusInstrumentSelectorPopover, instrument_selector_popover::MusicusInstrumentSelectorPopover,
@ -25,6 +25,9 @@ mod imp {
#[properties(wrapper_type = super::MusicusWorkEditor)] #[properties(wrapper_type = super::MusicusWorkEditor)]
#[template(file = "data/ui/work_editor.blp")] #[template(file = "data/ui/work_editor.blp")]
pub struct MusicusWorkEditor { pub struct MusicusWorkEditor {
#[property(get, construct_only)]
pub navigation: OnceCell<adw::NavigationView>,
#[property(get, construct_only)] #[property(get, construct_only)]
pub library: OnceCell<MusicusLibrary>, pub library: OnceCell<MusicusLibrary>,
@ -38,6 +41,8 @@ mod imp {
pub persons_popover: OnceCell<MusicusPersonSelectorPopover>, pub persons_popover: OnceCell<MusicusPersonSelectorPopover>,
pub instruments_popover: OnceCell<MusicusInstrumentSelectorPopover>, pub instruments_popover: OnceCell<MusicusInstrumentSelectorPopover>,
#[template_child]
pub name_editor: TemplateChild<MusicusTranslationEditor>,
#[template_child] #[template_child]
pub composer_list: TemplateChild<gtk::ListBox>, pub composer_list: TemplateChild<gtk::ListBox>,
#[template_child] #[template_child]
@ -146,8 +151,21 @@ glib::wrapper! {
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl MusicusWorkEditor { impl MusicusWorkEditor {
pub fn new(library: &MusicusLibrary) -> Self { pub fn new(
glib::Object::builder().property("library", library).build() 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] #[template_callback]

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
album_tile::MusicusAlbumTile, album_tile::MusicusAlbumTile,
db::models::*, db::models::*,
editor::{person_editor::MusicusPersonEditor, work_editor::MusicusWorkEditor},
library::{LibraryQuery, MusicusLibrary}, library::{LibraryQuery, MusicusLibrary},
player::MusicusPlayer, player::MusicusPlayer,
playlist_item::PlaylistItem, playlist_item::PlaylistItem,
@ -25,6 +26,9 @@ mod imp {
#[properties(wrapper_type = super::MusicusHomePage)] #[properties(wrapper_type = super::MusicusHomePage)]
#[template(file = "data/ui/home_page.blp")] #[template(file = "data/ui/home_page.blp")]
pub struct MusicusHomePage { pub struct MusicusHomePage {
#[property(get, construct_only)]
pub navigation: OnceCell<adw::NavigationView>,
#[property(get, construct_only)] #[property(get, construct_only)]
pub library: OnceCell<MusicusLibrary>, pub library: OnceCell<MusicusLibrary>,
@ -43,6 +47,12 @@ mod imp {
#[template_child] #[template_child]
pub stack: TemplateChild<gtk::Stack>, pub stack: TemplateChild<gtk::Stack>,
#[template_child] #[template_child]
pub header_box: TemplateChild<gtk::Box>,
#[template_child]
pub title_label: TemplateChild<gtk::Label>,
#[template_child]
pub subtitle_label: TemplateChild<gtk::Label>,
#[template_child]
pub composers_flow_box: TemplateChild<gtk::FlowBox>, pub composers_flow_box: TemplateChild<gtk::FlowBox>,
#[template_child] #[template_child]
pub performers_flow_box: TemplateChild<gtk::FlowBox>, pub performers_flow_box: TemplateChild<gtk::FlowBox>,
@ -109,13 +119,41 @@ glib::wrapper! {
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl MusicusHomePage { impl MusicusHomePage {
pub fn new(library: &MusicusLibrary, player: &MusicusPlayer) -> Self { pub fn new(
navigation: &adw::NavigationView,
library: &MusicusLibrary,
player: &MusicusPlayer,
) -> Self {
glib::Object::builder() glib::Object::builder()
.property("navigation", navigation)
.property("library", library) .property("library", library)
.property("player", player) .property("player", player)
.build() .build()
} }
#[template_callback]
fn back_button_clicked(&self, _: &gtk::Button) {
self.imp().search_entry.reset();
}
#[template_callback]
fn edit_button_clicked(&self, _: &gtk::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] #[template_callback]
fn play(&self, _: &gtk::Button) { fn play(&self, _: &gtk::Button) {
log::info!("Play button clicked"); 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() { if results.is_empty() {
imp.stack.set_visible_child_name("empty"); imp.stack.set_visible_child_name("empty");
} else { } else {

View file

@ -5,7 +5,6 @@ use adw::{
use gtk::glib::{self, Properties}; use gtk::glib::{self, Properties};
use std::cell::OnceCell; use std::cell::OnceCell;
use crate::editor::work_editor::MusicusWorkEditor;
use crate::library::MusicusLibrary; use crate::library::MusicusLibrary;
mod imp { mod imp {
@ -36,12 +35,7 @@ mod imp {
} }
#[glib::derived_properties] #[glib::derived_properties]
impl ObjectImpl for LibraryManager { impl ObjectImpl for LibraryManager {}
fn constructed(&self) {
self.parent_constructed();
self.obj().set_child(Some(&MusicusWorkEditor::new(self.library.get().unwrap())));
}
}
impl WidgetImpl for LibraryManager {} impl WidgetImpl for LibraryManager {}
impl NavigationPageImpl for LibraryManager {} impl NavigationPageImpl for LibraryManager {}

View file

@ -174,6 +174,15 @@ impl MusicusSearchEntry {
self.emit_by_name::<()>("query-changed", &[]); self.emit_by_name::<()>("query-changed", &[]);
} }
pub fn tags(&self) -> Vec<Tag> {
self.imp()
.tags
.borrow()
.iter()
.map(|t| t.tag().to_owned())
.collect()
}
pub fn query(&self) -> LibraryQuery { pub fn query(&self) -> LibraryQuery {
let mut query = LibraryQuery { let mut query = LibraryQuery {
search: self.imp().text.text().to_string(), search: self.imp().text.text().to_string(),

View file

@ -155,12 +155,8 @@ impl MusicusWindow {
fn load_library(&self, path: impl AsRef<Path>) { fn load_library(&self, path: impl AsRef<Path>) {
let library = MusicusLibrary::new(path); let library = MusicusLibrary::new(path);
self.imp() let navigation = self.imp().navigation_view.get();
.navigation_view navigation.replace(&[MusicusHomePage::new(&navigation, &library, &self.imp().player).into()]);
.replace(&[MusicusHomePage::new(&library, &self.imp().player).into()]); navigation.add(&LibraryManager::new(&library));
self.imp()
.navigation_view
.add(&LibraryManager::new(&library));
} }
} }