Set up message passing between app window and an app thread

This commit is contained in:
Savanni D'Gerinel 2023-12-19 18:02:35 -05:00
parent 87994012fa
commit 3ca8bf64cc
3 changed files with 612 additions and 446 deletions

989
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
async-channel = "2.1.1"
emseries = { path = "../../emseries" }
ft-core = { path = "../core" }
gio = { version = "0.18" }

View File

@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with Fit
mod ui;
use adw::prelude::*;
use async_channel::{Receiver, Sender};
use emseries::{EmseriesReadError, Series};
use ft_core::TraxRecord;
use gio::resources_lookup_data;
@ -36,11 +37,16 @@ const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax";
const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/fitnesstrax/";
/// A set of events that can occur at the global application level. These events should represent
/// significant state changes that should go through a central dispatcher.
enum Events {
DatabaseChanged(Series<TraxRecord>),
#[derive(Debug)]
enum AppInvocation {
OpenDatabase(PathBuf),
}
#[derive(Debug)]
enum AppResponse {
DatabaseChanged,
}
// Note that I have not yet figured out the communication channel or how the central dispatcher
// should work. There's a dance between the App and the AppWindow that I haven't figured out yet.
@ -222,7 +228,7 @@ impl HistoricalView {
/// everything occurs here.
#[derive(Clone)]
struct AppWindow {
app: App,
app_tx: Sender<AppInvocation>,
window: adw::ApplicationWindow,
layout: gtk::Box,
current_view: Rc<RefCell<gtk::Widget>>,
@ -235,7 +241,7 @@ impl AppWindow {
/// otherwise we don't use this.
///
/// app is a core [App] object which encapsulates all of the basic logic.
fn new(adw_app: &adw::Application, app: App) -> AppWindow {
fn new(adw_app: &adw::Application, app_tx: Sender<AppInvocation>) -> AppWindow {
let window = adw::ApplicationWindow::builder()
.application(adw_app)
.width_request(800)
@ -275,15 +281,14 @@ impl AppWindow {
window.present();
let s = Self {
app: app.clone(),
app_tx,
window,
layout,
current_view: Rc::new(RefCell::new(initial_view.upcast())),
};
let initial_view = if app.database.read().unwrap().is_none() {
let initial_view = if true {
WelcomeView::new({
let app = app.clone();
let s = s.clone();
Box::new(move |path: PathBuf| {
// The user has selected a path. Perhaps the path is new, perhaps it already
@ -295,8 +300,20 @@ impl AppWindow {
//
// If the file does not exist, create a new one. Again, show the user an error if
// some kind of error occurs.
app.open_db(&path);
s.change_view(HistoricalView::new().upcast());
// app.open_db(&path);
// s.change_view(HistoricalView::new().upcast());
// let s = s.clone();
/*
glib::spawn_future_local(async move {
s.app_tx
.send(AppInvocation::OpenDatabase(path))
.await
.unwrap();
});
*/
s.app_tx
.send_blocking(AppInvocation::OpenDatabase(path))
.unwrap();
})
})
.upcast()
@ -342,20 +359,33 @@ fn main() {
let app = App::new(settings);
/*
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
*/
let adw_app = adw::Application::builder()
.application_id(app_id)
.resource_base_path(RESOURCE_BASE_PATH)
.build();
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
adw_app.connect_activate(move |adw_app| {
AppWindow::new(adw_app, app.clone());
let (gtk_tx, gtk_rx) = async_channel::unbounded::<AppResponse>();
let (app_tx, app_rx) = async_channel::unbounded::<AppInvocation>();
AppWindow::new(adw_app, app_tx.clone());
glib::spawn_future_local(async move {
while let Ok(response) = gtk_rx.recv().await {
println!("response received: {:?}", response);
}
});
runtime.spawn(async move {
while let Ok(invocation) = app_rx.recv().await {
println!("Received an invocation: {:?}", invocation);
}
});
});
let args: Vec<String> = env::args().collect();