diff --git a/Cargo.lock b/Cargo.lock index 098628d..7cbfe5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1021,7 +1021,6 @@ dependencies = [ "log", "rand", "thiserror", - "tokio", "uuid", ] diff --git a/backend/src/lib.rs b/backend/src/lib.rs index e7158c1..aab03ff 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,4 +1,4 @@ -use musicus_database::DbThread; +use musicus_database::Database; use std::cell::RefCell; use std::path::PathBuf; use std::rc::Rc; @@ -19,7 +19,7 @@ pub mod player; pub use player::*; /// General states the application can be in. -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] pub enum BackendState { /// The backend is not set up yet. This means that no backend methods except for setting the /// music library path should be called. The user interface should adapt and only present this @@ -36,8 +36,8 @@ pub enum BackendState { /// A collection of all backend state and functionality. pub struct Backend { - /// The internal sender to publish the state via state_stream. - state_sender: Sender, + /// A closure that will be called whenever the backend state changes. + state_cb: RefCell>>, /// Access to GSettings. settings: gio::Settings, @@ -50,7 +50,7 @@ pub struct Backend { library_updated_sender: Sender<()>, /// The database. This can be assumed to exist, when the state is set to BackendState::Ready. - database: RefCell>>, + database: RefCell>>, /// The player handling playlist and playback. This can be assumed to exist, when the state is /// set to BackendState::Ready. @@ -64,40 +64,46 @@ impl Backend { pub fn new() -> Self { logger::register(); - let (state_sender, _) = broadcast::channel(1024); let (library_updated_sender, _) = broadcast::channel(1024); Backend { - state_sender, + state_cb: RefCell::new(None), settings: gio::Settings::new("de.johrpan.musicus"), music_library_path: RefCell::new(None), library_updated_sender, database: RefCell::new(None), - player: RefCell::new(None) + player: RefCell::new(None), } } - /// Wait for the next state change. Initially, the state should be assumed to be - /// BackendState::Loading. Changes should be awaited before calling init(). - pub async fn next_state(&self) -> Result { - Ok(self.state_sender.subscribe().recv().await?) + /// Set the closure to be called whenever the backend state changes. + pub fn set_state_cb(&self, cb: F) { + self.state_cb.replace(Some(Box::new(cb))); } - /// Initialize the backend updating the state accordingly. - pub async fn init(&self) -> Result<()> { - self.init_library().await?; + /// Initialize the backend. A state callback should already have been registered using + /// [`set_state_cb()`] to react to the result. + pub fn init(&self) -> Result<()> { + self.init_library()?; - if self.get_music_library_path().is_none() { - self.set_state(BackendState::NoMusicLibrary); - } else { - self.set_state(BackendState::Ready); - } + match self.get_music_library_path() { + None => self.set_state(BackendState::NoMusicLibrary), + Some(_) => self.set_state(BackendState::Ready), + }; Ok(()) } /// Set the current state and notify the user interface. fn set_state(&self, state: BackendState) { - self.state_sender.send(state).unwrap(); + if let Some(cb) = &*self.state_cb.borrow() { + cb(state); + } + } +} + +impl Default for Backend { + fn default() -> Self { + Self::new() } } diff --git a/backend/src/library.rs b/backend/src/library.rs index 6f1775d..56d404a 100644 --- a/backend/src/library.rs +++ b/backend/src/library.rs @@ -1,24 +1,23 @@ use crate::{Backend, BackendState, Player, Result}; use gio::prelude::*; use log::warn; -use musicus_database::DbThread; +use musicus_database::Database; use std::path::PathBuf; use std::rc::Rc; impl Backend { /// Initialize the music library if it is set in the settings. - pub(super) async fn init_library(&self) -> Result<()> { + pub(super) fn init_library(&self) -> Result<()> { let path = self.settings.string("music-library-path"); if !path.is_empty() { - self.set_music_library_path_priv(PathBuf::from(path.to_string())) - .await?; + self.set_music_library_path_priv(PathBuf::from(path.to_string()))?; } Ok(()) } - /// Set the path to the music library folder and start a database thread in the background. - pub async fn set_music_library_path(&self, path: PathBuf) -> Result<()> { + /// Set the path to the music library folder and connect to the database. + pub fn set_music_library_path(&self, path: PathBuf) -> Result<()> { if let Err(err) = self .settings .set_string("music-library-path", path.to_str().unwrap()) @@ -30,23 +29,19 @@ impl Backend { ); } - self.set_music_library_path_priv(path).await + self.set_music_library_path_priv(path) } - /// Set the path to the music library folder and start a database thread in the background. - pub async fn set_music_library_path_priv(&self, path: PathBuf) -> Result<()> { + /// Set the path to the music library folder and and connect to the database. + pub fn set_music_library_path_priv(&self, path: PathBuf) -> Result<()> { self.set_state(BackendState::Loading); - if let Some(db) = &*self.database.borrow() { - db.stop().await?; - } - self.music_library_path.replace(Some(path.clone())); let mut db_path = path.clone(); db_path.push("musicus.db"); - let database = DbThread::new(db_path.to_str().unwrap().to_string()).await?; + let database = Database::new(db_path.to_str().unwrap())?; self.database.replace(Some(Rc::new(database))); let player = Player::new(path); @@ -62,14 +57,9 @@ impl Backend { self.music_library_path.borrow().clone() } - /// Get an interface to the current music library database. - pub fn get_database(&self) -> Option> { - self.database.borrow().clone() - } - /// Get an interface to the database and panic if there is none. - pub fn db(&self) -> Rc { - self.get_database().unwrap() + pub fn db(&self) -> Rc { + self.database.borrow().clone().unwrap() } /// Get an interface to the playback service. diff --git a/database/Cargo.toml b/database/Cargo.toml index d467cdd..4c79ed6 100644 --- a/database/Cargo.toml +++ b/database/Cargo.toml @@ -9,5 +9,4 @@ diesel_migrations = "1.4.0" log = "0.4.14" rand = "0.7.3" thiserror = "1.0.23" -tokio = { version = "1.4.0", features = ["sync"] } uuid = { version = "0.8", features = ["v4"] } diff --git a/database/src/error.rs b/database/src/error.rs index 48b5dab..4a78023 100644 --- a/database/src/error.rs +++ b/database/src/error.rs @@ -16,12 +16,6 @@ pub enum Error { #[error("Failed to parse {0} from '{1}'")] ParsingError(&'static str, String), - #[error(transparent)] - SendError(#[from] std::sync::mpsc::SendError), - - #[error(transparent)] - ReceiveError(#[from] tokio::sync::oneshot::error::RecvError), - #[error("{0}")] Other(&'static str), } diff --git a/database/src/lib.rs b/database/src/lib.rs index 90f575e..5e8d5a4 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -27,9 +27,6 @@ pub use persons::*; pub mod recordings; pub use recordings::*; -pub mod thread; -pub use thread::*; - pub mod works; pub use works::*; diff --git a/database/src/thread.rs b/database/src/thread.rs deleted file mode 100644 index 4d6e0e4..0000000 --- a/database/src/thread.rs +++ /dev/null @@ -1,397 +0,0 @@ -use super::*; -use log::debug; -use std::sync::mpsc; -use std::thread; -use tokio::sync::oneshot::{self, Sender}; - -/// An action the database thread can perform. -#[derive(Debug)] -pub enum Action { - UpdatePerson(Person, Sender>), - GetPerson(String, Sender>>), - DeletePerson(String, Sender>), - GetPersons(Sender>>), - UpdateInstrument(Instrument, Sender>), - GetInstrument(String, Sender>>), - DeleteInstrument(String, Sender>), - GetInstruments(Sender>>), - UpdateWork(Work, Sender>), - DeleteWork(String, Sender>), - GetWorks(String, Sender>>), - UpdateEnsemble(Ensemble, Sender>), - GetEnsemble(String, Sender>>), - DeleteEnsemble(String, Sender>), - GetEnsembles(Sender>>), - UpdateRecording(Recording, Sender>), - DeleteRecording(String, Sender>), - GetRecordingsForPerson(String, Sender>>), - GetRecordingsForEnsemble(String, Sender>>), - GetRecordingsForWork(String, Sender>>), - RecordingExists(String, Sender>), - UpdateMedium(Medium, Sender>), - GetMedium(String, Sender>>), - GetMediumsBySourceId(String, Sender>>), - GetMediumsForPerson(String, Sender>>), - GetMediumsForEnsemble(String, Sender>>), - DeleteMedium(String, Sender>), - GetTracks(String, Sender>>), - Stop(Sender<()>), -} - -use Action::*; - -/// A database running within a thread. -pub struct DbThread { - action_sender: mpsc::Sender, -} - -impl DbThread { - /// Create a new database connection in a background thread. - pub async fn new(path: String) -> Result { - let (action_sender, action_receiver) = mpsc::channel(); - let (ready_sender, ready_receiver) = oneshot::channel(); - - thread::spawn(move || { - debug!("Database thread for '{}' started", path); - - let db = match Database::new(&path) { - Ok(db) => { - ready_sender.send(Ok(())).unwrap(); - db - } - Err(error) => { - ready_sender.send(Err(error)).unwrap(); - return; - } - }; - - for action in action_receiver { - debug!("Database thread for '{}' got action {:?}", path, action); - match action { - UpdatePerson(person, sender) => { - sender.send(db.update_person(person)).unwrap(); - } - GetPerson(id, sender) => { - sender.send(db.get_person(&id)).unwrap(); - } - DeletePerson(id, sender) => { - sender.send(db.delete_person(&id)).unwrap(); - } - GetPersons(sender) => { - sender.send(db.get_persons()).unwrap(); - } - UpdateInstrument(instrument, sender) => { - sender.send(db.update_instrument(instrument)).unwrap(); - } - GetInstrument(id, sender) => { - sender.send(db.get_instrument(&id)).unwrap(); - } - DeleteInstrument(id, sender) => { - sender.send(db.delete_instrument(&id)).unwrap(); - } - GetInstruments(sender) => { - sender.send(db.get_instruments()).unwrap(); - } - UpdateWork(work, sender) => { - sender.send(db.update_work(work)).unwrap(); - } - DeleteWork(id, sender) => { - sender.send(db.delete_work(&id)).unwrap(); - } - GetWorks(id, sender) => { - sender.send(db.get_works(&id)).unwrap(); - } - UpdateEnsemble(ensemble, sender) => { - sender.send(db.update_ensemble(ensemble)).unwrap(); - } - GetEnsemble(id, sender) => { - sender.send(db.get_ensemble(&id)).unwrap(); - } - DeleteEnsemble(id, sender) => { - sender.send(db.delete_ensemble(&id)).unwrap(); - } - GetEnsembles(sender) => { - sender.send(db.get_ensembles()).unwrap(); - } - UpdateRecording(recording, sender) => { - sender.send(db.update_recording(recording)).unwrap(); - } - DeleteRecording(id, sender) => { - sender.send(db.delete_recording(&id)).unwrap(); - } - GetRecordingsForPerson(id, sender) => { - sender.send(db.get_recordings_for_person(&id)).unwrap(); - } - GetRecordingsForEnsemble(id, sender) => { - sender.send(db.get_recordings_for_ensemble(&id)).unwrap(); - } - GetRecordingsForWork(id, sender) => { - sender.send(db.get_recordings_for_work(&id)).unwrap(); - } - RecordingExists(id, sender) => { - sender.send(db.recording_exists(&id)).unwrap(); - } - UpdateMedium(medium, sender) => { - sender.send(db.update_medium(medium)).unwrap(); - } - GetMedium(id, sender) => { - sender.send(db.get_medium(&id)).unwrap(); - } - GetMediumsBySourceId(id, sender) => { - sender.send(db.get_mediums_by_source_id(&id)).unwrap(); - } - GetMediumsForPerson(id, sender) => { - sender.send(db.get_mediums_for_person(&id)).unwrap(); - } - GetMediumsForEnsemble(id, sender) => { - sender.send(db.get_mediums_for_ensemble(&id)).unwrap(); - } - DeleteMedium(id, sender) => { - sender.send(db.delete_medium(&id)).unwrap(); - } - GetTracks(recording_id, sender) => { - sender.send(db.get_tracks(&recording_id)).unwrap(); - } - Stop(sender) => { - sender.send(()).unwrap(); - break; - } - } - } - - debug!("Database thread for '{}' stopped", path); - }); - - ready_receiver.await??; - Ok(Self { action_sender }) - } - - /// Update an existing person or insert a new one. - pub async fn update_person(&self, person: Person) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(UpdatePerson(person, sender))?; - receiver.await? - } - - /// Get an existing person. - pub async fn get_person(&self, id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(GetPerson(id.to_string(), sender))?; - receiver.await? - } - - /// Delete an existing person. This will fail, if there are still other items referencing - /// this person. - pub async fn delete_person(&self, id: &str) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(DeletePerson(id.to_string(), sender))?; - receiver.await? - } - - /// Get all existing persons. - pub async fn get_persons(&self) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(GetPersons(sender))?; - receiver.await? - } - - /// Update an existing instrument or insert a new one. - pub async fn update_instrument(&self, instrument: Instrument) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(UpdateInstrument(instrument, sender))?; - receiver.await? - } - - /// Get an existing instrument. - pub async fn get_instrument(&self, id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetInstrument(id.to_string(), sender))?; - receiver.await? - } - - /// Delete an existing instrument. This will fail, if there are still other items referencing - /// this instrument. - pub async fn delete_instrument(&self, id: &str) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(DeleteInstrument(id.to_string(), sender))?; - receiver.await? - } - - /// Get all existing instruments. - pub async fn get_instruments(&self) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(GetInstruments(sender))?; - receiver.await? - } - - /// Update an existing work or insert a new one. - pub async fn update_work(&self, work: Work) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(UpdateWork(work, sender))?; - receiver.await? - } - - /// Delete an existing work. This will fail, if there are still other items referencing - /// this work. - pub async fn delete_work(&self, id: &str) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(DeleteWork(id.to_string(), sender))?; - receiver.await? - } - - /// Get information on all existing works by a composer. - pub async fn get_works(&self, person_id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetWorks(person_id.to_string(), sender))?; - receiver.await? - } - - /// Update an existing ensemble or insert a new one. - pub async fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(UpdateEnsemble(ensemble, sender))?; - receiver.await? - } - - /// Get an existing ensemble. - pub async fn get_ensemble(&self, id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetEnsemble(id.to_string(), sender))?; - receiver.await? - } - - /// Delete an existing ensemble. This will fail, if there are still other items referencing - /// this ensemble. - pub async fn delete_ensemble(&self, id: &str) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(DeleteEnsemble(id.to_string(), sender))?; - receiver.await? - } - - /// Get all existing ensembles. - pub async fn get_ensembles(&self) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(GetEnsembles(sender))?; - receiver.await? - } - - /// Update an existing recording or insert a new one. - pub async fn update_recording(&self, recording: Recording) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(UpdateRecording(recording, sender))?; - receiver.await? - } - - /// Delete an existing recording. - pub async fn delete_recording(&self, id: &str) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(DeleteRecording(id.to_string(), sender))?; - receiver.await? - } - - /// Get information on all recordings in which a person performs. - pub async fn get_recordings_for_person(&self, person_id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetRecordingsForPerson(person_id.to_string(), sender))?; - receiver.await? - } - - /// Get information on all recordings in which an ensemble performs. - pub async fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetRecordingsForEnsemble(ensemble_id.to_string(), sender))?; - receiver.await? - } - - /// Get information on all recordings of a work. - pub async fn get_recordings_for_work(&self, work_id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetRecordingsForWork(work_id.to_string(), sender))?; - receiver.await? - } - - /// Check whether a recording exists within the database. - pub async fn recording_exists(&self, id: &str) -> Result { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(RecordingExists(id.to_string(), sender))?; - receiver.await? - } - - /// Update an existing medium or insert a new one. - pub async fn update_medium(&self, medium: Medium) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(UpdateMedium(medium, sender))?; - receiver.await? - } - - /// Delete an existing medium. This will fail, if there are still other - /// items referencing this medium. - pub async fn delete_medium(&self, id: &str) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - - self.action_sender - .send(DeleteMedium(id.to_owned(), sender))?; - - receiver.await? - } - - /// Get an existing medium. - pub async fn get_medium(&self, id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(GetMedium(id.to_owned(), sender))?; - receiver.await? - } - - /// Get all mediums with the specified source ID. - pub async fn get_mediums_by_source_id(&self, id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetMediumsBySourceId(id.to_owned(), sender))?; - receiver.await? - } - - /// Get all mediums on which a person performs. - pub async fn get_mediums_for_person(&self, id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetMediumsForPerson(id.to_owned(), sender))?; - receiver.await? - } - - /// Get all mediums on which an ensemble performs. - pub async fn get_mediums_for_ensemble(&self, id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetMediumsForEnsemble(id.to_owned(), sender))?; - receiver.await? - } - - /// Get all tracks for a recording. - pub async fn get_tracks(&self, recording_id: &str) -> Result> { - let (sender, receiver) = oneshot::channel(); - self.action_sender - .send(GetTracks(recording_id.to_owned(), sender))?; - receiver.await? - } - - /// Stop the database thread. Any future access to the database will fail. - pub async fn stop(&self) -> Result<()> { - let (sender, receiver) = oneshot::channel(); - self.action_sender.send(Stop(sender))?; - Ok(receiver.await?) - } -} diff --git a/musicus/res/ui/editor.ui b/musicus/res/ui/editor.ui index b4ff4fa..768fbec 100644 --- a/musicus/res/ui/editor.ui +++ b/musicus/res/ui/editor.ui @@ -53,38 +53,6 @@ - - - loading - - - vertical - - - false - false - - - Loading - - - - - - - true - true - center - center - 32 - 32 - true - - - - - - error diff --git a/musicus/src/editors/ensemble.rs b/musicus/src/editors/ensemble.rs index 2b88b14..dbc056c 100644 --- a/musicus/src/editors/ensemble.rs +++ b/musicus/src/editors/ensemble.rs @@ -56,18 +56,15 @@ impl Screen, 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 { + fn save(&self) -> Result { 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) diff --git a/musicus/src/editors/instrument.rs b/musicus/src/editors/instrument.rs index 9c78fb8..cc64cf9 100644 --- a/musicus/src/editors/instrument.rs +++ b/musicus/src/editors/instrument.rs @@ -56,18 +56,15 @@ impl Screen, 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 { + fn save(&self) -> Result { 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) diff --git a/musicus/src/editors/person.rs b/musicus/src/editors/person.rs index ea973d7..af5e32d 100644 --- a/musicus/src/editors/person.rs +++ b/musicus/src/editors/person.rs @@ -63,18 +63,15 @@ impl Screen, 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) -> Result { + fn save(self: &Rc) -> Result { 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) diff --git a/musicus/src/editors/recording.rs b/musicus/src/editors/recording.rs index 21bd47c..8667e43 100644 --- a/musicus/src/editors/recording.rs +++ b/musicus/src/editors/recording.rs @@ -74,18 +74,15 @@ impl Screen, 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) -> Result { + fn save(self: &Rc) -> Result { 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) diff --git a/musicus/src/editors/work.rs b/musicus/src/editors/work.rs index 0c87778..7ba00d5 100644 --- a/musicus/src/editors/work.rs +++ b/musicus/src/editors/work.rs @@ -113,18 +113,15 @@ impl Screen, 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) -> Result { + fn save(self: &Rc) -> Result { 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) diff --git a/musicus/src/import/import_screen.rs b/musicus/src/import/import_screen.rs index 03c78f2..cd1b3af 100644 --- a/musicus/src/import/import_screen.rs +++ b/musicus/src/import/import_screen.rs @@ -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) => { diff --git a/musicus/src/import/medium_preview.rs b/musicus/src/import/medium_preview.rs index 682f634..ff6c893 100644 --- a/musicus/src/import/medium_preview.rs +++ b/musicus/src/import/medium_preview.rs @@ -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(()) diff --git a/musicus/src/preferences.rs b/musicus/src/preferences.rs index 6e62b89..9d1b21d 100644 --- a/musicus/src/preferences.rs +++ b/musicus/src/preferences.rs @@ -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(); - }); } } } diff --git a/musicus/src/screens/ensemble.rs b/musicus/src/screens/ensemble.rs index 8750de2..871bab0 100644 --- a/musicus/src/screens/ensemble.rs +++ b/musicus/src/screens/ensemble.rs @@ -59,7 +59,7 @@ impl Screen 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 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(§ion.widget); - } + let section = Section::new("Recordings", &this.recording_list.widget); + this.widget.add_content(§ion.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(§ion.widget); - } + let section = Section::new("Mediums", &this.medium_list.widget); + this.widget.add_content(§ion.widget); + } - this.widget.ready(); - }); + this.widget.ready(); this } diff --git a/musicus/src/screens/main.rs b/musicus/src/screens/main.rs index 8c7bb16..708b05a 100644 --- a/musicus/src/screens/main.rs +++ b/musicus/src/screens/main.rs @@ -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)); diff --git a/musicus/src/screens/person.rs b/musicus/src/screens/person.rs index 57c96f3..2bddc47 100644 --- a/musicus/src/screens/person.rs +++ b/musicus/src/screens/person.rs @@ -64,7 +64,7 @@ impl Screen 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 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(§ion.widget); - } + let section = Section::new("Works", &this.work_list.widget); + this.widget.add_content(§ion.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(§ion.widget); - } + let section = Section::new("Recordings", &this.recording_list.widget); + this.widget.add_content(§ion.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(§ion.widget); - } + let section = Section::new("Mediums", &this.medium_list.widget); + this.widget.add_content(§ion.widget); + } - this.widget.ready(); - }); + this.widget.ready(); this } diff --git a/musicus/src/screens/recording.rs b/musicus/src/screens/recording.rs index 9fbdc91..3df831e 100644 --- a/musicus/src/screens/recording.rs +++ b/musicus/src/screens/recording.rs @@ -65,7 +65,7 @@ impl Screen 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 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 } diff --git a/musicus/src/screens/welcome.rs b/musicus/src/screens/welcome.rs index b4a64fd..93e74ae 100644 --- a/musicus/src/screens/welcome.rs +++ b/musicus/src/screens/welcome.rs @@ -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(); } } } diff --git a/musicus/src/screens/work.rs b/musicus/src/screens/work.rs index 5e5f8a5..7768dde 100644 --- a/musicus/src/screens/work.rs +++ b/musicus/src/screens/work.rs @@ -55,7 +55,7 @@ impl Screen 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 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(§ion.widget); - } + let section = Section::new("Recordings", &this.recording_list.widget); + this.widget.add_content(§ion.widget); + } - this.widget.ready(); - }); + this.widget.ready(); this } diff --git a/musicus/src/selectors/ensemble.rs b/musicus/src/selectors/ensemble.rs index 5692c9a..f6695c0 100644 --- a/musicus/src/selectors/ensemble.rs +++ b/musicus/src/selectors/ensemble.rs @@ -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 } } diff --git a/musicus/src/selectors/instrument.rs b/musicus/src/selectors/instrument.rs index 54a5c83..2112071 100644 --- a/musicus/src/selectors/instrument.rs +++ b/musicus/src/selectors/instrument.rs @@ -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 } } diff --git a/musicus/src/selectors/medium.rs b/musicus/src/selectors/medium.rs index 29398e3..98a73e0 100644 --- a/musicus/src/selectors/medium.rs +++ b/musicus/src/selectors/medium.rs @@ -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 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 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 } } diff --git a/musicus/src/selectors/person.rs b/musicus/src/selectors/person.rs index 96810c2..e04baad 100644 --- a/musicus/src/selectors/person.rs +++ b/musicus/src/selectors/person.rs @@ -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 } } diff --git a/musicus/src/selectors/recording.rs b/musicus/src/selectors/recording.rs index 2e78688..c0d762e 100644 --- a/musicus/src/selectors/recording.rs +++ b/musicus/src/selectors/recording.rs @@ -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 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 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 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 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 } } diff --git a/musicus/src/selectors/selector.rs b/musicus/src/selectors/selector.rs index 53b6399..678cb49 100644 --- a/musicus/src/selectors/selector.rs +++ b/musicus/src/selectors/selector.rs @@ -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 { back_cb: RefCell>>, add_cb: RefCell>>, make_widget: RefCell gtk::Widget>>>, - load_local: RefCell Box>>>>>, filter: RefCell bool>>>, } @@ -53,7 +50,6 @@ impl Selector { 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 Selector { } })); - // Initialize - this.clone().load_local(); - this } @@ -125,16 +118,6 @@ impl Selector { self.add_cb.replace(Some(Box::new(cb))); } - /// Set the async closure to be called to get local items. - pub fn set_load_local(&self, cb: F) - where - F: (Fn() -> R) + 'static, - R: Future> + '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 gtk::Widget + 'static>(&self, make_widget: F) { self.make_widget.replace(Some(Box::new(make_widget))); @@ -146,20 +129,8 @@ impl Selector { self.filter.replace(Some(Box::new(filter))); } - fn load_local(self: Rc) { - 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) { + /// Set the list items the user may select from. + pub fn set_items(&self, items: Vec) { let length = items.len(); self.items.replace(items); self.list.update(length); diff --git a/musicus/src/selectors/work.rs b/musicus/src/selectors/work.rs index 67cf2d0..50f8b98 100644 --- a/musicus/src/selectors/work.rs +++ b/musicus/src/selectors/work.rs @@ -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 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 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 } } diff --git a/musicus/src/widgets/editor.rs b/musicus/src/widgets/editor.rs index 914013e..1b4693c 100644 --- a/musicus/src/widgets/editor.rs +++ b/musicus/src/widgets/editor.rs @@ -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) { diff --git a/musicus/src/window.rs b/musicus/src/window.rs index af041be..7d31f11 100644 --- a/musicus/src/window.rs +++ b/musicus/src/window.rs @@ -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 }