Compare commits

..

8 Commits

Author SHA1 Message Date
Savanni D'Gerinel 56a8133dd5 A lot of work, possibly meaningless, to try to handle state within the main window
A note to self, when I return to this: the overlay modals don't make
sense in this context. The main window should have views, and switching
from one view to the next should involve just replacing the child.
Modals can be put off until later.
2023-12-18 17:49:25 -05:00
Savanni D'Gerinel 29dc81a991 Set up a database selector row that can dispatch operations when a database file gets selected 2023-12-18 17:17:39 -05:00
Savanni D'Gerinel 5668f1d7b2 Style up the modal and add the database file chooser widget 2023-12-18 12:44:12 -05:00
Savanni D'Gerinel f6bba16b26 Elaborate a little more on the welcome dialog 2023-12-18 12:09:54 -05:00
Savanni D'Gerinel 1140377aa5 Add a lot of commentary 2023-12-18 12:09:53 -05:00
Savanni D'Gerinel 87a07955a3 Start setting up an app modal 2023-12-18 12:08:14 -05:00
Savanni D'Gerinel 7ee3e1432e put in a placeholder for a historical view and the logic to choose it 2023-12-18 12:08:12 -05:00
Savanni D'Gerinel e008a97f83 Add a view which would be displayed when there is no database 2023-12-18 12:06:50 -05:00
1 changed files with 113 additions and 42 deletions

View File

@ -27,7 +27,7 @@ use std::{
env, env,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use ui::welcome_modal; use ui::{welcome_modal, Modal};
const APP_ID_DEV: &str = "com.luminescent-dreams.fitnesstrax.dev"; const APP_ID_DEV: &str = "com.luminescent-dreams.fitnesstrax.dev";
const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax"; const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax";
@ -48,8 +48,6 @@ impl App {
} }
} }
/// This is the view to show if the application has not yet been configured. It will walk the user
/// through the most critical setup steps so that we can move on to the other views in the app.
pub struct UnconfiguredViewPrivate {} pub struct UnconfiguredViewPrivate {}
#[glib::object_subclass] #[glib::object_subclass]
@ -75,19 +73,14 @@ impl UnconfiguredView {
pub fn new() -> Self { pub fn new() -> Self {
let s: Self = Object::builder().build(); let s: Self = Object::builder().build();
// Replace this with the welcome screen that we set up in the fitnesstrax/unconfigured-page
// branch.
let label = gtk::Label::builder() let label = gtk::Label::builder()
.label("Welcome to FitnessTrax") .label("Database is not configured.")
.build(); .build();
s.append(&label); s.append(&label);
s s
} }
} }
/// The historical view will show a window into the main database. It will show some version of
/// daily summaries, daily details, and will provide all functions the user may need for editing
/// records.
pub struct HistoricalViewPrivate {} pub struct HistoricalViewPrivate {}
#[glib::object_subclass] #[glib::object_subclass]
@ -121,13 +114,43 @@ impl HistoricalView {
} }
} }
/// The application window, or the main window, is the main user interface for the app. Almost // window setup...
/// everything occurs here. // main window with all of its layout
// modals that overlay atop the main window and capture focus
// menus
// There is more than one view for the main window. There's the view for data entry, then another
// view for showing graphs. Then a third view, or maybe a modal, for editing a day.
//
// So, the ordinary data view is the history the metrics, and the calendar. Scrollable, and items
// within the scrolling area can be clicked upon in order to open the edit.
//
// I don't need to model the whole thing at once. The graphs will be some time out, and so I can
// model just the main view, which consists of metrics, the data, and the calendar. Day entries
// should be summaries of the day, expandable to the details.
//
// 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 {
/// 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.
struct AppWindow { struct AppWindow {
app: App, app: App,
window: adw::ApplicationWindow, window: adw::ApplicationWindow,
layout: gtk::Box, overlay: gtk::Overlay,
current_view: RefCell<gtk::Widget>,
current_view: RefCell<MainView>,
// We have to keep around a reference to the modal so that we know what to remove from the
// overlay.
modal: RefCell<Option<gtk::Widget>>,
} }
impl AppWindow { impl AppWindow {
@ -144,10 +167,38 @@ impl AppWindow {
.height_request(600) .height_request(600)
.build(); .build();
// GTK overlays aren't all that well documented. The Overlay object needs to be the
// content/child of the window. The main content should then be added to the overlay as
// `set_child`. The overlays/modals should be added as `add_overlay` and then removed with
// `remove_overlay`.
let overlay = gtk::Overlay::new();
window.set_content(Some(&overlay));
let current_view = if app.database.read().unwrap().is_none() { let current_view = if app.database.read().unwrap().is_none() {
UnconfiguredView::new().upcast() let view = UnconfiguredView::new();
/*
overlay.set_child(Some(&view));
// 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.
*/
MainView::Unconfigured(view)
} else { } else {
HistoricalView::new().upcast() let view = HistoricalView::new();
/*
overlay.set_child(Some(&view));
*/
MainView::Historical(view)
};
let s = Self {
app,
window: window.clone(),
overlay,
current_view: RefCell::new(current_view),
modal: RefCell::new(None),
}; };
let stylesheet = String::from_utf8( let stylesheet = String::from_utf8(
@ -166,38 +217,53 @@ impl AppWindow {
let context = window.style_context(); let context = window.style_context();
context.add_provider(&provider, STYLE_PROVIDER_PRIORITY_USER); context.add_provider(&provider, STYLE_PROVIDER_PRIORITY_USER);
let header = adw::HeaderBar::builder()
.title_widget(&gtk::Label::new(Some("FitnessTrax")))
.build();
let layout = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.build();
layout.append(&header);
layout.append(&current_view);
window.set_content(Some(&layout));
window.present(); window.present();
let s = Self { s.redraw();
app,
window,
layout,
current_view: RefCell::new(current_view),
};
s s
/*
let current_view = if app.database.read().unwrap().is_none() {
let view = UnconfiguredView::new();
overlay.set_child(Some(&view));
// 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.add_overlay(&welcome_modal(|path| {
// When a path gets selected, I want to set the path in the configuration system,
// and I want to open the database. After that, this window should be redrawn with
// its new state. So the view state also needs to change.
println!("path: {}", path.to_str().unwrap())
}));
MainView::Unconfigured(view)
} else {
let view = HistoricalView::new();
overlay.set_child(Some(&view));
MainView::HistoricalView(view)
};
*/
} }
// Switch views. fn redraw(&self) {
// match *self.current_view.borrow() {
// This function only replaces the old view with the one which matches the current view state. MainView::Unconfigured(ref view) => {
// It is responsible for ensuring that the new view goes into the layout in the correct self.overlay.set_child(Some(view));
// position.
fn change_view(&self, view: gtk::Widget) { let modal = welcome_modal(|path| {
let mut current_view = self.current_view.borrow_mut(); // When a path gets selected, I want to set the path in the configuration system,
self.layout.remove(&*current_view); // and I want to open the database. After that, this window should be redrawn with
*current_view = view; // its new state. So the view state also needs to change.
self.layout.append(&*current_view); println!("path: {}", path.to_str().unwrap())
});
self.overlay.add_overlay(&modal);
*self.modal.borrow_mut() = Some(modal.upcast());
}
MainView::Historical(ref view) => self.overlay.set_child(Some(view)),
}
} }
} }
@ -219,6 +285,11 @@ fn main() {
println!("database path: {}", settings.string("series-path")); println!("database path: {}", settings.string("series-path"));
let adw_app = adw::Application::builder()
.application_id(app_id)
.resource_base_path(RESOURCE_BASE_PATH)
.build();
let app = App::new(); let app = App::new();
/* /*