feat(ui): moved wine/dxvk settings to separate page

This commit is contained in:
Observer KRypt0n_ 2023-05-20 19:56:06 +02:00
parent 4676ac3a81
commit 3a418126c2
3 changed files with 524 additions and 400 deletions

View file

@ -159,7 +159,7 @@ fn main() {
}}
.round-bin {{
border-radius: 8px;
border-radius: 24px;
}}
", BACKGROUND_FILE.to_string_lossy()));

View file

@ -0,0 +1,447 @@
use relm4::prelude::*;
use relm4::component::*;
use gtk::prelude::*;
use adw::prelude::*;
use anime_launcher_sdk::wincompatlib::prelude::*;
use anime_launcher_sdk::components::*;
use anime_launcher_sdk::components::wine::WincompatlibWine;
use crate::ui::components;
use crate::ui::components::*;
use super::GeneralAppMsg;
use crate::i18n::*;
use crate::*;
pub struct ComponentsPage {
wine_components: AsyncController<ComponentsList<ComponentsPageMsg>>,
dxvk_components: AsyncController<ComponentsList<ComponentsPageMsg>>,
downloaded_wine_versions: Vec<(wine::Version, wine::Features)>,
downloaded_dxvk_versions: Vec<dxvk::Version>,
allow_dxvk_selection: bool,
selected_wine_version: u32,
selected_dxvk_version: u32,
selecting_wine_version: bool,
selecting_dxvk_version: bool
}
#[derive(Debug, Clone)]
pub enum ComponentsPageMsg {
WineRecommendedOnly(bool),
DxvkRecommendedOnly(bool),
UpdateDownloadedWine,
UpdateDownloadedDxvk,
SelectWine(usize),
SelectDxvk(usize),
ResetWineSelection(usize),
ResetDxvkSelection(usize)
}
#[relm4::component(async, pub)]
impl SimpleAsyncComponent for ComponentsPage {
type Init = ();
type Input = ComponentsPageMsg;
type Output = GeneralAppMsg;
view! {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
adw::HeaderBar {
pack_start = &gtk::Button {
set_icon_name: "go-previous-symbolic",
connect_clicked[sender] => move |_| {
sender.output(GeneralAppMsg::OpenMainPage).unwrap();
}
}
},
adw::PreferencesPage {
add = &adw::PreferencesGroup {
set_title: &tr("wine-version"),
adw::ComboRow {
set_title: &tr("selected-version"),
#[watch]
#[block_signal(wine_selected_notify)]
set_model: Some(&gtk::StringList::new(&model.downloaded_wine_versions.iter().map(|(version, _)| version.title.as_str()).collect::<Vec<&str>>())),
#[watch]
#[block_signal(wine_selected_notify)]
set_selected: model.selected_wine_version,
#[watch]
set_activatable: !model.selecting_wine_version,
connect_selected_notify[sender] => move |row| {
if is_ready() {
sender.input(ComponentsPageMsg::SelectWine(row.selected() as usize));
}
} @wine_selected_notify,
add_suffix = &gtk::Spinner {
set_spinning: true,
#[watch]
set_visible: model.selecting_wine_version
}
},
adw::ActionRow {
set_title: &tr("recommended-only"),
set_subtitle: &tr("wine-recommended-description"),
add_suffix = &gtk::Switch {
set_valign: gtk::Align::Center,
#[block_signal(wine_recommended_notify)]
set_state: true,
connect_state_notify[sender] => move |switch| {
if is_ready() {
sender.input(ComponentsPageMsg::WineRecommendedOnly(switch.state()));
}
} @wine_recommended_notify
}
}
},
add = &adw::PreferencesGroup {
add = model.wine_components.widget(),
},
add = &adw::PreferencesGroup {
set_title: &tr("dxvk-version"),
#[watch]
set_description: Some(&if !model.allow_dxvk_selection {
tr("dxvk-selection-disabled")
} else {
String::new()
}),
#[watch]
set_sensitive: model.allow_dxvk_selection,
adw::ComboRow {
set_title: &tr("selected-version"),
#[watch]
#[block_signal(dxvk_selected_notify)]
set_model: Some(&gtk::StringList::new(&model.downloaded_dxvk_versions.iter().map(|version| version.name.as_str()).collect::<Vec<&str>>())),
#[watch]
#[block_signal(dxvk_selected_notify)]
set_selected: model.selected_dxvk_version,
#[watch]
set_activatable: !model.selecting_dxvk_version,
connect_selected_notify[sender] => move |row| {
if is_ready() {
sender.input(ComponentsPageMsg::SelectDxvk(row.selected() as usize));
}
} @dxvk_selected_notify,
add_suffix = &gtk::Spinner {
set_spinning: true,
#[watch]
set_visible: model.selecting_dxvk_version
}
},
adw::ActionRow {
set_title: &tr("recommended-only"),
set_subtitle: &tr("dxvk-recommended-description"),
add_suffix = &gtk::Switch {
set_valign: gtk::Align::Center,
#[block_signal(dxvk_recommended_notify)]
set_state: true,
connect_state_notify[sender] => move |switch| {
if is_ready() {
sender.input(ComponentsPageMsg::DxvkRecommendedOnly(switch.state()));
}
} @dxvk_recommended_notify
}
}
},
add = &adw::PreferencesGroup {
#[watch]
set_sensitive: model.allow_dxvk_selection,
add = model.dxvk_components.widget(),
},
}
}
}
async fn init(
_init: Self::Init,
root: Self::Root,
sender: AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> {
tracing::info!("Initializing general settings -> components page");
let model = Self {
wine_components: ComponentsList::builder()
.launch(ComponentsListInit {
pattern: ComponentsListPattern {
download_folder: CONFIG.game.wine.builds.clone(),
groups: wine::get_groups(&CONFIG.components.path).unwrap_or_default()
.into_iter()
.map(|mut group| {
group.versions = group.versions.into_iter().take(12).collect();
let mut group: ComponentsListGroup = group.into();
let mut recommended = 6;
for i in 0..group.versions.len() {
if recommended > 0 && group.versions[i].recommended {
recommended -= 1;
}
else {
group.versions[i].recommended = false;
}
}
group
})
.collect()
},
on_downloaded: Some(ComponentsPageMsg::UpdateDownloadedWine),
on_deleted: Some(ComponentsPageMsg::UpdateDownloadedWine)
})
.forward(sender.input_sender(), std::convert::identity),
dxvk_components: ComponentsList::builder()
.launch(ComponentsListInit {
pattern: ComponentsListPattern {
download_folder: CONFIG.game.dxvk.builds.clone(),
groups: dxvk::get_groups(&CONFIG.components.path).unwrap_or_default()
.into_iter()
.map(|mut group| {
group.versions = group.versions.into_iter().take(12).collect();
let mut group: ComponentsListGroup = group.into();
let mut recommended = 6;
for i in 0..group.versions.len() {
if recommended > 0 && group.versions[i].recommended {
recommended -= 1;
}
else {
group.versions[i].recommended = false;
}
}
group
})
.collect()
},
on_downloaded: Some(ComponentsPageMsg::UpdateDownloadedDxvk),
on_deleted: Some(ComponentsPageMsg::UpdateDownloadedDxvk)
})
.forward(sender.input_sender(), std::convert::identity),
downloaded_wine_versions: vec![],
downloaded_dxvk_versions: vec![],
allow_dxvk_selection: match &CONFIG.game.wine.selected {
Some(version) => match wine::Group::find_in(&CONFIG.components.path, version) {
Ok(Some(group)) => group.features.unwrap_or_default().need_dxvk,
_ => true
}
None => true
},
selected_wine_version: 0,
selected_dxvk_version: 0,
selecting_wine_version: false,
selecting_dxvk_version: false
};
let widgets = view_output!();
AsyncComponentParts { model, widgets }
}
async fn update(&mut self, msg: Self::Input, sender: AsyncComponentSender<Self>) {
tracing::debug!("Called general settings event: {:?}", msg);
match msg {
ComponentsPageMsg::WineRecommendedOnly(state) => {
// todo
self.wine_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();
}
ComponentsPageMsg::DxvkRecommendedOnly(state) => {
// todo
self.dxvk_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();
}
ComponentsPageMsg::UpdateDownloadedWine => {
self.downloaded_wine_versions = wine::get_downloaded(&CONFIG.components.path, &CONFIG.game.wine.builds)
.unwrap_or_default()
.into_iter()
.flat_map(|group| group.versions.clone().into_iter()
.map(move |version| {
let features = version.features_in(&group).unwrap_or_default();
(version, features)
})
).collect();
self.selected_wine_version = if let Some(selected) = &CONFIG.game.wine.selected {
let mut index = 0;
for (i, (version, _)) in self.downloaded_wine_versions.iter().enumerate() {
if &version.name == selected {
index = i;
break;
}
}
index as u32
}
else {
0
};
}
ComponentsPageMsg::UpdateDownloadedDxvk => {
self.downloaded_dxvk_versions = dxvk::get_downloaded(&CONFIG.components.path, &CONFIG.game.dxvk.builds)
.unwrap_or_default()
.into_iter()
.flat_map(|group| group.versions)
.collect();
self.selected_dxvk_version = if let Ok(Some(selected)) = CONFIG.get_selected_dxvk() {
let mut index = 0;
for (i, version) in self.downloaded_dxvk_versions.iter().enumerate() {
if version.name == selected.name {
index = i;
break;
}
}
index as u32
}
else {
0
};
}
ComponentsPageMsg::SelectWine(index) => {
if let Ok(mut config) = Config::get() {
if let Some((version, features)) = self.downloaded_wine_versions.get(index) {
if config.game.wine.selected.as_ref() != Some(&version.title) {
self.selecting_wine_version = true;
self.allow_dxvk_selection = features.need_dxvk;
let wine = version
.to_wine(&config.components.path, Some(&config.game.wine.builds.join(&version.name)))
.with_prefix(&config.game.wine.prefix)
.with_loader(WineLoader::Current)
.with_arch(WineArch::Win64);
let wine_name = version.name.to_string();
std::thread::spawn(move || {
match wine.update_prefix::<&str>(None) {
Ok(_) => {
config.game.wine.selected = Some(wine_name);
Config::update(config);
}
Err(err) => {
sender.output(GeneralAppMsg::Toast {
title: tr("wine-prefix-update-failed"),
description: Some(err.to_string())
}).unwrap();
}
}
sender.input(ComponentsPageMsg::ResetWineSelection(index));
});
}
}
}
}
ComponentsPageMsg::ResetWineSelection(index) => {
self.selecting_wine_version = false;
self.selected_wine_version = index as u32;
}
ComponentsPageMsg::SelectDxvk(index) => {
if let Ok(config) = Config::get() {
if let Some(version) = self.downloaded_dxvk_versions.get(index) {
if let Ok(selected) = config.get_selected_dxvk() {
if selected.is_none() || selected.unwrap().name != version.name {
self.selecting_dxvk_version = true;
let mut wine = match config.get_selected_wine() {
Ok(Some(version)) => {
match version.to_wine(config.components.path, Some(config.game.wine.builds.join(&version.name))) {
WincompatlibWine::Default(wine) => wine,
WincompatlibWine::Proton(_) => return
}
}
_ => Wine::default()
};
wine = wine.with_prefix(config.game.wine.prefix);
let dxvk_folder = config.game.dxvk.builds.join(&version.name);
std::thread::spawn(move || {
if let Err(err) = Dxvk::install(&wine, dxvk_folder, InstallParams::default()) {
sender.output(GeneralAppMsg::Toast {
title: tr("dxvk-install-failed"),
description: Some(err.to_string())
}).unwrap();
}
sender.input(ComponentsPageMsg::ResetDxvkSelection(index));
});
}
}
}
}
}
ComponentsPageMsg::ResetDxvkSelection(index) => {
self.selecting_dxvk_version = false;
self.selected_dxvk_version = index as u32;
}
}
}
}

View file

@ -9,13 +9,10 @@ use relm4::factory::{
use gtk::prelude::*;
use adw::prelude::*;
use anime_launcher_sdk::anime_game_core::prelude::*;
use anime_launcher_sdk::anime_game_core::genshin::consts::GameEdition;
use anime_launcher_sdk::wincompatlib::prelude::*;
use anime_launcher_sdk::components::*;
use anime_launcher_sdk::components::wine::WincompatlibWine;
use anime_launcher_sdk::anime_game_core::prelude::*;
use anime_launcher_sdk::anime_game_core::genshin::consts::GameEdition;
use anime_launcher_sdk::config::ConfigExt;
use anime_launcher_sdk::genshin::config::Config;
@ -23,10 +20,12 @@ use anime_launcher_sdk::genshin::config::Config;
use anime_launcher_sdk::genshin::config::schema::launcher::LauncherStyle;
use anime_launcher_sdk::genshin::env_emulation::Environment;
pub mod components;
use components::*;
use super::main::PreferencesAppMsg;
use crate::ui::migrate_installation::MigrateInstallationApp;
use crate::ui::components;
use crate::ui::components::*;
use crate::i18n::*;
use crate::*;
@ -109,10 +108,8 @@ impl AsyncFactoryComponent for VoicePackageComponent {
pub struct GeneralApp {
voice_packages: AsyncFactoryVecDeque<VoicePackageComponent>,
migrate_installation: Controller<MigrateInstallationApp>,
wine_components: AsyncController<ComponentsList<GeneralAppMsg>>,
dxvk_components: AsyncController<ComponentsList<GeneralAppMsg>>,
components_page: AsyncController<ComponentsPage>,
game_diff: Option<VersionDiff>,
unity_player_patch: Option<UnityPlayerPatch>,
@ -120,17 +117,7 @@ pub struct GeneralApp {
style: LauncherStyle,
languages: Vec<String>,
downloaded_wine_versions: Vec<(wine::Version, wine::Features)>,
downloaded_dxvk_versions: Vec<dxvk::Version>,
allow_dxvk_selection: bool,
selected_wine_version: u32,
selected_dxvk_version: u32,
selecting_wine_version: bool,
selecting_dxvk_version: bool
languages: Vec<String>
}
#[derive(Debug, Clone)]
@ -154,23 +141,18 @@ pub enum GeneralAppMsg {
RemoveVoicePackage(DynamicIndex),
SetVoicePackageSensitivity(DynamicIndex, bool),
OpenMigrateInstallation,
RepairGame,
WineOpen(&'static [&'static str]),
UpdateLauncherStyle(LauncherStyle),
WineRecommendedOnly(bool),
DxvkRecommendedOnly(bool),
UpdateDownloadedWine,
UpdateDownloadedDxvk,
SelectWine(usize),
SelectDxvk(usize),
OpenMigrateInstallation,
RepairGame,
ResetWineSelection(usize),
ResetDxvkSelection(usize),
OpenMainPage,
OpenComponentsPage,
UpdateLauncherStyle(LauncherStyle),
WineOpen(&'static [&'static str]),
Toast {
title: String,
@ -185,7 +167,8 @@ impl SimpleAsyncComponent for GeneralApp {
type Output = PreferencesAppMsg;
view! {
adw::PreferencesPage {
#[root]
main_page = adw::PreferencesPage {
set_title: &tr("general"),
set_icon_name: Some("applications-system-symbolic"),
@ -646,60 +629,21 @@ impl SimpleAsyncComponent for GeneralApp {
},
add = &adw::PreferencesGroup {
set_title: &tr("wine-version"),
adw::ComboRow {
set_title: &tr("selected-version"),
#[watch]
#[block_signal(wine_selected_notify)]
set_model: Some(&gtk::StringList::new(&model.downloaded_wine_versions.iter().map(|(version, _)| version.title.as_str()).collect::<Vec<&str>>())),
#[watch]
#[block_signal(wine_selected_notify)]
set_selected: model.selected_wine_version,
#[watch]
set_activatable: !model.selecting_wine_version,
connect_selected_notify[sender] => move |row| {
if is_ready() {
sender.input(GeneralAppMsg::SelectWine(row.selected() as usize));
}
} @wine_selected_notify,
add_suffix = &gtk::Spinner {
set_spinning: true,
#[watch]
set_visible: model.selecting_wine_version
}
},
set_title: "Options",
adw::ActionRow {
set_title: &tr("recommended-only"),
set_subtitle: &tr("wine-recommended-description"),
set_title: "Components",
set_subtitle: "Manage your Wine and DXVK versions",
add_suffix = &gtk::Switch {
set_valign: gtk::Align::Center,
add_suffix = &gtk::Image {
set_icon_name: Some("go-next-symbolic")
},
#[block_signal(wine_recommended_notify)]
set_state: true,
set_activatable: true,
connect_state_notify[sender] => move |switch| {
if is_ready() {
sender.input(GeneralAppMsg::WineRecommendedOnly(switch.state()));
}
} @wine_recommended_notify
}
}
},
connect_activated => GeneralAppMsg::OpenComponentsPage
},
add = &adw::PreferencesGroup {
add = model.wine_components.widget(),
},
add = &adw::PreferencesGroup {
adw::ExpanderRow {
set_title: &tr("wine-tools"),
@ -757,75 +701,11 @@ impl SimpleAsyncComponent for GeneralApp {
connect_activated => GeneralAppMsg::WineOpen(&["start", "winedbg"])
}
}
},
}
},
add = &adw::PreferencesGroup {
set_title: &tr("dxvk-version"),
#[watch]
set_description: Some(&if !model.allow_dxvk_selection {
tr("dxvk-selection-disabled")
} else {
String::new()
}),
#[watch]
set_sensitive: model.allow_dxvk_selection,
adw::ComboRow {
set_title: &tr("selected-version"),
#[watch]
#[block_signal(dxvk_selected_notify)]
set_model: Some(&gtk::StringList::new(&model.downloaded_dxvk_versions.iter().map(|version| version.name.as_str()).collect::<Vec<&str>>())),
#[watch]
#[block_signal(dxvk_selected_notify)]
set_selected: model.selected_dxvk_version,
#[watch]
set_activatable: !model.selecting_dxvk_version,
connect_selected_notify[sender] => move |row| {
if is_ready() {
sender.input(GeneralAppMsg::SelectDxvk(row.selected() as usize));
}
} @dxvk_selected_notify,
add_suffix = &gtk::Spinner {
set_spinning: true,
#[watch]
set_visible: model.selecting_dxvk_version
}
},
adw::ActionRow {
set_title: &tr("recommended-only"),
set_subtitle: &tr("dxvk-recommended-description"),
add_suffix = &gtk::Switch {
set_valign: gtk::Align::Center,
#[block_signal(dxvk_recommended_notify)]
set_state: true,
connect_state_notify[sender] => move |switch| {
if is_ready() {
sender.input(GeneralAppMsg::DxvkRecommendedOnly(switch.state()));
}
} @dxvk_recommended_notify
}
}
},
add = &adw::PreferencesGroup {
#[watch]
set_sensitive: model.allow_dxvk_selection,
add = model.dxvk_components.widget(),
},
}
#[local_ref]
components_page -> gtk::Box {}
}
async fn init(
@ -842,66 +722,8 @@ impl SimpleAsyncComponent for GeneralApp {
.launch(())
.detach(),
wine_components: ComponentsList::builder()
.launch(ComponentsListInit {
pattern: ComponentsListPattern {
download_folder: CONFIG.game.wine.builds.clone(),
groups: wine::get_groups(&CONFIG.components.path).unwrap_or_default()
.into_iter()
.map(|mut group| {
group.versions = group.versions.into_iter().take(12).collect();
let mut group: ComponentsListGroup = group.into();
let mut recommended = 6;
for i in 0..group.versions.len() {
if recommended > 0 && group.versions[i].recommended {
recommended -= 1;
}
else {
group.versions[i].recommended = false;
}
}
group
})
.collect()
},
on_downloaded: Some(GeneralAppMsg::UpdateDownloadedWine),
on_deleted: Some(GeneralAppMsg::UpdateDownloadedWine)
})
.forward(sender.input_sender(), std::convert::identity),
dxvk_components: ComponentsList::builder()
.launch(ComponentsListInit {
pattern: ComponentsListPattern {
download_folder: CONFIG.game.dxvk.builds.clone(),
groups: dxvk::get_groups(&CONFIG.components.path).unwrap_or_default()
.into_iter()
.map(|mut group| {
group.versions = group.versions.into_iter().take(12).collect();
let mut group: ComponentsListGroup = group.into();
let mut recommended = 6;
for i in 0..group.versions.len() {
if recommended > 0 && group.versions[i].recommended {
recommended -= 1;
}
else {
group.versions[i].recommended = false;
}
}
group
})
.collect()
},
on_downloaded: Some(GeneralAppMsg::UpdateDownloadedDxvk),
on_deleted: Some(GeneralAppMsg::UpdateDownloadedDxvk)
})
components_page: ComponentsPage::builder()
.launch(())
.forward(sender.input_sender(), std::convert::identity),
game_diff: None,
@ -910,25 +732,7 @@ impl SimpleAsyncComponent for GeneralApp {
style: CONFIG.launcher.style,
languages: SUPPORTED_LANGUAGES.iter().map(|lang| tr(format_lang(lang).as_str())).collect(),
downloaded_wine_versions: vec![],
downloaded_dxvk_versions: vec![],
allow_dxvk_selection: match &CONFIG.game.wine.selected {
Some(version) => match wine::Group::find_in(&CONFIG.components.path, version) {
Ok(Some(group)) => group.features.unwrap_or_default().need_dxvk,
_ => true
}
None => true
},
selected_wine_version: 0,
selected_dxvk_version: 0,
selecting_wine_version: false,
selecting_dxvk_version: false
languages: SUPPORTED_LANGUAGES.iter().map(|lang| tr(format_lang(lang).as_str())).collect()
};
for package in VoiceLocale::list() {
@ -939,6 +743,7 @@ impl SimpleAsyncComponent for GeneralApp {
}
let voice_packages = model.voice_packages.widget();
let components_page = model.components_page.widget();
let widgets = view_output!();
@ -1018,6 +823,18 @@ impl SimpleAsyncComponent for GeneralApp {
}
}
GeneralAppMsg::UpdateDownloadedWine => {
self.components_page.sender()
.send(ComponentsPageMsg::UpdateDownloadedWine)
.unwrap();
}
GeneralAppMsg::UpdateDownloadedDxvk => {
self.components_page.sender()
.send(ComponentsPageMsg::UpdateDownloadedDxvk)
.unwrap();
}
GeneralAppMsg::OpenMigrateInstallation => unsafe {
if let Some(window) = crate::ui::main::PREFERENCES_WINDOW.as_ref() {
self.migrate_installation.widget().set_transient_for(Some(window.widget()));
@ -1026,33 +843,22 @@ impl SimpleAsyncComponent for GeneralApp {
self.migrate_installation.widget().show();
}
#[allow(unused_must_use)]
GeneralAppMsg::RepairGame => {
sender.output(Self::Output::RepairGame);
sender.output(Self::Output::RepairGame).unwrap();
}
GeneralAppMsg::WineOpen(executable) => {
let config = Config::get().unwrap_or_else(|_| CONFIG.clone());
GeneralAppMsg::OpenMainPage => unsafe {
PREFERENCES_WINDOW.as_ref()
.unwrap_unchecked()
.widget()
.close_subpage();
}
if let Ok(Some(wine)) = config.get_selected_wine() {
let result = wine
.to_wine(config.components.path, Some(config.game.wine.builds.join(&wine.name)))
.with_prefix(config.game.wine.prefix)
.with_loader(WineLoader::Current)
.with_arch(WineArch::Win64)
.run_args(executable);
if let Err(err) = result {
sender.input(GeneralAppMsg::Toast {
title: tr_args("wine-run-error", [
("executable", executable.join(" ").into())
]),
description: Some(err.to_string())
});
tracing::error!("Failed to run {:?} using wine: {err}", executable);
}
}
GeneralAppMsg::OpenComponentsPage => unsafe {
PREFERENCES_WINDOW.as_ref()
.unwrap_unchecked()
.widget()
.present_subpage(self.components_page.widget());
}
#[allow(unused_must_use)]
@ -1081,159 +887,30 @@ impl SimpleAsyncComponent for GeneralApp {
sender.output(Self::Output::SetLauncherStyle(style));
}
GeneralAppMsg::WineRecommendedOnly(state) => {
// todo
self.wine_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();
}
GeneralAppMsg::WineOpen(executable) => {
let config = Config::get().unwrap_or_else(|_| CONFIG.clone());
GeneralAppMsg::DxvkRecommendedOnly(state) => {
// todo
self.dxvk_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();
}
if let Ok(Some(wine)) = config.get_selected_wine() {
let result = wine
.to_wine(config.components.path, Some(config.game.wine.builds.join(&wine.name)))
.with_prefix(config.game.wine.prefix)
.with_loader(WineLoader::Current)
.with_arch(WineArch::Win64)
.run_args(executable);
GeneralAppMsg::UpdateDownloadedWine => {
self.downloaded_wine_versions = wine::get_downloaded(&CONFIG.components.path, &CONFIG.game.wine.builds)
.unwrap_or_default()
.into_iter()
.flat_map(|group| group.versions.clone().into_iter()
.map(move |version| {
let features = version.features_in(&group).unwrap_or_default();
if let Err(err) = result {
sender.input(GeneralAppMsg::Toast {
title: tr_args("wine-run-error", [
("executable", executable.join(" ").into())
]),
description: Some(err.to_string())
});
(version, features)
})
).collect();
self.selected_wine_version = if let Some(selected) = &CONFIG.game.wine.selected {
let mut index = 0;
for (i, (version, _)) in self.downloaded_wine_versions.iter().enumerate() {
if &version.name == selected {
index = i;
break;
}
}
index as u32
}
else {
0
};
}
GeneralAppMsg::UpdateDownloadedDxvk => {
self.downloaded_dxvk_versions = dxvk::get_downloaded(&CONFIG.components.path, &CONFIG.game.dxvk.builds)
.unwrap_or_default()
.into_iter()
.flat_map(|group| group.versions)
.collect();
self.selected_dxvk_version = if let Ok(Some(selected)) = CONFIG.get_selected_dxvk() {
let mut index = 0;
for (i, version) in self.downloaded_dxvk_versions.iter().enumerate() {
if version.name == selected.name {
index = i;
break;
}
}
index as u32
}
else {
0
};
}
GeneralAppMsg::SelectWine(index) => {
if let Ok(mut config) = Config::get() {
if let Some((version, features)) = self.downloaded_wine_versions.get(index) {
if config.game.wine.selected.as_ref() != Some(&version.title) {
self.selecting_wine_version = true;
self.allow_dxvk_selection = features.need_dxvk;
let wine = version
.to_wine(&config.components.path, Some(&config.game.wine.builds.join(&version.name)))
.with_prefix(&config.game.wine.prefix)
.with_loader(WineLoader::Current)
.with_arch(WineArch::Win64);
let wine_name = version.name.to_string();
std::thread::spawn(move || {
match wine.update_prefix::<&str>(None) {
Ok(_) => {
config.game.wine.selected = Some(wine_name);
Config::update(config);
}
Err(err) => {
sender.input(GeneralAppMsg::Toast {
title: tr("wine-prefix-update-failed"),
description: Some(err.to_string())
});
}
}
sender.input(GeneralAppMsg::ResetWineSelection(index));
});
}
tracing::error!("Failed to run {:?} using wine: {err}", executable);
}
}
}
GeneralAppMsg::ResetWineSelection(index) => {
self.selecting_wine_version = false;
self.selected_wine_version = index as u32;
}
GeneralAppMsg::SelectDxvk(index) => {
if let Ok(config) = Config::get() {
if let Some(version) = self.downloaded_dxvk_versions.get(index) {
if let Ok(selected) = config.get_selected_dxvk() {
if selected.is_none() || selected.unwrap().name != version.name {
self.selecting_dxvk_version = true;
let mut wine = match config.get_selected_wine() {
Ok(Some(version)) => {
match version.to_wine(config.components.path, Some(config.game.wine.builds.join(&version.name))) {
WincompatlibWine::Default(wine) => wine,
WincompatlibWine::Proton(_) => return
}
}
_ => Wine::default()
};
wine = wine.with_prefix(config.game.wine.prefix);
let dxvk_folder = config.game.dxvk.builds.join(&version.name);
std::thread::spawn(move || {
if let Err(err) = Dxvk::install(&wine, dxvk_folder, InstallParams::default()) {
sender.input(GeneralAppMsg::Toast {
title: tr("dxvk-install-failed"),
description: Some(err.to_string())
});
}
sender.input(GeneralAppMsg::ResetDxvkSelection(index));
});
}
}
}
}
}
GeneralAppMsg::ResetDxvkSelection(index) => {
self.selecting_dxvk_version = false;
self.selected_dxvk_version = index as u32;
}
#[allow(unused_must_use)]
GeneralAppMsg::Toast { title, description } => {
sender.output(Self::Output::Toast { title, description });