From 6ddee1d187cdc54c4b078b53dfe82e21d13f4bdb Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Thu, 29 Oct 2020 14:12:00 +0100 Subject: [PATCH] Add recording selector --- res/resources.xml | 2 + res/ui/recording_selector.ui | 202 ++++++++++++++++++++ res/ui/recording_selector_screen.ui | 58 ++++++ src/dialogs/mod.rs | 3 + src/dialogs/recording_selector.rs | 276 ++++++++++++++++++++++++++++ 5 files changed, 541 insertions(+) create mode 100644 res/ui/recording_selector.ui create mode 100644 res/ui/recording_selector_screen.ui create mode 100644 src/dialogs/recording_selector.rs diff --git a/res/resources.xml b/res/resources.xml index e91eca3..d59dcd3 100644 --- a/res/resources.xml +++ b/res/resources.xml @@ -15,6 +15,8 @@ ui/poe_list.ui ui/recording_editor.ui ui/recording_screen.ui + ui/recording_selector.ui + ui/recording_selector_screen.ui ui/section_editor.ui ui/window.ui ui/work_editor.ui diff --git a/res/ui/recording_selector.ui b/res/ui/recording_selector.ui new file mode 100644 index 0000000..37d8a8c --- /dev/null +++ b/res/ui/recording_selector.ui @@ -0,0 +1,202 @@ + + + + + + + True + False + vertical + + + True + False + True + Select a recording + True + + + False + True + 0 + + + + + True + False + Select a composer on the left. + + + True + True + 1 + + + + + True + False + vertical + + + True + False + True + True + + + False + True + 0 + + + + + True + False + + + True + False + True + + + loading + + + + + True + False + No works found. + + + empty + 1 + + + + + True + True + 1 + + + + + False + True + 600 + 424 + dialog + + + True + False + sidebar_box + True + + + 250 + True + False + False + vertical + + + True + False + False + + + True + True + True + + + True + False + list-add-symbolic + + + + + + + False + True + 0 + + + + + sidebar + + + + + True + False + vertical + + + + False + + + + + + + True + False + vertical + + + True + False + True + + + False + True + 0 + + + + + True + False + + + True + False + True + + + loading + + + + + True + False + No works found. + + + empty + 1 + + + + + True + True + 1 + + + + diff --git a/res/ui/recording_selector_screen.ui b/res/ui/recording_selector_screen.ui new file mode 100644 index 0000000..2499119 --- /dev/null +++ b/res/ui/recording_selector_screen.ui @@ -0,0 +1,58 @@ + + + + + + + True + False + vertical + + + True + False + True + + + True + True + True + + + True + False + go-previous-symbolic + + + + + + + False + True + 0 + + + + + True + False + + + True + False + True + + + loading + + + + + True + True + 1 + + + + diff --git a/src/dialogs/mod.rs b/src/dialogs/mod.rs index 3e2ec7f..0a472fe 100644 --- a/src/dialogs/mod.rs +++ b/src/dialogs/mod.rs @@ -25,6 +25,9 @@ pub use person_selector::*; pub mod recording_editor; pub use recording_editor::*; +pub mod recording_selector; +pub use recording_selector::*; + pub mod section_editor; pub use section_editor::*; diff --git a/src/dialogs/recording_selector.rs b/src/dialogs/recording_selector.rs new file mode 100644 index 0000000..55940c0 --- /dev/null +++ b/src/dialogs/recording_selector.rs @@ -0,0 +1,276 @@ +use super::*; +use crate::backend::Backend; +use crate::database::*; +use crate::widgets::*; +use gio::prelude::*; +use glib::clone; +use gtk::prelude::*; +use gtk_macros::get_widget; +use libhandy::prelude::*; +use libhandy::HeaderBarExt; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct RecordingSelector { + backend: Rc, + window: gtk::Window, + callback: Box () + 'static>, + leaflet: libhandy::Leaflet, + navigator: Rc, +} + +impl RecordingSelector { + pub fn new(backend: Rc, parent: &P, callback: F) -> Rc + where + P: IsA, + F: Fn(RecordingDescription) -> () + 'static, + { + let builder = + gtk::Builder::from_resource("/de/johrpan/musicus_editor/ui/recording_selector.ui"); + + get_widget!(builder, gtk::Window, window); + get_widget!(builder, libhandy::Leaflet, leaflet); + get_widget!(builder, gtk::Button, add_button); + get_widget!(builder, gtk::Box, sidebar_box); + get_widget!(builder, gtk::Box, empty_screen); + + let person_list = PersonList::new(backend.clone()); + sidebar_box.pack_start(&person_list.widget, true, true, 0); + + let navigator = Navigator::new(&empty_screen); + leaflet.add(&navigator.widget); + + let result = Rc::new(Self { + backend: backend, + window: window, + callback: Box::new(callback), + leaflet: leaflet, + navigator: navigator, + }); + + add_button.connect_clicked(clone!(@strong result => move |_| { + let editor = RecordingEditor::new( + result.backend.clone(), + &result.window, + None, + clone!(@strong result => move |recording| { + result.select(recording); + }), + ); + + editor.show(); + })); + + person_list.set_selected(clone!(@strong result => move |person| { + result.navigator.clone().replace(RecordingSelectorPersonScreen::new(result.backend.clone(), result.clone(), person.clone())); + result.leaflet.set_visible_child(&result.navigator.widget); + })); + + result.window.set_transient_for(Some(parent)); + + result + } + + pub fn show(&self) { + self.window.show(); + } + + pub fn select(&self, recording: RecordingDescription) { + self.window.close(); + (self.callback)(recording); + } +} + +struct RecordingSelectorPersonScreen { + backend: Rc, + selector: Rc, + widget: gtk::Box, + stack: gtk::Stack, + work_list: Rc>, + navigator: RefCell>>, +} + +impl RecordingSelectorPersonScreen { + pub fn new(backend: Rc, selector: Rc, person: Person) -> Rc { + let builder = gtk::Builder::from_resource( + "/de/johrpan/musicus_editor/ui/recording_selector_screen.ui", + ); + + get_widget!(builder, gtk::Box, widget); + get_widget!(builder, libhandy::HeaderBar, header); + get_widget!(builder, gtk::Button, back_button); + get_widget!(builder, gtk::Stack, stack); + + header.set_title(Some(&person.name_fl())); + + let work_list = List::new( + |work: &WorkDescription| { + let label = gtk::Label::new(Some(&work.title)); + label.set_halign(gtk::Align::Start); + label.upcast() + }, + |_| true, + "No works found.", + ); + + stack.add_named(&work_list.widget, "content"); + + let result = Rc::new(Self { + backend, + selector, + widget, + stack, + work_list, + navigator: RefCell::new(None), + }); + + back_button.connect_clicked(clone!(@strong result => move |_| { + let navigator = result.navigator.borrow().clone(); + if let Some(navigator) = navigator { + navigator.clone().pop(); + } + })); + + result + .work_list + .set_selected(clone!(@strong result => move |work| { + let navigator = result.navigator.borrow().clone(); + if let Some(navigator) = navigator { + navigator.push(RecordingSelectorWorkScreen::new(result.backend.clone(), result.selector.clone(), work.clone())); + } + })); + + let context = glib::MainContext::default(); + let clone = result.clone(); + context.spawn_local(async move { + let works = clone + .backend + .get_work_descriptions(person.id) + .await + .unwrap(); + + clone.work_list.show_items(works); + clone.stack.set_visible_child_name("content"); + }); + + result + } +} + +impl NavigatorScreen for RecordingSelectorPersonScreen { + fn attach_navigator(&self, navigator: Rc) { + self.navigator.replace(Some(navigator)); + } + + fn get_widget(&self) -> gtk::Widget { + self.widget.clone().upcast() + } + + fn detach_navigator(&self) { + self.navigator.replace(None); + } +} + +struct RecordingSelectorWorkScreen { + backend: Rc, + selector: Rc, + widget: gtk::Box, + stack: gtk::Stack, + recording_list: Rc>, + navigator: RefCell>>, +} + +impl RecordingSelectorWorkScreen { + pub fn new( + backend: Rc, + selector: Rc, + work: WorkDescription, + ) -> Rc { + let builder = gtk::Builder::from_resource( + "/de/johrpan/musicus_editor/ui/recording_selector_screen.ui", + ); + + get_widget!(builder, gtk::Box, widget); + get_widget!(builder, libhandy::HeaderBar, header); + get_widget!(builder, gtk::Button, back_button); + get_widget!(builder, gtk::Stack, stack); + + header.set_title(Some(&work.title)); + header.set_subtitle(Some(&work.composer.name_fl())); + + let recording_list = List::new( + |recording: &RecordingDescription| { + let work_label = gtk::Label::new(Some(&recording.work.get_title())); + + work_label.set_ellipsize(pango::EllipsizeMode::End); + work_label.set_halign(gtk::Align::Start); + + let performers_label = gtk::Label::new(Some(&recording.get_performers())); + performers_label.set_ellipsize(pango::EllipsizeMode::End); + performers_label.set_opacity(0.5); + performers_label.set_halign(gtk::Align::Start); + + let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.add(&work_label); + vbox.add(&performers_label); + + vbox.upcast() + }, + |_| true, + "No recordings found.", + ); + + stack.add_named(&recording_list.widget, "content"); + + let result = Rc::new(Self { + backend, + selector, + widget, + stack, + recording_list, + navigator: RefCell::new(None), + }); + + back_button.connect_clicked(clone!(@strong result => move |_| { + let navigator = result.navigator.borrow().clone(); + if let Some(navigator) = navigator { + navigator.clone().pop(); + } + })); + + result + .recording_list + .set_selected(clone!(@strong result => move |recording| { + result.selector.select(recording.clone()); + })); + + let context = glib::MainContext::default(); + let clone = result.clone(); + context.spawn_local(async move { + let recordings = clone + .backend + .get_recordings_for_work(work.id) + .await + .unwrap(); + + clone.recording_list.show_items(recordings); + clone.stack.set_visible_child_name("content"); + }); + + result + } +} + +impl NavigatorScreen for RecordingSelectorWorkScreen { + fn attach_navigator(&self, navigator: Rc) { + self.navigator.replace(Some(navigator)); + } + + fn get_widget(&self) -> gtk::Widget { + self.widget.clone().upcast() + } + + fn detach_navigator(&self) { + self.navigator.replace(None); + } +}