settings gui PoC

This commit is contained in:
Aleksander
2025-11-05 22:37:07 +01:00
parent 33955498cc
commit e087eb3743
18 changed files with 285 additions and 29 deletions

14
Cargo.lock generated
View File

@@ -1429,6 +1429,8 @@ dependencies = [
"gtk",
"log",
"rust-embed",
"serde",
"serde_json",
"wgui",
]
@@ -4790,9 +4792,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.225"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
@@ -4812,18 +4814,18 @@ dependencies = [
[[package]]
name = "serde_core"
version = "1.0.225"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.225"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -12,3 +12,5 @@ rust-embed = { workspace = true }
chrono = "0.4.42"
gio = "0.21.2"
gtk = "0.18.2"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"

View File

@@ -79,9 +79,9 @@
gradient="radial" color="#44BBFF22" color2="#00000000" />
<div
id="content"
flex_direction="column"
overflow_x="scroll"
overflow_y="scroll"
flex_direction="column"
padding_top="8"
padding_bottom="8"
padding_left="16"

View File

@@ -0,0 +1,24 @@
<layout>
<include src="theme.xml" />
<macro name="group_box"
min_width="200"
flex_grow="1"
flex_direction="column"
align_items="baseline"
border="2"
color="#00000055"
border_color="#FFFFFF66"
padding="12"
gap="8"
round="8" />
<!-- src, text, translation -->
<template name="GroupBoxTitle">
<div flex_direction="row" align_items="center" gap="8">
<sprite src="${src}" width="24" height="24" />
<label text="${text}" translation="${translation}" weight="bold" size="18" />
</div>
<rectangle color="#FFFFFF44" width="100%" height="2" />
</template>
</layout>

View File

@@ -1,7 +1,61 @@
<layout>
<include src="t_tab_title.xml" />
<include src="../t_group_box.xml" />
<elements>
<TabTitle translation="SETTINGS" icon="dashboard/settings.svg" />
<div flex_wrap="wrap" justify_content="stretch" gap="4">
<!-- Home screen -->
<rectangle macro="group_box">
<GroupBoxTitle translation="HOME_SCREEN" src="dashboard/wayvr_dashboard.svg" />
<CheckBox translation="APP_SETTINGS.HIDE_USERNAME" />
</rectangle>
<!-- General settings -->
<rectangle macro="group_box">
<GroupBoxTitle translation="GENERAL_SETTINGS" src="dashboard/settings.svg" />
<CheckBox translation="APP_SETTINGS.12_HOUR_CLOCK" />
<CheckBox translation="APP_SETTINGS.OPAQUE_BACKGROUND" />
</rectangle>
<!-- Application launcher -->
<rectangle macro="group_box">
<GroupBoxTitle translation="APPLICATION_LAUNCHER" src="dashboard/apps.svg" />
<CheckBox translation="APP_SETTINGS.RUN_IN_XWAYLAND_MODE_BY_DEFAULT" />
</rectangle>
<!-- headset settings -->
<rectangle macro="group_box">
<GroupBoxTitle translation="APP_SETTINGS.HEADSET_SETTINGS" src="dashboard/vr.svg" />
<label translation="APP_SETTINGS.BRIGHTNESS" />
<Slider width="100" height="24" min_value="0.0" max_value="100.0" />
</rectangle>
<!-- wlx-overlay-s settings -->
<rectangle macro="group_box">
<GroupBoxTitle translation="APP_SETTINGS.WLX_OVERLAY_S_SETTINGS" src="dashboard/vr.svg" />
<CheckBox translation="APP_SETTINGS.WLX.NOTIFICATIONS_ENABLED" />
<CheckBox translation="APP_SETTINGS.WLX.NOTIFICATIONS_SOUND_ENABLED" />
<CheckBox translation="APP_SETTINGS.WLX.KEYBOARD_SOUND_ENABLED" />
<CheckBox translation="APP_SETTINGS.WLX.BLOCK_GAME_INPUT" />
<label translation="APP_SETTINGS.WLX.SPACE_DRAG_MULTIPLIER" />
<Slider width="100" height="24" min_value="0.0" max_value="3.0" />
<CheckBox translation="APP_SETTINGS.WLX.SPACE_DRAG_ROTATION_ENABLED" />
<CheckBox translation="APP_SETTINGS.WLX.SHOW_SKYBOX" />
<CheckBox translation="APP_SETTINGS.WLX.ENABLE_PASSTHROUGH" />
</rectangle>
</div>
<div>
<!-- TODO: icon support in buttons -->
<Button color="#AA3333" height="32">
<div margin_left="8" margin_right="8" gap="4" align_items="center">
<sprite src="dashboard/refresh.svg" width="24" height="24" />
<label weight="bold" translation="APP_SETTINGS.RESTART_SOFTWARE" />
</div>
</Button>
</div>
</elements>
</layout>

View File

@@ -5,5 +5,27 @@
"GAMES": "Spiele",
"SETTINGS": "Einstellungen",
"PROCESSES": "Prozesse",
"HELLO_USER": "Hallo, {USER}!"
"HELLO_USER": "Hallo, {USER}!",
"GENERAL_SETTINGS": "Allgemeine Einstellungen",
"APPLICATION_LAUNCHER": "Anwendung Launcher",
"APP_SETTINGS": {
"HIDE_USERNAME": "Benutzernamen ausblenden",
"12_HOUR_CLOCK": "12-Stunden-Uhr",
"OPAQUE_BACKGROUND": "Undurchsichtiger Hintergrund",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Standardmäßig in XWayland-Modus ausführen",
"WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S Einstellungen",
"HEADSET_SETTINGS": "Headset-Einstellungen",
"BRIGHTNESS": "Helligkeit",
"WLX": {
"NOTIFICATIONS_ENABLED": "Benachrichtigungen aktiviert",
"NOTIFICATIONS_SOUND_ENABLED": "Benachrichtigungssound aktiviert",
"KEYBOARD_SOUND_ENABLED": "Tastaturgeräusch aktiviert",
"BLOCK_GAME_INPUT": "Spielsteuerung blockieren",
"SPACE_DRAG_MULTIPLIER": "Raum-Drag-Multiplikator",
"SPACE_DRAG_ROTATION_ENABLED": "Rotation im Space-Drag aktivieren",
"SHOW_SKYBOX": "Skybox anzeigen",
"ENABLE_PASSTHROUGH": "Passthrough aktivieren"
},
"RESTART_SOFTWARE": "Software neu starten"
}
}

View File

@@ -5,5 +5,27 @@
"GAMES": "Games",
"SETTINGS": "Settings",
"PROCESSES": "Processes",
"HELLO_USER": "Hello, {USER}!"
"HELLO_USER": "Hello, {USER}!",
"GENERAL_SETTINGS": "General settings",
"APPLICATION_LAUNCHER": "Application launcher",
"APP_SETTINGS": {
"RESTART_SOFTWARE": "Restart software",
"HIDE_USERNAME": "Hide username",
"12_HOUR_CLOCK": "12-hour clock",
"OPAQUE_BACKGROUND": "Opaque background",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Run in XWayland mode by default",
"WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S settings",
"HEADSET_SETTINGS": "Headset settings",
"BRIGHTNESS": "Brightness",
"WLX": {
"NOTIFICATIONS_ENABLED": "Notifications enabled",
"NOTIFICATIONS_SOUND_ENABLED": "Notifications sound enabled",
"KEYBOARD_SOUND_ENABLED": "Keyboard sound enabled",
"BLOCK_GAME_INPUT": "Block game input",
"SPACE_DRAG_MULTIPLIER": "Space-drag multiplier",
"SPACE_DRAG_ROTATION_ENABLED": "Enable rotation in space-drag",
"SHOW_SKYBOX": "Show skybox",
"ENABLE_PASSTHROUGH": "Enable passthrough"
}
}
}

View File

@@ -5,5 +5,27 @@
"GAMES": "Juegos",
"SETTINGS": "Ajustes",
"PROCESSES": "Procesos",
"HELLO_USER": "¡Hola, {USER}!"
"HELLO_USER": "¡Hola, {USER}!",
"GENERAL_SETTINGS": "Ajustes generales",
"APPLICATION_LAUNCHER": "Lanzador de aplicaciones",
"APP_SETTINGS": {
"HIDE_USERNAME": "Ocultar nombre de usuario",
"12_HOUR_CLOCK": "Reloj de 12 horas",
"OPAQUE_BACKGROUND": "Fondo opaco",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Ejecutar en modo XWayland por defecto",
"WLX_OVERLAY_S_SETTINGS": "Configuración de WlxOverlay-S",
"HEADSET_SETTINGS": "Configuración del casco",
"BRIGHTNESS": "Brillo",
"WLX": {
"NOTIFICATIONS_ENABLED": "Notificaciones activadas",
"NOTIFICATIONS_SOUND_ENABLED": "Sonido de notificaciones activado",
"KEYBOARD_SOUND_ENABLED": "Sonido del teclado activado",
"BLOCK_GAME_INPUT": "Bloquear entrada del juego",
"SPACE_DRAG_MULTIPLIER": "Multiplicador de movimiento por arrastre",
"SPACE_DRAG_ROTATION_ENABLED": "Habilitar rotación en space-drag",
"SHOW_SKYBOX": "Mostrar cielo",
"ENABLE_PASSTHROUGH": "Habilitar Passthrough"
},
"RESTART_SOFTWARE": "Reiniciar software"
}
}

View File

@@ -5,5 +5,27 @@
"GAMES": "ゲーム",
"SETTINGS": "設定",
"PROCESSES": "プロセス",
"HELLO_USER": "こんにちは、{USER}"
"HELLO_USER": "こんにちは、{USER}",
"GENERAL_SETTINGS": "全般設定",
"APPLICATION_LAUNCHER": "アプリケーションランチャー",
"APP_SETTINGS": {
"HIDE_USERNAME": "ユーザー名を表示しない",
"12_HOUR_CLOCK": "12時間表示",
"OPAQUE_BACKGROUND": "不透明な背景",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "XWaylandモードでデフォルトで実行する",
"WLX_OVERLAY_S_SETTINGS": "WlxOverlay-Sの設定",
"HEADSET_SETTINGS": "ヘッドセット設定",
"BRIGHTNESS": "明るさ",
"WLX": {
"NOTIFICATIONS_ENABLED": "通知が有効",
"NOTIFICATIONS_SOUND_ENABLED": "通知音を有効にする",
"KEYBOARD_SOUND_ENABLED": "キーボード音を有効にする",
"BLOCK_GAME_INPUT": "ゲーム入力をブロック",
"SPACE_DRAG_MULTIPLIER": "スペースドラッグ乗数",
"SPACE_DRAG_ROTATION_ENABLED": "スペースドラッグでの回転を有効にする",
"SHOW_SKYBOX": "スカイボックスを表示",
"ENABLE_PASSTHROUGH": "Passthroughを有効にする"
},
"RESTART_SOFTWARE": "ソフトウェアを再起動"
}
}

View File

@@ -1,9 +1,31 @@
{
"HOME_SCREEN": "Ekran główny",
"MONADO_RUNTIME": "„Monado” środowisko uruchomieniowe",
"APPLICATIONS": "Aplikacje",
"GAMES": "Gry",
"SETTINGS": "Ustawienia",
"PROCESSES": "Procesy",
"HELLO_USER": "Witaj, {USER}!"
}
"HOME_SCREEN": "Ekran główny",
"MONADO_RUNTIME": "„Monado” środowisko uruchomieniowe",
"APPLICATIONS": "Aplikacje",
"GAMES": "Gry",
"SETTINGS": "Ustawienia",
"PROCESSES": "Procesy",
"HELLO_USER": "Witaj, {USER}!",
"GENERAL_SETTINGS": "Ustawienia ogólne",
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
"APP_SETTINGS": {
"HIDE_USERNAME": "Ukryj nazwę użytkownika",
"12_HOUR_CLOCK": "12-godzinny zegar",
"OPAQUE_BACKGROUND": "Nieprzezroczysty tło",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Uruchom domyślnie w trybie XWayland",
"WLX_OVERLAY_S_SETTINGS": "Ustawienia wlx-overlay-s",
"HEADSET_SETTINGS": "Ustawienia głośności",
"BRIGHTNESS": "Jasność",
"WLX": {
"NOTIFICATIONS_ENABLED": "Powiadomienia",
"NOTIFICATIONS_SOUND_ENABLED": "Dźwięk powiadomień",
"KEYBOARD_SOUND_ENABLED": "Dźwięki klawiatury",
"BLOCK_GAME_INPUT": "Zablokuj sterowanie grą podczas używania Wlx",
"SPACE_DRAG_MULTIPLIER": "Mnożnik space-drag",
"SPACE_DRAG_ROTATION_ENABLED": "Włącz rotację w space-drag",
"SHOW_SKYBOX": "Pokaż skybox",
"ENABLE_PASSTHROUGH": "Włącz passthrough"
},
"RESTART_SOFTWARE": "Uruchom ponownie oprogramowanie"
}
}

View File

@@ -14,7 +14,7 @@ use wgui::{
};
use crate::{
assets,
assets, settings,
tab::{
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses,
settings::TabSettings,
@@ -25,6 +25,8 @@ pub struct Frontend {
pub layout: RcLayout,
globals: WguiGlobals,
settings: settings::Settings,
#[allow(dead_code)]
state: ParserState,
@@ -37,6 +39,10 @@ pub struct Frontend {
label_time_id: WidgetID,
}
pub struct InitParams {
pub settings: settings::Settings,
}
pub type RcFrontend = Rc<RefCell<Frontend>>;
pub enum FrontendTask {
@@ -44,7 +50,7 @@ pub enum FrontendTask {
}
impl Frontend {
pub fn new() -> anyhow::Result<(RcFrontend, RcLayout)> {
pub fn new(params: InitParams) -> anyhow::Result<(RcFrontend, RcLayout)> {
let globals = WguiGlobals::new(Box::new(assets::Asset {}), wgui::globals::Defaults::default())?;
let (layout, state) = wgui::parser::new_layout_from_assets(
@@ -71,6 +77,7 @@ impl Frontend {
tasks,
ticks: 0,
label_time_id,
settings: params.settings,
}));
Frontend::register_widgets(&res)?;

View File

@@ -1,5 +1,6 @@
mod assets;
pub mod frontend;
pub mod settings;
mod tab;
mod util;
mod various;

View File

@@ -0,0 +1,41 @@
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)]
pub struct HomeScreen {
pub hide_username: bool,
}
#[derive(Default, Serialize, Deserialize)]
pub struct General {
pub am_pm_clock: bool,
pub opaque_background: bool,
}
#[derive(Default, Serialize, Deserialize)]
pub enum ApplicationRunMode {
#[default]
Native, /* use Smithay compositor */
XWaylandCage,
}
#[derive(Default, Serialize, Deserialize)]
pub struct Tweaks {
pub default_run_mode: ApplicationRunMode,
}
#[derive(Default, Serialize, Deserialize)]
pub struct Settings {
pub home_screen: HomeScreen,
pub general: General,
pub tweaks: Tweaks,
}
impl Settings {
pub fn save(&self) -> String {
serde_json::to_string(&self).unwrap() /* want panic */
}
pub fn load(input: &str) -> anyhow::Result<Settings> {
Ok(serde_json::from_str::<Settings>(input)?)
}
}

View File

@@ -8,6 +8,8 @@ WayVR Dashboard: An application (and game) launcher which is displayed in front
OpenVR: API made by Valve
OpenXR: API made by Khronos
OSC: OpenSoundControl
Space-drag: A feature which allows the user to move their HMD origin position via a controller
Passthrough: Some headsets have built-in cameras for displaying image on the HMD screen
You will be given the input in the code blocks which needs to be translated to {TARGET_LANG} language. Write only the result in codeblocks, do not explain. Keep any newlines and other important formatting-required identifiers as-is.

View File

@@ -9,7 +9,9 @@ pub struct TestbedDashboard {
impl TestbedDashboard {
pub fn new() -> anyhow::Result<Self> {
let (frontend, layout) = frontend::Frontend::new()?;
let (frontend, layout) = frontend::Frontend::new(frontend::InitParams {
settings: Default::default(),
})?;
Ok(Self { frontend, layout })
}
}

View File

@@ -1,6 +1,6 @@
use crate::{
animation::{Animation, AnimationEasing},
components::{self, tooltip::ComponentTooltip, Component, ComponentBase, ComponentTrait, InitData},
components::{self, Component, ComponentBase, ComponentTrait, InitData, tooltip::ComponentTooltip},
drawing::{self, Boundary, Color},
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
i18n::Translation,
@@ -10,15 +10,15 @@ use crate::{
util::centered_matrix,
},
widget::{
ConstructEssentials, EventResult, WidgetData,
label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength,
ConstructEssentials, EventResult, WidgetData,
},
};
use glam::{Mat4, Vec3};
use std::{cell::RefCell, rc::Rc};
use taffy::{AlignItems, JustifyContent};
use taffy::{AlignItems, JustifyContent, prelude::length};
pub struct Params {
pub text: Option<Translation>, // if unset, label will not be populated
@@ -281,6 +281,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
style.justify_content = Some(JustifyContent::Center);
style.overflow.x = taffy::Overflow::Hidden;
style.overflow.y = taffy::Overflow::Hidden;
style.gap = length(4.0);
// update colors to default ones if they are not specified
let color = if let Some(color) = params.color {

View File

@@ -1,7 +1,7 @@
use std::{cell::RefCell, rc::Rc};
use taffy::{
prelude::{length, percent},
AlignItems, JustifyContent,
prelude::{length, percent},
};
use crate::{
@@ -13,10 +13,10 @@ use crate::{
layout::{self, LayoutState, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle},
widget::{
ConstructEssentials, EventResult,
label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength,
ConstructEssentials, EventResult,
},
};
@@ -223,6 +223,7 @@ fn register_event_mouse_release(
)
}
#[allow(clippy::too_many_lines)]
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentCheckbox>)> {
let mut style = params.style;
@@ -230,12 +231,21 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
style.flex_wrap = taffy::FlexWrap::NoWrap;
style.align_items = Some(AlignItems::Center);
style.justify_content = Some(JustifyContent::Center);
// make checkbox interaction box larger by setting padding and negative margin
style.padding = taffy::Rect {
left: length(4.0),
right: length(8.0),
top: length(4.0),
bottom: length(4.0),
};
style.margin = taffy::Rect {
left: length(-4.0),
right: length(-8.0),
top: length(-4.0),
bottom: length(-4.0),
};
//style.align_self = Some(taffy::AlignSelf::Start); // do not stretch self to the parent
style.gap = length(4.0);

View File

@@ -15,11 +15,11 @@ use crate::{
util,
},
widget::{
ConstructEssentials, EventResult,
div::WidgetDiv,
label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength,
ConstructEssentials, EventResult,
},
};
@@ -149,7 +149,7 @@ impl State {
}
}
const BODY_COLOR: drawing::Color = drawing::Color::new(0.6, 0.65, 0.7, 1.0);
const BODY_COLOR: drawing::Color = drawing::Color::new(0.6, 0.65, 0.7, 0.2);
const BODY_BORDER_COLOR: drawing::Color = drawing::Color::new(0.4, 0.45, 0.5, 1.0);
const HANDLE_BORDER_COLOR: drawing::Color = drawing::Color::new(0.85, 0.85, 0.85, 1.0);
const HANDLE_BORDER_COLOR_HOVERED: drawing::Color = drawing::Color::new(0.0, 0.0, 0.0, 1.0);