Compare commits
8 Commits
1d8653a0f5
...
56a8133dd5
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 56a8133dd5 | |
Savanni D'Gerinel | 29dc81a991 | |
Savanni D'Gerinel | 5668f1d7b2 | |
Savanni D'Gerinel | f6bba16b26 | |
Savanni D'Gerinel | 1140377aa5 | |
Savanni D'Gerinel | 87a07955a3 | |
Savanni D'Gerinel | 7ee3e1432e | |
Savanni D'Gerinel | e008a97f83 |
|
@ -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(>k::Label::new(Some("FitnessTrax")))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let layout = gtk::Box::builder()
|
|
||||||
.orientation(gtk::Orientation::Vertical)
|
|
||||||
.build();
|
|
||||||
layout.append(&header);
|
|
||||||
layout.append(¤t_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();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue