Synced project structure with krypt0nn/gtk-example-app
This commit is contained in:
parent
9d5c0cc9ee
commit
e2754a4eec
3 changed files with 147 additions and 67 deletions
10
src/main.rs
10
src/main.rs
|
@ -19,15 +19,9 @@ fn main() {
|
||||||
|
|
||||||
// Init app window and show it
|
// Init app window and show it
|
||||||
application.connect_activate(|app| {
|
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.show();
|
||||||
|
|
||||||
app.open_preferences.connect_clicked(move |_| {
|
|
||||||
app_copy.open_preferences_page();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.window.show();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run app
|
// Run app
|
||||||
|
|
198
src/ui/main.rs
198
src/ui/main.rs
|
@ -1,28 +1,31 @@
|
||||||
use gtk4::{self as gtk, prelude::*};
|
use gtk4::{self as gtk, prelude::*};
|
||||||
use libadwaita::{self as adw, 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::preferences::PreferencesStack;
|
||||||
use super::ToastError;
|
use super::ToastError;
|
||||||
|
|
||||||
use crate::lib::game;
|
use crate::lib::game;
|
||||||
|
|
||||||
pub enum AppState {
|
/// This structure is used to describe widgets used in application
|
||||||
Launch,
|
///
|
||||||
Progress {
|
/// `AppWidgets::try_get` function loads UI file from `.assets/ui/.dist` folder and returns structure with references to its widgets
|
||||||
title: String,
|
///
|
||||||
progress: f64
|
/// This function does not implement events
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct App {
|
pub struct AppWidgets {
|
||||||
pub window: adw::ApplicationWindow,
|
pub window: adw::ApplicationWindow,
|
||||||
|
pub toast_overlay: adw::ToastOverlay,
|
||||||
|
|
||||||
pub leaflet: adw::Leaflet,
|
pub leaflet: adw::Leaflet,
|
||||||
|
|
||||||
pub launch_game: adw::SplitButton,
|
pub launch_game: adw::SplitButton,
|
||||||
pub launch_game_debug: gtk::Button,
|
pub launch_game_debug: gtk::Button,
|
||||||
pub open_preferences: gtk::Button,
|
pub open_preferences: gtk::Button,
|
||||||
pub toast_overlay: adw::ToastOverlay,
|
|
||||||
|
|
||||||
pub launch_game_group: adw::PreferencesGroup,
|
pub launch_game_group: adw::PreferencesGroup,
|
||||||
pub progress_bar_group: adw::PreferencesGroup,
|
pub progress_bar_group: adw::PreferencesGroup,
|
||||||
|
@ -31,22 +34,22 @@ pub struct App {
|
||||||
pub preferences_stack: PreferencesStack
|
pub preferences_stack: PreferencesStack
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl AppWidgets {
|
||||||
pub fn new(app: >k::Application) -> Result<Self, String> {
|
fn try_get() -> Result<Self, String> {
|
||||||
// Create builder from UI file
|
|
||||||
let builder = gtk::Builder::from_string(include_str!("../../assets/ui/.dist/main.ui"));
|
let builder = gtk::Builder::from_string(include_str!("../../assets/ui/.dist/main.ui"));
|
||||||
|
|
||||||
let window = get_object::<adw::ApplicationWindow>(&builder, "window")?;
|
let window = get_object::<adw::ApplicationWindow>(&builder, "window")?;
|
||||||
let toast_overlay = get_object::<adw::ToastOverlay>(&builder, "toast_overlay")?;
|
let toast_overlay = get_object::<adw::ToastOverlay>(&builder, "toast_overlay")?;
|
||||||
|
|
||||||
// Parse objects from builder
|
|
||||||
let result = Self {
|
let result = Self {
|
||||||
window: window.clone(),
|
window: window.clone(),
|
||||||
|
toast_overlay: toast_overlay.clone(),
|
||||||
|
|
||||||
leaflet: get_object(&builder, "leaflet")?,
|
leaflet: get_object(&builder, "leaflet")?,
|
||||||
|
|
||||||
launch_game: get_object(&builder, "launch_game")?,
|
launch_game: get_object(&builder, "launch_game")?,
|
||||||
launch_game_debug: get_object(&builder, "launch_game_debug")?,
|
launch_game_debug: get_object(&builder, "launch_game_debug")?,
|
||||||
open_preferences: get_object(&builder, "open_preferences")?,
|
open_preferences: get_object(&builder, "open_preferences")?,
|
||||||
toast_overlay: toast_overlay.clone(),
|
|
||||||
|
|
||||||
launch_game_group: get_object(&builder, "launch_game_group")?,
|
launch_game_group: get_object(&builder, "launch_game_group")?,
|
||||||
progress_bar_group: get_object(&builder, "progress_bar_group")?,
|
progress_bar_group: get_object(&builder, "progress_bar_group")?,
|
||||||
|
@ -56,68 +59,151 @@ impl App {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add preferences page to the leaflet
|
// Add preferences page to the leaflet
|
||||||
let leaflet = result.leaflet.clone();
|
|
||||||
|
|
||||||
result.leaflet.append(&result.preferences_stack.preferences).set_name(Some("preferences_page"));
|
result.leaflet.append(&result.preferences_stack.preferences).set_name(Some("preferences_page"));
|
||||||
|
|
||||||
// Go back button for preferences page
|
Ok(result)
|
||||||
result.preferences_stack.preferences_go_back.connect_clicked(move |_| {
|
}
|
||||||
leaflet.navigate(adw::NavigationDirection::Back);
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Launch game
|
/// This enum is used to describe an action inside of this application
|
||||||
let app_copy = result.clone();
|
///
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
|
||||||
result.launch_game.connect_clicked(move |_| {
|
/// This enum is used to store some of this application data
|
||||||
// Display toast message if the game is failed to run
|
///
|
||||||
if let Err(err) = game::run(false) {
|
/// In this example we store a counter here to know what should we increment or decrement
|
||||||
app_copy.toast_error("Failed to run game", err);
|
///
|
||||||
}
|
/// This must implement `Default` trait
|
||||||
});
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Values;
|
||||||
|
|
||||||
// Launch game in debug mode
|
/// The main application structure
|
||||||
/*let app_copy = result.clone();
|
///
|
||||||
|
/// `Default` macro automatically calls `AppWidgets::default`, i.e. loads UI file and reference its widgets
|
||||||
|
///
|
||||||
|
/// `Rc<Cell<Values>>` means this:
|
||||||
|
/// - `Rc` addeds ability to reference the same value from various clones of the structure.
|
||||||
|
/// This will guarantee us that inner `Cell<Values>` 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<Cell<Values>>
|
||||||
|
}
|
||||||
|
|
||||||
result.launch_game_debug.connect_clicked(move |_| {
|
impl App {
|
||||||
// Display toast message if the game is failed to run
|
/// Create new application
|
||||||
if let Err(err) = game::run(true) {
|
pub fn new(app: >k::Application) -> Result<Self, String> {
|
||||||
app_copy.toast_error("Failed to run game", err);
|
let result = Self {
|
||||||
}
|
widgets: AppWidgets::try_get()?,
|
||||||
});*/
|
values: Default::default()
|
||||||
|
}.init_events();
|
||||||
|
|
||||||
// Bind app to the window
|
// Bind app to the window
|
||||||
result.window.set_application(Some(app));
|
result.widgets.window.set_application(Some(app));
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_preferences_page(&self) {
|
/// Add default events and values to the widgets
|
||||||
self.leaflet.set_visible_child_name("preferences_page");
|
fn init_events(self) -> Self {
|
||||||
|
// Open preferences page
|
||||||
|
let self_copy = self.clone();
|
||||||
|
|
||||||
if let Err(err) = self.preferences_stack.update() {
|
self.widgets.open_preferences.connect_clicked(move |_| {
|
||||||
self.toast_error("Failed to update preferences", err);
|
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) {
|
/// Update widgets state by calling some action
|
||||||
match state {
|
pub fn update(&self, action: Actions) {
|
||||||
AppState::Launch => {
|
let values = self.values.take();
|
||||||
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));
|
println!("[update] action: {:?}, counter: {:?}", &action, &values);
|
||||||
self.progress_bar.set_fraction(progress);
|
|
||||||
|
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 {
|
impl ToastError for App {
|
||||||
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod toast_error;
|
||||||
|
|
||||||
pub use main::{
|
pub use main::{
|
||||||
App as MainApp,
|
App as MainApp,
|
||||||
AppState as MainAppState,
|
// AppState as MainAppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use toast_error::ToastError;
|
pub use toast_error::ToastError;
|
||||||
|
|
Loading…
Reference in a new issue