feat: added game sessions manager

This commit is contained in:
Observer KRypt0n_ 2023-05-05 16:36:08 +02:00
parent e4ea3215c4
commit c872475fe6
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
5 changed files with 149 additions and 30 deletions

4
Cargo.lock generated
View file

@ -87,8 +87,8 @@ dependencies = [
[[package]] [[package]]
name = "anime-launcher-sdk" name = "anime-launcher-sdk"
version = "1.1.6" version = "1.2.1"
source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=1.1.6#4169e902252f7fd2c71cacbc31997ccc3a0e07d2" source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=1.2.1#f7bddd1d2fde4dcc7d04bda9e4d9160a5bc88218"
dependencies = [ dependencies = [
"anime-game-core", "anime-game-core",
"anyhow", "anyhow",

View file

@ -17,7 +17,7 @@ glib-build-tools = "0.17"
[dependencies.anime-launcher-sdk] [dependencies.anime-launcher-sdk]
git = "https://github.com/an-anime-team/anime-launcher-sdk" git = "https://github.com/an-anime-team/anime-launcher-sdk"
tag = "1.1.6" tag = "1.2.1"
features = ["all", "genshin"] features = ["all", "genshin"]
# path = "../anime-launcher-sdk" # ! for dev purposes only # path = "../anime-launcher-sdk" # ! for dev purposes only

View file

@ -167,11 +167,11 @@ impl SimpleAsyncComponent for EnvironmentApp {
async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender<Self>) { async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender<Self>) {
match msg { match msg {
EnvironmentAppMsg::Add => { EnvironmentAppMsg::Add => {
if let Ok(mut config) = Config::get() {
let name = self.name_entry.text().trim().to_string(); let name = self.name_entry.text().trim().to_string();
let value = self.value_entry.text().trim().to_string(); let value = self.value_entry.text().trim().to_string();
if !name.is_empty() && !value.is_empty() { if !name.is_empty() && !value.is_empty() {
if let Ok(mut config) = Config::get() {
self.name_entry.set_text(""); self.name_entry.set_text("");
self.value_entry.set_text(""); self.value_entry.set_text("");

View file

@ -4,14 +4,18 @@ use relm4::factory::*;
use adw::prelude::*; use adw::prelude::*;
use anime_launcher_sdk::sessions::SessionsExt;
use anime_launcher_sdk::genshin::sessions::Sessions;
use super::main::PreferencesAppMsg;
use crate::i18n::tr; use crate::i18n::tr;
use crate::*; use crate::*;
#[derive(Debug)] #[derive(Debug)]
struct GameSession { struct GameSession {
title: String, name: String,
description: Option<String>, description: Option<String>
id: usize
} }
#[relm4::factory(async)] #[relm4::factory(async)]
@ -25,16 +29,32 @@ impl AsyncFactoryComponent for GameSession {
view! { view! {
root = adw::ActionRow { root = adw::ActionRow {
set_title: &self.title, set_title: &self.name,
set_subtitle: match &self.description { set_subtitle: match &self.description {
Some(description) => description.as_str(), Some(description) => description.as_str(),
None => "" None => ""
}, },
add_suffix = &gtk::Button {
set_icon_name: "view-refresh-symbolic-symbolic",
add_css_class: "flat",
set_tooltip_text: Some("Update session using current wine prefix registry values"),
set_valign: gtk::Align::Center,
connect_clicked[sender, index] => move |_| {
sender.output(GameAppMsg::UpdateSession(index.clone()));
}
},
add_suffix = &gtk::Button { add_suffix = &gtk::Button {
set_icon_name: "user-trash-symbolic", set_icon_name: "user-trash-symbolic",
add_css_class: "flat", add_css_class: "flat",
set_tooltip_text: Some("Delete session"),
set_valign: gtk::Align::Center, set_valign: gtk::Align::Center,
connect_clicked[sender, index] => move |_| { connect_clicked[sender, index] => move |_| {
@ -60,21 +80,26 @@ impl AsyncFactoryComponent for GameSession {
pub struct GameApp { pub struct GameApp {
sessions: AsyncFactoryVecDeque<GameSession>, sessions: AsyncFactoryVecDeque<GameSession>,
active_sessions: gtk::StringList, sessions_names: Vec<String>,
sessions_combo: adw::ComboRow,
session_name_entry: adw::EntryRow session_name_entry: adw::EntryRow
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum GameAppMsg { pub enum GameAppMsg {
AddSession, AddSession,
RemoveSession(DynamicIndex) UpdateSession(DynamicIndex),
RemoveSession(DynamicIndex),
SetCurrent(u32),
UpdateCombo
} }
#[relm4::component(async, pub)] #[relm4::component(async, pub)]
impl SimpleAsyncComponent for GameApp { impl SimpleAsyncComponent for GameApp {
type Init = (); type Init = ();
type Input = GameAppMsg; type Input = GameAppMsg;
type Output = (); type Output = PreferencesAppMsg;
view! { view! {
adw::PreferencesPage { adw::PreferencesPage {
@ -84,11 +109,12 @@ impl SimpleAsyncComponent for GameApp {
add = &adw::PreferencesGroup { add = &adw::PreferencesGroup {
set_title: "Game sessions", set_title: "Game sessions",
adw::ComboRow { #[local_ref]
sessions_combo -> adw::ComboRow {
set_title: "Active session", set_title: "Active session",
set_subtitle: "Currently selected game session", set_subtitle: "Currently selected game session. Updates after each game launch",
set_model = Some(&model.active_sessions), connect_selected_notify[sender] => move |row| sender.input(GameAppMsg::SetCurrent(row.selected()))
} }
}, },
@ -123,45 +149,137 @@ impl SimpleAsyncComponent for GameApp {
let mut model = Self { let mut model = Self {
sessions: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()), sessions: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()),
active_sessions: gtk::StringList::new(&[]), sessions_names: Vec::new(),
sessions_combo: adw::ComboRow::new(),
session_name_entry: adw::EntryRow::new() session_name_entry: adw::EntryRow::new()
}; };
/*for (name, value) in &CONFIG.game.environment { for (name, _) in Sessions::list().unwrap_or_default() {
model.variables.guard().push_back(); model.sessions.guard().push_back(GameSession {
}*/ name: name.clone(),
description: None
});
}
let sessions = model.sessions.widget(); let sessions = model.sessions.widget();
let sessions_combo = &model.sessions_combo;
let session_name_entry = &model.session_name_entry; let session_name_entry = &model.session_name_entry;
let widgets = view_output!(); let widgets = view_output!();
sender.input(GameAppMsg::UpdateCombo);
AsyncComponentParts { model, widgets } AsyncComponentParts { model, widgets }
} }
async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender<Self>) { async fn update(&mut self, msg: Self::Input, sender: AsyncComponentSender<Self>) {
match msg { match msg {
GameAppMsg::AddSession => { GameAppMsg::AddSession => {
let name = self.session_name_entry.text().trim().to_string(); let name = self.session_name_entry.text().trim().to_string();
if !name.is_empty() { if !name.is_empty() {
if let Ok(config) = Config::get() {
self.session_name_entry.set_text(""); self.session_name_entry.set_text("");
self.active_sessions.append(&name); match Sessions::update(name.clone(), config.get_wine_prefix_path()) {
Ok(()) => {
self.sessions.guard().push_back(GameSession { self.sessions.guard().push_back(GameSession {
title: name, name,
description: None, description: None
id: 0
}); });
sender.input(GameAppMsg::UpdateCombo);
}
#[allow(unused_must_use)]
Err(err) => {
sender.output(PreferencesAppMsg::Toast {
title: String::from("Failed to update game session"),
description: Some(err.to_string())
});
}
}
}
}
}
GameAppMsg::UpdateSession(index) => {
if let Some(session) = self.sessions.guard().get(index.current_index()) {
if let Ok(config) = Config::get() {
#[allow(unused_must_use)]
if let Err(err) = Sessions::update(session.name.clone(), config.get_wine_prefix_path()) {
sender.output(PreferencesAppMsg::Toast {
title: String::from("Failed to update game session"),
description: Some(err.to_string())
});
}
}
} }
} }
GameAppMsg::RemoveSession(index) => { GameAppMsg::RemoveSession(index) => {
self.active_sessions.remove(index.current_index() as u32); if let Some(session) = self.sessions.guard().get(index.current_index()) {
match Sessions::remove(&session.name) {
Ok(()) => sender.input(GameAppMsg::UpdateCombo),
#[allow(unused_must_use)]
Err(err) => {
sender.output(PreferencesAppMsg::Toast {
title: String::from("Failed to update game session"),
description: Some(err.to_string())
});
return;
}
}
}
self.sessions.guard().remove(index.current_index()); self.sessions.guard().remove(index.current_index());
} }
GameAppMsg::SetCurrent(id) => {
if let Some(name) = self.sessions_names.get(id as usize) {
if let Ok(config) = Config::get() {
#[allow(unused_must_use)]
if let Err(err) = Sessions::set_current(name.to_owned()) {
sender.output(PreferencesAppMsg::Toast {
title: String::from("Failed to update game session"),
description: Some(err.to_string())
});
// Prevent session applying
return;
}
#[allow(unused_must_use)]
if let Err(err) = Sessions::apply(name.to_owned(), config.get_wine_prefix_path()) {
sender.output(PreferencesAppMsg::Toast {
title: String::from("Failed to update game session"),
description: Some(err.to_string())
});
}
}
}
}
GameAppMsg::UpdateCombo => {
let sessions = Sessions::get_sessions().unwrap_or_default();
self.sessions_names = sessions.sessions.into_keys().collect::<Vec<String>>();
let mut selected = 0;
for (i, name) in self.sessions_names.iter().enumerate() {
if sessions.current.as_ref() == Some(name) {
selected = i as u32;
}
}
self.sessions_combo.set_model(Some(&gtk::StringList::new(&self.sessions_names.iter().map(|name: &String| name.as_str()).collect::<Vec<&str>>())));
self.sessions_combo.set_selected(selected);
}
} }
} }
} }

View file

@ -7,6 +7,7 @@ use adw::prelude::*;
use anime_launcher_sdk::is_available; use anime_launcher_sdk::is_available;
use super::main::PreferencesAppMsg; use super::main::PreferencesAppMsg;
use crate::i18n::tr; use crate::i18n::tr;
use crate::*; use crate::*;