Validate entries in editors

This commit is contained in:
Elias Projahn 2021-04-25 13:34:21 +02:00
parent 24f493d7d6
commit e0619b6b27
8 changed files with 173 additions and 66 deletions

View file

@ -23,7 +23,7 @@ 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.
fn new(ensemble: Option<Ensemble>, handle: NavigationHandle<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/Role"); editor.set_title("Ensemble");
let list = gtk::ListBoxBuilder::new() let list = gtk::ListBoxBuilder::new()
.selection_mode(gtk::SelectionMode::None) .selection_mode(gtk::SelectionMode::None)
@ -75,11 +75,22 @@ impl Screen<Option<Ensemble>, Ensemble> for EnsembleEditor {
}); });
})); }));
this.name
.entry
.connect_changed(clone!(@weak this => move |_| this.validate()));
this.validate();
this this
} }
} }
impl EnsembleEditor { impl EnsembleEditor {
/// Validate inputs and enable/disable saving.
fn validate(&self) {
self.editor.set_may_save(!self.name.get_text().is_empty());
}
/// Save the ensemble and possibly upload it to the server. /// Save the ensemble and possibly upload it to the server.
async fn save(&self) -> Result<Ensemble> { async fn save(&self) -> Result<Ensemble> {
let name = self.name.get_text(); let name = self.name.get_text();
@ -93,7 +104,11 @@ impl EnsembleEditor {
self.handle.backend.cl().post_ensemble(&ensemble).await?; self.handle.backend.cl().post_ensemble(&ensemble).await?;
} }
self.handle.backend.db().update_ensemble(ensemble.clone()).await?; self.handle
.backend
.db()
.update_ensemble(ensemble.clone())
.await?;
self.handle.backend.library_changed(); self.handle.backend.library_changed();
Ok(ensemble) Ok(ensemble)
@ -105,4 +120,3 @@ impl Widget for EnsembleEditor {
self.editor.widget.clone().upcast() self.editor.widget.clone().upcast()
} }
} }

View file

@ -75,11 +75,22 @@ impl Screen<Option<Instrument>, Instrument> for InstrumentEditor {
}); });
})); }));
this.name
.entry
.connect_changed(clone!(@weak this => move |_| this.validate()));
this.validate();
this this
} }
} }
impl InstrumentEditor { impl InstrumentEditor {
/// Validate inputs and enable/disable saving.
fn validate(&self) {
self.editor.set_may_save(!self.name.get_text().is_empty());
}
/// Save the instrument and possibly upload it to the server. /// Save the instrument and possibly upload it to the server.
async fn save(&self) -> Result<Instrument> { async fn save(&self) -> Result<Instrument> {
let name = self.name.get_text(); let name = self.name.get_text();
@ -90,10 +101,18 @@ impl InstrumentEditor {
}; };
if self.upload.get_active() { if self.upload.get_active() {
self.handle.backend.cl().post_instrument(&instrument).await?; self.handle
.backend
.cl()
.post_instrument(&instrument)
.await?;
} }
self.handle.backend.db().update_instrument(instrument.clone()).await?; self.handle
.backend
.db()
.update_instrument(instrument.clone())
.await?;
self.handle.backend.library_changed(); self.handle.backend.library_changed();
Ok(instrument) Ok(instrument)
@ -105,4 +124,3 @@ impl Widget for InstrumentEditor {
self.editor.widget.clone().upcast() self.editor.widget.clone().upcast()
} }
} }

View file

@ -82,11 +82,28 @@ impl Screen<Option<Person>, Person> for PersonEditor {
}); });
})); }));
this.first_name
.entry
.connect_changed(clone!(@weak this => move |_| this.validate()));
this.last_name
.entry
.connect_changed(clone!(@weak this => move |_| this.validate()));
this.validate();
this this
} }
} }
impl PersonEditor { impl PersonEditor {
/// Validate inputs and enable/disable saving.
fn validate(&self) {
self.editor.set_may_save(
!self.first_name.get_text().is_empty() && !self.last_name.get_text().is_empty(),
);
}
/// 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<Person> { async fn save(self: &Rc<Self>) -> Result<Person> {
let first_name = self.first_name.get_text(); let first_name = self.first_name.get_text();
@ -102,7 +119,11 @@ impl PersonEditor {
self.handle.backend.cl().post_person(&person).await?; self.handle.backend.cl().post_person(&person).await?;
} }
self.handle.backend.db().update_person(person.clone()).await?; self.handle
.backend
.db()
.update_person(person.clone())
.await?;
self.handle.backend.library_changed(); self.handle.backend.library_changed();
Ok(person) Ok(person)
@ -114,4 +135,3 @@ impl Widget for PersonEditor {
self.editor.widget.clone().upcast() self.editor.widget.clone().upcast()
} }
} }

View file

@ -1,7 +1,7 @@
use super::work_part::WorkPartEditor; use super::work_part::WorkPartEditor;
use super::work_section::WorkSectionEditor; use super::work_section::WorkSectionEditor;
use crate::selectors::{InstrumentSelector, PersonSelector};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::selectors::{InstrumentSelector, PersonSelector};
use crate::widgets::{List, Widget}; use crate::widgets::{List, Widget};
use anyhow::Result; use anyhow::Result;
use gettextrs::gettext; use gettextrs::gettext;
@ -119,7 +119,8 @@ impl Screen<Option<Work>, Work> for WorkEditor {
this.handle.pop(None); this.handle.pop(None);
})); }));
this.save_button.connect_clicked(clone!(@weak this => move |_| { this.save_button
.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move { spawn!(@clone this, async move {
this.widget.set_visible_child_name("loading"); this.widget.set_visible_child_name("loading");
match this.save().await { match this.save().await {
@ -143,7 +144,11 @@ impl Screen<Option<Work>, Work> for WorkEditor {
}); });
})); }));
this.instrument_list.set_make_widget_cb(clone!(@weak this => move |index| { this.title_entry
.connect_changed(clone!(@weak this => move |_| this.validate()));
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"));
@ -243,7 +248,8 @@ impl Screen<Option<Work>, Work> for WorkEditor {
row.upcast() row.upcast()
})); }));
this.part_list.set_move_cb(clone!(@weak 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);
@ -299,7 +305,14 @@ impl WorkEditor {
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")));
self.composer_row.set_subtitle(Some(&person.name_fl())); self.composer_row.set_subtitle(Some(&person.name_fl()));
self.save_button.set_sensitive(true); self.validate();
}
/// Validate inputs and enable/disable saving.
fn validate(&self) {
self.save_button.set_sensitive(
!self.title_entry.get_text().is_empty() && self.composer.borrow().is_some(),
);
} }
/// Save the work and possibly upload it to the server. /// Save the work and possibly upload it to the server.
@ -338,7 +351,8 @@ impl WorkEditor {
self.handle.backend.cl().post_work(&work).await?; self.handle.backend.cl().post_work(&work).await?;
} }
self.handle.backend self.handle
.backend
.db() .db()
.update_work(work.clone().into()) .update_work(work.clone().into())
.await .await

View file

@ -10,6 +10,7 @@ use std::rc::Rc;
pub struct WorkPartEditor { pub struct WorkPartEditor {
handle: NavigationHandle<WorkPart>, handle: NavigationHandle<WorkPart>,
widget: gtk::Box, widget: gtk::Box,
save_button: gtk::Button,
title_entry: gtk::Entry, title_entry: gtk::Entry,
} }
@ -32,6 +33,7 @@ impl Screen<Option<WorkPart>, WorkPart> for WorkPartEditor {
let this = Rc::new(Self { let this = Rc::new(Self {
handle, handle,
widget, widget,
save_button,
title_entry, title_entry,
}); });
@ -41,7 +43,8 @@ impl Screen<Option<WorkPart>, WorkPart> for WorkPartEditor {
this.handle.pop(None); this.handle.pop(None);
})); }));
save_button.connect_clicked(clone!(@weak this => move |_| { this.save_button
.connect_clicked(clone!(@weak this => move |_| {
let section = WorkPart { let section = WorkPart {
title: this.title_entry.get_text().to_string(), title: this.title_entry.get_text().to_string(),
}; };
@ -49,10 +52,23 @@ impl Screen<Option<WorkPart>, WorkPart> for WorkPartEditor {
this.handle.pop(Some(section)); this.handle.pop(Some(section));
})); }));
this.title_entry
.connect_changed(clone!(@weak this => move |_| this.validate()));
this.validate();
this this
} }
} }
impl WorkPartEditor {
/// Validate inputs and enable/disable saving.
fn validate(&self) {
self.save_button
.set_sensitive(!self.title_entry.get_text().is_empty());
}
}
impl Widget for WorkPartEditor { impl Widget for WorkPartEditor {
fn get_widget(&self) -> gtk::Widget { fn get_widget(&self) -> gtk::Widget {
self.widget.clone().upcast() self.widget.clone().upcast()

View file

@ -10,6 +10,7 @@ use std::rc::Rc;
pub struct WorkSectionEditor { pub struct WorkSectionEditor {
handle: NavigationHandle<WorkSection>, handle: NavigationHandle<WorkSection>,
widget: gtk::Box, widget: gtk::Box,
save_button: gtk::Button,
title_entry: gtk::Entry, title_entry: gtk::Entry,
} }
@ -32,6 +33,7 @@ impl Screen<Option<WorkSection>, WorkSection> for WorkSectionEditor {
let this = Rc::new(Self { let this = Rc::new(Self {
handle, handle,
widget, widget,
save_button,
title_entry, title_entry,
}); });
@ -41,7 +43,8 @@ impl Screen<Option<WorkSection>, WorkSection> for WorkSectionEditor {
this.handle.pop(None); this.handle.pop(None);
})); }));
save_button.connect_clicked(clone!(@weak this => move |_| { this.save_button
.connect_clicked(clone!(@weak this => move |_| {
let section = WorkSection { let section = WorkSection {
before_index: 0, before_index: 0,
title: this.title_entry.get_text().to_string(), title: this.title_entry.get_text().to_string(),
@ -50,10 +53,23 @@ impl Screen<Option<WorkSection>, WorkSection> for WorkSectionEditor {
this.handle.pop(Some(section)); this.handle.pop(Some(section));
})); }));
this.title_entry
.connect_changed(clone!(@weak this => move |_| this.validate()));
this.validate();
this this
} }
} }
impl WorkSectionEditor {
/// Validate inputs and enable/disable saving.
fn validate(&self) {
self.save_button
.set_sensitive(!self.title_entry.get_text().is_empty());
}
}
impl Widget for WorkSectionEditor { impl Widget for WorkSectionEditor {
fn get_widget(&self) -> gtk::Widget { fn get_widget(&self) -> gtk::Widget {
self.widget.clone().upcast() self.widget.clone().upcast()

View file

@ -78,6 +78,8 @@ impl Screen<(Arc<ImportSession>, Option<Medium>), Medium> for MediumEditor {
}); });
})); }));
this.name_entry.connect_changed(clone!(@weak this => move |_| this.validate()));
add_button.connect_clicked(clone!(@weak this => move |_| { add_button.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move { spawn!(@clone this, async move {
if let Some(track_set) = push!(this.handle, TrackSetEditor, Arc::clone(&this.session)).await { if let Some(track_set) = push!(this.handle, TrackSetEditor, Arc::clone(&this.session)).await {
@ -157,11 +159,18 @@ impl Screen<(Arc<ImportSession>, Option<Medium>), Medium> for MediumEditor {
this.track_set_list.update(length); this.track_set_list.update(length);
} }
this.validate();
this this
} }
} }
impl MediumEditor { impl MediumEditor {
/// Validate inputs and enable/disable saving.
fn validate(&self) {
self.done_button.set_sensitive(!self.name_entry.get_text().is_empty());
}
/// Create the medium and, if necessary, upload it to the server. /// Create the medium and, if necessary, upload it to the server.
async fn save(&self) -> Result<Medium> { async fn save(&self) -> Result<Medium> {
// Convert the track set data to real track sets. // Convert the track set data to real track sets.

View file

@ -7,7 +7,7 @@ pub struct EntryRow {
pub widget: libadwaita::ActionRow, pub widget: libadwaita::ActionRow,
/// The managed entry. /// The managed entry.
entry: gtk::Entry, pub entry: gtk::Entry,
} }
impl EntryRow { impl EntryRow {