Compare commits

..

8 Commits

1 changed files with 42 additions and 113 deletions

View File

@ -27,7 +27,7 @@ use std::{
env, env,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use ui::{welcome_modal, Modal}; use ui::welcome_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,6 +48,8 @@ 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]
@ -73,14 +75,19 @@ 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("Database is not configured.") .label("Welcome to FitnessTrax")
.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]
@ -114,43 +121,13 @@ impl HistoricalView {
} }
} }
// window setup... /// The application window, or the main window, is the main user interface for the app. Almost
// main window with all of its layout /// everything occurs here.
// 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,
overlay: gtk::Overlay, layout: gtk::Box,
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 {
@ -167,38 +144,10 @@ 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() {
let view = UnconfiguredView::new(); UnconfiguredView::new().upcast()
/*
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 {
let view = HistoricalView::new(); HistoricalView::new().upcast()
/*
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(
@ -217,53 +166,38 @@ 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();
s.redraw(); let s = Self {
app,
s window,
layout,
/* current_view: RefCell::new(current_view),
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)
}; };
*/ s
} }
fn redraw(&self) { // Switch views.
match *self.current_view.borrow() { //
MainView::Unconfigured(ref view) => { // This function only replaces the old view with the one which matches the current view state.
self.overlay.set_child(Some(view)); // It is responsible for ensuring that the new view goes into the layout in the correct
// position.
let modal = welcome_modal(|path| { fn change_view(&self, view: gtk::Widget) {
// When a path gets selected, I want to set the path in the configuration system, let mut current_view = self.current_view.borrow_mut();
// and I want to open the database. After that, this window should be redrawn with self.layout.remove(&*current_view);
// its new state. So the view state also needs to change. *current_view = view;
println!("path: {}", path.to_str().unwrap()) self.layout.append(&*current_view);
});
self.overlay.add_overlay(&modal);
*self.modal.borrow_mut() = Some(modal.upcast());
}
MainView::Historical(ref view) => self.overlay.set_child(Some(view)),
}
} }
} }
@ -285,11 +219,6 @@ 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();
/* /*