database: Remove wrapper thread

This commit is contained in:
Elias Projahn 2022-01-23 14:35:33 +01:00
parent 678367ec1a
commit 42d1d047e3
31 changed files with 267 additions and 826 deletions

View file

@ -53,38 +53,6 @@
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">loading</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="AdwHeaderBar">
<property name="show-start-title-buttons">false</property>
<property name="show-end-title-buttons">false</property>
<property name="title-widget">
<object class="AdwWindowTitle">
<property name="title" translatable="true">Loading</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkSpinner">
<property name="hexpand">true</property>
<property name="vexpand">true</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="width-request">32</property>
<property name="height-request">32</property>
<property name="spinning">true</property>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">error</property>

View file

@ -56,18 +56,15 @@ impl Screen<Option<Ensemble>, Ensemble> for EnsembleEditor {
}));
this.editor.set_save_cb(clone!(@weak this => move || {
spawn!(@clone this, async move {
this.editor.loading();
match this.save().await {
Ok(ensemble) => {
this.handle.pop(Some(ensemble));
}
Err(err) => {
let description = gettext!("Cause: {}", err);
this.editor.error(&gettext("Failed to save ensemble!"), &description);
}
match this.save() {
Ok(ensemble) => {
this.handle.pop(Some(ensemble));
}
});
Err(err) => {
let description = gettext!("Cause: {}", err);
this.editor.error(&gettext("Failed to save ensemble!"), &description);
}
}
}));
this.name
@ -87,7 +84,7 @@ impl EnsembleEditor {
}
/// Save the ensemble.
async fn save(&self) -> Result<Ensemble> {
fn save(&self) -> Result<Ensemble> {
let name = self.name.get_text();
let ensemble = Ensemble {
@ -95,12 +92,7 @@ impl EnsembleEditor {
name,
};
self.handle
.backend
.db()
.update_ensemble(ensemble.clone())
.await?;
self.handle.backend.db().update_ensemble(ensemble.clone())?;
self.handle.backend.library_changed();
Ok(ensemble)

View file

@ -56,18 +56,15 @@ impl Screen<Option<Instrument>, Instrument> for InstrumentEditor {
}));
this.editor.set_save_cb(clone!(@weak this => move || {
spawn!(@clone this, async move {
this.editor.loading();
match this.save().await {
Ok(instrument) => {
this.handle.pop(Some(instrument));
}
Err(err) => {
let description = gettext!("Cause: {}", err);
this.editor.error(&gettext("Failed to save instrument!"), &description);
}
match this.save() {
Ok(instrument) => {
this.handle.pop(Some(instrument));
}
});
Err(err) => {
let description = gettext!("Cause: {}", err);
this.editor.error(&gettext("Failed to save instrument!"), &description);
}
}
}));
this.name
@ -87,7 +84,7 @@ impl InstrumentEditor {
}
/// Save the instrument.
async fn save(&self) -> Result<Instrument> {
fn save(&self) -> Result<Instrument> {
let name = self.name.get_text();
let instrument = Instrument {
@ -95,12 +92,7 @@ impl InstrumentEditor {
name,
};
self.handle
.backend
.db()
.update_instrument(instrument.clone())
.await?;
self.handle.backend.db().update_instrument(instrument.clone())?;
self.handle.backend.library_changed();
Ok(instrument)

View file

@ -63,18 +63,15 @@ impl Screen<Option<Person>, Person> for PersonEditor {
}));
this.editor.set_save_cb(clone!(@strong this => move || {
spawn!(@clone this, async move {
this.editor.loading();
match this.save().await {
Ok(person) => {
this.handle.pop(Some(person));
}
Err(err) => {
let description = gettext!("Cause: {}", err);
this.editor.error(&gettext("Failed to save person!"), &description);
}
match this.save() {
Ok(person) => {
this.handle.pop(Some(person));
}
});
Err(err) => {
let description = gettext!("Cause: {}", err);
this.editor.error(&gettext("Failed to save person!"), &description);
}
}
}));
this.first_name
@ -100,7 +97,7 @@ impl PersonEditor {
}
/// Save the person.
async fn save(self: &Rc<Self>) -> Result<Person> {
fn save(self: &Rc<Self>) -> Result<Person> {
let first_name = self.first_name.get_text();
let last_name = self.last_name.get_text();
@ -110,11 +107,7 @@ impl PersonEditor {
last_name,
};
self.handle
.backend
.db()
.update_person(person.clone())
.await?;
self.handle.backend.db().update_person(person.clone())?;
self.handle.backend.library_changed();
Ok(person)

View file

@ -74,18 +74,15 @@ impl Screen<Option<Recording>, Recording> for RecordingEditor {
this.save_button
.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move {
this.widget.set_visible_child_name("loading");
match this.save().await {
Ok(recording) => {
this.handle.pop(Some(recording));
}
Err(_) => {
this.info_bar.set_revealed(true);
this.widget.set_visible_child_name("content");
}
match this.save() {
Ok(recording) => {
this.handle.pop(Some(recording));
}
});
Err(_) => {
this.info_bar.set_revealed(true);
this.widget.set_visible_child_name("content");
}
}
}));
work_button.connect_clicked(clone!(@weak this => move |_| {
@ -179,7 +176,7 @@ impl RecordingEditor {
}
/// Save the recording.
async fn save(self: &Rc<Self>) -> Result<Recording> {
fn save(self: &Rc<Self>) -> Result<Recording> {
let recording = Recording {
id: self.id.clone(),
work: self
@ -191,13 +188,7 @@ impl RecordingEditor {
performances: self.performances.borrow().clone(),
};
self.handle
.backend
.db()
.update_recording(recording.clone())
.await
.unwrap();
self.handle.backend.db().update_recording(recording.clone())?;
self.handle.backend.library_changed();
Ok(recording)

View file

@ -113,18 +113,15 @@ impl Screen<Option<Work>, Work> for WorkEditor {
this.save_button
.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move {
this.widget.set_visible_child_name("loading");
match this.save().await {
Ok(work) => {
this.handle.pop(Some(work));
}
Err(_) => {
this.info_bar.set_revealed(true);
this.widget.set_visible_child_name("content");
}
match this.save() {
Ok(work) => {
this.handle.pop(Some(work));
}
});
Err(_) => {
this.info_bar.set_revealed(true);
this.widget.set_visible_child_name("content");
}
}
}));
composer_button.connect_clicked(clone!(@weak this => move |_| {
@ -313,7 +310,7 @@ impl WorkEditor {
}
/// Save the work.
async fn save(self: &Rc<Self>) -> Result<Work> {
fn save(self: &Rc<Self>) -> Result<Work> {
let mut section_count: usize = 0;
let mut parts = Vec::new();
let mut sections = Vec::new();
@ -343,13 +340,7 @@ impl WorkEditor {
sections,
};
self.handle
.backend
.db()
.update_work(work.clone())
.await
.unwrap();
self.handle.backend.db().update_work(work.clone())?;
self.handle.backend.library_changed();
Ok(work)

View file

@ -28,7 +28,7 @@ impl ImportScreen {
let this = self;
spawn!(@clone this, async move {
let mediums = this.handle.backend.db().get_mediums_by_source_id(this.session.source_id()).await;
let mediums = this.handle.backend.db().get_mediums_by_source_id(this.session.source_id());
match mediums {
Ok(mediums) => {

View file

@ -256,12 +256,7 @@ impl MediumPreview {
tracks,
};
self.handle
.backend
.db()
.update_medium(medium.clone())
.await?;
self.handle.backend.db().update_medium(medium)?;
self.handle.backend.library_changed();
Ok(())

View file

@ -48,11 +48,8 @@ impl Preferences {
if let gtk::ResponseType::Accept = response {
if let Some(file) = dialog.file() {
if let Some(path) = file.path() {
this.backend.set_music_library_path(path.clone()).unwrap();
this.music_library_path_row.set_subtitle(path.to_str().unwrap());
spawn!(@clone this, async move {
this.backend.set_music_library_path(path).await.unwrap();
});
}
}
}

View file

@ -59,7 +59,7 @@ impl Screen<Ensemble, ()> for EnsembleScreen {
&gettext("Delete ensemble"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
this.handle.backend.db().delete_ensemble(&this.ensemble.id).await.unwrap();
this.handle.backend.db().delete_ensemble(&this.ensemble.id).unwrap();
this.handle.backend.library_changed();
});
}),
@ -128,43 +128,41 @@ impl Screen<Ensemble, ()> for EnsembleScreen {
search.is_empty() || name.contains(&search)
}));
// Load the content asynchronously.
// Load the content.
spawn!(@clone this, async move {
let recordings = this.handle
.backend
.db()
.get_recordings_for_ensemble(&this.ensemble.id)
.await
.unwrap();
let recordings = this
.handle
.backend
.db()
.get_recordings_for_ensemble(&this.ensemble.id)
.unwrap();
let mediums = this.handle
.backend
.db()
.get_mediums_for_ensemble(&this.ensemble.id)
.await
.unwrap();
let mediums = this
.handle
.backend
.db()
.get_mediums_for_ensemble(&this.ensemble.id)
.unwrap();
if !recordings.is_empty() {
let length = recordings.len();
this.recordings.replace(recordings);
this.recording_list.update(length);
if !recordings.is_empty() {
let length = recordings.len();
this.recordings.replace(recordings);
this.recording_list.update(length);
let section = Section::new("Recordings", &this.recording_list.widget);
this.widget.add_content(&section.widget);
}
let section = Section::new("Recordings", &this.recording_list.widget);
this.widget.add_content(&section.widget);
}
if !mediums.is_empty() {
let length = mediums.len();
this.mediums.replace(mediums);
this.medium_list.update(length);
if !mediums.is_empty() {
let length = mediums.len();
this.mediums.replace(mediums);
this.medium_list.update(length);
let section = Section::new("Mediums", &this.medium_list.widget);
this.widget.add_content(&section.widget);
}
let section = Section::new("Mediums", &this.medium_list.widget);
this.widget.add_content(&section.widget);
}
this.widget.ready();
});
this.widget.ready();
this
}

View file

@ -136,16 +136,15 @@ impl Screen<(), ()> for MainScreen {
});
}));
// Load the content asynchronously.
// Load the content whenever there is a new library update.
spawn!(@clone this, async move {
loop {
this.navigator.reset();
let mut poes = Vec::new();
let persons = this.handle.backend.db().get_persons().await.unwrap();
let ensembles = this.handle.backend.db().get_ensembles().await.unwrap();
let persons = this.handle.backend.db().get_persons().unwrap();
let ensembles = this.handle.backend.db().get_ensembles().unwrap();
for person in persons {
poes.push(PersonOrEnsemble::Person(person));

View file

@ -64,7 +64,7 @@ impl Screen<Person, ()> for PersonScreen {
&gettext("Delete person"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
this.handle.backend.db().delete_person(&this.person.id).await.unwrap();
this.handle.backend.db().delete_person(&this.person.id).unwrap();
this.handle.backend.library_changed();
});
}),
@ -162,59 +162,52 @@ impl Screen<Person, ()> for PersonScreen {
search.is_empty() || name.contains(&search)
}));
// Load the content asynchronously.
// Load the content.
spawn!(@clone this, async move {
let works = this.handle
.backend
.db()
.get_works(&this.person.id)
.await
.unwrap();
let works = this.handle.backend.db().get_works(&this.person.id).unwrap();
let recordings = this.handle
.backend
.db()
.get_recordings_for_person(&this.person.id)
.await
.unwrap();
let recordings = this
.handle
.backend
.db()
.get_recordings_for_person(&this.person.id)
.unwrap();
let mediums = this.handle
.backend
.db()
.get_mediums_for_person(&this.person.id)
.await
.unwrap();
let mediums = this
.handle
.backend
.db()
.get_mediums_for_person(&this.person.id)
.unwrap();
if !works.is_empty() {
let length = works.len();
this.works.replace(works);
this.work_list.update(length);
if !works.is_empty() {
let length = works.len();
this.works.replace(works);
this.work_list.update(length);
let section = Section::new("Works", &this.work_list.widget);
this.widget.add_content(&section.widget);
}
let section = Section::new("Works", &this.work_list.widget);
this.widget.add_content(&section.widget);
}
if !recordings.is_empty() {
let length = recordings.len();
this.recordings.replace(recordings);
this.recording_list.update(length);
if !recordings.is_empty() {
let length = recordings.len();
this.recordings.replace(recordings);
this.recording_list.update(length);
let section = Section::new("Recordings", &this.recording_list.widget);
this.widget.add_content(&section.widget);
}
let section = Section::new("Recordings", &this.recording_list.widget);
this.widget.add_content(&section.widget);
}
if !mediums.is_empty() {
let length = mediums.len();
this.mediums.replace(mediums);
this.medium_list.update(length);
if !mediums.is_empty() {
let length = mediums.len();
this.mediums.replace(mediums);
this.medium_list.update(length);
let section = Section::new("Mediums", &this.medium_list.widget);
this.widget.add_content(&section.widget);
}
let section = Section::new("Mediums", &this.medium_list.widget);
this.widget.add_content(&section.widget);
}
this.widget.ready();
});
this.widget.ready();
this
}

View file

@ -65,7 +65,7 @@ impl Screen<Recording, ()> for RecordingScreen {
&gettext("Delete recording"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
this.handle.backend.db().delete_recording(&this.recording.id).await.unwrap();
this.handle.backend.db().delete_recording(&this.recording.id).unwrap();
this.handle.backend.library_changed();
});
}),
@ -93,19 +93,17 @@ impl Screen<Recording, ()> for RecordingScreen {
row.upcast()
}));
// Load the content asynchronously.
// Load the content.
spawn!(@clone this, async move {
let tracks = this.handle
.backend
.db()
.get_tracks(&this.recording.id)
.await
.unwrap();
let tracks = this
.handle
.backend
.db()
.get_tracks(&this.recording.id)
.unwrap();
this.show_tracks(tracks);
this.widget.ready();
});
this.show_tracks(tracks);
this.widget.ready();
this
}

View file

@ -62,9 +62,7 @@ impl Screen<(), ()> for WelcomeScreen {
if let gtk::ResponseType::Accept = response {
if let Some(file) = dialog.file() {
if let Some(path) = file.path() {
spawn!(@clone this, async move {
this.handle.backend.set_music_library_path(path).await.unwrap();
});
this.handle.backend.set_music_library_path(path).unwrap();
}
}
}

View file

@ -55,7 +55,7 @@ impl Screen<Work, ()> for WorkScreen {
&gettext("Delete work"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
this.handle.backend.db().delete_work(&this.work.id).await.unwrap();
this.handle.backend.db().delete_work(&this.work.id).unwrap();
this.handle.backend.library_changed();
});
}),
@ -95,27 +95,25 @@ impl Screen<Work, ()> for WorkScreen {
search.is_empty() || text.to_lowercase().contains(&search)
}));
// Load the content asynchronously.
// Load the content.
spawn!(@clone this, async move {
let recordings = this.handle
.backend
.db()
.get_recordings_for_work(&this.work.id)
.await
.unwrap();
let recordings = this
.handle
.backend
.db()
.get_recordings_for_work(&this.work.id)
.unwrap();
if !recordings.is_empty() {
let length = recordings.len();
this.recordings.replace(recordings);
this.recording_list.update(length);
if !recordings.is_empty() {
let length = recordings.len();
this.recordings.replace(recordings);
this.recording_list.update(length);
let section = Section::new("Recordings", &this.recording_list.widget);
this.widget.add_content(&section.widget);
}
let section = Section::new("Recordings", &this.recording_list.widget);
this.widget.add_content(&section.widget);
}
this.widget.ready();
});
this.widget.ready();
this
}

View file

@ -38,12 +38,6 @@ impl Screen<(), Ensemble> for EnsembleSelector {
});
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
let clone = this;
async move { clone.handle.backend.db().get_ensembles().await.unwrap() }
}));
this.selector
.set_make_widget(clone!(@weak this => @default-panic, move |ensemble| {
let row = adw::ActionRowBuilder::new()
@ -62,6 +56,9 @@ impl Screen<(), Ensemble> for EnsembleSelector {
this.selector
.set_filter(|search, ensemble| ensemble.name.to_lowercase().contains(search));
this.selector
.set_items(this.handle.backend.db().get_ensembles().unwrap());
this
}
}

View file

@ -38,12 +38,6 @@ impl Screen<(), Instrument> for InstrumentSelector {
});
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
let clone = this;
async move { clone.handle.backend.db().get_instruments().await.unwrap() }
}));
this.selector
.set_make_widget(clone!(@weak this => @default-panic, move |instrument| {
let row = adw::ActionRowBuilder::new()
@ -62,6 +56,9 @@ impl Screen<(), Instrument> for InstrumentSelector {
this.selector
.set_filter(|search, instrument| instrument.name.to_lowercase().contains(search));
this.selector
.set_items(this.handle.backend.db().get_instruments().unwrap());
this
}
}

View file

@ -28,26 +28,6 @@ impl Screen<(), Medium> for MediumSelector {
this.handle.pop(None);
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
async move {
let mut poes = Vec::new();
let persons = this.handle.backend.db().get_persons().await.unwrap();
let ensembles = this.handle.backend.db().get_ensembles().await.unwrap();
for person in persons {
poes.push(PersonOrEnsemble::Person(person));
}
for ensemble in ensembles {
poes.push(PersonOrEnsemble::Ensemble(ensemble));
}
poes
}
}));
this.selector.set_make_widget(clone!(@weak this => @default-panic, move |poe| {
let row = adw::ActionRowBuilder::new()
.activatable(true)
@ -70,6 +50,23 @@ impl Screen<(), Medium> for MediumSelector {
this.selector
.set_filter(|search, poe| poe.get_title().to_lowercase().contains(search));
// Initialize items.
let mut poes = Vec::new();
let persons = this.handle.backend.db().get_persons().unwrap();
let ensembles = this.handle.backend.db().get_ensembles().unwrap();
for person in persons {
poes.push(PersonOrEnsemble::Person(person));
}
for ensemble in ensembles {
poes.push(PersonOrEnsemble::Ensemble(ensemble));
}
this.selector.set_items(poes);
this
}
}
@ -103,25 +100,6 @@ impl Screen<PersonOrEnsemble, Medium> for MediumSelectorMediumScreen {
this.handle.pop(None);
}));
match this.poe.clone() {
PersonOrEnsemble::Person(person) => {
// this.selector.set_load_online(clone!(@weak this => move || {
// async move { this.handle.backend.cl().get_mediums_for_person(&person.id).await }
// }));
this.selector.set_load_local(clone!(@weak this => @default-panic, move || {
let person = person.clone();
async move { this.handle.backend.db().get_mediums_for_person(&person.id).await.unwrap() }
}));
}
PersonOrEnsemble::Ensemble(ensemble) => {
this.selector.set_load_local(clone!(@weak this => @default-panic, move || {
let ensemble = ensemble.clone();
async move { this.handle.backend.db().get_mediums_for_ensemble(&ensemble.id).await.unwrap() }
}));
}
}
this.selector
.set_make_widget(clone!(@weak this => @default-panic, move |medium| {
let row = adw::ActionRowBuilder::new()
@ -140,6 +118,28 @@ impl Screen<PersonOrEnsemble, Medium> for MediumSelectorMediumScreen {
this.selector
.set_filter(|search, medium| medium.name.to_lowercase().contains(search));
// Initialize items.
match this.poe.clone() {
PersonOrEnsemble::Person(person) => {
this.selector.set_items(
this.handle
.backend
.db()
.get_mediums_for_person(&person.id)
.unwrap(),
);
}
PersonOrEnsemble::Ensemble(ensemble) => {
this.selector.set_items(
this.handle
.backend
.db()
.get_mediums_for_ensemble(&ensemble.id)
.unwrap(),
);
}
}
this
}
}

View file

@ -38,12 +38,6 @@ impl Screen<(), Person> for PersonSelector {
});
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
let clone = this;
async move { clone.handle.backend.db().get_persons().await.unwrap() }
}));
this.selector
.set_make_widget(clone!(@weak this => @default-panic, move |person| {
let row = adw::ActionRowBuilder::new()
@ -62,6 +56,9 @@ impl Screen<(), Person> for PersonSelector {
this.selector
.set_filter(|search, person| person.name_fl().to_lowercase().contains(search));
this.selector
.set_items(this.handle.backend.db().get_persons().unwrap());
this
}
}

View file

@ -50,11 +50,6 @@ impl Screen<(), Recording> for RecordingSelector {
});
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
async move { this.handle.backend.db().get_persons().await.unwrap() }
}));
this.selector.set_make_widget(clone!(@weak this => @default-panic, move |person| {
let row = adw::ActionRowBuilder::new()
.activatable(true)
@ -84,6 +79,9 @@ impl Screen<(), Recording> for RecordingSelector {
this.selector
.set_filter(|search, person| person.name_fl().to_lowercase().contains(search));
this.selector
.set_items(this.handle.backend.db().get_persons().unwrap());
this
}
}
@ -126,11 +124,6 @@ impl Screen<Person, Work> for RecordingSelectorWorkScreen {
});
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
async move { this.handle.backend.db().get_works(&this.person.id).await.unwrap() }
}));
this.selector
.set_make_widget(clone!(@weak this => @default-panic, move |work| {
let row = adw::ActionRowBuilder::new()
@ -149,6 +142,9 @@ impl Screen<Person, Work> for RecordingSelectorWorkScreen {
this.selector
.set_filter(|search, work| work.title.to_lowercase().contains(search));
this.selector
.set_items(this.handle.backend.db().get_works(&this.person.id).unwrap());
this
}
}
@ -191,10 +187,6 @@ impl Screen<Work, Recording> for RecordingSelectorRecordingScreen {
});
}));
this.selector.set_load_local(clone!(@weak this => @default-panic, move || {
async move { this.handle.backend.db().get_recordings_for_work(&this.work.id).await.unwrap() }
}));
this.selector
.set_make_widget(clone!(@weak this => @default-panic, move |recording| {
let row = adw::ActionRowBuilder::new()
@ -214,6 +206,14 @@ impl Screen<Work, Recording> for RecordingSelectorRecordingScreen {
recording.get_performers().to_lowercase().contains(search)
});
this.selector.set_items(
this.handle
.backend
.db()
.get_recordings_for_work(&this.work.id)
.unwrap(),
);
this
}
}

View file

@ -3,8 +3,6 @@ use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
/// A screen that presents a list of items from the library.
@ -19,7 +17,6 @@ pub struct Selector<T: 'static> {
back_cb: RefCell<Option<Box<dyn Fn()>>>,
add_cb: RefCell<Option<Box<dyn Fn()>>>,
make_widget: RefCell<Option<Box<dyn Fn(&T) -> gtk::Widget>>>,
load_local: RefCell<Option<Box<dyn Fn() -> Box<dyn Future<Output = Vec<T>>>>>>,
filter: RefCell<Option<Box<dyn Fn(&str, &T) -> bool>>>,
}
@ -53,7 +50,6 @@ impl<T> Selector<T> {
back_cb: RefCell::new(None),
add_cb: RefCell::new(None),
make_widget: RefCell::new(None),
load_local: RefCell::new(None),
filter: RefCell::new(None),
});
@ -98,9 +94,6 @@ impl<T> Selector<T> {
}
}));
// Initialize
this.clone().load_local();
this
}
@ -125,16 +118,6 @@ impl<T> Selector<T> {
self.add_cb.replace(Some(Box::new(cb)));
}
/// Set the async closure to be called to get local items.
pub fn set_load_local<F, R>(&self, cb: F)
where
F: (Fn() -> R) + 'static,
R: Future<Output = Vec<T>> + 'static,
{
self.load_local
.replace(Some(Box::new(move || Box::new(cb()))));
}
/// Set the closure to be called for creating a new list row.
pub fn set_make_widget<F: Fn(&T) -> gtk::Widget + 'static>(&self, make_widget: F) {
self.make_widget.replace(Some(Box::new(make_widget)));
@ -146,20 +129,8 @@ impl<T> Selector<T> {
self.filter.replace(Some(Box::new(filter)));
}
fn load_local(self: Rc<Self>) {
let context = glib::MainContext::default();
let clone = self.clone();
context.spawn_local(async move {
if let Some(cb) = &*self.load_local.borrow() {
self.stack.set_visible_child_name("loading");
let items = Pin::from(cb()).await;
clone.show_items(items);
}
});
}
fn show_items(&self, items: Vec<T>) {
/// Set the list items the user may select from.
pub fn set_items(&self, items: Vec<T>) {
let length = items.len();
self.items.replace(items);
self.list.update(length);

View file

@ -44,11 +44,6 @@ impl Screen<(), Work> for WorkSelector {
});
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
async move { this.handle.backend.db().get_persons().await.unwrap() }
}));
this.selector.set_make_widget(clone!(@weak this => @default-panic, move |person| {
let row = adw::ActionRowBuilder::new()
.activatable(true)
@ -74,6 +69,9 @@ impl Screen<(), Work> for WorkSelector {
this.selector
.set_filter(|search, person| person.name_fl().to_lowercase().contains(search));
this.selector
.set_items(this.handle.backend.db().get_persons().unwrap());
this
}
}
@ -116,11 +114,6 @@ impl Screen<Person, Work> for WorkSelectorWorkScreen {
});
}));
this.selector
.set_load_local(clone!(@weak this => @default-panic, move || {
async move { this.handle.backend.db().get_works(&this.person.id).await.unwrap() }
}));
this.selector
.set_make_widget(clone!(@weak this => @default-panic, move |work| {
let row = adw::ActionRowBuilder::new()
@ -139,6 +132,9 @@ impl Screen<Person, Work> for WorkSelectorWorkScreen {
this.selector
.set_filter(|search, work| work.title.to_lowercase().contains(search));
this.selector
.set_items(this.handle.backend.db().get_works(&this.person.id).unwrap());
this
}
}

View file

@ -70,11 +70,6 @@ impl Editor {
self.save_button.connect_clicked(move |_| cb());
}
/// Show a loading page.
pub fn loading(&self) {
self.widget.set_visible_child_name("loading");
}
/// Show an error page. The page contains a button to get back to the
/// actual editor.
pub fn error(&self, title: &str, description: &str) {

View file

@ -1,5 +1,6 @@
use crate::navigator::Navigator;
use crate::screens::{MainScreen, WelcomeScreen};
use glib::clone;
use gtk::prelude::*;
use musicus_backend::{Backend, BackendState};
use std::rc::Rc;
@ -50,21 +51,17 @@ impl Window {
navigator,
});
spawn!(@clone this, async move {
while let Ok(state) = this.backend.next_state().await {
match state {
BackendState::Loading => this.navigator.reset(),
BackendState::NoMusicLibrary => this.show_welcome_screen(),
BackendState::Ready => this.show_main_screen(),
}
// Listen for backend state changes.
this.backend.set_state_cb(clone!(@weak this => move |state| {
match state {
BackendState::Loading => this.navigator.reset(),
BackendState::NoMusicLibrary => this.show_welcome_screen(),
BackendState::Ready => this.show_main_screen(),
}
});
}));
spawn!(@clone this, async move {
// This is not done in the async block above, because backend state changes may happen
// while this method is running.
this.backend.init().await.unwrap();
});
// Initialize the backend.
this.backend.init().unwrap();
this
}