diff --git a/assets/ui/preferences_enhanced.blp b/assets/ui/preferences_enhanced.blp index d4bc55b..2bc2fd7 100644 --- a/assets/ui/preferences_enhanced.blp +++ b/assets/ui/preferences_enhanced.blp @@ -7,7 +7,7 @@ Adw.PreferencesPage enhanced_page { Adw.PreferencesGroup { title: "Wine"; - Adw.ComboRow { + Adw.ComboRow hud_combo { title: "HUD"; model: Gtk.StringList { @@ -19,7 +19,7 @@ Adw.PreferencesPage enhanced_page { }; } - Adw.ComboRow { + Adw.ComboRow sync_combo { title: "Synchronization"; subtitle: "Technology used to synchronize inner wine events"; @@ -33,7 +33,7 @@ Adw.PreferencesPage enhanced_page { }; } - Adw.ComboRow { + Adw.ComboRow fsr_combo { title: "FSR"; subtitle: "AMD FSR scales game resolution while rendering the game with the lower one"; diff --git a/src/lib/config.rs b/src/lib/config.rs index f05051b..b144459 100644 --- a/src/lib/config.rs +++ b/src/lib/config.rs @@ -52,7 +52,43 @@ pub fn update(config: Config) -> Result<(), Error> { } } -#[derive(Debug, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum WineHUD { + None, + DXVK, + MangoHUD +} + +impl Default for WineHUD { + fn default() -> Self { + Self::None + } +} + +impl TryFrom for WineHUD { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(Self::None), + 1 => Ok(Self::DXVK), + 2 => Ok(Self::MangoHUD), + _ => Err(String::from("Failed to convert number to HUD enum")) + } + } +} + +impl Into for WineHUD { + fn into(self) -> u32 { + match self { + WineHUD::None => 0, + WineHUD::DXVK => 1, + WineHUD::MangoHUD => 2 + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Config { pub launcher: Launcher, pub game: Game, @@ -105,7 +141,7 @@ impl Config { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Launcher { pub language: String } @@ -118,7 +154,7 @@ impl Default for Launcher { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Patch { pub path: String, pub servers: Vec @@ -136,7 +172,7 @@ impl Default for Patch { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Game { pub path: String, pub voices: Vec, @@ -162,7 +198,7 @@ impl Default for Game { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Wine { pub prefix: String, pub builds: String, @@ -187,13 +223,14 @@ impl Default for Wine { } } -#[derive(Debug, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] pub struct Enhancements { pub fsr: Fsr, - pub gamemode: bool + pub gamemode: bool, + pub hud: WineHUD } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct Fsr { pub strength: u8, pub enabled: bool diff --git a/src/ui/preferences/enhanced_page.rs b/src/ui/preferences/enhanced_page.rs index cb89a8c..2625866 100644 --- a/src/ui/preferences/enhanced_page.rs +++ b/src/ui/preferences/enhanced_page.rs @@ -1,23 +1,56 @@ use gtk4::{self as gtk, prelude::*}; use libadwaita::{self as adw, prelude::*}; +use std::io::Error; + use crate::ui::get_object; +use crate::lib::config; #[derive(Clone)] pub struct Page { - pub page: adw::PreferencesPage + pub page: adw::PreferencesPage, + + pub hud_combo: adw::ComboRow, + pub sync_combo: adw::ComboRow, + pub fsr_combo: adw::ComboRow } impl Page { pub fn new() -> Result { let builder = gtk::Builder::from_string(include_str!("../../../assets/ui/.dist/preferences_enhanced.ui")); - Ok(Self { - page: get_object(&builder, "enhanced_page")? - }) + let result = Self { + page: get_object(&builder, "enhanced_page")?, + + hud_combo: get_object(&builder, "hud_combo")?, + sync_combo: get_object(&builder, "sync_combo")?, + fsr_combo: get_object(&builder, "fsr_combo")? + }; + + // Wine HUD selection + result.hud_combo.connect_selected_notify(|hud| { + if let Ok(mut config) = config::get() { + // TODO: show toast + config.game.enhancements.hud = config::WineHUD::try_from(hud.selected()).unwrap(); + + config::update(config).unwrap(); + } + }); + + Ok(result) } pub fn title() -> String { String::from("Enhanced") } + + /// This method is being called by the `PreferencesStack::update` + pub fn update(&self) -> Result<(), Error> { + let config = config::get()?; + + // Update Wine HUD + self.hud_combo.set_selected(config.game.enhancements.hud.into()); + + Ok(()) + } } diff --git a/src/ui/preferences/general_page.rs b/src/ui/preferences/general_page.rs index e63a105..b97862e 100644 --- a/src/ui/preferences/general_page.rs +++ b/src/ui/preferences/general_page.rs @@ -1,11 +1,17 @@ use gtk4::{self as gtk, prelude::*}; use libadwaita::{self as adw, prelude::*}; +use std::io::Error; + +use anime_game_core::prelude::*; + use crate::ui::get_object; +use crate::lib::config; #[derive(Clone)] pub struct Page { pub page: adw::PreferencesPage, + pub game_version: gtk::Label, pub patch_version: gtk::Label } @@ -24,4 +30,68 @@ impl Page { pub fn title() -> String { String::from("General") } + + /// This method is being called by the `PreferencesStack::update` + pub fn update(&self) -> 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); + + match game.try_get_diff()? { + VersionDiff::Latest(version) => { + self.game_version.set_label(&version.to_string()); + }, + VersionDiff::Diff { current, latest, .. } => { + self.game_version.set_label(¤t.to_string()); + self.game_version.set_css_classes(&["warning"]); + + self.game_version.set_tooltip_text(Some(&format!("Game update available: {} -> {}", current, latest))); + }, + VersionDiff::Outdated { current, latest } => { + self.game_version.set_label(¤t.to_string()); + self.game_version.set_css_classes(&["error"]); + + self.game_version.set_tooltip_text(Some(&format!("Game is too outdated and can't be updated. Latest version: {}", latest))); + }, + VersionDiff::NotInstalled { .. } => { + self.game_version.set_label("not installed"); + self.game_version.set_css_classes(&[]); + } + } + + match Patch::try_fetch(config.patch.servers)? { + Patch::NotAvailable => { + self.patch_version.set_label("not available"); + self.patch_version.set_css_classes(&["error"]); + + self.patch_version.set_tooltip_text(Some("Patch is not available")); + }, + Patch::Outdated { current, latest, .. } => { + self.patch_version.set_label("outdated"); + self.patch_version.set_css_classes(&["warning"]); + + self.patch_version.set_tooltip_text(Some(&format!("Patch is outdated ({} -> {})", current, latest))); + }, + Patch::Preparation { .. } => { + self.patch_version.set_label("preparation"); + self.patch_version.set_css_classes(&["warning"]); + + self.patch_version.set_tooltip_text(Some("Patch is in preparation state and will be available later")); + }, + Patch::Testing { version, .. } => { + self.patch_version.set_label(&version.to_string()); + self.patch_version.set_css_classes(&["warning"]); + + self.patch_version.set_tooltip_text(Some("Patch is in testing phase")); + }, + Patch::Available { version, .. } => { + self.patch_version.set_label(&version.to_string()); + self.patch_version.set_css_classes(&["success"]); + } + } + + Ok(()) + } } diff --git a/src/ui/preferences/mod.rs b/src/ui/preferences/mod.rs index 2994a11..6a82605 100644 --- a/src/ui/preferences/mod.rs +++ b/src/ui/preferences/mod.rs @@ -3,10 +3,7 @@ use libadwaita::{self as adw, prelude::*}; use std::io::Error; -use anime_game_core::prelude::*; - use crate::ui::get_object; -use crate::lib::config; mod general_page; mod enhanced_page; @@ -20,7 +17,9 @@ pub mod pages { pub struct PreferencesStack { pub preferences: gtk::Box, pub preferences_go_back: gtk::Button, + pub stack: gtk::Stack, + pub general_page: pages::GeneralPage, pub enhanced_page: pages::EnhancedPage } @@ -47,66 +46,10 @@ impl PreferencesStack { /// /// Being called from the `MainApp` struct /// - /// TODO: do it asynchronously + /// 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> { - let config = config::get()?; - let game = Game::new(config.game.path); - - self.general_page.game_version.set_tooltip_text(None); - self.general_page.patch_version.set_tooltip_text(None); - - match game.try_get_diff()? { - VersionDiff::Latest(version) => { - self.general_page.game_version.set_label(&version.to_string()); - }, - VersionDiff::Diff { current, latest, .. } => { - self.general_page.game_version.set_label(¤t.to_string()); - self.general_page.game_version.set_css_classes(&["warning"]); - - self.general_page.game_version.set_tooltip_text(Some(&format!("Game update available: {} -> {}", current, latest))); - }, - VersionDiff::Outdated { current, latest } => { - self.general_page.game_version.set_label(¤t.to_string()); - self.general_page.game_version.set_css_classes(&["error"]); - - self.general_page.game_version.set_tooltip_text(Some(&format!("Game is too outdated and can't be updated. Latest version: {}", latest))); - }, - VersionDiff::NotInstalled { .. } => { - self.general_page.game_version.set_label("not installed"); - self.general_page.game_version.set_css_classes(&[]); - } - } - - match Patch::try_fetch(config.patch.servers)? { - Patch::NotAvailable => { - self.general_page.patch_version.set_label("not available"); - self.general_page.patch_version.set_css_classes(&["error"]); - - self.general_page.patch_version.set_tooltip_text(Some("Patch is not available")); - }, - Patch::Outdated { current, latest, .. } => { - self.general_page.patch_version.set_label("outdated"); - self.general_page.patch_version.set_css_classes(&["warning"]); - - self.general_page.patch_version.set_tooltip_text(Some(&format!("Patch is outdated ({} -> {})", current, latest))); - }, - Patch::Preparation { .. } => { - self.general_page.patch_version.set_label("preparation"); - self.general_page.patch_version.set_css_classes(&["warning"]); - - self.general_page.patch_version.set_tooltip_text(Some("Patch is in preparation state and will be available later")); - }, - Patch::Testing { version, .. } => { - self.general_page.patch_version.set_label(&version.to_string()); - self.general_page.patch_version.set_css_classes(&["warning"]); - - self.general_page.patch_version.set_tooltip_text(Some("Patch is in testing phase")); - }, - Patch::Available { version, .. } => { - self.general_page.patch_version.set_label(&version.to_string()); - self.general_page.patch_version.set_css_classes(&["success"]); - } - } + self.general_page.update()?; + self.enhanced_page.update()?; Ok(()) }