From 178c45ae48b8c30b17bb856338d83e73adcb7d20 Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Sat, 2 Jul 2022 12:18:44 +0200 Subject: [PATCH] Preparations for loading page - added `Adw.StatusPage` to the preferences - made preparations for progress bar and launcher states system - fixed toasts resizing window issue --- assets/ui/main.blp | 33 +++++++++--- assets/ui/preferences.blp | 10 +++- src/main.rs | 2 +- src/ui/main.rs | 81 ++++++++++++++++------------- src/ui/mod.rs | 8 ++- src/ui/preferences/enhanced_page.rs | 4 +- src/ui/preferences/general_page.rs | 6 ++- src/ui/preferences/mod.rs | 36 +++++++++++-- src/ui/toast_error.rs | 42 +++++++++++++++ 9 files changed, 173 insertions(+), 49 deletions(-) create mode 100644 src/ui/toast_error.rs diff --git a/assets/ui/main.blp b/assets/ui/main.blp index 42944aa..81c087d 100644 --- a/assets/ui/main.blp +++ b/assets/ui/main.blp @@ -7,9 +7,7 @@ Adw.ApplicationWindow window { styles ["devel"] - content: Gtk.Box { - orientation: vertical; - + content: Adw.ToastOverlay toast_overlay { Adw.Leaflet leaflet { can-navigate-back: true; can-unfold: false; @@ -40,7 +38,7 @@ Adw.ApplicationWindow window { } } - Adw.PreferencesGroup { + Adw.PreferencesGroup launch_game_group { vexpand: true; valign: center; @@ -69,9 +67,32 @@ Adw.ApplicationWindow window { } } } - } - Adw.ToastOverlay toast_overlay {} + Adw.PreferencesGroup progress_bar_group { + vexpand: true; + valign: center; + visible: false; + + Gtk.Box { + halign: center; + margin-top: 64; + spacing: 20; + + Gtk.ProgressBar progress_bar { + text: "Downloading: 37% (3.7 of 10 GB)\n"; + show-text: true; + + width-request: 260; + fraction: 0.37; + valign: center; + } + + Gtk.Button { + label: "Pause"; + } + } + } + } } } }; diff --git a/assets/ui/preferences.blp b/assets/ui/preferences.blp index a19cec8..133bda8 100644 --- a/assets/ui/preferences.blp +++ b/assets/ui/preferences.blp @@ -15,8 +15,16 @@ Gtk.Box preferences { } } - Adw.Flap { + Adw.StatusPage status_page { + icon-name: "image-loading-symbolic"; + title: "Loading data"; + vexpand: true; + } + + Adw.Flap flap { + vexpand: true; + visible: false; flap: Gtk.StackSidebar { width-request: 200; diff --git a/src/main.rs b/src/main.rs index b37d182..28857c2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use libadwaita::{self as adw, prelude::*}; pub mod ui; pub mod lib; -use ui::MainApp; +use ui::*; fn main() { gtk::init().expect("GTK initialization failed"); diff --git a/src/ui/main.rs b/src/ui/main.rs index 600e9d8..874d22c 100644 --- a/src/ui/main.rs +++ b/src/ui/main.rs @@ -3,11 +3,20 @@ use libadwaita::{self as adw, prelude::*}; use std::io::Error; -use super::{get_object, add_action}; +use super::get_object; use super::preferences::PreferencesStack; +use super::ToastError; use crate::lib::game; +pub enum AppState { + Launch, + Progress { + title: String, + progress: f64 + } +} + #[derive(Clone)] pub struct App { pub window: adw::ApplicationWindow, @@ -17,6 +26,10 @@ pub struct App { pub open_preferences: gtk::Button, pub toast_overlay: adw::ToastOverlay, + pub launch_game_group: adw::PreferencesGroup, + pub progress_bar_group: adw::PreferencesGroup, + pub progress_bar: gtk::ProgressBar, + pub preferences_stack: PreferencesStack } @@ -25,16 +38,23 @@ impl App { // Create builder from UI file 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: get_object(&builder, "window")?, + window: window.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: get_object(&builder, "toast_overlay")?, + toast_overlay: toast_overlay.clone(), - preferences_stack: PreferencesStack::new()? + launch_game_group: get_object(&builder, "launch_game_group")?, + progress_bar_group: get_object(&builder, "progress_bar_group")?, + progress_bar: get_object(&builder, "progress_bar")?, + + preferences_stack: PreferencesStack::new(window, toast_overlay)? }; // Add preferences page to the leaflet @@ -73,37 +93,6 @@ impl App { Ok(result) } - /// Show toast with `toast` title and `See message` button - /// - /// This button will show message dialog with error message - pub fn toast_error(&self, toast: &str, err: std::io::Error) { - let toast = adw::Toast::new(toast); - - toast.set_button_label(Some("See message")); - toast.set_action_name(Some("see-message.see-message")); - - let window_copy = self.window.clone(); - - // Show error message in a dialog window - add_action(&self.toast_overlay, "see-message", move || { - let dialog = gtk::MessageDialog::new( - Some(&window_copy), - gtk::DialogFlags::all(), - gtk::MessageType::Info, - gtk::ButtonsType::Close, - &err.to_string() - ); - - dialog.connect_response(move |dialog, _| { - dialog.close(); - }); - - dialog.show(); - }); - - self.toast_overlay.add_toast(&toast); - } - pub fn open_preferences_page(&self) -> Result<(), Error> { self.preferences_stack.update()?; @@ -111,4 +100,26 @@ impl App { Ok(()) } + + 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); + } + } + } +} + +impl ToastError for App { + fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) { + (self.window.clone(), self.toast_overlay.clone()) + } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index a67d792..284e3d1 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -3,8 +3,14 @@ use libadwaita::{self as adw, prelude::*}; mod main; mod preferences; +mod toast_error; -pub use main::App as MainApp; +pub use main::{ + App as MainApp, + AppState as MainAppState, +}; + +pub use toast_error::ToastError; /// This function loads object from builder or panics if it doesn't exist pub fn get_object>(builder: >k::Builder, name: &str) -> Result { diff --git a/src/ui/preferences/enhanced_page.rs b/src/ui/preferences/enhanced_page.rs index f83f71a..0a8cab7 100644 --- a/src/ui/preferences/enhanced_page.rs +++ b/src/ui/preferences/enhanced_page.rs @@ -103,9 +103,11 @@ impl Page { } /// This method is being called by the `PreferencesStack::update` - pub fn update(&self) -> Result<(), Error> { + pub fn update(&self, status_page: &adw::StatusPage) -> Result<(), Error> { let config = config::get()?; + status_page.set_description(Some("Loading preferences...")); + // Update Wine sync self.sync_combo.set_selected(config.game.wine.sync.into()); diff --git a/src/ui/preferences/general_page.rs b/src/ui/preferences/general_page.rs index b97862e..2e41cc3 100644 --- a/src/ui/preferences/general_page.rs +++ b/src/ui/preferences/general_page.rs @@ -32,13 +32,15 @@ impl Page { } /// This method is being called by the `PreferencesStack::update` - pub fn update(&self) -> Result<(), Error> { + pub fn update(&self, status_page: &adw::StatusPage) -> Result<(), Error> { let config = config::get()?; let game = Game::new(config.game.path); self.game_version.set_tooltip_text(None); self.patch_version.set_tooltip_text(None); + status_page.set_description(Some("Updating game info...")); + match game.try_get_diff()? { VersionDiff::Latest(version) => { self.game_version.set_label(&version.to_string()); @@ -61,6 +63,8 @@ impl Page { } } + status_page.set_description(Some("Updating patch info...")); + match Patch::try_fetch(config.patch.servers)? { Patch::NotAvailable => { self.patch_version.set_label("not available"); diff --git a/src/ui/preferences/mod.rs b/src/ui/preferences/mod.rs index 6a82605..f662500 100644 --- a/src/ui/preferences/mod.rs +++ b/src/ui/preferences/mod.rs @@ -4,6 +4,7 @@ use libadwaita::{self as adw, prelude::*}; use std::io::Error; use crate::ui::get_object; +use crate::ui::ToastError; mod general_page; mod enhanced_page; @@ -15,9 +16,15 @@ pub mod pages { #[derive(Clone)] pub struct PreferencesStack { + pub window: adw::ApplicationWindow, + pub toast_overlay: adw::ToastOverlay, + pub preferences: gtk::Box, pub preferences_go_back: gtk::Button, + pub status_page: adw::StatusPage, + pub flap: adw::Flap, + pub stack: gtk::Stack, pub general_page: pages::GeneralPage, @@ -25,13 +32,21 @@ pub struct PreferencesStack { } impl PreferencesStack { - pub fn new() -> Result { + pub fn new(window: adw::ApplicationWindow, toast_overlay: adw::ToastOverlay) -> Result { let builder = gtk::Builder::from_string(include_str!("../../../assets/ui/.dist/preferences.ui")); let result = Self { + window, + toast_overlay, + preferences: get_object(&builder, "preferences")?, preferences_go_back: get_object(&builder, "preferences_go_back")?, + + status_page: get_object(&builder, "status_page")?, + flap: get_object(&builder, "flap")?, + stack: get_object(&builder, "stack")?, + general_page: pages::GeneralPage::new()?, enhanced_page: pages::EnhancedPage::new()? }; @@ -48,9 +63,24 @@ impl PreferencesStack { /// /// TODO: do it asynchronously. The problem is that I somehow need to handle this function's error to display it as a toast pub fn update(&self) -> Result<(), Error> { - self.general_page.update()?; - self.enhanced_page.update()?; + self.status_page.set_visible(true); + self.status_page.set_description(None); + self.flap.set_visible(false); + + self.general_page.update(&self.status_page)?; + self.enhanced_page.update(&self.status_page)?; + + self.status_page.set_visible(false); + self.flap.set_visible(true); + + self.toast_error("Aboba amogus", Error::last_os_error()); Ok(()) } } + +impl ToastError for PreferencesStack { + fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) { + (self.window.clone(), self.toast_overlay.clone()) + } +} diff --git a/src/ui/toast_error.rs b/src/ui/toast_error.rs new file mode 100644 index 0000000..9618c23 --- /dev/null +++ b/src/ui/toast_error.rs @@ -0,0 +1,42 @@ +use gtk4::{self as gtk, prelude::*}; +use libadwaita::{self as adw, prelude::*}; + +use std::io::Error; + +use super::add_action; + +pub trait ToastError { + fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay); + + /// Show toast with `toast` title and `See message` button + /// + /// This button will show message dialog with error message + fn toast_error(&self, toast: &str, err: Error) { + let toast = adw::Toast::new(toast); + + toast.set_button_label(Some("See message")); + toast.set_action_name(Some("see-message.see-message")); + toast.set_timeout(0); + + let (window, toast_overlay) = self.get_toast_widgets(); + + // Show error message in a dialog window + add_action(&toast_overlay, "see-message", move || { + let dialog = gtk::MessageDialog::new( + Some(&window), + gtk::DialogFlags::all(), + gtk::MessageType::Info, + gtk::ButtonsType::Close, + &err.to_string() + ); + + dialog.connect_response(move |dialog, _| { + dialog.close(); + }); + + dialog.show(); + }); + + toast_overlay.add_toast(&toast); + } +}