feat: added voice packages selection to settings

This commit is contained in:
Observer KRypt0n_ 2023-02-28 22:42:08 +02:00
parent 6cc5b26561
commit f5231fe637
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
6 changed files with 198 additions and 60 deletions

View file

@ -12,6 +12,7 @@ background-downloading-failed = Download des Hintergrundbildes fehlgeschlagen
config-update-error = Speichern der Konfiguration fehlgeschlagen
wine-prefix-update-failed = Aktualisierung des wine prefix fehlgeschlagen
dxvk-install-failed = DXVK konnte nicht installiert werden
voice-package-deletion-error = Failed to delete voice package
game-diff-finding-error = Spiel-Diff nicht gefunden
patch-info-fetching-error = Patch-Informationen konnten nicht abgerufen werden

View file

@ -12,6 +12,7 @@ background-downloading-failed = Failed to download background picture
config-update-error = Failed to save config
wine-prefix-update-failed = Failed to update wine prefix
dxvk-install-failed = Failed to install DXVK
voice-package-deletion-error = Failed to delete voice package
game-diff-finding-error = Failed to find game diff
patch-info-fetching-error = Failed to fetch patch info

View file

@ -12,6 +12,7 @@ background-downloading-failed = Не удалось загрузить фоно
config-update-error = Ошибка сохранения настроек
wine-prefix-update-failed = Ошибка обновления префикса Wine
dxvk-install-failed = Ошибка установки DXVK
voice-package-deletion-error = Не удалось удалить языковой пакет
game-diff-finding-error = Не удалось вычислить обновление игры
patch-info-fetching-error = Не удалось получить информацию о патче

View file

@ -637,6 +637,8 @@ impl SimpleComponent for App {
AppMsg::UpdateLauncherState { perform_on_download_needed, show_status_page } => {
if show_status_page {
sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("loading-launcher-state")))));
} else {
self.disabled_buttons = true;
}
let updater = clone!(@strong sender => move |state| {
@ -676,6 +678,8 @@ impl SimpleComponent for App {
if show_status_page {
sender.input(AppMsg::SetLoadingStatus(None));
} else {
self.disabled_buttons = false;
}
if perform_on_download_needed {

View file

@ -1,5 +1,10 @@
use relm4::prelude::*;
use relm4::component::*;
use relm4::factory::{
AsyncFactoryVecDeque,
AsyncFactoryComponent,
AsyncFactorySender
};
use gtk::prelude::*;
use adw::prelude::*;
@ -10,12 +15,92 @@ use anime_launcher_sdk::anime_game_core::prelude::*;
use anime_launcher_sdk::components::*;
use anime_launcher_sdk::wincompatlib::prelude::*;
use super::main::PreferencesAppMsg;
use crate::ui::components;
use crate::ui::components::*;
use crate::i18n::*;
use crate::*;
#[derive(Debug)]
struct VoicePackageComponent {
locale: VoiceLocale,
installed: bool,
sensitive: bool
}
#[relm4::factory(async)]
impl AsyncFactoryComponent for VoicePackageComponent {
type Init = (VoiceLocale, bool);
type Input = GeneralAppMsg;
type Output = GeneralAppMsg;
type CommandOutput = ();
type ParentInput = GeneralAppMsg;
type ParentWidget = adw::ExpanderRow;
view! {
root = adw::ActionRow {
set_title: &tr(&self.locale.to_name().to_ascii_lowercase()),
add_suffix = &gtk::Button {
#[watch]
set_visible: self.installed,
#[watch]
set_sensitive: self.sensitive,
set_icon_name: "user-trash-symbolic",
add_css_class: "flat",
set_valign: gtk::Align::Center,
connect_clicked[sender, index] => move |_| {
sender.input(GeneralAppMsg::RemoveVoicePackage(index.clone()));
}
},
add_suffix = &gtk::Button {
#[watch]
set_visible: !self.installed,
#[watch]
set_sensitive: self.sensitive,
set_icon_name: "document-save-symbolic",
add_css_class: "flat",
set_valign: gtk::Align::Center,
connect_clicked[sender, index] => move |_| {
sender.input(GeneralAppMsg::AddVoicePackage(index.clone()));
}
}
}
}
fn output_to_parent_input(output: Self::Output) -> Option<Self::ParentInput> {
Some(output)
}
async fn init_model(
init: Self::Init,
_index: &DynamicIndex,
_sender: AsyncFactorySender<Self>,
) -> Self {
Self {
locale: init.0,
installed: init.1,
sensitive: true
}
}
async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender<Self>) {
self.installed = !self.installed;
sender.output(msg);
}
}
pub struct GeneralApp {
voice_packages: AsyncFactoryVecDeque<VoicePackageComponent>,
wine_components: AsyncController<ComponentsList<GeneralAppMsg>>,
dxvk_components: AsyncController<ComponentsList<GeneralAppMsg>>,
@ -46,27 +131,38 @@ pub enum GeneralAppMsg {
/// was retrieved from remote repos
SetPatch(Option<Patch>),
// If one ever wich to change it to accept VoiceLocale
// I'd recommend to use clone!(@strong self.locale as locale => move |_| { .. })
// in the VoicePackage component
AddVoicePackage(DynamicIndex),
RemoveVoicePackage(DynamicIndex),
SetVoicePackageSensitivity(DynamicIndex, bool),
UpdateLauncherStyle(LauncherStyle),
WineRecommendedOnly(bool),
DxvkRecommendedOnly(bool),
UpdateDownloadedWine,
UpdateDownloadedDxvk,
SelectWine(usize),
SelectDxvk(usize),
ResetWineSelection(usize),
ResetDxvkSelection(usize),
Toast {
title: String,
description: Option<String>
},
UpdateLauncherStyle(LauncherStyle),
WineRecommendedOnly(bool),
DxvkRecommendedOnly(bool),
UpdateDownloadedWine,
UpdateDownloadedDxvk,
SelectWine(usize),
SelectDxvk(usize),
ResetWineSelection(usize),
ResetDxvkSelection(usize)
}
}
#[relm4::component(async, pub)]
impl SimpleAsyncComponent for GeneralApp {
type Init = ();
type Input = GeneralAppMsg;
type Output = super::main::PreferencesAppMsg;
type Output = PreferencesAppMsg;
view! {
adw::PreferencesPage {
@ -192,48 +288,9 @@ impl SimpleAsyncComponent for GeneralApp {
}
},
adw::ExpanderRow {
set_title: &tr("game-voiceovers"),
add_row = &adw::ActionRow {
set_title: &tr("english"),
add_suffix = &gtk::Button {
add_css_class: "flat",
set_icon_name: "user-trash-symbolic",
set_valign: gtk::Align::Center
}
},
add_row = &adw::ActionRow {
set_title: &tr("japanese"),
add_suffix = &gtk::Button {
add_css_class: "flat",
set_icon_name: "user-trash-symbolic",
set_valign: gtk::Align::Center
}
},
add_row = &adw::ActionRow {
set_title: &tr("korean"),
add_suffix = &gtk::Button {
add_css_class: "flat",
set_icon_name: "user-trash-symbolic",
set_valign: gtk::Align::Center
}
},
add_row = &adw::ActionRow {
set_title: &tr("chinese"),
add_suffix = &gtk::Button {
add_css_class: "flat",
set_icon_name: "user-trash-symbolic",
set_valign: gtk::Align::Center
}
}
#[local_ref]
voice_packages -> adw::ExpanderRow {
set_title: &tr("game-voiceovers")
},
gtk::Box {
@ -474,7 +531,9 @@ impl SimpleAsyncComponent for GeneralApp {
) -> AsyncComponentParts<Self> {
tracing::info!("Initializing general settings");
let model = Self {
let mut model = Self {
voice_packages: AsyncFactoryVecDeque::new(adw::ExpanderRow::new(), sender.input_sender()),
wine_components: ComponentsList::builder()
.launch(ComponentsListInit {
pattern: ComponentsListPattern {
@ -514,6 +573,15 @@ impl SimpleAsyncComponent for GeneralApp {
selecting_dxvk_version: false
};
for package in VoiceLocale::list() {
model.voice_packages.guard().push_back((
*package,
CONFIG.game.voices.iter().any(|voice| VoiceLocale::from_str(voice) == Some(*package))
));
}
let voice_packages = model.voice_packages.widget();
let widgets = view_output!();
AsyncComponentParts { model, widgets }
@ -531,6 +599,56 @@ impl SimpleAsyncComponent for GeneralApp {
self.patch = patch;
}
#[allow(unused_must_use)]
GeneralAppMsg::AddVoicePackage(index) => {
if let Some(package) = self.voice_packages.get(index.current_index()) {
if let Ok(mut config) = config::get() {
if !config.game.voices.iter().any(|voice| VoiceLocale::from_str(voice) == Some(package.locale)) {
config.game.voices.push(package.locale.to_code().to_string());
config::update(config);
sender.output(PreferencesAppMsg::UpdateLauncherState);
}
}
}
}
#[allow(unused_must_use)]
GeneralAppMsg::RemoveVoicePackage(index) => {
if let Some(package) = self.voice_packages.guard().get_mut(index.current_index()) {
if let Ok(mut config) = config::get() {
package.sensitive = false;
config.game.voices.retain(|voice| VoiceLocale::from_str(voice) != Some(package.locale));
config::update(config.clone());
let package = VoicePackage::with_locale(package.locale).unwrap();
std::thread::spawn(move || {
if let Err(err) = package.delete_in(&config.game.path) {
tracing::error!("Failed to delete voice package: {:?}", package.locale());
sender.input(GeneralAppMsg::Toast {
title: tr("voice-package-deletion-error"),
description: Some(err.to_string())
});
}
sender.input(GeneralAppMsg::SetVoicePackageSensitivity(index, true));
sender.output(PreferencesAppMsg::UpdateLauncherState);
});
}
}
}
GeneralAppMsg::SetVoicePackageSensitivity(index, sensitive) => {
if let Some(package) = self.voice_packages.guard().get_mut(index.current_index()) {
package.sensitive = sensitive;
}
}
#[allow(unused_must_use)]
GeneralAppMsg::UpdateLauncherStyle(style) => {
if style == LauncherStyle::Classic && !BACKGROUND_FILE.exists() {
@ -557,11 +675,6 @@ impl SimpleAsyncComponent for GeneralApp {
sender.output(Self::Output::SetLauncherStyle(style));
}
#[allow(unused_must_use)]
GeneralAppMsg::Toast { title, description } => {
sender.output(Self::Output::Toast { title, description });
}
GeneralAppMsg::WineRecommendedOnly(state) => {
// todo
self.wine_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();
@ -689,6 +802,11 @@ impl SimpleAsyncComponent for GeneralApp {
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 });
}
}
}
}

View file

@ -34,6 +34,8 @@ pub enum PreferencesAppMsg {
SetLauncherStyle(LauncherStyle),
UpdateLauncherState,
Toast {
title: String,
description: Option<String>
@ -53,6 +55,9 @@ impl SimpleAsyncComponent for PreferencesApp {
set_hide_on_close: true,
set_modal: true,
// FIXME: doesn't work for any reason
set_search_enabled: false,
add = model.general.widget(),
add = model.enhancements.widget(),
add = model.environment.widget(),
@ -126,6 +131,14 @@ impl SimpleAsyncComponent for PreferencesApp {
sender.output(Self::Output::SetLauncherStyle(style));
}
#[allow(unused_must_use)]
PreferencesAppMsg::UpdateLauncherState => {
sender.output(Self::Output::UpdateLauncherState {
perform_on_download_needed: false,
show_status_page: false
});
}
PreferencesAppMsg::Toast { title, description } => unsafe {
let toast = adw::Toast::new(&title);