mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 19:57:25 +01:00
Port most screens to the new navigator
This commit is contained in:
parent
7eff62b5a4
commit
18e33c3d0d
23 changed files with 499 additions and 1063 deletions
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::generate_id;
|
use crate::database::generate_id;
|
||||||
use crate::database::Ensemble;
|
use crate::database::Ensemble;
|
||||||
use crate::widgets::{Editor, EntryRow, Navigator, NavigatorScreen, Section, UploadSection};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -11,7 +12,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for creating or editing a ensemble.
|
/// A dialog for creating or editing a ensemble.
|
||||||
pub struct EnsembleEditor {
|
pub struct EnsembleEditor {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<Ensemble>,
|
||||||
|
|
||||||
/// The ID of the ensemble that is edited or a newly generated one.
|
/// The ID of the ensemble that is edited or a newly generated one.
|
||||||
id: String,
|
id: String,
|
||||||
|
|
@ -19,15 +20,13 @@ pub struct EnsembleEditor {
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
name: EntryRow,
|
name: EntryRow,
|
||||||
upload: UploadSection,
|
upload: UploadSection,
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Ensemble) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnsembleEditor {
|
impl Screen<Option<Ensemble>, Ensemble> for EnsembleEditor {
|
||||||
/// Create a new ensemble editor and optionally initialize it.
|
/// Create a new ensemble editor and optionally initialize it.
|
||||||
pub fn new(backend: Rc<Backend>, ensemble: Option<Ensemble>) -> Rc<Self> {
|
fn new(ensemble: Option<Ensemble>, handle: NavigationHandle<Ensemble>) -> Rc<Self> {
|
||||||
let editor = Editor::new();
|
let editor = Editor::new();
|
||||||
editor.set_title("Ensemble");
|
editor.set_title("Ensemble/Role");
|
||||||
|
|
||||||
let list = gtk::ListBoxBuilder::new()
|
let list = gtk::ListBoxBuilder::new()
|
||||||
.selection_mode(gtk::SelectionMode::None)
|
.selection_mode(gtk::SelectionMode::None)
|
||||||
|
|
@ -51,55 +50,41 @@ impl EnsembleEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
id,
|
id,
|
||||||
editor,
|
editor,
|
||||||
name,
|
name,
|
||||||
upload,
|
upload,
|
||||||
saved_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
this.editor.set_back_cb(clone!(@strong this => move || {
|
this.editor.set_back_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.editor.set_save_cb(clone!(@strong this => move || {
|
this.editor.set_save_cb(clone!(@weak this => move || {
|
||||||
let context = glib::MainContext::default();
|
spawn!(@clone this, async move {
|
||||||
let clone = this.clone();
|
this.editor.loading();
|
||||||
context.spawn_local(async move {
|
match this.save().await {
|
||||||
clone.editor.loading();
|
Ok(ensemble) => {
|
||||||
match clone.clone().save().await {
|
this.handle.pop(Some(ensemble));
|
||||||
Ok(_) => {
|
|
||||||
let navigator = clone.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let description = gettext!("Cause: {}", err);
|
let description = gettext!("Cause: {}", err);
|
||||||
clone.editor.error(&gettext("Failed to save ensemble!"), &description);
|
this.editor.error(&gettext("Failed to save ensemble!"), &description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called if the ensemble was saved.
|
|
||||||
pub fn set_saved_cb<F: Fn(Ensemble) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EnsembleEditor {
|
||||||
/// Save the ensemble and possibly upload it to the server.
|
/// Save the ensemble and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(&self) -> Result<Ensemble> {
|
||||||
let name = self.name.get_text();
|
let name = self.name.get_text();
|
||||||
|
|
||||||
let ensemble = Ensemble {
|
let ensemble = Ensemble {
|
||||||
|
|
@ -108,31 +93,19 @@ impl EnsembleEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.upload.get_active() {
|
if self.upload.get_active() {
|
||||||
self.backend.post_ensemble(&ensemble).await?;
|
self.handle.backend.post_ensemble(&ensemble).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend.db().update_ensemble(ensemble.clone()).await?;
|
self.handle.backend.db().update_ensemble(ensemble.clone()).await?;
|
||||||
self.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
if let Some(cb) = &*self.saved_cb.borrow() {
|
Ok(ensemble)
|
||||||
cb(ensemble.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for EnsembleEditor {
|
impl Widget for EnsembleEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.editor.widget.clone().upcast()
|
self.editor.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::generate_id;
|
use crate::database::generate_id;
|
||||||
use crate::database::Instrument;
|
use crate::database::Instrument;
|
||||||
use crate::widgets::{Editor, EntryRow, Navigator, NavigatorScreen, Section, UploadSection};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -11,7 +12,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for creating or editing a instrument.
|
/// A dialog for creating or editing a instrument.
|
||||||
pub struct InstrumentEditor {
|
pub struct InstrumentEditor {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<Instrument>,
|
||||||
|
|
||||||
/// The ID of the instrument that is edited or a newly generated one.
|
/// The ID of the instrument that is edited or a newly generated one.
|
||||||
id: String,
|
id: String,
|
||||||
|
|
@ -19,13 +20,11 @@ pub struct InstrumentEditor {
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
name: EntryRow,
|
name: EntryRow,
|
||||||
upload: UploadSection,
|
upload: UploadSection,
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Instrument) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstrumentEditor {
|
impl Screen<Option<Instrument>, Instrument> for InstrumentEditor {
|
||||||
/// Create a new instrument editor and optionally initialize it.
|
/// Create a new instrument editor and optionally initialize it.
|
||||||
pub fn new(backend: Rc<Backend>, instrument: Option<Instrument>) -> Rc<Self> {
|
fn new(instrument: Option<Instrument>, handle: NavigationHandle<Instrument>) -> Rc<Self> {
|
||||||
let editor = Editor::new();
|
let editor = Editor::new();
|
||||||
editor.set_title("Instrument/Role");
|
editor.set_title("Instrument/Role");
|
||||||
|
|
||||||
|
|
@ -51,55 +50,41 @@ impl InstrumentEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
id,
|
id,
|
||||||
editor,
|
editor,
|
||||||
name,
|
name,
|
||||||
upload,
|
upload,
|
||||||
saved_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
this.editor.set_back_cb(clone!(@strong this => move || {
|
this.editor.set_back_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.editor.set_save_cb(clone!(@strong this => move || {
|
this.editor.set_save_cb(clone!(@weak this => move || {
|
||||||
let context = glib::MainContext::default();
|
spawn!(@clone this, async move {
|
||||||
let clone = this.clone();
|
this.editor.loading();
|
||||||
context.spawn_local(async move {
|
match this.save().await {
|
||||||
clone.editor.loading();
|
Ok(instrument) => {
|
||||||
match clone.clone().save().await {
|
this.handle.pop(Some(instrument));
|
||||||
Ok(_) => {
|
|
||||||
let navigator = clone.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let description = gettext!("Cause: {}", err);
|
let description = gettext!("Cause: {}", err);
|
||||||
clone.editor.error(&gettext("Failed to save instrument!"), &description);
|
this.editor.error(&gettext("Failed to save instrument!"), &description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called if the instrument was saved.
|
|
||||||
pub fn set_saved_cb<F: Fn(Instrument) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InstrumentEditor {
|
||||||
/// Save the instrument and possibly upload it to the server.
|
/// Save the instrument and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(&self) -> Result<Instrument> {
|
||||||
let name = self.name.get_text();
|
let name = self.name.get_text();
|
||||||
|
|
||||||
let instrument = Instrument {
|
let instrument = Instrument {
|
||||||
|
|
@ -108,31 +93,19 @@ impl InstrumentEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.upload.get_active() {
|
if self.upload.get_active() {
|
||||||
self.backend.post_instrument(&instrument).await?;
|
self.handle.backend.post_instrument(&instrument).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend.db().update_instrument(instrument.clone()).await?;
|
self.handle.backend.db().update_instrument(instrument.clone()).await?;
|
||||||
self.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
if let Some(cb) = &*self.saved_cb.borrow() {
|
Ok(instrument)
|
||||||
cb(instrument.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for InstrumentEditor {
|
impl Widget for InstrumentEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.editor.widget.clone().upcast()
|
self.editor.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
use crate::selectors::{EnsembleSelector, InstrumentSelector, PersonSelector};
|
use crate::selectors::{EnsembleSelector, InstrumentSelector, PersonSelector};
|
||||||
use crate::widgets::{Editor, Navigator, NavigatorScreen, Section, ButtonRow, Widget};
|
use crate::widgets::{Editor, Section, ButtonRow, Widget};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -11,7 +12,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for editing a performance within a recording.
|
/// A dialog for editing a performance within a recording.
|
||||||
pub struct PerformanceEditor {
|
pub struct PerformanceEditor {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<Performance>,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
person_row: ButtonRow,
|
person_row: ButtonRow,
|
||||||
ensemble_row: ButtonRow,
|
ensemble_row: ButtonRow,
|
||||||
|
|
@ -20,13 +21,11 @@ pub struct PerformanceEditor {
|
||||||
person: RefCell<Option<Person>>,
|
person: RefCell<Option<Person>>,
|
||||||
ensemble: RefCell<Option<Ensemble>>,
|
ensemble: RefCell<Option<Ensemble>>,
|
||||||
role: RefCell<Option<Instrument>>,
|
role: RefCell<Option<Instrument>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Performance) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerformanceEditor {
|
impl Screen<Option<Performance>, Performance> for PerformanceEditor {
|
||||||
/// Create a new performance editor.
|
/// Create a new performance editor.
|
||||||
pub fn new(backend: Rc<Backend>, performance: Option<Performance>) -> Rc<Self> {
|
fn new(performance: Option<Performance>, handle: NavigationHandle<Performance>) -> Rc<Self> {
|
||||||
let editor = Editor::new();
|
let editor = Editor::new();
|
||||||
editor.set_title("Performance");
|
editor.set_title("Performance");
|
||||||
editor.set_may_save(false);
|
editor.set_may_save(false);
|
||||||
|
|
@ -68,7 +67,7 @@ impl PerformanceEditor {
|
||||||
editor.add_content(&role_section);
|
editor.add_content(&role_section);
|
||||||
|
|
||||||
let this = Rc::new(PerformanceEditor {
|
let this = Rc::new(PerformanceEditor {
|
||||||
backend,
|
handle,
|
||||||
editor,
|
editor,
|
||||||
person_row,
|
person_row,
|
||||||
ensemble_row,
|
ensemble_row,
|
||||||
|
|
@ -77,79 +76,51 @@ impl PerformanceEditor {
|
||||||
person: RefCell::new(None),
|
person: RefCell::new(None),
|
||||||
ensemble: RefCell::new(None),
|
ensemble: RefCell::new(None),
|
||||||
role: RefCell::new(None),
|
role: RefCell::new(None),
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.set_back_cb(clone!(@strong this => move || {
|
this.editor.set_back_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.editor.set_save_cb(clone!(@weak this => move || {
|
this.editor.set_save_cb(clone!(@weak this => move || {
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
let performance = Performance {
|
||||||
cb(Performance {
|
|
||||||
person: this.person.borrow().clone(),
|
person: this.person.borrow().clone(),
|
||||||
ensemble: this.ensemble.borrow().clone(),
|
ensemble: this.ensemble.borrow().clone(),
|
||||||
role: this.role.borrow().clone(),
|
role: this.role.borrow().clone(),
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(Some(performance));
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.person_row.set_cb(clone!(@weak this => move || {
|
this.person_row.set_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(person) = push!(this.handle, PersonSelector).await {
|
||||||
let selector = PersonSelector::new(this.backend.clone());
|
|
||||||
|
|
||||||
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
|
||||||
this.show_person(Some(&person));
|
this.show_person(Some(&person));
|
||||||
this.person.replace(Some(person.clone()));
|
this.person.replace(Some(person.clone()));
|
||||||
this.show_ensemble(None);
|
this.show_ensemble(None);
|
||||||
this.ensemble.replace(None);
|
this.ensemble.replace(None);
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(selector);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.ensemble_row.set_cb(clone!(@weak this => move || {
|
this.ensemble_row.set_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(ensemble) = push!(this.handle, EnsembleSelector).await {
|
||||||
let selector = EnsembleSelector::new(this.backend.clone());
|
|
||||||
|
|
||||||
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |ensemble| {
|
|
||||||
this.show_person(None);
|
this.show_person(None);
|
||||||
this.person.replace(None);
|
this.person.replace(None);
|
||||||
this.show_ensemble(Some(&ensemble));
|
this.show_ensemble(Some(&ensemble));
|
||||||
this.ensemble.replace(Some(ensemble.clone()));
|
this.ensemble.replace(None);
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(selector);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.role_row.set_cb(clone!(@weak this => move || {
|
this.role_row.set_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(role) = push!(this.handle, InstrumentSelector).await {
|
||||||
let selector = InstrumentSelector::new(this.backend.clone());
|
|
||||||
|
|
||||||
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |role| {
|
|
||||||
this.show_role(Some(&role));
|
this.show_role(Some(&role));
|
||||||
this.role.replace(Some(role.clone()));
|
this.role.replace(Some(role));
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(selector);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.reset_role_button.connect_clicked(clone!(@weak this => move |_| {
|
this.reset_role_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
|
|
@ -176,12 +147,9 @@ impl PerformanceEditor {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a closure to be called when the user has chosen to save the performance.
|
|
||||||
pub fn set_selected_cb<F: Fn(Performance) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PerformanceEditor {
|
||||||
/// Update the UI according to person.
|
/// Update the UI according to person.
|
||||||
fn show_person(&self, person: Option<&Person>) {
|
fn show_person(&self, person: Option<&Person>) {
|
||||||
if let Some(person) = person {
|
if let Some(person) = person {
|
||||||
|
|
@ -214,16 +182,8 @@ impl PerformanceEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for PerformanceEditor {
|
impl Widget for PerformanceEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.editor.widget.clone().upcast()
|
self.editor.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::generate_id;
|
use crate::database::generate_id;
|
||||||
use crate::database::Person;
|
use crate::database::Person;
|
||||||
use crate::widgets::{Editor, EntryRow, Navigator, NavigatorScreen, Section, UploadSection};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -11,7 +12,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for creating or editing a person.
|
/// A dialog for creating or editing a person.
|
||||||
pub struct PersonEditor {
|
pub struct PersonEditor {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<Person>,
|
||||||
|
|
||||||
/// The ID of the person that is edited or a newly generated one.
|
/// The ID of the person that is edited or a newly generated one.
|
||||||
id: String,
|
id: String,
|
||||||
|
|
@ -20,13 +21,11 @@ pub struct PersonEditor {
|
||||||
first_name: EntryRow,
|
first_name: EntryRow,
|
||||||
last_name: EntryRow,
|
last_name: EntryRow,
|
||||||
upload: UploadSection,
|
upload: UploadSection,
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Person) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersonEditor {
|
impl Screen<Option<Person>, Person> for PersonEditor {
|
||||||
/// Create a new person editor and optionally initialize it.
|
/// Create a new person editor and optionally initialize it.
|
||||||
pub fn new(backend: Rc<Backend>, person: Option<Person>) -> Rc<Self> {
|
fn new(person: Option<Person>, handle: NavigationHandle<Person>) -> Rc<Self> {
|
||||||
let editor = Editor::new();
|
let editor = Editor::new();
|
||||||
editor.set_title("Person");
|
editor.set_title("Person");
|
||||||
|
|
||||||
|
|
@ -57,56 +56,42 @@ impl PersonEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
id,
|
id,
|
||||||
editor,
|
editor,
|
||||||
first_name,
|
first_name,
|
||||||
last_name,
|
last_name,
|
||||||
upload,
|
upload,
|
||||||
saved_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
this.editor.set_back_cb(clone!(@strong this => move || {
|
this.editor.set_back_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.editor.set_save_cb(clone!(@strong this => move || {
|
this.editor.set_save_cb(clone!(@strong this => move || {
|
||||||
let context = glib::MainContext::default();
|
spawn!(@clone this, async move {
|
||||||
let clone = this.clone();
|
this.editor.loading();
|
||||||
context.spawn_local(async move {
|
match this.save().await {
|
||||||
clone.editor.loading();
|
Ok(person) => {
|
||||||
match clone.clone().save().await {
|
this.handle.pop(Some(person));
|
||||||
Ok(_) => {
|
|
||||||
let navigator = clone.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let description = gettext!("Cause: {}", err);
|
let description = gettext!("Cause: {}", err);
|
||||||
clone.editor.error(&gettext("Failed to save person!"), &description);
|
this.editor.error(&gettext("Failed to save person!"), &description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called if the person was saved.
|
|
||||||
pub fn set_saved_cb<F: Fn(Person) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PersonEditor {
|
||||||
/// Save the person and possibly upload it to the server.
|
/// Save the person and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(self: &Rc<Self>) -> Result<Person> {
|
||||||
let first_name = self.first_name.get_text();
|
let first_name = self.first_name.get_text();
|
||||||
let last_name = self.last_name.get_text();
|
let last_name = self.last_name.get_text();
|
||||||
|
|
||||||
|
|
@ -117,31 +102,19 @@ impl PersonEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.upload.get_active() {
|
if self.upload.get_active() {
|
||||||
self.backend.post_person(&person).await?;
|
self.handle.backend.post_person(&person).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend.db().update_person(person.clone()).await?;
|
self.handle.backend.db().update_person(person.clone()).await?;
|
||||||
self.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
if let Some(cb) = &*self.saved_cb.borrow() {
|
Ok(person)
|
||||||
cb(person.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for PersonEditor {
|
impl Widget for PersonEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.editor.widget.clone().upcast()
|
self.editor.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use super::performance::PerformanceEditor;
|
use super::performance::PerformanceEditor;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::selectors::{PersonSelector, WorkSelector};
|
use crate::selectors::PersonSelector;
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen};
|
use crate::widgets::{List, Widget};
|
||||||
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -14,8 +15,8 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A widget for creating or editing a recording.
|
/// A widget for creating or editing a recording.
|
||||||
pub struct RecordingEditor {
|
pub struct RecordingEditor {
|
||||||
pub widget: gtk::Stack,
|
handle: NavigationHandle<Recording>,
|
||||||
backend: Rc<Backend>,
|
widget: gtk::Stack,
|
||||||
save_button: gtk::Button,
|
save_button: gtk::Button,
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
work_row: libadwaita::ActionRow,
|
work_row: libadwaita::ActionRow,
|
||||||
|
|
@ -25,13 +26,11 @@ pub struct RecordingEditor {
|
||||||
id: String,
|
id: String,
|
||||||
work: RefCell<Option<Work>>,
|
work: RefCell<Option<Work>>,
|
||||||
performances: RefCell<Vec<Performance>>,
|
performances: RefCell<Vec<Performance>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecordingEditor {
|
impl Screen<Option<Recording>, Recording> for RecordingEditor {
|
||||||
/// Create a new recording editor widget and optionally initialize it.
|
/// Create a new recording editor widget and optionally initialize it.
|
||||||
pub fn new(backend: Rc<Backend>, recording: Option<Recording>) -> Rc<Self> {
|
fn new(recording: Option<Recording>, handle: NavigationHandle<Recording>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_editor.ui");
|
||||||
|
|
@ -59,8 +58,8 @@ impl RecordingEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
let this = Rc::new(RecordingEditor {
|
let this = Rc::new(RecordingEditor {
|
||||||
|
handle,
|
||||||
widget,
|
widget,
|
||||||
backend,
|
|
||||||
save_button,
|
save_button,
|
||||||
info_bar,
|
info_bar,
|
||||||
work_row,
|
work_row,
|
||||||
|
|
@ -70,71 +69,42 @@ impl RecordingEditor {
|
||||||
id,
|
id,
|
||||||
work: RefCell::new(work),
|
work: RefCell::new(work),
|
||||||
performances: RefCell::new(performances),
|
performances: RefCell::new(performances),
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.clone().pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.save_button
|
this.save_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
.connect_clicked(clone!(@strong this => move |_| {
|
spawn!(@clone this, async move {
|
||||||
let context = glib::MainContext::default();
|
this.widget.set_visible_child_name("loading");
|
||||||
let clone = this.clone();
|
match this.save().await {
|
||||||
context.spawn_local(async move {
|
Ok(recording) => {
|
||||||
clone.widget.set_visible_child_name("loading");
|
this.handle.pop(Some(recording));
|
||||||
match clone.clone().save().await {
|
|
||||||
Ok(_) => {
|
|
||||||
let navigator = clone.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.clone().pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clone.info_bar.set_revealed(true);
|
this.info_bar.set_revealed(true);
|
||||||
clone.widget.set_visible_child_name("content");
|
this.widget.set_visible_child_name("content");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
work_button.connect_clicked(clone!(@strong this => move |_| {
|
work_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
// TODO: We need the pushed screen to return a work here.
|
||||||
let person_selector = PersonSelector::new(this.backend.clone());
|
});
|
||||||
|
|
||||||
person_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
|
||||||
let work_selector = WorkSelector::new(this.backend.clone(), person.clone());
|
|
||||||
|
|
||||||
work_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |work| {
|
|
||||||
this.work_selected(&work);
|
|
||||||
this.work.replace(Some(work.clone()));
|
|
||||||
|
|
||||||
navigator.clone().pop();
|
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
navigator.clone().push(work_selector);
|
this.performance_list.set_make_widget_cb(clone!(@weak this => move |index| {
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(person_selector);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.performance_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
|
||||||
let performance = &this.performances.borrow()[index];
|
let performance = &this.performances.borrow()[index];
|
||||||
|
|
||||||
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||||
delete_button.set_valign(gtk::Align::Center);
|
delete_button.set_valign(gtk::Align::Center);
|
||||||
|
|
||||||
delete_button.connect_clicked(clone!(@strong this => move |_| {
|
delete_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let length = {
|
let length = {
|
||||||
let mut performances = this.performances.borrow_mut();
|
let mut performances = this.performances.borrow_mut();
|
||||||
performances.remove(index);
|
performances.remove(index);
|
||||||
|
|
@ -147,17 +117,10 @@ impl RecordingEditor {
|
||||||
let edit_button = gtk::Button::from_icon_name(Some("document-edit-symbolic"));
|
let edit_button = gtk::Button::from_icon_name(Some("document-edit-symbolic"));
|
||||||
edit_button.set_valign(gtk::Align::Center);
|
edit_button.set_valign(gtk::Align::Center);
|
||||||
|
|
||||||
edit_button.connect_clicked(clone!(@strong this => move |_| {
|
edit_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let performance = &this.performances.borrow()[index];
|
let performance = &this.performances.borrow()[index];
|
||||||
|
if let Some(performance) = push!(this.handle, PerformanceEditor, Some(performance.to_owned())).await {
|
||||||
let editor = PerformanceEditor::new(
|
|
||||||
this.backend.clone(),
|
|
||||||
Some(performance.clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.set_selected_cb(clone!(@strong this, @strong navigator => move |performance| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut performances = this.performances.borrow_mut();
|
let mut performances = this.performances.borrow_mut();
|
||||||
performances[index] = performance;
|
performances[index] = performance;
|
||||||
|
|
@ -165,12 +128,8 @@ impl RecordingEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.performance_list.update(length);
|
this.performance_list.update(length);
|
||||||
|
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let row = libadwaita::ActionRow::new();
|
let row = libadwaita::ActionRow::new();
|
||||||
|
|
@ -184,11 +143,8 @@ impl RecordingEditor {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
add_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(performance) = push!(this.handle, PerformanceEditor, None).await {
|
||||||
let editor = PerformanceEditor::new(this.backend.clone(), None);
|
|
||||||
|
|
||||||
editor.set_selected_cb(clone!(@strong this, @strong navigator => move |performance| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut performances = this.performances.borrow_mut();
|
let mut performances = this.performances.borrow_mut();
|
||||||
performances.push(performance);
|
performances.push(performance);
|
||||||
|
|
@ -196,12 +152,8 @@ impl RecordingEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.performance_list.update(length);
|
this.performance_list.update(length);
|
||||||
|
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
|
|
@ -215,12 +167,9 @@ impl RecordingEditor {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called if the recording was created.
|
|
||||||
pub fn set_selected_cb<F: Fn(Recording) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RecordingEditor {
|
||||||
/// Update the UI according to work.
|
/// Update the UI according to work.
|
||||||
fn work_selected(&self, work: &Work) {
|
fn work_selected(&self, work: &Work) {
|
||||||
self.work_row.set_title(Some(&gettext("Work")));
|
self.work_row.set_title(Some(&gettext("Work")));
|
||||||
|
|
@ -229,7 +178,7 @@ impl RecordingEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save the recording and possibly upload it to the server.
|
/// Save the recording and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(self: &Rc<Self>) -> Result<Recording> {
|
||||||
let recording = Recording {
|
let recording = Recording {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
work: self
|
work: self
|
||||||
|
|
@ -243,40 +192,23 @@ impl RecordingEditor {
|
||||||
|
|
||||||
let upload = self.upload_switch.get_active();
|
let upload = self.upload_switch.get_active();
|
||||||
if upload {
|
if upload {
|
||||||
self.backend.post_recording(&recording).await?;
|
self.handle.backend.post_recording(&recording).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend
|
self.handle.backend
|
||||||
.db()
|
.db()
|
||||||
.update_recording(recording.clone().into())
|
.update_recording(recording.clone().into())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
if let Some(cb) = &*self.selected_cb.borrow() {
|
Ok(recording)
|
||||||
cb(recording.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let navigator = self.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.clone().pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for RecordingEditor {
|
impl Widget for RecordingEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use super::work_part::WorkPartEditor;
|
use super::work_part::WorkPartEditor;
|
||||||
use super::work_section::WorkSectionEditor;
|
use super::work_section::WorkSectionEditor;
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::selectors::{InstrumentSelector, PersonSelector};
|
use crate::selectors::{InstrumentSelector, PersonSelector};
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::{List, Widget};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -32,8 +32,8 @@ impl PartOrSection {
|
||||||
|
|
||||||
/// A widget for editing and creating works.
|
/// A widget for editing and creating works.
|
||||||
pub struct WorkEditor {
|
pub struct WorkEditor {
|
||||||
|
handle: NavigationHandle<Work>,
|
||||||
widget: gtk::Stack,
|
widget: gtk::Stack,
|
||||||
backend: Rc<Backend>,
|
|
||||||
save_button: gtk::Button,
|
save_button: gtk::Button,
|
||||||
title_entry: gtk::Entry,
|
title_entry: gtk::Entry,
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
|
|
@ -45,13 +45,11 @@ pub struct WorkEditor {
|
||||||
composer: RefCell<Option<Person>>,
|
composer: RefCell<Option<Person>>,
|
||||||
instruments: RefCell<Vec<Instrument>>,
|
instruments: RefCell<Vec<Instrument>>,
|
||||||
structure: RefCell<Vec<PartOrSection>>,
|
structure: RefCell<Vec<PartOrSection>>,
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Work) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkEditor {
|
impl Screen<Option<Work>, Work> for WorkEditor {
|
||||||
/// Create a new work editor widget and optionally initialize it.
|
/// Create a new work editor widget and optionally initialize it.
|
||||||
pub fn new(backend: Rc<Backend>, work: Option<Work>) -> Rc<Self> {
|
fn new(work: Option<Work>, handle: NavigationHandle<Work>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_editor.ui");
|
||||||
|
|
@ -100,8 +98,8 @@ impl WorkEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
|
handle,
|
||||||
widget,
|
widget,
|
||||||
backend,
|
|
||||||
save_button,
|
save_button,
|
||||||
id,
|
id,
|
||||||
info_bar,
|
info_bar,
|
||||||
|
|
@ -113,57 +111,39 @@ impl WorkEditor {
|
||||||
composer: RefCell::new(composer),
|
composer: RefCell::new(composer),
|
||||||
instruments: RefCell::new(instruments),
|
instruments: RefCell::new(instruments),
|
||||||
structure: RefCell::new(structure),
|
structure: RefCell::new(structure),
|
||||||
saved_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.save_button
|
this.save_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
.connect_clicked(clone!(@strong this => move |_| {
|
spawn!(@clone this, async move {
|
||||||
let context = glib::MainContext::default();
|
this.widget.set_visible_child_name("loading");
|
||||||
let clone = this.clone();
|
match this.save().await {
|
||||||
context.spawn_local(async move {
|
Ok(work) => {
|
||||||
clone.widget.set_visible_child_name("loading");
|
this.handle.pop(Some(work));
|
||||||
match clone.clone().save().await {
|
|
||||||
Ok(_) => {
|
|
||||||
let navigator = clone.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clone.info_bar.set_revealed(true);
|
this.info_bar.set_revealed(true);
|
||||||
clone.widget.set_visible_child_name("content");
|
this.widget.set_visible_child_name("content");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
composer_button.connect_clicked(clone!(@strong this => move |_| {
|
composer_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(person) = push!(this.handle, PersonSelector).await {
|
||||||
let selector = PersonSelector::new(this.backend.clone());
|
this.show_composer(&person);
|
||||||
|
this.composer.replace(Some(person.to_owned()));
|
||||||
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
|
||||||
this.show_composer(person);
|
|
||||||
this.composer.replace(Some(person.clone()));
|
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(selector);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.instrument_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
this.instrument_list.set_make_widget_cb(clone!(@weak this => move |index| {
|
||||||
let instrument = &this.instruments.borrow()[index];
|
let instrument = &this.instruments.borrow()[index];
|
||||||
|
|
||||||
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||||
|
|
@ -186,12 +166,9 @@ impl WorkEditor {
|
||||||
row.upcast()
|
row.upcast()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_instrument_button.connect_clicked(clone!(@strong this => move |_| {
|
add_instrument_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(instrument) = push!(this.handle, InstrumentSelector).await {
|
||||||
let selector = InstrumentSelector::new(this.backend.clone());
|
|
||||||
|
|
||||||
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |instrument| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut instruments = this.instruments.borrow_mut();
|
let mut instruments = this.instruments.borrow_mut();
|
||||||
instruments.push(instrument.clone());
|
instruments.push(instrument.clone());
|
||||||
|
|
@ -199,20 +176,17 @@ impl WorkEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.instrument_list.update(length);
|
this.instrument_list.update(length);
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(selector);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.part_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
this.part_list.set_make_widget_cb(clone!(@weak this => move |index| {
|
||||||
let pos = &this.structure.borrow()[index];
|
let pos = &this.structure.borrow()[index];
|
||||||
|
|
||||||
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||||
delete_button.set_valign(gtk::Align::Center);
|
delete_button.set_valign(gtk::Align::Center);
|
||||||
|
|
||||||
delete_button.connect_clicked(clone!(@strong this => move |_| {
|
delete_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let length = {
|
let length = {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure.remove(index);
|
structure.remove(index);
|
||||||
|
|
@ -225,14 +199,11 @@ impl WorkEditor {
|
||||||
let edit_button = gtk::Button::from_icon_name(Some("document-edit-symbolic"));
|
let edit_button = gtk::Button::from_icon_name(Some("document-edit-symbolic"));
|
||||||
edit_button.set_valign(gtk::Align::Center);
|
edit_button.set_valign(gtk::Align::Center);
|
||||||
|
|
||||||
edit_button.connect_clicked(clone!(@strong this => move |_| {
|
edit_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
match this.structure.borrow()[index].clone() {
|
match this.structure.borrow()[index].clone() {
|
||||||
PartOrSection::Part(part) => {
|
PartOrSection::Part(part) => {
|
||||||
let editor = WorkPartEditor::new(this.backend.clone(), Some(part));
|
if let Some(part) = push!(this.handle, WorkPartEditor, Some(part)).await {
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |part| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure[index] = PartOrSection::Part(part);
|
structure[index] = PartOrSection::Part(part);
|
||||||
|
|
@ -240,15 +211,10 @@ impl WorkEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.part_list.update(length);
|
this.part_list.update(length);
|
||||||
navigator.clone().pop();
|
}
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
PartOrSection::Section(section) => {
|
PartOrSection::Section(section) => {
|
||||||
let editor = WorkSectionEditor::new(Some(section));
|
if let Some(section) = push!(this.handle, WorkSectionEditor, Some(section)).await {
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |section| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure[index] = PartOrSection::Section(section);
|
structure[index] = PartOrSection::Section(section);
|
||||||
|
|
@ -256,13 +222,10 @@ impl WorkEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.part_list.update(length);
|
this.part_list.update(length);
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let row = libadwaita::ActionRow::new();
|
let row = libadwaita::ActionRow::new();
|
||||||
|
|
@ -280,7 +243,7 @@ impl WorkEditor {
|
||||||
row.upcast()
|
row.upcast()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.part_list.set_move_cb(clone!(@strong this => move |old_index, new_index| {
|
this.part_list.set_move_cb(clone!(@weak this => move |old_index, new_index| {
|
||||||
let length = {
|
let length = {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure.swap(old_index, new_index);
|
structure.swap(old_index, new_index);
|
||||||
|
|
@ -290,12 +253,9 @@ impl WorkEditor {
|
||||||
this.part_list.update(length);
|
this.part_list.update(length);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_part_button.connect_clicked(clone!(@strong this => move |_| {
|
add_part_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(part) = push!(this.handle, WorkPartEditor, None).await {
|
||||||
let editor = WorkPartEditor::new(this.backend.clone(), None);
|
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |part| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure.push(PartOrSection::Part(part));
|
structure.push(PartOrSection::Part(part));
|
||||||
|
|
@ -303,19 +263,13 @@ impl WorkEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.part_list.update(length);
|
this.part_list.update(length);
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_section_button.connect_clicked(clone!(@strong this => move |_| {
|
add_section_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(section) = push!(this.handle, WorkSectionEditor, None).await {
|
||||||
let editor = WorkSectionEditor::new(None);
|
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |section| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure.push(PartOrSection::Section(section));
|
structure.push(PartOrSection::Section(section));
|
||||||
|
|
@ -323,11 +277,8 @@ impl WorkEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.part_list.update(length);
|
this.part_list.update(length);
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
|
|
@ -341,12 +292,9 @@ impl WorkEditor {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The closure to call when a work was created.
|
|
||||||
pub fn set_saved_cb<F: Fn(Work) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WorkEditor {
|
||||||
/// Update the UI according to person.
|
/// Update the UI according to person.
|
||||||
fn show_composer(&self, person: &Person) {
|
fn show_composer(&self, person: &Person) {
|
||||||
self.composer_row.set_title(Some(&gettext("Composer")));
|
self.composer_row.set_title(Some(&gettext("Composer")));
|
||||||
|
|
@ -355,7 +303,7 @@ impl WorkEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save the work and possibly upload it to the server.
|
/// Save the work and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(self: &Rc<Self>) -> Result<Work> {
|
||||||
let mut section_count: usize = 0;
|
let mut section_count: usize = 0;
|
||||||
let mut parts = Vec::new();
|
let mut parts = Vec::new();
|
||||||
let mut sections = Vec::new();
|
let mut sections = Vec::new();
|
||||||
|
|
@ -387,35 +335,23 @@ impl WorkEditor {
|
||||||
|
|
||||||
let upload = self.upload_switch.get_active();
|
let upload = self.upload_switch.get_active();
|
||||||
if upload {
|
if upload {
|
||||||
self.backend.post_work(&work).await?;
|
self.handle.backend.post_work(&work).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend
|
self.handle.backend
|
||||||
.db()
|
.db()
|
||||||
.update_work(work.clone().into())
|
.update_work(work.clone().into())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
if let Some(cb) = &*self.saved_cb.borrow() {
|
Ok(work)
|
||||||
cb(work.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for WorkEditor {
|
impl Widget for WorkEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::selectors::PersonSelector;
|
use crate::selectors::PersonSelector;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -12,19 +12,17 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for creating or editing a work part.
|
/// A dialog for creating or editing a work part.
|
||||||
pub struct WorkPartEditor {
|
pub struct WorkPartEditor {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<WorkPart>,
|
||||||
widget: gtk::Box,
|
widget: gtk::Box,
|
||||||
title_entry: gtk::Entry,
|
title_entry: gtk::Entry,
|
||||||
composer_row: libadwaita::ActionRow,
|
composer_row: libadwaita::ActionRow,
|
||||||
reset_composer_button: gtk::Button,
|
reset_composer_button: gtk::Button,
|
||||||
composer: RefCell<Option<Person>>,
|
composer: RefCell<Option<Person>>,
|
||||||
ready_cb: RefCell<Option<Box<dyn Fn(WorkPart) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkPartEditor {
|
impl Screen<Option<WorkPart>, WorkPart> for WorkPartEditor {
|
||||||
/// Create a new part editor and optionally initialize it.
|
/// Create a new part editor and optionally initialize it.
|
||||||
pub fn new(backend: Rc<Backend>, part: Option<WorkPart>) -> Rc<Self> {
|
fn new(part: Option<WorkPart>, handle: NavigationHandle<WorkPart>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_part_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_part_editor.ui");
|
||||||
|
|
@ -46,53 +44,36 @@ impl WorkPartEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
widget,
|
widget,
|
||||||
title_entry,
|
title_entry,
|
||||||
composer_row,
|
composer_row,
|
||||||
reset_composer_button,
|
reset_composer_button,
|
||||||
composer: RefCell::new(composer),
|
composer: RefCell::new(composer),
|
||||||
ready_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong this => move |_| {
|
save_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
if let Some(cb) = &*this.ready_cb.borrow() {
|
let part = WorkPart {
|
||||||
cb(WorkPart {
|
|
||||||
title: this.title_entry.get_text().unwrap().to_string(),
|
title: this.title_entry.get_text().unwrap().to_string(),
|
||||||
composer: this.composer.borrow().clone(),
|
composer: this.composer.borrow().clone(),
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(Some(part));
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
composer_button.connect_clicked(clone!(@strong this => move |_| {
|
composer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(person) = push!(this.handle, PersonSelector).await {
|
||||||
let selector = PersonSelector::new(this.backend.clone());
|
this.show_composer(Some(&person));
|
||||||
|
this.composer.replace(Some(person.to_owned()));
|
||||||
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
|
||||||
this.show_composer(Some(person));
|
|
||||||
this.composer.replace(Some(person.clone()));
|
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(selector);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.reset_composer_button
|
this.reset_composer_button
|
||||||
|
|
@ -109,12 +90,9 @@ impl WorkPartEditor {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when the user wants to save the part.
|
|
||||||
pub fn set_ready_cb<F: Fn(WorkPart) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.ready_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WorkPartEditor {
|
||||||
/// Update the UI according to person.
|
/// Update the UI according to person.
|
||||||
fn show_composer(&self, person: Option<&Person>) {
|
fn show_composer(&self, person: Option<&Person>) {
|
||||||
if let Some(person) = person {
|
if let Some(person) = person {
|
||||||
|
|
@ -129,16 +107,8 @@ impl WorkPartEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for WorkPartEditor {
|
impl Widget for WorkPartEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk_macros::get_widget;
|
use gtk_macros::get_widget;
|
||||||
|
|
@ -8,15 +9,14 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for creating or editing a work section.
|
/// A dialog for creating or editing a work section.
|
||||||
pub struct WorkSectionEditor {
|
pub struct WorkSectionEditor {
|
||||||
|
handle: NavigationHandle<WorkSection>,
|
||||||
widget: gtk::Box,
|
widget: gtk::Box,
|
||||||
title_entry: gtk::Entry,
|
title_entry: gtk::Entry,
|
||||||
ready_cb: RefCell<Option<Box<dyn Fn(WorkSection) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkSectionEditor {
|
impl Screen<Option<WorkSection>, WorkSection> for WorkSectionEditor {
|
||||||
/// Create a new section editor and optionally initialize it.
|
/// Create a new section editor and optionally initialize it.
|
||||||
pub fn new(section: Option<WorkSection>) -> Rc<Self> {
|
fn new(section: Option<WorkSection>, handle: NavigationHandle<WorkSection>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_section_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_section_editor.ui");
|
||||||
|
|
@ -31,56 +31,32 @@ impl WorkSectionEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
|
handle,
|
||||||
widget,
|
widget,
|
||||||
title_entry,
|
title_entry,
|
||||||
ready_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong this => move |_| {
|
save_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
if let Some(cb) = &*this.ready_cb.borrow() {
|
let section = WorkSection {
|
||||||
cb(WorkSection {
|
|
||||||
before_index: 0,
|
before_index: 0,
|
||||||
title: this.title_entry.get_text().unwrap().to_string(),
|
title: this.title_entry.get_text().unwrap().to_string(),
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(Some(section));
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when the user wants to save the section. Note that the
|
|
||||||
/// resulting object will always have `before_index` set to 0. The caller is expected to
|
|
||||||
/// change that later before adding the section to the database.
|
|
||||||
pub fn set_ready_cb<F: Fn(WorkSection) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.ready_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for WorkSectionEditor {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Widget for WorkSectionEditor {
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use super::source::Source;
|
||||||
use super::track_set_editor::{TrackSetData, TrackSetEditor};
|
use super::track_set_editor::{TrackSetData, TrackSetEditor};
|
||||||
use crate::database::{generate_id, Medium, Track, TrackSet};
|
use crate::database::{generate_id, Medium, Track, TrackSet};
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::{List, Widget};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use glib::prelude::*;
|
use glib::prelude::*;
|
||||||
|
|
@ -14,7 +15,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for editing metadata while importing music into the music library.
|
/// A dialog for editing metadata while importing music into the music library.
|
||||||
pub struct MediumEditor {
|
pub struct MediumEditor {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<()>,
|
||||||
source: Rc<Box<dyn Source>>,
|
source: Rc<Box<dyn Source>>,
|
||||||
widget: gtk::Stack,
|
widget: gtk::Stack,
|
||||||
done_button: gtk::Button,
|
done_button: gtk::Button,
|
||||||
|
|
@ -24,12 +25,11 @@ pub struct MediumEditor {
|
||||||
publish_switch: gtk::Switch,
|
publish_switch: gtk::Switch,
|
||||||
track_set_list: Rc<List>,
|
track_set_list: Rc<List>,
|
||||||
track_sets: RefCell<Vec<TrackSetData>>,
|
track_sets: RefCell<Vec<TrackSetData>>,
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediumEditor {
|
impl Screen<Rc<Box<dyn Source>>, ()> for MediumEditor {
|
||||||
/// Create a new medium editor.
|
/// Create a new medium editor.
|
||||||
pub fn new(backend: Rc<Backend>, source: Rc<Box<dyn Source>>) -> Rc<Self> {
|
fn new(source: Rc<Box<dyn Source>>, handle: NavigationHandle<()>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/medium_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/medium_editor.ui");
|
||||||
|
|
@ -48,7 +48,7 @@ impl MediumEditor {
|
||||||
frame.set_child(Some(&list.widget));
|
frame.set_child(Some(&list.widget));
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
source,
|
source,
|
||||||
widget,
|
widget,
|
||||||
done_button,
|
done_button,
|
||||||
|
|
@ -58,40 +58,30 @@ impl MediumEditor {
|
||||||
publish_switch,
|
publish_switch,
|
||||||
track_set_list: list,
|
track_set_list: list,
|
||||||
track_sets: RefCell::new(Vec::new()),
|
track_sets: RefCell::new(Vec::new()),
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.done_button.connect_clicked(clone!(@strong this => move |_| {
|
this.done_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let context = glib::MainContext::default();
|
this.widget.set_visible_child_name("loading");
|
||||||
let clone = this.clone();
|
spawn!(@clone this, async move {
|
||||||
context.spawn_local(async move {
|
match this.save().await {
|
||||||
clone.widget.set_visible_child_name("loading");
|
|
||||||
match clone.clone().save().await {
|
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
// TODO: Display errors.
|
||||||
println!("{:?}", err);
|
println!("{:?}", err);
|
||||||
// clone.info_bar.set_revealed(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_button.connect_clicked(clone!(@strong this => move |_| {
|
add_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
spawn!(@clone this, async move {
|
||||||
if let Some(navigator) = navigator {
|
if let Some(track_set) = push!(this.handle, TrackSetEditor, Rc::clone(&this.source)).await {
|
||||||
let editor = TrackSetEditor::new(this.backend.clone(), Rc::clone(&this.source));
|
|
||||||
|
|
||||||
editor.set_done_cb(clone!(@strong this => move |track_set| {
|
|
||||||
let length = {
|
let length = {
|
||||||
let mut track_sets = this.track_sets.borrow_mut();
|
let mut track_sets = this.track_sets.borrow_mut();
|
||||||
track_sets.push(track_set);
|
track_sets.push(track_set);
|
||||||
|
|
@ -99,13 +89,11 @@ impl MediumEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.track_set_list.update(length);
|
this.track_set_list.update(length);
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.track_set_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
this.track_set_list.set_make_widget_cb(clone!(@weak this => move |index| {
|
||||||
let track_set = &this.track_sets.borrow()[index];
|
let track_set = &this.track_sets.borrow()[index];
|
||||||
|
|
||||||
let title = track_set.recording.work.get_title();
|
let title = track_set.recording.work.get_title();
|
||||||
|
|
@ -124,39 +112,38 @@ impl MediumEditor {
|
||||||
row.add_suffix(&edit_button);
|
row.add_suffix(&edit_button);
|
||||||
row.set_activatable_widget(Some(&edit_button));
|
row.set_activatable_widget(Some(&edit_button));
|
||||||
|
|
||||||
edit_button.connect_clicked(clone!(@strong this => move |_| {
|
edit_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
|
// TODO: Implement editing.
|
||||||
}));
|
}));
|
||||||
|
|
||||||
row.upcast()
|
row.upcast()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Copy the source in the background.
|
spawn!(@clone this, async move {
|
||||||
let context = glib::MainContext::default();
|
match this.source.copy().await {
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.source.copy().await {
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
// TODO: Present error.
|
// TODO: Present error.
|
||||||
println!("Failed to copy source: {}", error);
|
println!("Failed to copy source: {}", error);
|
||||||
},
|
},
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
clone.done_stack.set_visible_child(&clone.done);
|
this.done_stack.set_visible_child(&this.done);
|
||||||
clone.done_button.set_sensitive(true);
|
this.done_button.set_sensitive(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MediumEditor {
|
||||||
/// Save the medium and possibly upload it to the server.
|
/// Save the medium and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(&self) -> Result<()> {
|
||||||
let name = self.name_entry.get_text().unwrap().to_string();
|
let name = self.name_entry.get_text().unwrap().to_string();
|
||||||
|
|
||||||
// Create a new directory in the music library path for the imported medium.
|
// Create a new directory in the music library path for the imported medium.
|
||||||
|
|
||||||
let mut path = self.backend.get_music_library_path().unwrap().clone();
|
let mut path = self.handle.backend.get_music_library_path().unwrap().clone();
|
||||||
path.push(&name);
|
path.push(&name);
|
||||||
std::fs::create_dir(&path)?;
|
std::fs::create_dir(&path)?;
|
||||||
|
|
||||||
|
|
@ -205,35 +192,22 @@ impl MediumEditor {
|
||||||
|
|
||||||
let upload = self.publish_switch.get_active();
|
let upload = self.publish_switch.get_active();
|
||||||
if upload {
|
if upload {
|
||||||
self.backend.post_medium(&medium).await?;
|
self.handle.backend.post_medium(&medium).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend
|
self.handle.backend
|
||||||
.db()
|
.db()
|
||||||
.update_medium(medium.clone())
|
.update_medium(medium.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
let navigator = self.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.clone().pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for MediumEditor {
|
impl Widget for MediumEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use super::disc_source::DiscSource;
|
||||||
use super::folder_source::FolderSource;
|
use super::folder_source::FolderSource;
|
||||||
use super::source::Source;
|
use super::source::Source;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -14,16 +15,15 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for starting to import music.
|
/// A dialog for starting to import music.
|
||||||
pub struct SourceSelector {
|
pub struct SourceSelector {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<()>,
|
||||||
widget: gtk::Box,
|
widget: gtk::Box,
|
||||||
stack: gtk::Stack,
|
stack: gtk::Stack,
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceSelector {
|
impl Screen<(), ()> for SourceSelector {
|
||||||
/// Create a new source selector.
|
/// Create a new source selector.
|
||||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
fn new(_: (), handle: NavigationHandle<()>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/source_selector.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/source_selector.ui");
|
||||||
|
|
@ -36,60 +36,47 @@ impl SourceSelector {
|
||||||
get_widget!(builder, gtk::Button, disc_button);
|
get_widget!(builder, gtk::Button, disc_button);
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
widget,
|
widget,
|
||||||
stack,
|
stack,
|
||||||
info_bar,
|
info_bar,
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
folder_button.connect_clicked(clone!(@strong this => move |_| {
|
folder_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let window = this.navigator.borrow().clone().unwrap().window.clone();
|
|
||||||
let dialog = gtk::FileChooserDialog::new(
|
let dialog = gtk::FileChooserDialog::new(
|
||||||
Some(&gettext("Select folder")),
|
Some(&gettext("Select folder")),
|
||||||
Some(&window),
|
Some(&this.handle.window),
|
||||||
gtk::FileChooserAction::SelectFolder,
|
gtk::FileChooserAction::SelectFolder,
|
||||||
&[
|
&[
|
||||||
(&gettext("Cancel"), gtk::ResponseType::Cancel),
|
(&gettext("Cancel"), gtk::ResponseType::Cancel),
|
||||||
(&gettext("Select"), gtk::ResponseType::Accept),
|
(&gettext("Select"), gtk::ResponseType::Accept),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
dialog.connect_response(clone!(@strong this => move |dialog, response| {
|
dialog.connect_response(clone!(@weak this => move |dialog, response| {
|
||||||
this.stack.set_visible_child_name("loading");
|
this.stack.set_visible_child_name("loading");
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
|
|
||||||
if let gtk::ResponseType::Accept = response {
|
if let gtk::ResponseType::Accept = response {
|
||||||
if let Some(file) = dialog.get_file() {
|
if let Some(file) = dialog.get_file() {
|
||||||
if let Some(path) = file.get_path() {
|
if let Some(path) = file.get_path() {
|
||||||
let context = glib::MainContext::default();
|
spawn!(@clone this, async move {
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let folder = FolderSource::new(PathBuf::from(path));
|
let folder = FolderSource::new(PathBuf::from(path));
|
||||||
match folder.load().await {
|
match folder.load().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let navigator = clone.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let source = Rc::new(Box::new(folder) as Box<dyn Source>);
|
let source = Rc::new(Box::new(folder) as Box<dyn Source>);
|
||||||
let editor = MediumEditor::new(clone.backend.clone(), source);
|
push!(this.handle, MediumEditor, source).await;
|
||||||
navigator.push(editor);
|
this.handle.pop(Some(()));
|
||||||
}
|
|
||||||
|
|
||||||
clone.info_bar.set_revealed(false);
|
|
||||||
clone.stack.set_visible_child_name("start");
|
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// TODO: Present error.
|
// TODO: Present error.
|
||||||
clone.info_bar.set_revealed(true);
|
this.info_bar.set_revealed(true);
|
||||||
clone.stack.set_visible_child_name("start");
|
this.stack.set_visible_child_name("start");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -101,29 +88,21 @@ impl SourceSelector {
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
disc_button.connect_clicked(clone!(@strong this => move |_| {
|
disc_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
this.stack.set_visible_child_name("loading");
|
this.stack.set_visible_child_name("loading");
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
spawn!(@clone this, async move {
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let disc = DiscSource::new().unwrap();
|
let disc = DiscSource::new().unwrap();
|
||||||
match disc.load().await {
|
match disc.load().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let navigator = clone.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let source = Rc::new(Box::new(disc) as Box<dyn Source>);
|
let source = Rc::new(Box::new(disc) as Box<dyn Source>);
|
||||||
let editor = MediumEditor::new(clone.backend.clone(), source);
|
push!(this.handle, MediumEditor, source).await;
|
||||||
navigator.push(editor);
|
this.handle.pop(Some(()));
|
||||||
}
|
|
||||||
|
|
||||||
clone.info_bar.set_revealed(false);
|
|
||||||
clone.stack.set_visible_child_name("start");
|
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// TODO: Present error.
|
// TODO: Present error.
|
||||||
clone.info_bar.set_revealed(true);
|
this.info_bar.set_revealed(true);
|
||||||
clone.stack.set_visible_child_name("start");
|
this.stack.set_visible_child_name("start");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -133,16 +112,8 @@ impl SourceSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for SourceSelector {
|
impl Widget for SourceSelector {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::database::Recording;
|
use crate::database::Recording;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk_macros::get_widget;
|
use gtk_macros::get_widget;
|
||||||
|
|
@ -9,15 +10,14 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A screen for editing a single track.
|
/// A screen for editing a single track.
|
||||||
pub struct TrackEditor {
|
pub struct TrackEditor {
|
||||||
|
handle: NavigationHandle<Vec<usize>>,
|
||||||
widget: gtk::Box,
|
widget: gtk::Box,
|
||||||
selection: RefCell<Vec<usize>>,
|
selection: RefCell<Vec<usize>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Vec<usize>)>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrackEditor {
|
impl Screen<(Recording, Vec<usize>), Vec<usize>> for TrackEditor {
|
||||||
/// Create a new track editor.
|
/// Create a new track editor.
|
||||||
pub fn new(recording: Recording, selection: Vec<usize>) -> Rc<Self> {
|
fn new((recording, selection): (Recording, Vec<usize>), handle: NavigationHandle<Vec<usize>>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_editor.ui");
|
||||||
|
|
@ -34,38 +34,27 @@ impl TrackEditor {
|
||||||
parts_frame.set_child(Some(&parts_list));
|
parts_frame.set_child(Some(&parts_list));
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
|
handle,
|
||||||
widget,
|
widget,
|
||||||
selection: RefCell::new(selection),
|
selection: RefCell::new(selection),
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
select_button.connect_clicked(clone!(@strong this => move |_| {
|
select_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
let selection = this.selection.borrow().clone();
|
let selection = this.selection.borrow().clone();
|
||||||
cb(selection);
|
this.handle.pop(Some(selection));
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
for (index, part) in recording.work.parts.iter().enumerate() {
|
for (index, part) in recording.work.parts.iter().enumerate() {
|
||||||
let check = gtk::CheckButton::new();
|
let check = gtk::CheckButton::new();
|
||||||
check.set_active(this.selection.borrow().contains(&index));
|
check.set_active(this.selection.borrow().contains(&index));
|
||||||
|
|
||||||
check.connect_toggled(clone!(@strong this => move |check| {
|
check.connect_toggled(clone!(@weak this => move |check| {
|
||||||
let mut selection = this.selection.borrow_mut();
|
let mut selection = this.selection.borrow_mut();
|
||||||
if check.get_active() {
|
if check.get_active() {
|
||||||
selection.push(index);
|
selection.push(index);
|
||||||
|
|
@ -86,23 +75,10 @@ impl TrackEditor {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when the user has edited the track.
|
|
||||||
pub fn set_selected_cb<F: Fn(Vec<usize>) + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for TrackEditor {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Widget for TrackEditor {
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use super::source::Source;
|
use super::source::Source;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk_macros::get_widget;
|
use gtk_macros::get_widget;
|
||||||
|
|
@ -9,17 +10,16 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A screen for selecting tracks from a source.
|
/// A screen for selecting tracks from a source.
|
||||||
pub struct TrackSelector {
|
pub struct TrackSelector {
|
||||||
|
handle: NavigationHandle<Vec<usize>>,
|
||||||
source: Rc<Box<dyn Source>>,
|
source: Rc<Box<dyn Source>>,
|
||||||
widget: gtk::Box,
|
widget: gtk::Box,
|
||||||
select_button: gtk::Button,
|
select_button: gtk::Button,
|
||||||
selection: RefCell<Vec<usize>>,
|
selection: RefCell<Vec<usize>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Vec<usize>)>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrackSelector {
|
impl Screen<Rc<Box<dyn Source>>, Vec<usize>> for TrackSelector {
|
||||||
/// Create a new track selector.
|
/// Create a new track selector.
|
||||||
pub fn new(source: Rc<Box<dyn Source>>) -> Rc<Self> {
|
fn new(source: Rc<Box<dyn Source>>, handle: NavigationHandle<Vec<usize>>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_selector.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_selector.ui");
|
||||||
|
|
@ -36,33 +36,22 @@ impl TrackSelector {
|
||||||
tracks_frame.set_child(Some(&track_list));
|
tracks_frame.set_child(Some(&track_list));
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
|
handle,
|
||||||
source,
|
source,
|
||||||
widget,
|
widget,
|
||||||
select_button,
|
select_button,
|
||||||
selection: RefCell::new(Vec::new()),
|
selection: RefCell::new(Vec::new()),
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.select_button.connect_clicked(clone!(@strong this => move |_| {
|
this.select_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
let selection = this.selection.borrow().clone();
|
let selection = this.selection.borrow().clone();
|
||||||
cb(selection);
|
this.handle.pop(Some(selection));
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let tracks = this.source.tracks().unwrap();
|
let tracks = this.source.tracks().unwrap();
|
||||||
|
|
@ -70,7 +59,7 @@ impl TrackSelector {
|
||||||
for (index, track) in tracks.iter().enumerate() {
|
for (index, track) in tracks.iter().enumerate() {
|
||||||
let check = gtk::CheckButton::new();
|
let check = gtk::CheckButton::new();
|
||||||
|
|
||||||
check.connect_toggled(clone!(@strong this => move |check| {
|
check.connect_toggled(clone!(@weak this => move |check| {
|
||||||
let mut selection = this.selection.borrow_mut();
|
let mut selection = this.selection.borrow_mut();
|
||||||
if check.get_active() {
|
if check.get_active() {
|
||||||
selection.push(index);
|
selection.push(index);
|
||||||
|
|
@ -98,25 +87,10 @@ impl TrackSelector {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when the user has selected tracks. The
|
|
||||||
/// closure will be called with the indices of the selected tracks as its
|
|
||||||
/// argument.
|
|
||||||
pub fn set_selected_cb<F: Fn(Vec<usize>) + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for TrackSelector {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Widget for TrackSelector {
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ use super::track_editor::TrackEditor;
|
||||||
use super::track_selector::TrackSelector;
|
use super::track_selector::TrackSelector;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::Recording;
|
use crate::database::Recording;
|
||||||
use crate::selectors::{PersonSelector, RecordingSelector, WorkSelector};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen};
|
use crate::selectors::PersonSelector;
|
||||||
|
use crate::widgets::{List, Widget};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -32,7 +33,7 @@ pub struct TrackData {
|
||||||
|
|
||||||
/// A screen for editing a set of tracks for one recording.
|
/// A screen for editing a set of tracks for one recording.
|
||||||
pub struct TrackSetEditor {
|
pub struct TrackSetEditor {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<TrackSetData>,
|
||||||
source: Rc<Box<dyn Source>>,
|
source: Rc<Box<dyn Source>>,
|
||||||
widget: gtk::Box,
|
widget: gtk::Box,
|
||||||
save_button: gtk::Button,
|
save_button: gtk::Button,
|
||||||
|
|
@ -40,13 +41,11 @@ pub struct TrackSetEditor {
|
||||||
track_list: Rc<List>,
|
track_list: Rc<List>,
|
||||||
recording: RefCell<Option<Recording>>,
|
recording: RefCell<Option<Recording>>,
|
||||||
tracks: RefCell<Vec<TrackData>>,
|
tracks: RefCell<Vec<TrackData>>,
|
||||||
done_cb: RefCell<Option<Box<dyn Fn(TrackSetData)>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrackSetEditor {
|
impl Screen<Rc<Box<dyn Source>>, TrackSetData> for TrackSetEditor {
|
||||||
/// Create a new track set editor.
|
/// Create a new track set editor.
|
||||||
pub fn new(backend: Rc<Backend>, source: Rc<Box<dyn Source>>) -> Rc<Self> {
|
fn new(source: Rc<Box<dyn Source>>, handle: NavigationHandle<TrackSetData>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_set_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_set_editor.ui");
|
||||||
|
|
@ -63,7 +62,7 @@ impl TrackSetEditor {
|
||||||
tracks_frame.set_child(Some(&track_list.widget));
|
tracks_frame.set_child(Some(&track_list.widget));
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
source,
|
source,
|
||||||
widget,
|
widget,
|
||||||
save_button,
|
save_button,
|
||||||
|
|
@ -71,71 +70,30 @@ impl TrackSetEditor {
|
||||||
track_list,
|
track_list,
|
||||||
recording: RefCell::new(None),
|
recording: RefCell::new(None),
|
||||||
tracks: RefCell::new(Vec::new()),
|
tracks: RefCell::new(Vec::new()),
|
||||||
done_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.save_button.connect_clicked(clone!(@strong this => move |_| {
|
this.save_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
if let Some(cb) = &*this.done_cb.borrow() {
|
|
||||||
let data = TrackSetData {
|
let data = TrackSetData {
|
||||||
recording: this.recording.borrow().clone().unwrap(),
|
recording: this.recording.borrow().clone().unwrap(),
|
||||||
tracks: this.tracks.borrow().clone(),
|
tracks: this.tracks.borrow().clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
cb(data);
|
this.handle.pop(Some(data));
|
||||||
}
|
|
||||||
|
|
||||||
let navigator = this.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
select_recording_button.connect_clicked(clone!(@strong this => move |_| {
|
select_recording_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
// TODO: We need to push a screen returning a recording here.
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let person_selector = PersonSelector::new(this.backend.clone());
|
|
||||||
|
|
||||||
person_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
|
||||||
let work_selector = WorkSelector::new(this.backend.clone(), person.clone());
|
|
||||||
|
|
||||||
work_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |work| {
|
|
||||||
let recording_selector = RecordingSelector::new(this.backend.clone(), work.clone());
|
|
||||||
|
|
||||||
recording_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |recording| {
|
|
||||||
this.recording.replace(Some(recording.clone()));
|
|
||||||
this.recording_selected();
|
|
||||||
|
|
||||||
navigator.clone().pop();
|
|
||||||
navigator.clone().pop();
|
|
||||||
navigator.clone().pop();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
navigator.clone().push(recording_selector);
|
edit_tracks_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
}));
|
spawn!(@clone this, async move {
|
||||||
|
if let Some(selection) = push!(this.handle, TrackSelector, Rc::clone(&this.source)).await {
|
||||||
navigator.clone().push(work_selector);
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.clone().push(person_selector);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
edit_tracks_button.connect_clicked(clone!(@strong this => move |_| {
|
|
||||||
let navigator = this.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let selector = TrackSelector::new(Rc::clone(&this.source));
|
|
||||||
|
|
||||||
selector.set_selected_cb(clone!(@strong this => move |selection| {
|
|
||||||
let mut tracks = Vec::new();
|
let mut tracks = Vec::new();
|
||||||
|
|
||||||
for index in selection {
|
for index in selection {
|
||||||
|
|
@ -151,13 +109,11 @@ impl TrackSetEditor {
|
||||||
this.tracks.replace(tracks);
|
this.tracks.replace(tracks);
|
||||||
this.track_list.update(length);
|
this.track_list.update(length);
|
||||||
this.autofill_parts();
|
this.autofill_parts();
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(selector);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.track_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
this.track_list.set_make_widget_cb(clone!(@weak this => move |index| {
|
||||||
let track = &this.tracks.borrow()[index];
|
let track = &this.tracks.borrow()[index];
|
||||||
|
|
||||||
let mut title_parts = Vec::<String>::new();
|
let mut title_parts = Vec::<String>::new();
|
||||||
|
|
@ -190,16 +146,12 @@ impl TrackSetEditor {
|
||||||
row.add_suffix(&edit_button);
|
row.add_suffix(&edit_button);
|
||||||
row.set_activatable_widget(Some(&edit_button));
|
row.set_activatable_widget(Some(&edit_button));
|
||||||
|
|
||||||
edit_button.connect_clicked(clone!(@strong this => move |_| {
|
edit_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
let recording = this.recording.borrow().clone();
|
let recording = this.recording.borrow().clone();
|
||||||
let navigator = this.navigator.borrow().clone();
|
if let Some(recording) = recording {
|
||||||
|
spawn!(@clone this, async move {
|
||||||
if let (Some(recording), Some(navigator)) = (recording, navigator) {
|
|
||||||
let track = &this.tracks.borrow()[index];
|
let track = &this.tracks.borrow()[index];
|
||||||
|
if let Some(selection) = push!(this.handle, TrackEditor, (recording, track.work_parts.clone())).await {
|
||||||
let editor = TrackEditor::new(recording, track.work_parts.clone());
|
|
||||||
|
|
||||||
editor.set_selected_cb(clone!(@strong this => move |selection| {
|
|
||||||
{
|
{
|
||||||
let mut tracks = this.tracks.borrow_mut();
|
let mut tracks = this.tracks.borrow_mut();
|
||||||
let mut track = &mut tracks[index];
|
let mut track = &mut tracks[index];
|
||||||
|
|
@ -207,9 +159,8 @@ impl TrackSetEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update_tracks();
|
this.update_tracks();
|
||||||
}));
|
}
|
||||||
|
});
|
||||||
navigator.push(editor);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -218,12 +169,9 @@ impl TrackSetEditor {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when the user has created the track set.
|
|
||||||
pub fn set_done_cb<F: Fn(TrackSetData) + 'static>(&self, cb: F) {
|
|
||||||
self.done_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TrackSetEditor {
|
||||||
/// Set everything up after selecting a recording.
|
/// Set everything up after selecting a recording.
|
||||||
fn recording_selected(&self) {
|
fn recording_selected(&self) {
|
||||||
if let Some(recording) = &*self.recording.borrow() {
|
if let Some(recording) = &*self.recording.borrow() {
|
||||||
|
|
@ -260,21 +208,9 @@ impl TrackSetEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatorScreen for TrackSetEditor {
|
impl Widget for TrackSetEditor {
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.widget.clone().upcast()
|
self.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ macro_rules! push {
|
||||||
($handle:expr, $screen:ty) => {
|
($handle:expr, $screen:ty) => {
|
||||||
$handle.push::<_, _, $screen>(())
|
$handle.push::<_, _, $screen>(())
|
||||||
};
|
};
|
||||||
($handle:expr, $screen:ty, $input:ident) => {
|
($handle:expr, $screen:ty, $input:expr) => {
|
||||||
$handle.push::<_, _, $screen>($input)
|
$handle.push::<_, _, $screen>($input)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ macro_rules! replace {
|
||||||
($navigator:expr, $screen:ty) => {
|
($navigator:expr, $screen:ty) => {
|
||||||
$navigator.replace::<_, _, $screen>(())
|
$navigator.replace::<_, _, $screen>(())
|
||||||
};
|
};
|
||||||
($navigator:expr, $screen:ty, $input:ident) => {
|
($navigator:expr, $screen:ty, $input:expr) => {
|
||||||
$navigator.replace::<_, _, $screen>($input)
|
$navigator.replace::<_, _, $screen>($input)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use super::RecordingScreen;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::{Ensemble, Recording};
|
use crate::database::{Ensemble, Recording};
|
||||||
use crate::editors::EnsembleEditor;
|
use crate::editors::EnsembleEditor;
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow, Screen, Section};
|
use crate::navigator::NavigatorWindow;
|
||||||
|
use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section};
|
||||||
|
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -49,9 +50,10 @@ impl EnsembleScreen {
|
||||||
|
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Edit ensemble"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Edit ensemble"), clone!(@strong this => move || {
|
||||||
let editor = EnsembleEditor::new(this.backend.clone(), Some(this.ensemble.clone()));
|
spawn!(@clone this, async move {
|
||||||
let window = NavigatorWindow::new(editor);
|
let window = NavigatorWindow::new(this.backend.clone());
|
||||||
window.show();
|
replace!(window.navigator, EnsembleEditor, None).await;
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Delete ensemble"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Delete ensemble"), clone!(@strong this => move || {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use super::{WorkScreen, RecordingScreen};
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::{Person, Recording, Work};
|
use crate::database::{Person, Recording, Work};
|
||||||
use crate::editors::PersonEditor;
|
use crate::editors::PersonEditor;
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow, Screen, Section};
|
use crate::navigator::NavigatorWindow;
|
||||||
|
use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section};
|
||||||
|
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -54,9 +55,10 @@ impl PersonScreen {
|
||||||
|
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Edit person"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Edit person"), clone!(@strong this => move || {
|
||||||
let editor = PersonEditor::new(this.backend.clone(), Some(this.person.clone()));
|
spawn!(@clone this, async move {
|
||||||
let window = NavigatorWindow::new(editor);
|
let window = NavigatorWindow::new(this.backend.clone());
|
||||||
window.show();
|
replace!(window.navigator, PersonEditor, None).await;
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Delete person"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Delete person"), clone!(@strong this => move || {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::Recording;
|
use crate::database::Recording;
|
||||||
use crate::editors::RecordingEditor;
|
use crate::editors::RecordingEditor;
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow, Screen, Section};
|
use crate::navigator::NavigatorWindow;
|
||||||
|
use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section};
|
||||||
|
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -48,9 +49,10 @@ impl RecordingScreen {
|
||||||
|
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Edit recording"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Edit recording"), clone!(@strong this => move || {
|
||||||
let editor = RecordingEditor::new(this.backend.clone(), Some(this.recording.clone()));
|
spawn!(@clone this, async move {
|
||||||
let window = NavigatorWindow::new(editor);
|
let window = NavigatorWindow::new(this.backend.clone());
|
||||||
window.show();
|
replace!(window.navigator, RecordingEditor, None).await;
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Delete recording"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Delete recording"), clone!(@strong this => move || {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use super::RecordingScreen;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::{Work, Recording};
|
use crate::database::{Work, Recording};
|
||||||
use crate::editors::WorkEditor;
|
use crate::editors::WorkEditor;
|
||||||
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow, Screen, Section};
|
use crate::navigator::NavigatorWindow;
|
||||||
|
use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section};
|
||||||
|
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -50,9 +51,10 @@ impl WorkScreen {
|
||||||
|
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Edit work"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Edit work"), clone!(@strong this => move || {
|
||||||
let editor = WorkEditor::new(this.backend.clone(), Some(this.work.clone()));
|
spawn!(@clone this, async move {
|
||||||
let window = NavigatorWindow::new(editor);
|
let window = NavigatorWindow::new(this.backend.clone());
|
||||||
window.show();
|
replace!(window.navigator, WorkEditor, None).await;
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.widget.add_action(&gettext("Delete work"), clone!(@strong this => move || {
|
this.widget.add_action(&gettext("Delete work"), clone!(@strong this => move || {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use super::selector::Selector;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::Ensemble;
|
use crate::database::Ensemble;
|
||||||
use crate::editors::EnsembleEditor;
|
use crate::editors::EnsembleEditor;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -12,66 +13,55 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A screen for selecting a ensemble.
|
/// A screen for selecting a ensemble.
|
||||||
pub struct EnsembleSelector {
|
pub struct EnsembleSelector {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<Ensemble>,
|
||||||
selector: Rc<Selector<Ensemble>>,
|
selector: Rc<Selector<Ensemble>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(&Ensemble) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnsembleSelector {
|
impl Screen<(), Ensemble> for EnsembleSelector {
|
||||||
/// Create a new ensemble selector.
|
/// Create a new ensemble selector.
|
||||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
fn new(_: (), handle: NavigationHandle<Ensemble>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let selector = Selector::<Ensemble>::new();
|
let selector = Selector::<Ensemble>::new();
|
||||||
selector.set_title(&gettext("Select ensemble"));
|
selector.set_title(&gettext("Select ensemble"));
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
selector,
|
selector,
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
this.selector.set_back_cb(clone!(@strong this => move || {
|
this.selector.set_back_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
}));
|
||||||
navigator.pop();
|
|
||||||
|
this.selector.set_add_cb(clone!(@weak this => move || {
|
||||||
|
spawn!(@clone this, async move {
|
||||||
|
if let Some(ensemble) = push!(this.handle, EnsembleEditor, None).await {
|
||||||
|
this.handle.pop(Some(ensemble));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector.set_add_cb(clone!(@strong this => move || {
|
this.selector.set_load_online(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let editor = EnsembleEditor::new(this.backend.clone(), None);
|
|
||||||
editor
|
|
||||||
.set_saved_cb(clone!(@strong this => move |ensemble| this.select(&ensemble)));
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.selector
|
|
||||||
.set_load_online(clone!(@strong this => move || {
|
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
async move { clone.backend.get_ensembles().await }
|
async move { clone.handle.backend.get_ensembles().await }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector
|
this.selector.set_load_local(clone!(@weak this => move || {
|
||||||
.set_load_local(clone!(@strong this => move || {
|
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
async move { clone.backend.db().get_ensembles().await.unwrap() }
|
async move { clone.handle.backend.db().get_ensembles().await.unwrap() }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector.set_make_widget(clone!(@strong this => move |ensemble| {
|
this.selector.set_make_widget(clone!(@weak this => move |ensemble| {
|
||||||
let row = libadwaita::ActionRow::new();
|
let row = libadwaita::ActionRow::new();
|
||||||
row.set_activatable(true);
|
row.set_activatable(true);
|
||||||
row.set_title(Some(&ensemble.name));
|
row.set_title(Some(&ensemble.name));
|
||||||
|
|
||||||
let ensemble = ensemble.to_owned();
|
let ensemble = ensemble.to_owned();
|
||||||
row.connect_activated(clone!(@strong this => move |_| {
|
row.connect_activated(clone!(@weak this => move |_| {
|
||||||
this.select(&ensemble);
|
this.handle.pop(Some(ensemble.clone()))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
row.upcast()
|
row.upcast()
|
||||||
|
|
@ -82,31 +72,10 @@ impl EnsembleSelector {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when an item is selected.
|
|
||||||
pub fn set_selected_cb<F: Fn(&Ensemble) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select an ensemble.
|
|
||||||
fn select(&self, ensemble: &Ensemble) {
|
|
||||||
if let Some(cb) = &*self.selected_cb.borrow() {
|
|
||||||
cb(&ensemble);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for EnsembleSelector {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Widget for EnsembleSelector {
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.selector.widget.clone().upcast()
|
self.selector.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use super::selector::Selector;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::Instrument;
|
use crate::database::Instrument;
|
||||||
use crate::editors::InstrumentEditor;
|
use crate::editors::InstrumentEditor;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -12,66 +13,55 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A screen for selecting a instrument.
|
/// A screen for selecting a instrument.
|
||||||
pub struct InstrumentSelector {
|
pub struct InstrumentSelector {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<Instrument>,
|
||||||
selector: Rc<Selector<Instrument>>,
|
selector: Rc<Selector<Instrument>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(&Instrument) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstrumentSelector {
|
impl Screen<(), Instrument> for InstrumentSelector {
|
||||||
/// Create a new instrument selector.
|
/// Create a new instrument selector.
|
||||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
fn new(_: (), handle: NavigationHandle<Instrument>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let selector = Selector::<Instrument>::new();
|
let selector = Selector::<Instrument>::new();
|
||||||
selector.set_title(&gettext("Select instrument"));
|
selector.set_title(&gettext("Select instrument"));
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
selector,
|
selector,
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
this.selector.set_back_cb(clone!(@strong this => move || {
|
this.selector.set_back_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
}));
|
||||||
navigator.pop();
|
|
||||||
|
this.selector.set_add_cb(clone!(@weak this => move || {
|
||||||
|
spawn!(@clone this, async move {
|
||||||
|
if let Some(instrument) = push!(this.handle, InstrumentEditor, None).await {
|
||||||
|
this.handle.pop(Some(instrument));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector.set_add_cb(clone!(@strong this => move || {
|
this.selector.set_load_online(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let editor = InstrumentEditor::new(this.backend.clone(), None);
|
|
||||||
editor
|
|
||||||
.set_saved_cb(clone!(@strong this => move |instrument| this.select(&instrument)));
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.selector
|
|
||||||
.set_load_online(clone!(@strong this => move || {
|
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
async move { clone.backend.get_instruments().await }
|
async move { clone.handle.backend.get_instruments().await }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector
|
this.selector.set_load_local(clone!(@weak this => move || {
|
||||||
.set_load_local(clone!(@strong this => move || {
|
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
async move { clone.backend.db().get_instruments().await.unwrap() }
|
async move { clone.handle.backend.db().get_instruments().await.unwrap() }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector.set_make_widget(clone!(@strong this => move |instrument| {
|
this.selector.set_make_widget(clone!(@weak this => move |instrument| {
|
||||||
let row = libadwaita::ActionRow::new();
|
let row = libadwaita::ActionRow::new();
|
||||||
row.set_activatable(true);
|
row.set_activatable(true);
|
||||||
row.set_title(Some(&instrument.name));
|
row.set_title(Some(&instrument.name));
|
||||||
|
|
||||||
let instrument = instrument.to_owned();
|
let instrument = instrument.to_owned();
|
||||||
row.connect_activated(clone!(@strong this => move |_| {
|
row.connect_activated(clone!(@weak this => move |_| {
|
||||||
this.select(&instrument);
|
this.handle.pop(Some(instrument.clone()))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
row.upcast()
|
row.upcast()
|
||||||
|
|
@ -82,30 +72,10 @@ impl InstrumentSelector {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when an item is selected.
|
|
||||||
pub fn set_selected_cb<F: Fn(&Instrument) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select an instrument.
|
|
||||||
fn select(&self, instrument: &Instrument) {
|
|
||||||
if let Some(cb) = &*self.selected_cb.borrow() {
|
|
||||||
cb(&instrument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for InstrumentSelector {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Widget for InstrumentSelector {
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.selector.widget.clone().upcast()
|
self.selector.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,12 @@ pub use instrument::*;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
pub use person::*;
|
pub use person::*;
|
||||||
|
|
||||||
pub mod recording;
|
// TODO: Readd a better version of these.
|
||||||
pub use recording::*;
|
//
|
||||||
|
// pub mod recording;
|
||||||
pub mod work;
|
// pub use recording::*;
|
||||||
pub use work::*;
|
//
|
||||||
|
// pub mod work;
|
||||||
|
// pub use work::*;
|
||||||
|
|
||||||
mod selector;
|
mod selector;
|
||||||
|
|
@ -2,7 +2,8 @@ use super::selector::Selector;
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::Person;
|
use crate::database::Person;
|
||||||
use crate::editors::PersonEditor;
|
use crate::editors::PersonEditor;
|
||||||
use crate::widgets::{Navigator, NavigatorScreen};
|
use crate::navigator::{NavigationHandle, Screen};
|
||||||
|
use crate::widgets::Widget;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -12,66 +13,55 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A screen for selecting a person.
|
/// A screen for selecting a person.
|
||||||
pub struct PersonSelector {
|
pub struct PersonSelector {
|
||||||
backend: Rc<Backend>,
|
handle: NavigationHandle<Person>,
|
||||||
selector: Rc<Selector<Person>>,
|
selector: Rc<Selector<Person>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(&Person) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersonSelector {
|
impl Screen<(), Person> for PersonSelector {
|
||||||
/// Create a new person selector.
|
/// Create a new person selector.
|
||||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
fn new(_: (), handle: NavigationHandle<Person>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let selector = Selector::<Person>::new();
|
let selector = Selector::<Person>::new();
|
||||||
selector.set_title(&gettext("Select person"));
|
selector.set_title(&gettext("Select person"));
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
handle,
|
||||||
selector,
|
selector,
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
this.selector.set_back_cb(clone!(@strong this => move || {
|
this.selector.set_back_cb(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
this.handle.pop(None);
|
||||||
if let Some(navigator) = navigator {
|
}));
|
||||||
navigator.pop();
|
|
||||||
|
this.selector.set_add_cb(clone!(@weak this => move || {
|
||||||
|
spawn!(@clone this, async move {
|
||||||
|
if let Some(person) = push!(this.handle, PersonEditor, None).await {
|
||||||
|
this.handle.pop(Some(person));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector.set_add_cb(clone!(@strong this => move || {
|
this.selector.set_load_online(clone!(@weak this => move || {
|
||||||
let navigator = this.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
let editor = PersonEditor::new(this.backend.clone(), None);
|
|
||||||
editor
|
|
||||||
.set_saved_cb(clone!(@strong this => move |person| this.select(&person)));
|
|
||||||
navigator.push(editor);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.selector
|
|
||||||
.set_load_online(clone!(@strong this => move || {
|
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
async move { clone.backend.get_persons().await }
|
async move { clone.handle.backend.get_persons().await }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector
|
this.selector.set_load_local(clone!(@weak this => move || {
|
||||||
.set_load_local(clone!(@strong this => move || {
|
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
async move { clone.backend.db().get_persons().await.unwrap() }
|
async move { clone.handle.backend.db().get_persons().await.unwrap() }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.selector.set_make_widget(clone!(@strong this => move |person| {
|
this.selector.set_make_widget(clone!(@weak this => move |person| {
|
||||||
let row = libadwaita::ActionRow::new();
|
let row = libadwaita::ActionRow::new();
|
||||||
row.set_activatable(true);
|
row.set_activatable(true);
|
||||||
row.set_title(Some(&person.name_lf()));
|
row.set_title(Some(&person.name_lf()));
|
||||||
|
|
||||||
let person = person.to_owned();
|
let person = person.to_owned();
|
||||||
row.connect_activated(clone!(@strong this => move |_| {
|
row.connect_activated(clone!(@weak this => move |_| {
|
||||||
this.select(&person);
|
this.handle.pop(Some(person.clone()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
row.upcast()
|
row.upcast()
|
||||||
|
|
@ -82,30 +72,10 @@ impl PersonSelector {
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the closure to be called when an item is selected.
|
|
||||||
pub fn set_selected_cb<F: Fn(&Person) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select a person.
|
|
||||||
fn select(&self, person: &Person) {
|
|
||||||
if let Some(cb) = &*self.selected_cb.borrow() {
|
|
||||||
cb(&person);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for PersonSelector {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Widget for PersonSelector {
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
self.selector.widget.clone().upcast()
|
self.selector.widget.clone().upcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::import::SourceSelector;
|
||||||
use crate::preferences::Preferences;
|
use crate::preferences::Preferences;
|
||||||
use crate::screens::*;
|
use crate::screens::*;
|
||||||
use crate::widgets::*;
|
use crate::widgets::*;
|
||||||
|
use crate::navigator::NavigatorWindow;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
|
|
@ -95,18 +96,10 @@ impl Window {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_button.connect_clicked(clone!(@strong result => move |_| {
|
add_button.connect_clicked(clone!(@strong result => move |_| {
|
||||||
// let editor = TracksEditor::new(result.backend.clone(), None, Vec::new());
|
spawn!(@clone result, async move {
|
||||||
|
let window = NavigatorWindow::new(result.backend.clone());
|
||||||
// editor.set_callback(clone!(@strong result => move || {
|
replace!(window.navigator, SourceSelector).await;
|
||||||
// result.reload();
|
});
|
||||||
// }));
|
|
||||||
|
|
||||||
// let window = NavigatorWindow::new(editor);
|
|
||||||
// window.show();
|
|
||||||
|
|
||||||
let dialog = SourceSelector::new(result.backend.clone());
|
|
||||||
let window = NavigatorWindow::new(dialog);
|
|
||||||
window.show();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue