feat: added game sessions manager
This commit is contained in:
parent
e4ea3215c4
commit
c872475fe6
5 changed files with 149 additions and 30 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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("");
|
||||||
|
|
||||||
|
|
|
@ -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 = >k::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 = >k::Button {
|
add_suffix = >k::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(>k::StringList::new(&self.sessions_names.iter().map(|name: &String| name.as_str()).collect::<Vec<&str>>())));
|
||||||
|
self.sessions_combo.set_selected(selected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue