diff --git a/assets/locales/de/environment.ftl b/assets/locales/de/environment.ftl new file mode 100644 index 0000000..6545a14 --- /dev/null +++ b/assets/locales/de/environment.ftl @@ -0,0 +1,7 @@ +environment = Environment +game-command = Game command +game-command-description = Command used to launch the game. Placeholder %command% is generated automatically by the launcher. For example: gamemoderun '%command%' +new-variable = New variable +name = Name +value = Value +add = Add diff --git a/assets/locales/en/environment.ftl b/assets/locales/en/environment.ftl new file mode 100644 index 0000000..6545a14 --- /dev/null +++ b/assets/locales/en/environment.ftl @@ -0,0 +1,7 @@ +environment = Environment +game-command = Game command +game-command-description = Command used to launch the game. Placeholder %command% is generated automatically by the launcher. For example: gamemoderun '%command%' +new-variable = New variable +name = Name +value = Value +add = Add diff --git a/assets/locales/ru/environment.ftl b/assets/locales/ru/environment.ftl new file mode 100644 index 0000000..d2e4973 --- /dev/null +++ b/assets/locales/ru/environment.ftl @@ -0,0 +1,7 @@ +environment = Окружение +game-command = Команда запуска +game-command-description = Команда, используемая для запуска игры. Ключ %command% генерируется лаунчером автоматически. Например: gamemoderun '%command%' +new-variable = Новая переменная +name = Имя +value = Значение +add = Добавить diff --git a/src/main.rs b/src/main.rs index 82d78ca..1c9b36f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -151,7 +151,7 @@ fn main() { ", BACKGROUND_FILE.to_string_lossy())); // Set UI language - let lang = config::get().unwrap().launcher.language.parse().expect("Wrong language format used in config"); + let lang = CONFIG.launcher.language.parse().expect("Wrong language format used in config"); i18n::set_lang(lang).expect("Failed to set launcher language"); diff --git a/src/ui/first_run/default_paths.rs b/src/ui/first_run/default_paths.rs index 8cacacb..1cefbda 100644 --- a/src/ui/first_run/default_paths.rs +++ b/src/ui/first_run/default_paths.rs @@ -200,20 +200,18 @@ impl SimpleAsyncComponent for DefaultPathsApp { root: Self::Root, _sender: AsyncComponentSender, ) -> AsyncComponentParts { - let config = config::get().unwrap_or_default(); - let model = Self { show_additional: false, launcher: LAUNCHER_FOLDER.to_path_buf(), - runners: config.game.wine.builds, - dxvks: config.game.dxvk.builds, - prefix: config.game.wine.prefix, - game: config.game.path, - patch: config.patch.path, + runners: CONFIG.game.wine.builds.clone(), + dxvks: CONFIG.game.dxvk.builds.clone(), + prefix: CONFIG.game.wine.prefix.clone(), + game: CONFIG.game.path.clone(), + patch: CONFIG.patch.path.clone(), #[allow(clippy::or_fun_call)] - temp: config.launcher.temp.unwrap_or(PathBuf::from("/tmp")) + temp: CONFIG.launcher.temp.clone().unwrap_or(PathBuf::from("/tmp")) }; let widgets = view_output!(); diff --git a/src/ui/preferences/enhancements.rs b/src/ui/preferences/enhancements.rs index 98d79ff..1e7cbd6 100644 --- a/src/ui/preferences/enhancements.rs +++ b/src/ui/preferences/enhancements.rs @@ -411,8 +411,4 @@ impl SimpleAsyncComponent for EnhancementsApp { AsyncComponentParts { model, widgets } } - - async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender) { - tracing::debug!("Called enhancements settings event: {:?}", msg); - } } diff --git a/src/ui/preferences/environment.rs b/src/ui/preferences/environment.rs new file mode 100644 index 0000000..90681eb --- /dev/null +++ b/src/ui/preferences/environment.rs @@ -0,0 +1,203 @@ +use relm4::factory::AsyncFactoryVecDeque; +use relm4::prelude::*; +use relm4::component::*; +use relm4::factory::*; + +use adw::prelude::*; + +use anime_launcher_sdk::config; + +use crate::i18n::tr; +use crate::*; + +#[derive(Debug)] +struct Variable { + key: String, + value: String +} + +#[relm4::factory(async)] +impl AsyncFactoryComponent for Variable { + type Init = (String, String); + type Input = EnvironmentMsg; + type Output = EnvironmentMsg; + type CommandOutput = (); + type ParentInput = EnvironmentMsg; + type ParentWidget = adw::PreferencesGroup; + + view! { + root = adw::ActionRow { + set_title: &self.key, + set_subtitle: &self.value, + + add_suffix = >k::Button { + set_icon_name: "user-trash-symbolic", + add_css_class: "flat", + set_valign: gtk::Align::Center, + + connect_clicked[sender, index] => move |_| { + sender.input(EnvironmentMsg::Remove(index.clone())); + } + } + } + } + + fn output_to_parent_input(output: Self::Output) -> Option { + Some(output) + } + + async fn init_model( + init: Self::Init, + _index: &DynamicIndex, + _sender: AsyncFactorySender, + ) -> Self { + Self { + key: init.0, + value: init.1 + } + } + + async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender) { + sender.output(msg); + } +} + +pub struct EnvironmentApp { + variables: AsyncFactoryVecDeque, + + name: gtk::Entry, + value: gtk::Entry +} + +#[derive(Debug, Clone)] +pub enum EnvironmentMsg { + Add, + Remove(DynamicIndex) +} + +#[relm4::component(async, pub)] +impl SimpleAsyncComponent for EnvironmentApp { + type Init = (); + type Input = EnvironmentMsg; + type Output = (); + + view! { + adw::PreferencesPage { + set_title: &tr("environment"), + set_icon_name: Some("document-properties"), + + add = &adw::PreferencesGroup { + set_title: &tr("game-command"), + set_description: Some(&tr("game-command-description")), + + adw::EntryRow { + set_title: "%command%", + set_text: CONFIG.game.command.as_ref().unwrap_or(&String::new()), + + connect_changed => |entry| { + if let Ok(mut config) = config::get() { + let command = entry.text().to_string(); + + config.game.command = if command.is_empty() { + None + } else { + Some(command) + }; + + config::update(config); + } + } + } + }, + + add = &adw::PreferencesGroup { + set_title: &tr("new-variable"), + + gtk::Box { + set_orientation: gtk::Orientation::Horizontal, + set_spacing: 8, + + #[local_ref] + name -> gtk::Entry { + set_placeholder_text: Some(&tr("name")) + }, + + #[local_ref] + value -> gtk::Entry { + set_placeholder_text: Some(&tr("value")), + set_hexpand: true + } + }, + + gtk::Button { + set_label: &tr("add"), + + set_margin_top: 8, + set_halign: gtk::Align::Start, + + connect_clicked => EnvironmentMsg::Add + } + }, + + #[local_ref] + add = variables -> adw::PreferencesGroup {} + } + } + + async fn init( + _init: Self::Init, + root: Self::Root, + sender: AsyncComponentSender, + ) -> AsyncComponentParts { + tracing::info!("Initializing environment settings"); + + let mut model = Self { + variables: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()), + + name: gtk::Entry::new(), + value: gtk::Entry::new() + }; + + for (name, value) in &CONFIG.game.environment { + model.variables.guard().push_back((name.clone(), value.clone())); + } + + let variables = model.variables.widget(); + + let name = &model.name; + let value = &model.value; + + let widgets = view_output!(); + + AsyncComponentParts { model, widgets } + } + + async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender) { + match msg { + EnvironmentMsg::Add => { + if let Ok(mut config) = config::get() { + let name = self.name.text().to_string(); + let value = self.value.text().to_string(); + + config.game.environment.insert(name.clone(), value.clone()); + + config::update(config); + + self.variables.guard().push_back((name, value)); + } + } + + EnvironmentMsg::Remove(index) => { + if let Ok(mut config) = config::get() { + if let Some(var) = self.variables.guard().get(index.current_index()) { + config.game.environment.remove(&var.key); + + config::update(config); + } + + self.variables.guard().remove(index.current_index()); + } + } + } + } +} diff --git a/src/ui/preferences/main.rs b/src/ui/preferences/main.rs index e39028a..5128d9a 100644 --- a/src/ui/preferences/main.rs +++ b/src/ui/preferences/main.rs @@ -12,12 +12,14 @@ use crate::i18n::tr; use super::general::*; use super::enhancements::*; +use super::environment::*; pub static mut PREFERENCES_WINDOW: Option = None; pub struct PreferencesApp { general: AsyncController, - enhancements: AsyncController + enhancements: AsyncController, + environment: AsyncController } #[derive(Debug, Clone)] @@ -53,6 +55,7 @@ impl SimpleAsyncComponent for PreferencesApp { add = model.general.widget(), add = model.enhancements.widget(), + add = model.environment.widget(), connect_close_request[sender] => move |_| { if let Err(err) = anime_launcher_sdk::config::flush() { @@ -80,6 +83,10 @@ impl SimpleAsyncComponent for PreferencesApp { .forward(sender.input_sender(), std::convert::identity), enhancements: EnhancementsApp::builder() + .launch(()) + .detach(), + + environment: EnvironmentApp::builder() .launch(()) .detach() }; diff --git a/src/ui/preferences/mod.rs b/src/ui/preferences/mod.rs index ebc7185..4bc8d46 100644 --- a/src/ui/preferences/mod.rs +++ b/src/ui/preferences/mod.rs @@ -1,3 +1,4 @@ pub mod main; pub mod general; pub mod enhancements; +pub mod environment;