mirror of
https://github.com/an-anime-team/sleepy-launcher.git
synced 2025-02-17 02:09:46 +03:00
commit
dceb4ea188
23 changed files with 361 additions and 130 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added arguments and symlinks editor to sandbox settings
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed game running issue if you have spaces in paths
|
||||
|
||||
## [3.5.1] (hotfix) - 16.04.2023
|
||||
|
||||
### Fixed
|
||||
|
|
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -40,8 +40,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anime-game-core"
|
||||
version = "1.7.0"
|
||||
source = "git+https://github.com/an-anime-team/anime-game-core?tag=1.7.0#290cc5ff9399f68799e8e00fd3f78fa26d076296"
|
||||
version = "1.7.1"
|
||||
source = "git+https://github.com/an-anime-team/anime-game-core?tag=1.7.1#8978728ce5d94aec5c6aecfe470e45fc43d5b522"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bzip2",
|
||||
|
@ -87,8 +87,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anime-launcher-sdk"
|
||||
version = "1.0.2"
|
||||
source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=1.0.2#979ca46f5b4ba8961b0effa4e35ba8f8069c9607"
|
||||
version = "1.0.6"
|
||||
source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=1.0.6#2a8272c1afdd006925c150ac480a82be645f4b52"
|
||||
dependencies = [
|
||||
"anime-game-core",
|
||||
"anyhow",
|
||||
|
|
|
@ -17,7 +17,7 @@ glib-build-tools = "0.17"
|
|||
|
||||
[dependencies.anime-launcher-sdk]
|
||||
git = "https://github.com/an-anime-team/anime-launcher-sdk"
|
||||
tag = "1.0.2"
|
||||
tag = "1.0.6"
|
||||
features = ["all", "genshin"]
|
||||
|
||||
# path = "../anime-launcher-sdk" # ! for dev purposes only
|
||||
|
@ -30,7 +30,7 @@ adw = { package = "libadwaita", version = "0.3", features = ["v1_2"] }
|
|||
rfd = { version = "0.11", features = ["xdg-portal"], default-features = false }
|
||||
open = "4.0"
|
||||
|
||||
serde_json = "1"
|
||||
serde_json = "1.0"
|
||||
anyhow = "1.0"
|
||||
lazy_static = "1.4.0"
|
||||
cached = { version = "0.43", features = ["proc_macro"] }
|
||||
|
|
|
@ -37,6 +37,11 @@ dxvk-unpack-error = Entpacken von DXVK fehlgeschlagen
|
|||
dxvk-apply-error = DXVK konnte nicht angewendet werden
|
||||
|
||||
downloaded-wine-list-failed = Auflistung der heruntergeladenen Wine versionen fehlgeschlagen
|
||||
|
||||
patch-sync-failed = Synchronisierung des Patch-Ordners fehlgeschlagen
|
||||
patch-state-check-failed = Status des Patchordners konnte nicht überprüft werden
|
||||
game-patching-error = Spiel konnte nicht gepatcht werden
|
||||
|
||||
# Sandbox
|
||||
|
||||
documentation-url-open-failed = Failed to open documentation URL
|
||||
|
|
|
@ -8,6 +8,7 @@ hide-home-directory = Hide home directory
|
|||
hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game
|
||||
|
||||
hostname = Hostname
|
||||
additional-arguments = Additional arguments
|
||||
|
||||
private-directories = Private directories
|
||||
private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game
|
||||
|
@ -22,3 +23,6 @@ new-path = New path
|
|||
|
||||
read-only = Read-only
|
||||
read-only-description = Forbid game to write any data to this directory
|
||||
|
||||
symlinks = Symlinks
|
||||
symlinks-description = Symlink original path to the new one inside of your sandbox
|
||||
|
|
|
@ -41,3 +41,7 @@ downloaded-wine-list-failed = Failed to list downloaded wine versions
|
|||
patch-sync-failed = Failed to sync patch folder
|
||||
patch-state-check-failed = Failed to check patch folder state
|
||||
game-patching-error = Failed to patch game
|
||||
|
||||
# Sandbox
|
||||
|
||||
documentation-url-open-failed = Failed to open documentation URL
|
||||
|
|
|
@ -8,6 +8,7 @@ hide-home-directory = Hide home directory
|
|||
hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game
|
||||
|
||||
hostname = Hostname
|
||||
additional-arguments = Additional arguments
|
||||
|
||||
private-directories = Private directories
|
||||
private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game
|
||||
|
@ -22,3 +23,6 @@ new-path = New path
|
|||
|
||||
read-only = Read-only
|
||||
read-only-description = Forbid game to write any data to this directory
|
||||
|
||||
symlinks = Symlinks
|
||||
symlinks-description = Symlink original path to the new one inside of your sandbox
|
||||
|
|
|
@ -41,3 +41,7 @@ downloaded-wine-list-failed = Fallo al listar versiones descargadas de Wine
|
|||
patch-sync-failed = Fallo al sincronizar carpeta del parche
|
||||
patch-state-check-failed = Fallo al comprobar estado de carpeta del parche
|
||||
game-patching-error = Fallo al parchear el juego
|
||||
|
||||
# Sandbox
|
||||
|
||||
documentation-url-open-failed = Failed to open documentation URL
|
||||
|
|
|
@ -69,7 +69,7 @@ registry-editor = Editor del Registro
|
|||
explorer = Explorador
|
||||
task-manager = Administrador de Tareas
|
||||
configuration = Configuración
|
||||
debugger = Debugger
|
||||
debugger = Depurador
|
||||
|
||||
dxvk-version = Versión de DXVK
|
||||
dxvk-selection-disabled = La selección de DXVK está deshabilitada por las preferencias de su grupo de vinos
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
sandbox = Sandbox
|
||||
sandbox-description = Run the game in isolated environment, preventing it from accessing your personal data
|
||||
sandbox = Aislamiento
|
||||
sandbox-description = Ejecutar el juego en un entorno aislado, previniendo el acceso a datos personales
|
||||
|
||||
enable-sandboxing = Enable sandboxing
|
||||
enable-sandboxing-description = Run the game in read-only copy of your root filesystem
|
||||
enable-sandboxing = Activar aislamiento
|
||||
enable-sandboxing-description = Ejecutar el juego en una copia de sólo lectura de tu sistema de archivos
|
||||
|
||||
hide-home-directory = Hide home directory
|
||||
hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game
|
||||
hide-home-directory = Esconder el directorio home
|
||||
hide-home-directory-description = Aisla las carpetas /home, /var/home/$USER, y $HOME del juego
|
||||
|
||||
hostname = Hostname
|
||||
hostname = Nombre del host
|
||||
additional-arguments = Additional arguments
|
||||
|
||||
private-directories = Private directories
|
||||
private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game
|
||||
private-directories = Directorios privados
|
||||
private-directories-description = Estas carpetas serán reemplazadas por un sistema de archivos virtual (tmpfs) vacío, y su contenido real no será accesible al juego aislado
|
||||
|
||||
path = Path
|
||||
path = Ruta
|
||||
|
||||
shared-directories = Shared directories
|
||||
shared-directories-description = These directories will be symlinked to directories in your host system
|
||||
shared-directories = Directorios compartidos
|
||||
shared-directories-description = Estos directorios serán enlazados a directorios de tu sistema anfitrión
|
||||
|
||||
original-path = Original path
|
||||
new-path = New path
|
||||
original-path = Ruta original
|
||||
new-path = Nueva ruta
|
||||
|
||||
read-only = Read-only
|
||||
read-only-description = Forbid game to write any data to this directory
|
||||
read-only = Sólo lectura
|
||||
read-only-description = Le prohibe al juego escribir datos en este directorio
|
||||
|
||||
symlinks = Symlinks
|
||||
symlinks-description = Symlink original path to the new one inside of your sandbox
|
||||
|
|
|
@ -41,3 +41,7 @@ downloaded-wine-list-failed = Impossible de lister les versions de wine téléch
|
|||
patch-sync-failed = Impossible de synchroniser le patch
|
||||
patch-state-check-failed = Impossible de déterminer l'état du patch
|
||||
game-patching-error = Le patch du jeu a échoué
|
||||
|
||||
# Sandbox
|
||||
|
||||
documentation-url-open-failed = Failed to open documentation URL
|
||||
|
|
|
@ -8,6 +8,7 @@ hide-home-directory = Hide home directory
|
|||
hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game
|
||||
|
||||
hostname = Hostname
|
||||
additional-arguments = Additional arguments
|
||||
|
||||
private-directories = Private directories
|
||||
private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game
|
||||
|
@ -22,3 +23,6 @@ new-path = New path
|
|||
|
||||
read-only = Read-only
|
||||
read-only-description = Forbid game to write any data to this directory
|
||||
|
||||
symlinks = Symlinks
|
||||
symlinks-description = Symlink original path to the new one inside of your sandbox
|
||||
|
|
|
@ -41,3 +41,7 @@ downloaded-wine-list-failed = Не удалось получить список
|
|||
patch-sync-failed = Ошибка синхронизации папки патча
|
||||
patch-state-check-failed = Ошибка проверки статуса папки патча
|
||||
game-patching-error = Не удалось установить патч игры
|
||||
|
||||
# Sandbox
|
||||
|
||||
documentation-url-open-failed = Не удалось открыть ссылку с документацией
|
||||
|
|
|
@ -8,6 +8,7 @@ hide-home-directory = Скрыть домашнюю директорию
|
|||
hide-home-directory-description = Изолировать ваши директории /home, /var/home/$USER, и $HOME от игры
|
||||
|
||||
hostname = Имя хоста
|
||||
additional-arguments = Дополнительные аргументы
|
||||
|
||||
private-directories = Приватные директории
|
||||
private-directories-description = Эти папки будут заменены пустой виртуальной файловой системой (tmpfs) и их изначальное содержимое не будет доступно игре
|
||||
|
@ -22,3 +23,6 @@ new-path = Путь в песочнице
|
|||
|
||||
read-only = Только для чтения
|
||||
read-only-description = Запретить игре изменять содержимое этой директории
|
||||
|
||||
symlinks = Ссылки
|
||||
symlinks-description = Добавить ссылку на оригинальный файл или папку в вашу песочницу
|
||||
|
|
|
@ -41,3 +41,7 @@ downloaded-wine-list-failed = İndirilmiş Wine versiyonlarını listeleme başa
|
|||
patch-sync-failed = Yama dosyalarını senkronize etme başarısız oldu
|
||||
patch-state-check-failed = Yama dosyalarının durumunu kontrol etme başarısız oldu
|
||||
game-patching-error = Yamayı uygulamada sorun çıktı
|
||||
|
||||
# Sandbox
|
||||
|
||||
documentation-url-open-failed = Failed to open documentation URL
|
||||
|
|
|
@ -8,6 +8,7 @@ hide-home-directory = Hide home directory
|
|||
hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game
|
||||
|
||||
hostname = Hostname
|
||||
additional-arguments = Additional arguments
|
||||
|
||||
private-directories = Private directories
|
||||
private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game
|
||||
|
@ -22,3 +23,6 @@ new-path = New path
|
|||
|
||||
read-only = Read-only
|
||||
read-only-description = Forbid game to write any data to this directory
|
||||
|
||||
symlinks = Symlinks
|
||||
symlinks-description = Symlink original path to the new one inside of your sandbox
|
||||
|
|
|
@ -41,3 +41,7 @@ downloaded-wine-list-failed = 列举 Wine 版本失败
|
|||
patch-sync-failed = 同步补丁文件夹失败
|
||||
patch-state-check-failed = 检查补丁文件夹失败
|
||||
game-patching-error = 应用游戏补丁失败
|
||||
|
||||
# Sandbox
|
||||
|
||||
documentation-url-open-failed = Failed to open documentation URL
|
||||
|
|
|
@ -8,6 +8,7 @@ hide-home-directory = 隐藏家目录
|
|||
hide-home-directory-description = 将 /home、 /var/home/$USER 和 $HOME 目录与游戏隔离
|
||||
|
||||
hostname = 主机名
|
||||
additional-arguments = Additional arguments
|
||||
|
||||
private-directories = 隐私目录
|
||||
private-directories-description = 这些目录将会被空的虚拟文件系统(tmpfs)替代,其中的原始内容不可被沙盒中的游戏访问
|
||||
|
@ -22,3 +23,6 @@ new-path = 新路径
|
|||
|
||||
read-only = 只读
|
||||
read-only-description = 禁止游戏向此目录写入任何数据
|
||||
|
||||
symlinks = Symlinks
|
||||
symlinks-description = Symlink original path to the new one inside of your sandbox
|
||||
|
|
74
fluentscan.py
Normal file
74
fluentscan.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# Author: @xstraok
|
||||
# Modified by Observer KRypt0n_ <https://github.com/krypt0nn>
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
path = "assets/locales/" + sys.argv[1] + "/"
|
||||
|
||||
for filename in os.listdir("assets/locales/en"):
|
||||
with open(os.path.join("assets/locales/en", filename), 'r') as locale_file:
|
||||
created_locale = open(path + filename)
|
||||
|
||||
def to_dict(text):
|
||||
result={}
|
||||
|
||||
for i in text:
|
||||
if " = " in i:
|
||||
try:
|
||||
result[i.split()[0]] = ' '.join(i.split()[2:])
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
elif i:
|
||||
result[list(result.keys())[-1]] += i
|
||||
|
||||
return result
|
||||
|
||||
def dict_compare(d1, d2):
|
||||
d1_keys = set(d1.keys())
|
||||
d2_keys = set(d2.keys())
|
||||
|
||||
shared_keys = d1_keys.intersection(d2_keys)
|
||||
|
||||
added = d1_keys - d2_keys
|
||||
removed = d2_keys - d1_keys
|
||||
|
||||
modified = {o : (d1[o], d2[o]) for o in shared_keys if d1[o] != d2[o]}
|
||||
|
||||
same = set(o for o in shared_keys if d1[o] == d2[o])
|
||||
|
||||
return added, removed, modified, same
|
||||
|
||||
expected=to_dict(locale_file)
|
||||
expected2=to_dict(created_locale)
|
||||
|
||||
# TODO: why modified is not used?
|
||||
added, removed, modified, same = dict_compare(expected, expected2)
|
||||
|
||||
if added or removed or same:
|
||||
print(f"[{created_locale.name[15:]}]")
|
||||
|
||||
if added:
|
||||
print(" [Added]")
|
||||
|
||||
for i in added:
|
||||
print(f" {i} = {expected[i]}")
|
||||
|
||||
if removed:
|
||||
print(" [Removed]")
|
||||
|
||||
for i in removed:
|
||||
print(f" {i} = {expected2[i]}")
|
||||
|
||||
#workaround
|
||||
if same and same != "set()":
|
||||
print(" [Untranslated]")
|
||||
|
||||
for i in same:
|
||||
print(f" {i} = {expected[i]}")
|
||||
|
||||
print("")
|
|
@ -82,10 +82,16 @@ impl SimpleComponent for AboutDialog {
|
|||
|
||||
set_release_notes_version: &APP_VERSION,
|
||||
set_release_notes: &[
|
||||
"<p>Added</p>",
|
||||
|
||||
"<ul>",
|
||||
"<li>Added arguments and symlinks editor to sandbox settings</li>",
|
||||
"</ul>",
|
||||
|
||||
"<p>Fixed</p>",
|
||||
|
||||
"<ul>",
|
||||
"<li>Fixed telemetry checking</li>",
|
||||
"<li>Fixed game running issue if you have spaces in paths</li>",
|
||||
"</ul>",
|
||||
].join("\n"),
|
||||
|
||||
|
|
|
@ -172,6 +172,9 @@ impl SimpleAsyncComponent for EnvironmentApp {
|
|||
let value = self.value_entry.text().trim().to_string();
|
||||
|
||||
if !name.is_empty() && !value.is_empty() {
|
||||
self.name_entry.set_text("");
|
||||
self.value_entry.set_text("");
|
||||
|
||||
config.game.environment.insert(name.clone(), value.clone());
|
||||
|
||||
Config::update(config);
|
||||
|
|
|
@ -103,7 +103,7 @@ impl SimpleAsyncComponent for PreferencesApp {
|
|||
|
||||
sandbox: SandboxApp::builder()
|
||||
.launch(())
|
||||
.detach(),
|
||||
.forward(sender.input_sender(), std::convert::identity),
|
||||
|
||||
environment: EnvironmentApp::builder()
|
||||
.launch(())
|
||||
|
|
|
@ -6,119 +6,86 @@ use adw::prelude::*;
|
|||
|
||||
use anime_launcher_sdk::is_available;
|
||||
|
||||
use super::main::PreferencesAppMsg;
|
||||
use crate::i18n::tr;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PrivateDirectory {
|
||||
path: String
|
||||
}
|
||||
macro_rules! impl_directory {
|
||||
($name:ident, $msg:expr) => {
|
||||
#[derive(Debug)]
|
||||
struct $name {
|
||||
from: String,
|
||||
to: Option<String>
|
||||
}
|
||||
|
||||
#[relm4::factory(async)]
|
||||
impl AsyncFactoryComponent for PrivateDirectory {
|
||||
type Init = String;
|
||||
type Input = SandboxAppMsg;
|
||||
type Output = SandboxAppMsg;
|
||||
type CommandOutput = ();
|
||||
type ParentInput = SandboxAppMsg;
|
||||
type ParentWidget = adw::PreferencesGroup;
|
||||
#[relm4::factory(async)]
|
||||
impl AsyncFactoryComponent for $name {
|
||||
type Init = (String, Option<String>);
|
||||
type Input = SandboxAppMsg;
|
||||
type Output = SandboxAppMsg;
|
||||
type CommandOutput = ();
|
||||
type ParentInput = SandboxAppMsg;
|
||||
type ParentWidget = adw::PreferencesGroup;
|
||||
|
||||
view! {
|
||||
root = adw::ActionRow {
|
||||
set_title: &self.path,
|
||||
view! {
|
||||
root = adw::ActionRow {
|
||||
set_title: &self.from,
|
||||
set_subtitle: match self.to.as_ref() {
|
||||
Some(to) => to,
|
||||
None => ""
|
||||
},
|
||||
|
||||
add_suffix = >k::Button {
|
||||
set_icon_name: "user-trash-symbolic",
|
||||
add_css_class: "flat",
|
||||
set_valign: gtk::Align::Center,
|
||||
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(SandboxAppMsg::RemovePrivate(index.clone()));
|
||||
connect_clicked[sender, index] => move |_| {
|
||||
sender.input($msg(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 {
|
||||
from: init.0,
|
||||
to: init.1
|
||||
}
|
||||
}
|
||||
|
||||
async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender<Self>) {
|
||||
sender.output(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
path: init
|
||||
}
|
||||
}
|
||||
|
||||
async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender<Self>) {
|
||||
sender.output(msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SharedDirectory {
|
||||
mount_from: String,
|
||||
mount_to: String
|
||||
}
|
||||
|
||||
#[relm4::factory(async)]
|
||||
impl AsyncFactoryComponent for SharedDirectory {
|
||||
type Init = (String, String);
|
||||
type Input = SandboxAppMsg;
|
||||
type Output = SandboxAppMsg;
|
||||
type CommandOutput = ();
|
||||
type ParentInput = SandboxAppMsg;
|
||||
type ParentWidget = adw::PreferencesGroup;
|
||||
|
||||
view! {
|
||||
root = adw::ActionRow {
|
||||
set_title: &self.mount_to,
|
||||
set_subtitle: &self.mount_from,
|
||||
|
||||
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(SandboxAppMsg::RemoveShared(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 {
|
||||
mount_from: init.0,
|
||||
mount_to: init.1
|
||||
}
|
||||
}
|
||||
|
||||
async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender<Self>) {
|
||||
sender.output(msg);
|
||||
}
|
||||
}
|
||||
impl_directory!(PrivateDirectory, SandboxAppMsg::RemovePrivate);
|
||||
impl_directory!(SharedDirectory, SandboxAppMsg::RemoveShared);
|
||||
impl_directory!(SymlinkPath, SandboxAppMsg::RemoveSymlink);
|
||||
|
||||
pub struct SandboxApp {
|
||||
private_paths: AsyncFactoryVecDeque<PrivateDirectory>,
|
||||
shared_paths: AsyncFactoryVecDeque<SharedDirectory>,
|
||||
symlink_paths: AsyncFactoryVecDeque<SymlinkPath>,
|
||||
|
||||
private_path_entry: adw::EntryRow,
|
||||
|
||||
shared_path_from_entry: adw::EntryRow,
|
||||
shared_path_to_entry: adw::EntryRow,
|
||||
read_only_switch: gtk::Switch
|
||||
read_only_switch: gtk::Switch,
|
||||
|
||||
symlink_path_from_entry: adw::EntryRow,
|
||||
symlink_path_to_entry: adw::EntryRow
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -127,14 +94,17 @@ pub enum SandboxAppMsg {
|
|||
RemovePrivate(DynamicIndex),
|
||||
|
||||
AddShared,
|
||||
RemoveShared(DynamicIndex)
|
||||
RemoveShared(DynamicIndex),
|
||||
|
||||
AddSymlink,
|
||||
RemoveSymlink(DynamicIndex)
|
||||
}
|
||||
|
||||
#[relm4::component(async, pub)]
|
||||
impl SimpleAsyncComponent for SandboxApp {
|
||||
type Init = ();
|
||||
type Input = SandboxAppMsg;
|
||||
type Output = ();
|
||||
type Output = PreferencesAppMsg;
|
||||
|
||||
view! {
|
||||
adw::PreferencesPage {
|
||||
|
@ -206,6 +176,39 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
Config::update(config);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
adw::EntryRow {
|
||||
set_title: &tr("additional-arguments"),
|
||||
set_text: CONFIG.sandbox.args.as_ref().unwrap_or(&String::new()).trim(),
|
||||
|
||||
connect_changed => |entry| {
|
||||
if let Ok(mut config) = Config::get() {
|
||||
let command = entry.text().trim().to_string();
|
||||
|
||||
config.sandbox.args = if command.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(command)
|
||||
};
|
||||
|
||||
Config::update(config);
|
||||
}
|
||||
},
|
||||
|
||||
add_suffix = >k::Button {
|
||||
set_valign: gtk::Align::Center,
|
||||
set_icon_name: "dialog-information-symbolic",
|
||||
|
||||
connect_clicked[sender] => move |_| {
|
||||
if let Err(err) = open::that("https://man.archlinux.org/man/bwrap.1") {
|
||||
sender.output(PreferencesAppMsg::Toast {
|
||||
title: tr("documentation-url-open-failed"),
|
||||
description: Some(err.to_string())
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -268,7 +271,35 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
},
|
||||
|
||||
#[local_ref]
|
||||
add = shared_paths -> adw::PreferencesGroup {}
|
||||
add = shared_paths -> adw::PreferencesGroup {},
|
||||
|
||||
add = &adw::PreferencesGroup {
|
||||
set_title: &tr("symlinks"),
|
||||
set_description: Some(&tr("symlinks-description")),
|
||||
|
||||
#[local_ref]
|
||||
symlink_path_from_entry -> adw::EntryRow {
|
||||
set_title: &tr("original-path")
|
||||
},
|
||||
|
||||
#[local_ref]
|
||||
symlink_path_to_entry -> adw::EntryRow {
|
||||
set_title: &tr("new-path")
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &tr("add"),
|
||||
add_css_class: "pill",
|
||||
|
||||
set_margin_top: 8,
|
||||
set_halign: gtk::Align::Start,
|
||||
|
||||
connect_clicked => SandboxAppMsg::AddSymlink
|
||||
}
|
||||
},
|
||||
|
||||
#[local_ref]
|
||||
add = symlink_paths -> adw::PreferencesGroup {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,39 +308,51 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
root: Self::Root,
|
||||
sender: AsyncComponentSender<Self>,
|
||||
) -> AsyncComponentParts<Self> {
|
||||
tracing::info!("Initializing environment settings");
|
||||
tracing::info!("Initializing sandbox settings");
|
||||
|
||||
let mut model = Self {
|
||||
private_paths: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()),
|
||||
shared_paths: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()),
|
||||
symlink_paths: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()),
|
||||
|
||||
private_path_entry: adw::EntryRow::new(),
|
||||
|
||||
shared_path_from_entry: adw::EntryRow::new(),
|
||||
shared_path_to_entry: adw::EntryRow::new(),
|
||||
read_only_switch: gtk::Switch::new()
|
||||
read_only_switch: gtk::Switch::new(),
|
||||
|
||||
symlink_path_from_entry: adw::EntryRow::new(),
|
||||
symlink_path_to_entry: adw::EntryRow::new()
|
||||
};
|
||||
|
||||
for path in &CONFIG.sandbox.private {
|
||||
model.private_paths.guard().push_back(path.trim().to_string());
|
||||
model.private_paths.guard().push_back((path.trim().to_string(), None));
|
||||
}
|
||||
|
||||
for (from, to) in &CONFIG.sandbox.mounts.read_only {
|
||||
model.shared_paths.guard().push_back((
|
||||
from.trim().to_string(),
|
||||
format!("[read-only] {}", to.trim())
|
||||
Some(format!("[read-only] {}", to.trim()))
|
||||
));
|
||||
}
|
||||
|
||||
for (from, to) in &CONFIG.sandbox.mounts.bind {
|
||||
model.shared_paths.guard().push_back((
|
||||
from.trim().to_string(),
|
||||
to.trim().to_string()
|
||||
Some(to.trim().to_string())
|
||||
));
|
||||
}
|
||||
|
||||
for (from, to) in &CONFIG.sandbox.mounts.symlinks {
|
||||
model.symlink_paths.guard().push_back((
|
||||
from.trim().to_string(),
|
||||
Some(to.trim().to_string())
|
||||
));
|
||||
}
|
||||
|
||||
let private_paths = model.private_paths.widget();
|
||||
let shared_paths = model.shared_paths.widget();
|
||||
let symlink_paths = model.symlink_paths.widget();
|
||||
|
||||
let private_path_entry = &model.private_path_entry;
|
||||
|
||||
|
@ -317,6 +360,9 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
let shared_path_to_entry = &model.shared_path_to_entry;
|
||||
let read_only_switch = &model.read_only_switch;
|
||||
|
||||
let symlink_path_from_entry = &model.symlink_path_from_entry;
|
||||
let symlink_path_to_entry = &model.symlink_path_to_entry;
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
AsyncComponentParts { model, widgets }
|
||||
|
@ -329,11 +375,13 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
let path = self.private_path_entry.text().trim().to_string();
|
||||
|
||||
if !path.is_empty() {
|
||||
self.private_path_entry.set_text("");
|
||||
|
||||
config.sandbox.private.push(path.clone());
|
||||
|
||||
Config::update(config);
|
||||
|
||||
self.private_paths.guard().push_back(path);
|
||||
self.private_paths.guard().push_back((path, None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,7 +389,7 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
SandboxAppMsg::RemovePrivate(index) => {
|
||||
if let Ok(mut config) = Config::get() {
|
||||
if let Some(var) = self.private_paths.guard().get(index.current_index()) {
|
||||
config.sandbox.private.retain(|item| item != &var.path);
|
||||
config.sandbox.private.retain(|item| item != &var.from);
|
||||
|
||||
Config::update(config);
|
||||
}
|
||||
|
@ -358,6 +406,9 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
let read_only = self.read_only_switch.state();
|
||||
|
||||
if !from.is_empty() && !to.is_empty() {
|
||||
self.shared_path_from_entry.set_text("");
|
||||
self.shared_path_to_entry.set_text("");
|
||||
|
||||
if read_only {
|
||||
config.sandbox.mounts.read_only.insert(from.clone(), to.clone());
|
||||
} else {
|
||||
|
@ -368,11 +419,11 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
|
||||
self.shared_paths.guard().push_back((
|
||||
from,
|
||||
if read_only {
|
||||
Some(if read_only {
|
||||
format!("[read-only] {}", to)
|
||||
} else {
|
||||
to
|
||||
}
|
||||
})
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -381,14 +432,44 @@ impl SimpleAsyncComponent for SandboxApp {
|
|||
SandboxAppMsg::RemoveShared(index) => {
|
||||
if let Ok(mut config) = Config::get() {
|
||||
if let Some(var) = self.shared_paths.guard().get(index.current_index()) {
|
||||
config.sandbox.mounts.read_only.remove(&var.mount_from);
|
||||
config.sandbox.mounts.bind.remove(&var.mount_from);
|
||||
config.sandbox.mounts.read_only.remove(&var.from);
|
||||
config.sandbox.mounts.bind.remove(&var.from);
|
||||
|
||||
Config::update(config);
|
||||
}
|
||||
|
||||
self.shared_paths.guard().remove(index.current_index());
|
||||
}
|
||||
},
|
||||
|
||||
SandboxAppMsg::AddSymlink => {
|
||||
if let Ok(mut config) = Config::get() {
|
||||
let from = self.symlink_path_from_entry.text().trim().to_string();
|
||||
let to = self.symlink_path_to_entry.text().trim().to_string();
|
||||
|
||||
if !from.is_empty() && !to.is_empty() {
|
||||
self.symlink_path_from_entry.set_text("");
|
||||
self.symlink_path_to_entry.set_text("");
|
||||
|
||||
config.sandbox.mounts.symlinks.insert(from.clone(), to.clone());
|
||||
|
||||
Config::update(config);
|
||||
|
||||
self.symlink_paths.guard().push_back((from, Some(to)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SandboxAppMsg::RemoveSymlink(index) => {
|
||||
if let Ok(mut config) = Config::get() {
|
||||
if let Some(var) = self.symlink_paths.guard().get(index.current_index()) {
|
||||
config.sandbox.mounts.symlinks.remove(&var.from);
|
||||
|
||||
Config::update(config);
|
||||
}
|
||||
|
||||
self.symlink_paths.guard().remove(index.current_index());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue