From 6678ab9852be7e4863fc9c21476193294fac95d2 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Fri, 22 Dec 2023 14:28:23 -0500 Subject: [PATCH] Documentation --- fitnesstrax/app/src/main.rs | 44 ++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/fitnesstrax/app/src/main.rs b/fitnesstrax/app/src/main.rs index ed28429..7f91f5f 100644 --- a/fitnesstrax/app/src/main.rs +++ b/fitnesstrax/app/src/main.rs @@ -37,16 +37,36 @@ const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax"; const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/fitnesstrax/"; +/// Invocations are how parts of the application, primarily the UI, will send requests to the core. #[derive(Debug)] enum AppInvocation { + /// Tell the core to try to open a database. OpenDatabase(PathBuf), + + /// Request a set of records from the core. + // Note: this will require a time range, but doesn't yet. RequestRecords, } +/// Responses are messages that the core sends to the UI. Though they are called responses, the +/// could actually be pre-emptively sent, such as notifications. The UI will need to be able to +/// process those any time they arrive. +/// +/// A typical use would be for the UI to send an [AppInvocation::RequestRecords] request and +/// receive [AppResponse::Records]. #[derive(Debug)] enum AppResponse { + /// No database is available. The UI should typically display a placeholder, such as the + /// welcome view. NoDatabase, + + /// The database is open and here is a set of records. Typically, the set of records will be + /// all of the records within a time frame, but this can actually be any set of records. Records, + + /// The database has been changed. This message is useful for telling the UI that a significant + /// change has happened. Further, the UI needs to save PathBuf to settings, because the + /// gio::Settings system can't be run in the fully async background. DatabaseChanged(PathBuf), } @@ -411,28 +431,46 @@ fn main() { .unwrap(); adw_app.connect_activate(move |adw_app| { - let (gtk_tx, gtk_rx) = async_channel::unbounded::(); + // These channels are used to send messages to the UI. Anything that needs to send a + // message to the UI will send it via `ui_tx`. We will have one single process that owns + // `ui_rx`. That process will read messages coming in and send them to AppWindow for proper + // processing. + // + // The core app will usually only send messages in response to a request, but this channel + // can also be used to tell the UI that something happened in the background, such as + // detecting a watch, detecting new tracks to import, and so forth. + let (ui_tx, ui_rx) = async_channel::unbounded::(); + + // These channels are used for communicating with the app. Already I can see that a lot of + // different event handlers will need copies of app_tx in order to send requests into the + // UI. let (app_tx, app_rx) = async_channel::unbounded::(); let window = AppWindow::new(app_id, adw_app, app_tx.clone()); + // Spawn a future where the UI will receive messages for the app window. Previously, this + // would have been done by creating a glib::MainContext::channel(), but that has been + // deprecated since gtk 4.10 in favor of using `async_channel`. glib::spawn_future_local(async move { // The app requests data to start with. This kicks everything off. The response from // the app will cause the window to be updated shortly. let _ = app_tx.send(AppInvocation::RequestRecords).await; - while let Ok(response) = gtk_rx.recv().await { + while let Ok(response) = ui_rx.recv().await { println!("response received: {:?}", response); window.process_response(response); } }); + // The tokio runtime starts up here and will handle all of the asynchronous operations that + // the application needs to do. Messages arrive on `app_rx` and responses will be sent via + // `ui_tx`. runtime.spawn({ let app = app.clone(); async move { while let Ok(invocation) = app_rx.recv().await { let response = app.process_invocation(invocation).await; - let _ = gtk_tx.send(response).await; + let _ = ui_tx.send(response).await; } } });