From 87a07955a3d03a99152bb5d6c6634897c03624a7 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 17 Dec 2023 14:21:31 -0500 Subject: [PATCH] Start setting up an app modal --- fitnesstrax/app/src/main.rs | 20 ++++++++- fitnesstrax/app/src/ui/mod.rs | 2 + fitnesstrax/app/src/ui/modal.rs | 75 +++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 fitnesstrax/app/src/ui/mod.rs create mode 100644 fitnesstrax/app/src/ui/modal.rs diff --git a/fitnesstrax/app/src/main.rs b/fitnesstrax/app/src/main.rs index 67e02a9..e8c8556 100644 --- a/fitnesstrax/app/src/main.rs +++ b/fitnesstrax/app/src/main.rs @@ -8,6 +8,9 @@ use std::{ env, sync::{Arc, RwLock}, }; +use ui::welcome_modal; + +mod ui; const APP_ID_DEV: &str = "com.luminescent-dreams.fitnesstrax.dev"; const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax"; @@ -117,6 +120,7 @@ enum MainView { struct AppWindow { app: App, window: adw::ApplicationWindow, + overlay: gtk::Overlay, current_view: MainView, } @@ -146,19 +150,31 @@ impl AppWindow { window.present(); + // 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 + // `add_overlay`. The overlays/modals should be added as `set_child`. + let overlay = gtk::Overlay::new(); + window.set_content(Some(&overlay)); + let current_view = if app.database.read().unwrap().is_none() { let view = NoDatabaseView::new(); - window.set_content(Some(&view)); + overlay.add_overlay(&view); + + let modal = welcome_modal(); + overlay.set_child(Some(&modal)); + MainView::NoDatabase(view) } else { let view = HistoricalView::new(); - window.set_content(Some(&view)); + overlay.add_overlay(&view); + MainView::HistoricalView(view) }; Self { app, window, + overlay, current_view, } } diff --git a/fitnesstrax/app/src/ui/mod.rs b/fitnesstrax/app/src/ui/mod.rs new file mode 100644 index 0000000..3a0fe05 --- /dev/null +++ b/fitnesstrax/app/src/ui/mod.rs @@ -0,0 +1,2 @@ +mod modal; +pub use modal::{welcome_modal, Modal}; diff --git a/fitnesstrax/app/src/ui/modal.rs b/fitnesstrax/app/src/ui/modal.rs new file mode 100644 index 0000000..a8ce309 --- /dev/null +++ b/fitnesstrax/app/src/ui/modal.rs @@ -0,0 +1,75 @@ +//! The Modal is a reusable component with a title, arbitrary content, and up to three action +//! buttons. It does not itself enforce being a modal, but is meant to become a child of an Overlay +//! component. +use glib::Object; +use gtk::{prelude::*, subclass::prelude::*}; +use std::cell::RefCell; + +pub struct ModalPrivate { + title: gtk::Label, + content: RefCell, + primary_action: gtk::Button, + secondary_action: Option, + tertiary_action: Option, +} + +#[glib::object_subclass] +impl ObjectSubclass for ModalPrivate { + const NAME: &'static str = "Modal"; + type Type = Modal; + type ParentType = gtk::Box; + + fn new() -> Self { + let title = gtk::Label::builder().label("Modal").build(); + let content = gtk::Box::new(gtk::Orientation::Vertical, 0); + let actions = gtk::Box::new(gtk::Orientation::Horizontal, 0); + + Self { + title, + content: RefCell::new(content.upcast()), + primary_action: gtk::Button::new(), + secondary_action: None, + tertiary_action: None, + } + } +} + +impl ObjectImpl for ModalPrivate {} +impl WidgetImpl for ModalPrivate {} +impl BoxImpl for ModalPrivate {} + +glib::wrapper! { + pub struct Modal(ObjectSubclass) @extends gtk::Box, gtk::Widget; +} + +impl Modal { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + + s.append(&s.imp().title); + s.append(&*s.imp().content.borrow()); + // s.append(&s.imp().actions); + + s + } + + pub fn set_title(&self, text: &str) { + self.imp().title.set_text(text); + } + + pub fn set_content(&self, content: gtk::Widget) { + self.remove(&*self.imp().content.borrow()); + self.insert_child_after(&content, Some(&self.imp().title)); + *self.imp().content.borrow_mut() = content; + } +} + +/// The welcome modal is the first thing the user will see when FitnessTrax starts up if the +/// database has not been configured yet. +/// +/// This is a [Modal] component with all of the welcome content. +pub fn welcome_modal() -> Modal { + let modal = Modal::new(); + modal.set_title("Welcome to FitnessTrax"); + modal +}