diff --git a/fitnesstrax/app/src/main.rs b/fitnesstrax/app/src/main.rs index e8c8556..8f78901 100644 --- a/fitnesstrax/app/src/main.rs +++ b/fitnesstrax/app/src/main.rs @@ -1,22 +1,39 @@ +/* +Copyright 2023, Savanni D'Gerinel + +This file is part of FitnessTrax. + +FitnessTrax is free software: you can redistribute it and/or modify it under the terms of the GNU +General Public License as published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +FitnessTrax is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see . +*/ + +mod ui; + use adw::prelude::*; use emseries::Series; use ft_core::TraxRecord; use gio::resources_lookup_data; use glib::Object; -use gtk::{prelude::*, subclass::prelude::*, STYLE_PROVIDER_PRIORITY_USER}; +use gtk::{subclass::prelude::*, STYLE_PROVIDER_PRIORITY_USER}; use std::{ env, sync::{Arc, RwLock}, }; -use ui::welcome_modal; - -mod ui; +use ui::{welcome_modal, Modal}; 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/"; +/// The real, headless application. This is where all of the logic will reside. #[derive(Clone)] struct App { database: Arc>>>, @@ -30,12 +47,12 @@ impl App { } } -pub struct NoDatabaseViewPrivate {} +pub struct UnconfiguredViewPrivate {} #[glib::object_subclass] -impl ObjectSubclass for NoDatabaseViewPrivate { - const NAME: &'static str = "NoDatabaseView"; - type Type = NoDatabaseView; +impl ObjectSubclass for UnconfiguredViewPrivate { + const NAME: &'static str = "UnconfiguredView"; + type Type = UnconfiguredView; type ParentType = gtk::Box; fn new() -> Self { @@ -43,15 +60,15 @@ impl ObjectSubclass for NoDatabaseViewPrivate { } } -impl ObjectImpl for NoDatabaseViewPrivate {} -impl WidgetImpl for NoDatabaseViewPrivate {} -impl BoxImpl for NoDatabaseViewPrivate {} +impl ObjectImpl for UnconfiguredViewPrivate {} +impl WidgetImpl for UnconfiguredViewPrivate {} +impl BoxImpl for UnconfiguredViewPrivate {} glib::wrapper! { - pub struct NoDatabaseView(ObjectSubclass) @extends gtk::Box, gtk::Widget; + pub struct UnconfiguredView(ObjectSubclass) @extends gtk::Box, gtk::Widget; } -impl NoDatabaseView { +impl UnconfiguredView { pub fn new() -> Self { let s: Self = Object::builder().build(); @@ -112,11 +129,17 @@ impl HistoricalView { // // Then there is the view which notifies the user that the database has not been configured. +/// These are the possible states of the main application view. enum MainView { - NoDatabase(NoDatabaseView), + /// 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. HistoricalView(HistoricalView), } +/// The application window, or the main window, is the main user interface for the app. struct AppWindow { app: App, window: adw::ApplicationWindow, @@ -125,6 +148,12 @@ struct AppWindow { } 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 stylesheet = String::from_utf8( resources_lookup_data( @@ -157,13 +186,14 @@ impl AppWindow { window.set_content(Some(&overlay)); let current_view = if app.database.read().unwrap().is_none() { - let view = NoDatabaseView::new(); + let view = UnconfiguredView::new(); overlay.add_overlay(&view); - let modal = welcome_modal(); - overlay.set_child(Some(&modal)); + // I have to access the overlay directly here because I haven't fully constructed Self + // yet, and so I don't have access to `open_modal` yet. + overlay.set_child(Some(&welcome_modal())); - MainView::NoDatabase(view) + MainView::Unconfigured(view) } else { let view = HistoricalView::new(); overlay.add_overlay(&view); @@ -178,6 +208,17 @@ impl AppWindow { current_view, } } + + /// Use [modal] as a modal overlay of the application window. + fn open_modal(&self, modal: Modal) { + self.overlay.set_child(Some(&modal)); + } + + /// Close the modal by discarding the component. + fn close_modal(&self) { + let none: Option<>k::Widget> = None; + self.overlay.set_child(none); + } } fn main() {