From e2754a4eece51560c9f00e61efc60913e2a631bc Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Sun, 3 Jul 2022 17:28:07 +0200 Subject: [PATCH] Synced project structure with krypt0nn/gtk-example-app --- src/main.rs | 10 +-- src/ui/main.rs | 202 +++++++++++++++++++++++++++++++++++-------------- src/ui/mod.rs | 2 +- 3 files changed, 147 insertions(+), 67 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9cb1c28..a7beaba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,15 +19,9 @@ fn main() { // Init app window and show it application.connect_activate(|app| { - let app = MainApp::new(app).unwrap(); + let app = MainApp::new(app).expect("Failed to init MainApp"); - let app_copy = app.clone(); - - app.open_preferences.connect_clicked(move |_| { - app_copy.open_preferences_page(); - }); - - app.window.show(); + app.show(); }); // Run app diff --git a/src/ui/main.rs b/src/ui/main.rs index dbc8bb4..e0a8829 100644 --- a/src/ui/main.rs +++ b/src/ui/main.rs @@ -1,28 +1,31 @@ use gtk4::{self as gtk, prelude::*}; use libadwaita::{self as adw, prelude::*}; -use super::get_object; +use std::rc::Rc; +use std::cell::Cell; + +use crate::ui::*; + use super::preferences::PreferencesStack; use super::ToastError; use crate::lib::game; -pub enum AppState { - Launch, - Progress { - title: String, - progress: f64 - } -} - +/// This structure is used to describe widgets used in application +/// +/// `AppWidgets::try_get` function loads UI file from `.assets/ui/.dist` folder and returns structure with references to its widgets +/// +/// This function does not implement events #[derive(Clone)] -pub struct App { +pub struct AppWidgets { pub window: adw::ApplicationWindow, + pub toast_overlay: adw::ToastOverlay, + pub leaflet: adw::Leaflet, + pub launch_game: adw::SplitButton, pub launch_game_debug: gtk::Button, pub open_preferences: gtk::Button, - pub toast_overlay: adw::ToastOverlay, pub launch_game_group: adw::PreferencesGroup, pub progress_bar_group: adw::PreferencesGroup, @@ -31,22 +34,22 @@ pub struct App { pub preferences_stack: PreferencesStack } -impl App { - pub fn new(app: >k::Application) -> Result { - // Create builder from UI file +impl AppWidgets { + fn try_get() -> Result { let builder = gtk::Builder::from_string(include_str!("../../assets/ui/.dist/main.ui")); let window = get_object::(&builder, "window")?; let toast_overlay = get_object::(&builder, "toast_overlay")?; - // Parse objects from builder let result = Self { window: window.clone(), + toast_overlay: toast_overlay.clone(), + leaflet: get_object(&builder, "leaflet")?, + launch_game: get_object(&builder, "launch_game")?, launch_game_debug: get_object(&builder, "launch_game_debug")?, open_preferences: get_object(&builder, "open_preferences")?, - toast_overlay: toast_overlay.clone(), launch_game_group: get_object(&builder, "launch_game_group")?, progress_bar_group: get_object(&builder, "progress_bar_group")?, @@ -56,68 +59,151 @@ impl App { }; // Add preferences page to the leaflet - let leaflet = result.leaflet.clone(); - result.leaflet.append(&result.preferences_stack.preferences).set_name(Some("preferences_page")); - // Go back button for preferences page - result.preferences_stack.preferences_go_back.connect_clicked(move |_| { - leaflet.navigate(adw::NavigationDirection::Back); - }); + Ok(result) + } +} - // Launch game - let app_copy = result.clone(); - - result.launch_game.connect_clicked(move |_| { - // Display toast message if the game is failed to run - if let Err(err) = game::run(false) { - app_copy.toast_error("Failed to run game", err); - } - }); +/// This enum is used to describe an action inside of this application +/// +/// It may be helpful if you want to add the same event for several widgets, or call an action inside of another action +#[derive(Debug)] +pub enum Actions { + OpenPreferencesPage, + PreferencesGoBack, + LaunchGame +} - // Launch game in debug mode - /*let app_copy = result.clone(); - - result.launch_game_debug.connect_clicked(move |_| { - // Display toast message if the game is failed to run - if let Err(err) = game::run(true) { - app_copy.toast_error("Failed to run game", err); - } - });*/ +/// This enum is used to store some of this application data +/// +/// In this example we store a counter here to know what should we increment or decrement +/// +/// This must implement `Default` trait +#[derive(Debug, Default)] +pub struct Values; + +/// The main application structure +/// +/// `Default` macro automatically calls `AppWidgets::default`, i.e. loads UI file and reference its widgets +/// +/// `Rc>` means this: +/// - `Rc` addeds ability to reference the same value from various clones of the structure. +/// This will guarantee us that inner `Cell` is the same for all the `App::clone()` values +/// - `Cell` addeds inner mutability to its value, so we can mutate it even without mutable reference. +/// +/// So we have a shared reference to some value that can be changed without mutable reference. +/// That's what we need and what we use in `App::update` method +#[derive(Clone)] +pub struct App { + widgets: AppWidgets, + values: Rc> +} + +impl App { + /// Create new application + pub fn new(app: >k::Application) -> Result { + let result = Self { + widgets: AppWidgets::try_get()?, + values: Default::default() + }.init_events(); // Bind app to the window - result.window.set_application(Some(app)); + result.widgets.window.set_application(Some(app)); Ok(result) } - pub fn open_preferences_page(&self) { - self.leaflet.set_visible_child_name("preferences_page"); + /// Add default events and values to the widgets + fn init_events(self) -> Self { + // Open preferences page + let self_copy = self.clone(); - if let Err(err) = self.preferences_stack.update() { - self.toast_error("Failed to update preferences", err); - } + self.widgets.open_preferences.connect_clicked(move |_| { + self_copy.update(Actions::OpenPreferencesPage); + }); + + // Go back button for preferences page + let self_copy = self.clone(); + + self.widgets.preferences_stack.preferences_go_back.connect_clicked(move |_| { + self_copy.update(Actions::PreferencesGoBack); + }); + + // Launch game + let self_copy = self.clone(); + + self.widgets.launch_game.connect_clicked(move |_| { + self_copy.update(Actions::LaunchGame); + }); + + self } - pub fn update_state(&self, state: AppState) { - match state { - AppState::Launch => { - self.launch_game_group.set_visible(true); - self.progress_bar_group.set_visible(false); - }, - AppState::Progress { title, progress } => { - self.launch_game_group.set_visible(false); - self.progress_bar_group.set_visible(true); + /// Update widgets state by calling some action + pub fn update(&self, action: Actions) { + let values = self.values.take(); - self.progress_bar.set_text(Some(&title)); - self.progress_bar.set_fraction(progress); + println!("[update] action: {:?}, counter: {:?}", &action, &values); + + match action { + Actions::OpenPreferencesPage => { + self.widgets.leaflet.set_visible_child_name("preferences_page"); + + if let Err(err) = self.widgets.preferences_stack.update() { + self.toast_error("Failed to update preferences", err); + } + } + + Actions::PreferencesGoBack => { + self.widgets.leaflet.navigate(adw::NavigationDirection::Back); + } + + Actions::LaunchGame => { + // Display toast message if the game is failed to run + if let Err(err) = game::run(false) { + self.toast_error("Failed to run game", err); + } } } + + self.values.set(values); + } + + /// Show application window + pub fn show(&self) { + self.widgets.window.show(); } } impl ToastError for App { fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) { - (self.window.clone(), self.toast_overlay.clone()) + (self.widgets.window.clone(), self.widgets.toast_overlay.clone()) } } + +/* +pub enum AppState { + Launch, + Progress { + title: String, + progress: f64 + } +} + +pub fn update_state(&self, state: AppState) { + match state { + AppState::Launch => { + self.launch_game_group.set_visible(true); + self.progress_bar_group.set_visible(false); + }, + AppState::Progress { title, progress } => { + self.launch_game_group.set_visible(false); + self.progress_bar_group.set_visible(true); + + self.progress_bar.set_text(Some(&title)); + self.progress_bar.set_fraction(progress); + } + } +} +*/ diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 284e3d1..717f852 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -7,7 +7,7 @@ mod toast_error; pub use main::{ App as MainApp, - AppState as MainAppState, + // AppState as MainAppState, }; pub use toast_error::ToastError;