Added foundation of the launcher states functionality

This commit is contained in:
Observer KRypt0n_ 2022-07-16 14:52:20 +02:00
parent 82a6e01200
commit bc5b9d2b45
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
5 changed files with 205 additions and 45 deletions

View file

@ -22,7 +22,16 @@ Adw.ApplicationWindow window {
}; };
} }
Adw.PreferencesPage { Adw.StatusPage status_page {
icon-name: "image-loading-symbolic";
title: "Loading data";
vexpand: true;
}
Adw.PreferencesPage launcher_content {
visible: false;
Adw.PreferencesGroup { Adw.PreferencesGroup {
Gtk.Image { Gtk.Image {
file: "assets/images/icon.png"; file: "assets/images/icon.png";
@ -47,19 +56,13 @@ Adw.ApplicationWindow window {
margin-top: 64; margin-top: 64;
spacing: 8; spacing: 8;
Adw.SplitButton launch_game { Gtk.Button launch_game {
label: "Launch"; label: "Launch";
hexpand: false; hexpand: false;
width-request: 200; width-request: 200;
styles ["suggested-action"] styles ["suggested-action"]
popover: Gtk.Popover {
Gtk.Button launch_game_debug {
label: "Launch in debug mode";
}
};
} }
Gtk.Button open_preferences { Gtk.Button open_preferences {

1
src/lib/launcher/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod states;

View file

@ -0,0 +1,89 @@
use gtk4::{self as gtk, prelude::*};
use libadwaita::{self as adw, prelude::*};
use anime_game_core::prelude::*;
use crate::lib::config;
#[derive(Debug, Clone)]
pub enum LauncherState {
Launch,
PatchAvailable(Patch),
// Always contains `VersionDiff::Diff`
VoiceUpdateAvailable(VersionDiff),
/// Always contains `VersionDiff::Outdated`
VoiceOutdated(VersionDiff),
/// Always contains `VersionDiff::NotInstalled`
VoiceNotInstalled(VersionDiff),
// Always contains `VersionDiff::Diff`
GameUpdateAvailable(VersionDiff),
/// Always contains `VersionDiff::Outdated`
GameOutdated(VersionDiff),
/// Always contains `VersionDiff::NotInstalled`
GameNotInstalled(VersionDiff)
}
impl Default for LauncherState {
fn default() -> Self {
Self::Launch
}
}
impl LauncherState {
pub fn get(status_page: Option<adw::StatusPage>) -> std::io::Result<Self> {
let config = config::get()?;
let game = Game::new(&config.game.path);
if let Some(status_page) = &status_page {
status_page.set_description(Some("Updating game info..."));
}
let diff = game.try_get_diff()?;
Ok(match diff {
VersionDiff::Latest(_) => {
if let Some(status_page) = &status_page {
status_page.set_description(Some("Updating voice info..."));
}
for voice_package in game.get_voice_packages()? {
if let Some(status_page) = &status_page {
status_page.set_description(Some(format!("Updating voice info ({})...", voice_package.locale().to_name()).as_str()));
}
let diff = voice_package.try_get_diff()?;
match diff {
VersionDiff::Latest(_) => continue,
VersionDiff::Diff { .. } => return Ok(Self::VoiceUpdateAvailable(diff)),
VersionDiff::Outdated { .. } => return Ok(Self::VoiceOutdated(diff)),
VersionDiff::NotInstalled { .. } => return Ok(Self::VoiceNotInstalled(diff))
}
}
if let Some(status_page) = &status_page {
status_page.set_description(Some("Updating patch info..."));
}
let patch = Patch::try_fetch(config.patch.servers.clone())?;
if patch.is_applied(&config.game.path)? {
Self::Launch
}
else {
Self::PatchAvailable(patch)
}
},
VersionDiff::Diff { .. } => Self::GameUpdateAvailable(diff),
VersionDiff::Outdated { .. } => Self::GameOutdated(diff),
VersionDiff::NotInstalled { .. } => Self::GameNotInstalled(diff)
})
}
}

View file

@ -4,3 +4,4 @@ pub mod tasks;
pub mod game; pub mod game;
pub mod dxvk; pub mod dxvk;
pub mod wine; pub mod wine;
pub mod launcher;

View file

@ -14,6 +14,7 @@ use super::traits::toast_error::ToastError;
use crate::lib::game; use crate::lib::game;
use crate::lib::tasks; use crate::lib::tasks;
use crate::lib::launcher::states::LauncherState;
/// This structure is used to describe widgets used in application /// This structure is used to describe widgets used in application
/// ///
@ -26,9 +27,10 @@ pub struct AppWidgets {
pub toast_overlay: adw::ToastOverlay, pub toast_overlay: adw::ToastOverlay,
pub leaflet: adw::Leaflet, pub leaflet: adw::Leaflet,
pub status_page: adw::StatusPage,
pub launcher_content: adw::PreferencesPage,
pub launch_game: adw::SplitButton, pub launch_game: gtk::Button,
pub launch_game_debug: gtk::Button,
pub open_preferences: gtk::Button, pub open_preferences: gtk::Button,
pub launch_game_group: adw::PreferencesGroup, pub launch_game_group: adw::PreferencesGroup,
@ -50,9 +52,10 @@ impl AppWidgets {
toast_overlay: toast_overlay.clone(), toast_overlay: toast_overlay.clone(),
leaflet: get_object(&builder, "leaflet")?, leaflet: get_object(&builder, "leaflet")?,
status_page: get_object(&builder, "status_page")?,
launcher_content: get_object(&builder, "launcher_content")?,
launch_game: get_object(&builder, "launch_game")?, launch_game: get_object(&builder, "launch_game")?,
launch_game_debug: get_object(&builder, "launch_game_debug")?,
open_preferences: get_object(&builder, "open_preferences")?, open_preferences: get_object(&builder, "open_preferences")?,
launch_game_group: get_object(&builder, "launch_game_group")?, launch_game_group: get_object(&builder, "launch_game_group")?,
@ -76,7 +79,11 @@ impl AppWidgets {
pub enum Actions { pub enum Actions {
OpenPreferencesPage, OpenPreferencesPage,
PreferencesGoBack, PreferencesGoBack,
LaunchGame PerformButtonEvent,
LaunchGame,
ShowProgressBar,
UpdateProgress { fraction: Rc<f64>, title: Rc<String> },
HideProgressBar
} }
impl Actions { impl Actions {
@ -93,7 +100,9 @@ impl Actions {
/// ///
/// This must implement `Default` trait /// This must implement `Default` trait
#[derive(Debug, Default, glib::Downgrade)] #[derive(Debug, Default, glib::Downgrade)]
pub struct Values; pub struct Values {
state: Rc<LauncherState>
}
/// The main application structure /// The main application structure
/// ///
@ -125,6 +134,19 @@ impl App {
// Bind app to the window // Bind app to the window
result.widgets.window.set_application(Some(app)); result.widgets.window.set_application(Some(app));
// Load initial launcher state
std::thread::spawn(clone!(@strong result => move || {
match LauncherState::get(Some(result.widgets.status_page.clone())) {
Ok(state) => {
result.set_state(state);
result.widgets.status_page.set_visible(false);
result.widgets.launcher_content.set_visible(true);
},
Err(err) => result.toast_error("Failed to get initial launcher state", err)
}
}));
Ok(result) Ok(result)
} }
@ -137,7 +159,7 @@ impl App {
self.widgets.preferences_stack.preferences_go_back.connect_clicked(Actions::PreferencesGoBack.into_fn(&self)); self.widgets.preferences_stack.preferences_go_back.connect_clicked(Actions::PreferencesGoBack.into_fn(&self));
// Launch game // Launch game
self.widgets.launch_game.connect_clicked(Actions::LaunchGame.into_fn(&self)); self.widgets.launch_game.connect_clicked(Actions::PerformButtonEvent.into_fn(&self));
self self
} }
@ -152,10 +174,8 @@ impl App {
let this = self.clone(); let this = self.clone();
receiver.attach(None, move |action| { receiver.attach(None, move |action| {
let values = this.values.take();
// Some debug output // Some debug output
println!("[main] [update] action: {:?}, values: {:?}", &action, &values); println!("[main] [update] action: {:?}", &action);
match action { match action {
Actions::OpenPreferencesPage => { Actions::OpenPreferencesPage => {
@ -175,15 +195,48 @@ impl App {
this.widgets.leaflet.navigate(adw::NavigationDirection::Back); this.widgets.leaflet.navigate(adw::NavigationDirection::Back);
} }
Actions::PerformButtonEvent => {
let values = this.values.take();
let state = (*values.state).clone();
this.values.set(values);
match state {
LauncherState::Launch => {
this.update(Actions::LaunchGame);
},
LauncherState::PatchAvailable(_) => todo!(),
LauncherState::VoiceUpdateAvailable(_) => todo!(),
LauncherState::VoiceOutdated(_) => todo!(),
LauncherState::VoiceNotInstalled(_) => todo!(),
LauncherState::GameUpdateAvailable(_) => todo!(),
LauncherState::GameOutdated(_) => todo!(),
LauncherState::GameNotInstalled(_) => todo!()
}
}
Actions::LaunchGame => { Actions::LaunchGame => {
// Display toast message if the game is failed to run // Display toast message if the game is failed to run
if let Err(err) = game::run(false) { if let Err(err) = game::run(false) {
this.toast_error("Failed to run game", err); this.toast_error("Failed to run game", err);
} }
} }
Actions::ShowProgressBar => {
this.widgets.launch_game_group.set_visible(false);
this.widgets.progress_bar_group.set_visible(true);
} }
this.values.set(values); Actions::UpdateProgress { fraction, title } => {
this.widgets.progress_bar.set_text(Some(title.as_str()));
this.widgets.progress_bar.set_fraction(*fraction);
}
Actions::HideProgressBar => {
this.widgets.launch_game_group.set_visible(true);
this.widgets.progress_bar_group.set_visible(false);
}
}
glib::Continue(true) glib::Continue(true)
}); });
@ -211,6 +264,45 @@ impl App {
pub fn show(&self) { pub fn show(&self) {
self.widgets.window.show(); self.widgets.window.show();
} }
pub fn set_state(&self, state: LauncherState) {
println!("[main] [set_state] state: {:?}", &state);
match state {
LauncherState::Launch => {
self.widgets.launch_game.set_label("Launch");
}
LauncherState::PatchAvailable(_) => {
self.widgets.launch_game.set_label("Apply patch");
}
LauncherState::GameUpdateAvailable(_) => {
self.widgets.launch_game.set_label("Update");
}
LauncherState::VoiceUpdateAvailable(_) => {
self.widgets.launch_game.set_label("Update");
}
LauncherState::GameNotInstalled(_) => {
self.widgets.launch_game.set_label("Download");
}
LauncherState::VoiceNotInstalled(_) => {
self.widgets.launch_game.set_label("Download");
}
LauncherState::VoiceOutdated(_) => todo!(),
LauncherState::GameOutdated(_) => todo!()
}
let mut values = self.values.take();
values.state = Rc::new(state);
self.values.set(values);
}
} }
impl ToastError for App { impl ToastError for App {
@ -221,29 +313,3 @@ impl ToastError for App {
unsafe impl Send for App {} unsafe impl Send for App {}
unsafe impl Sync for App {} unsafe impl Sync for App {}
/*
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);
}
}
}
*/