Create the asynchronous communication channel between the UI and the core app loop. #126
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
||||||
|
async-channel = "2.1.1"
|
||||||
emseries = { path = "../../emseries" }
|
emseries = { path = "../../emseries" }
|
||||||
ft-core = { path = "../core" }
|
ft-core = { path = "../core" }
|
||||||
gio = { version = "0.18" }
|
gio = { version = "0.18" }
|
||||||
|
|
|
@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with Fit
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
use async_channel::{Receiver, Sender};
|
||||||
use emseries::{EmseriesReadError, Series};
|
use emseries::{EmseriesReadError, Series};
|
||||||
use ft_core::TraxRecord;
|
use ft_core::TraxRecord;
|
||||||
use gio::resources_lookup_data;
|
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/";
|
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
|
#[derive(Debug)]
|
||||||
/// significant state changes that should go through a central dispatcher.
|
enum AppInvocation {
|
||||||
enum Events {
|
OpenDatabase(PathBuf),
|
||||||
DatabaseChanged(Series<TraxRecord>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum AppResponse {
|
||||||
|
DatabaseChanged,
|
||||||
|
}
|
||||||
|
|
||||||
// Note that I have not yet figured out the communication channel or how the central dispatcher
|
// 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.
|
// 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.
|
/// everything occurs here.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppWindow {
|
struct AppWindow {
|
||||||
app: App,
|
app_tx: Sender<AppInvocation>,
|
||||||
window: adw::ApplicationWindow,
|
window: adw::ApplicationWindow,
|
||||||
layout: gtk::Box,
|
layout: gtk::Box,
|
||||||
current_view: Rc<RefCell<gtk::Widget>>,
|
current_view: Rc<RefCell<gtk::Widget>>,
|
||||||
|
@ -235,7 +241,7 @@ impl AppWindow {
|
||||||
/// otherwise we don't use this.
|
/// otherwise we don't use this.
|
||||||
///
|
///
|
||||||
/// app is a core [App] object which encapsulates all of the basic logic.
|
/// 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()
|
let window = adw::ApplicationWindow::builder()
|
||||||
.application(adw_app)
|
.application(adw_app)
|
||||||
.width_request(800)
|
.width_request(800)
|
||||||
|
@ -275,15 +281,14 @@ impl AppWindow {
|
||||||
window.present();
|
window.present();
|
||||||
|
|
||||||
let s = Self {
|
let s = Self {
|
||||||
app: app.clone(),
|
app_tx,
|
||||||
window,
|
window,
|
||||||
layout,
|
layout,
|
||||||
current_view: Rc::new(RefCell::new(initial_view.upcast())),
|
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({
|
WelcomeView::new({
|
||||||
let app = app.clone();
|
|
||||||
let s = s.clone();
|
let s = s.clone();
|
||||||
Box::new(move |path: PathBuf| {
|
Box::new(move |path: PathBuf| {
|
||||||
// The user has selected a path. Perhaps the path is new, perhaps it already
|
// 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
|
// If the file does not exist, create a new one. Again, show the user an error if
|
||||||
// some kind of error occurs.
|
// some kind of error occurs.
|
||||||
app.open_db(&path);
|
// app.open_db(&path);
|
||||||
s.change_view(HistoricalView::new().upcast());
|
// 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()
|
.upcast()
|
||||||
|
@ -342,20 +359,33 @@ fn main() {
|
||||||
|
|
||||||
let app = App::new(settings);
|
let app = App::new(settings);
|
||||||
|
|
||||||
/*
|
|
||||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
*/
|
|
||||||
|
|
||||||
let adw_app = adw::Application::builder()
|
let adw_app = adw::Application::builder()
|
||||||
.application_id(app_id)
|
.application_id(app_id)
|
||||||
.resource_base_path(RESOURCE_BASE_PATH)
|
.resource_base_path(RESOURCE_BASE_PATH)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
adw_app.connect_activate(move |adw_app| {
|
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();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
Loading…
Reference in New Issue