From baf652173c069e3bc55657bf557d6e3e1d92876c Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Mon, 18 Dec 2023 18:30:41 -0500 Subject: [PATCH] Set up the main views for the window, as well as the redraw policy Whenever we change views, we need to call the redraw function. That function will handle dropping the old view and populating the new one. --- Cargo.lock | 1 + fitnesstrax/app/Cargo.toml | 1 + fitnesstrax/app/src/main.rs | 191 ++++++++++++++++++++++++++++++------ fitnesstrax/core/src/lib.rs | 1 + 4 files changed, 166 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c04fd4..4b85df9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -976,6 +976,7 @@ checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" name = "fitnesstrax" version = "0.1.0" dependencies = [ + "emseries", "ft-core", "gio", "glib", diff --git a/fitnesstrax/app/Cargo.toml b/fitnesstrax/app/Cargo.toml index 005fc16..470ecea 100644 --- a/fitnesstrax/app/Cargo.toml +++ b/fitnesstrax/app/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] } +emseries = { path = "../../emseries" } ft-core = { path = "../core" } gio = { version = "0.18" } glib = { version = "0.18" } diff --git a/fitnesstrax/app/src/main.rs b/fitnesstrax/app/src/main.rs index 570c7c9..8875bef 100644 --- a/fitnesstrax/app/src/main.rs +++ b/fitnesstrax/app/src/main.rs @@ -1,16 +1,171 @@ +use adw::prelude::*; +use emseries::Series; +use ft_core::TraxRecord; use gio::resources_lookup_data; -use gtk::{prelude::*, STYLE_PROVIDER_PRIORITY_USER}; -use std::env; +use glib::Object; +use gtk::{prelude::*, subclass::prelude::*, STYLE_PROVIDER_PRIORITY_USER}; +use std::{ + cell::RefCell, + env, + sync::{Arc, RwLock}, +}; const APP_ID_DEV: &str = "com.luminescent-dreams.fitnesstrax.dev"; const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax"; const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/fitnesstrax/"; -struct AppState {} +/// The real, headless application. This is where all of the logic will reside. +#[derive(Clone)] +struct App { + database: Arc>>>, +} +impl App { + pub fn new() -> Self { + Self { + database: Arc::new(RwLock::new(None)), + } + } +} + +pub struct UnconfiguredViewPrivate {} + +#[glib::object_subclass] +impl ObjectSubclass for UnconfiguredViewPrivate { + const NAME: &'static str = "UnconfiguredView"; + type Type = UnconfiguredView; + type ParentType = gtk::Box; + + fn new() -> Self { + Self {} + } +} + +impl ObjectImpl for UnconfiguredViewPrivate {} +impl WidgetImpl for UnconfiguredViewPrivate {} +impl BoxImpl for UnconfiguredViewPrivate {} + +glib::wrapper! { + pub struct UnconfiguredView(ObjectSubclass) @extends gtk::Box, gtk::Widget; +} + +impl UnconfiguredView { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + + let label = gtk::Label::builder() + .label("Database is not configured.") + .build(); + s.append(&label); + s + } +} + +pub struct HistoricalViewPrivate {} + +#[glib::object_subclass] +impl ObjectSubclass for HistoricalViewPrivate { + const NAME: &'static str = "HistoricalView"; + type Type = HistoricalView; + type ParentType = gtk::Box; + + fn new() -> Self { + Self {} + } +} + +impl ObjectImpl for HistoricalViewPrivate {} +impl WidgetImpl for HistoricalViewPrivate {} +impl BoxImpl for HistoricalViewPrivate {} + +glib::wrapper! { + pub struct HistoricalView(ObjectSubclass) @extends gtk::Box, gtk::Widget; +} + +impl HistoricalView { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + + let label = gtk::Label::builder() + .label("Database has been configured and now it is time to show data") + .build(); + s.append(&label); + s + } +} + +/// These are the possible states of the main application view. +enum MainView { + /// The application is not configured yet. This is a basic background widget to take up the + /// space when there is no data to be shown. + Unconfigured(UnconfiguredView), + + /// The Historical view shows a history of records and whatnot. + Historical(HistoricalView), +} + +/// The application window, or the main window, is the main user interface for the app. Almost +/// everything occurs here. struct AppWindow { + app: App, window: adw::ApplicationWindow, + current_view: RefCell, +} + +impl AppWindow { + /// Construct a new App Window. + /// + /// adw_app is an Adwaita application. Application windows need to have access to this, but + /// 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 { + let window = adw::ApplicationWindow::builder() + .application(adw_app) + .width_request(800) + .height_request(600) + .build(); + + let current_view = if app.database.read().unwrap().is_none() { + MainView::Unconfigured(UnconfiguredView::new()) + } else { + MainView::Historical(HistoricalView::new()) + }; + + let stylesheet = String::from_utf8( + resources_lookup_data( + &format!("{}style.css", RESOURCE_BASE_PATH), + gio::ResourceLookupFlags::NONE, + ) + .expect("stylesheet must be available in the resources") + .to_vec(), + ) + .expect("to parse stylesheet"); + + let provider = gtk::CssProvider::new(); + provider.load_from_data(&stylesheet); + + let context = window.style_context(); + context.add_provider(&provider, STYLE_PROVIDER_PRIORITY_USER); + + window.present(); + + let s = Self { + app, + window, + current_view: RefCell::new(current_view), + }; + s.redraw(); + s + } + + fn redraw(&self) { + match *self.current_view.borrow() { + MainView::Unconfigured(ref view) => self.window.set_content(Some(view)), + MainView::Historical(ref view) => self.window.set_content(Some(view)), + } + } } fn main() { @@ -31,10 +186,7 @@ fn main() { println!("database path: {}", settings.string("series-path")); - let app = adw::Application::builder() - .application_id(app_id) - .resource_base_path(RESOURCE_BASE_PATH) - .build(); + let app = App::new(); /* let runtime = tokio::runtime::Builder::new_multi_thread() @@ -43,32 +195,15 @@ fn main() { .unwrap(); */ - let app = adw::Application::builder() + let adw_app = adw::Application::builder() .application_id(app_id) .resource_base_path(RESOURCE_BASE_PATH) .build(); - app.connect_activate(move |app| { - let stylesheet = String::from_utf8( - resources_lookup_data( - &format!("{}style.css", RESOURCE_BASE_PATH), - gio::ResourceLookupFlags::NONE, - ) - .expect("stylesheet must be available in the resources") - .to_vec(), - ) - .expect("to parse stylesheet"); - - let provider = gtk::CssProvider::new(); - provider.load_from_data(&stylesheet); - - let window = adw::ApplicationWindow::new(app); - let context = window.style_context(); - context.add_provider(&provider, STYLE_PROVIDER_PRIORITY_USER); - - window.present(); + adw_app.connect_activate(move |adw_app| { + AppWindow::new(adw_app, app.clone()); }); let args: Vec = env::args().collect(); - ApplicationExtManual::run_with_args(&app, &args); + ApplicationExtManual::run_with_args(&adw_app, &args); } diff --git a/fitnesstrax/core/src/lib.rs b/fitnesstrax/core/src/lib.rs index b591639..9d7103a 100644 --- a/fitnesstrax/core/src/lib.rs +++ b/fitnesstrax/core/src/lib.rs @@ -4,3 +4,4 @@ use emseries::DateTimeTz; mod legacy; mod types; +pub use types::TraxRecord;