diff --git a/fitnesstrax/app/src/app_window.rs b/fitnesstrax/app/src/app_window.rs index 0a127c4..31a05c3 100644 --- a/fitnesstrax/app/src/app_window.rs +++ b/fitnesstrax/app/src/app_window.rs @@ -56,6 +56,9 @@ impl AppWindow { .width_request(800) .height_request(746) .build(); + window.connect_destroy(|s| { + let _ = gtk::prelude::WidgetExt::activate_action(s, "app.quit", None); + }); let stylesheet = String::from_utf8( resources_lookup_data( @@ -83,10 +86,22 @@ impl AppWindow { let initial_view = View::Placeholder(PlaceholderView::default().upcast()); + let header_bar = adw::HeaderBar::new(); + + let main_menu = gio::Menu::new(); + main_menu.append(Some("Quit"), Some("app.quit")); + let main_menu_button = gtk::MenuButton::builder() + .icon_name("open-menu") + .direction(gtk::ArrowType::Down) + .halign(gtk::Align::End) + .menu_model(&main_menu) + .build(); + header_bar.pack_end(&main_menu_button); + layout.append(&initial_view.widget()); let nav_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); - nav_layout.append(&adw::HeaderBar::new()); + nav_layout.append(&header_bar); nav_layout.append(&layout); navigation.push( &adw::NavigationPage::builder() diff --git a/fitnesstrax/app/src/main.rs b/fitnesstrax/app/src/main.rs index bbad804..921f708 100644 --- a/fitnesstrax/app/src/main.rs +++ b/fitnesstrax/app/src/main.rs @@ -25,6 +25,8 @@ mod views; use adw::prelude::*; use app_window::AppWindow; +use components::ActionGroup; +use gio::{Action, ActionEntry}; use std::{env, path::PathBuf}; const APP_ID_DEV: &str = "com.luminescent-dreams.fitnesstrax.dev"; @@ -32,6 +34,20 @@ const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax"; const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/fitnesstrax/"; +/// Sets up an application-global action, `app.quit`, which will terminate the application. +fn setup_app_close_action(app: &adw::Application) { + let action = ActionEntry::builder("quit") + .activate(|app: &adw::Application, _, _| { + // right now, stopping the application is dirt simple. But we could use this + // block to add extra code that does additional shutdown steps if we ever want + // some states that shouldn't be discarded. + app.quit(); + }) + .build(); + app.add_action_entries([action]); + app.set_accels_for_action("app.quit", &["Q"]); +} + fn main() { // I still don't fully understand gio resources. resources_register_include! is convenient // because I don't have to deal with filesystem locations at runtime. However, I think other @@ -65,6 +81,8 @@ fn main() { let icon_theme = gtk::IconTheme::for_display(&gdk::Display::default().unwrap()); icon_theme.add_resource_path(&(RESOURCE_BASE_PATH.to_owned() + "/icons/scalable/actions")); + setup_app_close_action(&adw_app); + AppWindow::new(app_id, RESOURCE_BASE_PATH, adw_app, ft_app.clone()); });