mirror of
https://github.com/an-anime-team/sleepy-launcher.git
synced 2025-03-15 06:28:27 +03:00
feat(ui): added environment settings page
Added Environment settings page where you can specify command which will be used to run the game, and manage its environment variables
This commit is contained in:
parent
6f794947b9
commit
3b40ce75e6
9 changed files with 240 additions and 14 deletions
7
assets/locales/de/environment.ftl
Normal file
7
assets/locales/de/environment.ftl
Normal file
|
@ -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
|
7
assets/locales/en/environment.ftl
Normal file
7
assets/locales/en/environment.ftl
Normal file
|
@ -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
|
7
assets/locales/ru/environment.ftl
Normal file
7
assets/locales/ru/environment.ftl
Normal file
|
@ -0,0 +1,7 @@
|
|||
environment = Окружение
|
||||
game-command = Команда запуска
|
||||
game-command-description = Команда, используемая для запуска игры. Ключ %command% генерируется лаунчером автоматически. Например: gamemoderun '%command%'
|
||||
new-variable = Новая переменная
|
||||
name = Имя
|
||||
value = Значение
|
||||
add = Добавить
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -200,20 +200,18 @@ impl SimpleAsyncComponent for DefaultPathsApp {
|
|||
root: Self::Root,
|
||||
_sender: AsyncComponentSender<Self>,
|
||||
) -> AsyncComponentParts<Self> {
|
||||
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!();
|
||||
|
|
|
@ -411,8 +411,4 @@ impl SimpleAsyncComponent for EnhancementsApp {
|
|||
|
||||
AsyncComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender<Self>) {
|
||||
tracing::debug!("Called enhancements settings event: {:?}", msg);
|
||||
}
|
||||
}
|
||||
|
|
203
src/ui/preferences/environment.rs
Normal file
203
src/ui/preferences/environment.rs
Normal file
|
@ -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<Self::ParentInput> {
|
||||
Some(output)
|
||||
}
|
||||
|
||||
async fn init_model(
|
||||
init: Self::Init,
|
||||
_index: &DynamicIndex,
|
||||
_sender: AsyncFactorySender<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key: init.0,
|
||||
value: init.1
|
||||
}
|
||||
}
|
||||
|
||||
async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender<Self>) {
|
||||
sender.output(msg);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnvironmentApp {
|
||||
variables: AsyncFactoryVecDeque<Variable>,
|
||||
|
||||
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<Self>,
|
||||
) -> AsyncComponentParts<Self> {
|
||||
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<Self>) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,12 +12,14 @@ use crate::i18n::tr;
|
|||
|
||||
use super::general::*;
|
||||
use super::enhancements::*;
|
||||
use super::environment::*;
|
||||
|
||||
pub static mut PREFERENCES_WINDOW: Option<adw::PreferencesWindow> = None;
|
||||
|
||||
pub struct PreferencesApp {
|
||||
general: AsyncController<GeneralApp>,
|
||||
enhancements: AsyncController<EnhancementsApp>
|
||||
enhancements: AsyncController<EnhancementsApp>,
|
||||
environment: AsyncController<EnvironmentApp>
|
||||
}
|
||||
|
||||
#[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()
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod main;
|
||||
pub mod general;
|
||||
pub mod enhancements;
|
||||
pub mod environment;
|
||||
|
|
Loading…
Add table
Reference in a new issue