From ce558855d4ffd5335b9dc47839d0372db8c0beee Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Wed, 7 Jan 2026 02:16:43 +0900 Subject: [PATCH] settings implementation --- Cargo.lock | 1 + dash-frontend/assets/gui/tab/settings.xml | 68 ++- dash-frontend/assets/lang/en.json | 75 ++- dash-frontend/src/frontend.rs | 49 +- dash-frontend/src/lib.rs | 1 - dash-frontend/src/settings.rs | 42 -- dash-frontend/src/tab/apps.rs | 2 +- dash-frontend/src/tab/home.rs | 10 +- dash-frontend/src/tab/settings.rs | 451 +++++++++++++++---- dash-frontend/src/util/popup_manager.rs | 12 +- dash-frontend/src/views/app_launcher.rs | 11 +- uidev/src/testbed/testbed_dashboard.rs | 61 +-- wgui/src/parser/component_button.rs | 18 +- wgui/src/parser/component_checkbox.rs | 12 +- wlx-common/Cargo.toml | 1 + wlx-common/src/config.rs | 17 +- wlx-common/src/dash_interface.rs | 3 +- wlx-common/src/dash_interface_emulated.rs | 11 +- wlx-overlay-s/src/backend/wayvr/mod.rs | 2 +- wlx-overlay-s/src/overlays/dashboard.rs | 61 +-- wlx-overlay-s/src/overlays/screen/backend.rs | 4 +- 21 files changed, 561 insertions(+), 351 deletions(-) delete mode 100644 dash-frontend/src/settings.rs diff --git a/Cargo.lock b/Cargo.lock index cd24e4e7..79e09859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6928,6 +6928,7 @@ dependencies = [ "rodio", "rust-ini", "serde", + "serde_json", "smol", "walkdir", "wayvr-ipc", diff --git a/dash-frontend/assets/gui/tab/settings.xml b/dash-frontend/assets/gui/tab/settings.xml index 58b1547b..aadb40fe 100644 --- a/dash-frontend/assets/gui/tab/settings.xml +++ b/dash-frontend/assets/gui/tab/settings.xml @@ -2,53 +2,37 @@ + + + + + + + + + + -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
- \ No newline at end of file + diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index b87ccc17..426af1cc 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -3,23 +3,68 @@ "RECENTER_PLAYSPACE": "Re-center playspace" }, "APP_SETTINGS": { + "LOOK_AND_FEEL": "Look & Feel", + + "OPAQUE_BACKGROUND": "Opaque background", + "HIDE_USERNAME": "Hide username", + "HIDE_GRAB_HELP": "Hide grab help", + "ANIMATION_SPEED": "UI Animation speed", + "ROUND_MULTIPLIER": "UI Edge roundness", + "SINGLE_SET_MODE": "Single set mode", + "USE_SKYBOX": "Enable skybox", + "USE_PASSTHROUGH": "Enable passthrough", + "CLOCK_12H": "12-hour clock", + + "FEATURES": "Features", + + "NOTIFICATIONS_ENABLED": "Enable notifications", + "NOTIFICATIONS_SOUND_ENABLED": "Notification sounds", + "KEYBOARD_SOUND_ENABLED": "Keyboard sounds", + "SPACE_DRAG_MULTIPLIER": "Space drag multiplier", + "SPACE_DRAG_UNLOCKED": "Allow space drag on all axes", + "SPACE_ROTATE_UNLOCKED": "Allow space rotate on all axes", + "BLOCK_GAME_INPUT": "Block game input", + "BLOCK_GAME_INPUT_IGNORE_WATCH": "Ignore watch when blocking input", + + "CONTROLS": "Controls", + + "FOCUS_FOLLOWS_MOUSE_MODE": "Mouse move on trigger touch", + "LEFT_HANDED_MOUSE": "Left-handed mouse", + "ALLOW_SLIDING": "Stick interaction during grab", + "INVERT_SCROLL_DIRECTION_X": "Invert horizontal scroll direction", + "INVERT_SCROLL_DIRECTION_Y": "Invert vertical scroll direction", + "SCROLL_SPEED": "Scroll speed", + "LONG_PRESS_DURATION": "Long press duration", + "POINTER_LERP_FACTOR": "Pointer smoothing", + "XR_CLICK_SENSITIVITY": "XR click sensitivity", + "XR_CLICK_SENSITIVITY_RELEASE": "XR release sensitivity", + "CLICK_FREEZE_TIME_MS": "Click freeze time (ms)", + + "MISC": "Miscellaneous", + + "XWAYLAND_BY_DEFAULT": "Run apps in Compatibility mode by default", + "UPRIGHT_SCREEN_FIX": "Upright screen fix", + "DOUBLE_CURSOR_FIX": "Double cursor fix", + "SCREEN_RENDER_DOWN": "Render screen at lower resolution", + + "UPRIGHT_SCREEN_FIX_HELP": "Fixes upright screens on some desktops", + "DOUBLE_CURSOR_FIX_HELP": "Enable this if you see 2 cursors", + "SINGLE_SET_MODE_HELP": "Optimize the watch for working with a single set", + "XR_CLICK_SENSITIVITY_HELP": "Analog trigger sensitivity", + "XR_CLICK_SENSITIVITY_RELEASE_HELP": "Must be lower than click", + "CLICK_FREEZE_TIME_MS_HELP": "Helps with double-click precision", + "LEFT_HANDED_MOUSE_HELP": "Use this if mouse buttons are swapped", + "BLOCK_GAME_INPUT_HELP": "Blocks all input when an overlay is hovered", + "BLOCK_GAME_INPUT_IGNORE_WATCH_HELP": "Do not block input when watch is hovered", + "USE_SKYBOX_HELP": "Show a skybox if there's no scene app or passthrough", + "USE_PASSTHROUGH_HELP": "Allow passthrough if the XR runtime supports it", + "SCREEN_RENDER_DOWN_HELP": "Helps with aliasing on high-res screens", + + "__UNUSED": "---- Below this are unused values ----", + "BRIGHTNESS": "Brightness", "HEADSET_SETTINGS": "Headset settings", - "HIDE_USERNAME": "Hide username", - "OPAQUE_BACKGROUND": "Opaque background", - "RESTART_SOFTWARE": "Restart software", - "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Run in XWayland mode by default", - "WLX": { - "BLOCK_GAME_INPUT": "Block game input", - "ENABLE_PASSTHROUGH": "Enable passthrough", - "KEYBOARD_SOUND_ENABLED": "Keyboard sound enabled", - "NOTIFICATIONS_ENABLED": "Notifications enabled", - "NOTIFICATIONS_SOUND_ENABLED": "Notifications sound enabled", - "SHOW_SKYBOX": "Show skybox", - "SPACE_DRAG_MULTIPLIER": "Space-drag multiplier", - "SPACE_DRAG_ROTATION_ENABLED": "Enable rotation in space-drag" - }, - "WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S settings" + "RESTART_SOFTWARE": "Restart software" }, "APPLICATION_LAUNCHER": "Application launcher", "APPLICATION_STARTED": "Application started", diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs index 30cbb2e5..63458b84 100644 --- a/dash-frontend/src/frontend.rs +++ b/dash-frontend/src/frontend.rs @@ -17,10 +17,10 @@ use wgui::{ use wlx_common::{audio, dash_interface::BoxDashInterface, timestep::Timestep}; use crate::{ - assets, settings, + assets, tab::{ - Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, - settings::TabSettings, + apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, settings::TabSettings, + Tab, TabType, }, util::{ popup_manager::{MountPopupParams, PopupManager, PopupManagerParams}, @@ -41,7 +41,6 @@ pub struct Frontend { pub layout: Layout, globals: WguiGlobals, - pub settings: Box, pub interface: BoxDashInterface, // async runtime executor @@ -79,7 +78,6 @@ pub struct FrontendUpdateResult { } pub struct InitParams { - pub settings: Box, pub interface: BoxDashInterface, } @@ -104,7 +102,7 @@ pub enum FrontendTask { } impl Frontend { - pub fn new(params: InitParams) -> anyhow::Result> { + pub fn new(params: InitParams, data: &mut T) -> anyhow::Result> { let mut assets = Box::new(assets::Asset {}); let font_binary_bold = assets.load_from_path_gzip("Quicksand-Bold.ttf.gz")?; @@ -159,7 +157,6 @@ impl Frontend { id_rect_content, }, timestep, - settings: params.settings, interface: params.interface, popup_manager, toast_manager, @@ -170,8 +167,8 @@ impl Frontend { }; // init some things first - frontend.update_background()?; - frontend.update_time()?; + frontend.update_background(data)?; + frontend.update_time(data)?; Frontend::register_widgets(&mut frontend)?; @@ -232,7 +229,7 @@ impl Frontend { fn tick(&mut self, params: FrontendUpdateParams) -> anyhow::Result { // fixme: timer events instead of this thing if self.ticks.is_multiple_of(1000) { - self.update_time()?; + self.update_time(params.data)?; } { @@ -253,7 +250,7 @@ impl Frontend { }) } - fn update_time(&mut self) -> anyhow::Result<()> { + fn update_time(&mut self, data: &mut T) -> anyhow::Result<()> { let mut c = self.layout.start_common(); let mut common = c.common(); @@ -266,12 +263,12 @@ impl Frontend { let hours = now.hour(); let minutes = now.minute(); - let text: String = if !self.settings.get().general.am_pm_clock { - format!("{hours:02}:{minutes:02}") - } else { + let text: String = if self.interface.general_config(data).clock_12h { let hours_ampm = (hours + 11) % 12 + 1; let suffix = if hours >= 12 { "PM" } else { "AM" }; format!("{hours_ampm:02}:{minutes:02} {suffix}") + } else { + format!("{hours:02}:{minutes:02}") }; label.set_text(&mut common, Translation::from_raw_text(&text)); @@ -281,13 +278,15 @@ impl Frontend { Ok(()) } - fn mount_popup(&mut self, params: MountPopupParams) -> anyhow::Result<()> { + fn mount_popup(&mut self, params: MountPopupParams, data: &mut T) -> anyhow::Result<()> { + let config = self.interface.general_config(data); + self.popup_manager.mount_popup( self.globals.clone(), - self.settings.as_ref(), &mut self.layout, self.tasks.clone(), params, + config, )?; Ok(()) } @@ -299,7 +298,7 @@ impl Frontend { Ok(()) } - fn update_background(&self) -> anyhow::Result<()> { + fn update_background(&mut self, data: &mut T) -> anyhow::Result<()> { let Some(mut rect) = self .layout .state @@ -309,10 +308,10 @@ impl Frontend { anyhow::bail!(""); }; - let (alpha1, alpha2) = if !self.settings.get().general.opaque_background { - (0.8666, 0.9333) - } else { + let (alpha1, alpha2) = if self.interface.general_config(data).opaque_background { (1.0, 1.0) + } else { + (0.8666, 0.9333) }; rect.params.color.a = alpha1; @@ -324,9 +323,9 @@ impl Frontend { fn process_task(&mut self, params: &mut FrontendUpdateParams, task: FrontendTask) -> anyhow::Result<()> { match task { FrontendTask::SetTab(tab_type) => self.set_tab(params.data, tab_type)?, - FrontendTask::RefreshClock => self.update_time()?, - FrontendTask::RefreshBackground => self.update_background()?, - FrontendTask::MountPopup(params) => self.mount_popup(params)?, + FrontendTask::RefreshClock => self.update_time(params.data)?, + FrontendTask::RefreshBackground => self.update_background(params.data)?, + FrontendTask::MountPopup(popup_params) => self.mount_popup(popup_params, params.data)?, FrontendTask::RefreshPopupManager => self.refresh_popup_manager()?, FrontendTask::ShowAudioSettings => self.action_show_audio_settings()?, FrontendTask::UpdateAudioSettingsView => self.action_update_audio_settings()?, @@ -343,12 +342,12 @@ impl Frontend { self.layout.remove_children(widget_content.id); let tab: Box> = match tab_type { - TabType::Home => Box::new(TabHome::new(self, widget_content.id)?), + TabType::Home => Box::new(TabHome::new(self, widget_content.id, data)?), TabType::Apps => Box::new(TabApps::new(self, widget_content.id, data)?), TabType::Games => Box::new(TabGames::new(self, widget_content.id)?), TabType::Monado => Box::new(TabMonado::new(self, widget_content.id)?), TabType::Processes => Box::new(TabProcesses::new(self, widget_content.id)?), - TabType::Settings => Box::new(TabSettings::new(self, widget_content.id)?), + TabType::Settings => Box::new(TabSettings::new(self, widget_content.id, data)?), }; self.current_tab = Some(tab); diff --git a/dash-frontend/src/lib.rs b/dash-frontend/src/lib.rs index 5266836c..916ed0cf 100644 --- a/dash-frontend/src/lib.rs +++ b/dash-frontend/src/lib.rs @@ -1,6 +1,5 @@ mod assets; pub mod frontend; -pub mod settings; mod tab; mod util; mod various; diff --git a/dash-frontend/src/settings.rs b/dash-frontend/src/settings.rs deleted file mode 100644 index d4e24021..00000000 --- a/dash-frontend/src/settings.rs +++ /dev/null @@ -1,42 +0,0 @@ -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 struct Tweaks { - pub xwayland_by_default: bool, -} - -#[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_pretty(&self).unwrap() /* want panic */ - } - - pub fn load(input: &str) -> anyhow::Result { - Ok(serde_json::from_str::(input)?) - } -} - -pub trait SettingsIO { - fn get_mut(&mut self) -> &mut Settings; - fn get(&self) -> &Settings; - fn save_to_disk(&mut self); - fn read_from_disk(&mut self); - fn mark_as_dirty(&mut self); -} diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs index 0c4b0789..6fd8b90a 100644 --- a/dash-frontend/src/tab/apps.rs +++ b/dash-frontend/src/tab/apps.rs @@ -104,7 +104,7 @@ fn on_app_click( layout: data.layout, parent_id: data.id_content, frontend_tasks: &frontend_tasks, - settings: data.settings, + config: data.config, on_launched, })?; diff --git a/dash-frontend/src/tab/home.rs b/dash-frontend/src/tab/home.rs index 06dff109..9c4bba39 100644 --- a/dash-frontend/src/tab/home.rs +++ b/dash-frontend/src/tab/home.rs @@ -9,10 +9,10 @@ use wgui::{ parser::{Fetchable, ParseDocumentParams, ParserState}, widget::label::WidgetLabel, }; +use wlx_common::config::GeneralConfig; use crate::{ frontend::{Frontend, FrontendTask}, - settings, tab::{Tab, TabType}, various, }; @@ -29,7 +29,7 @@ impl Tab for TabHome { } } -fn configure_label_hello(common: &mut CallbackDataCommon, label_hello: Widget, settings: &settings::Settings) { +fn configure_label_hello(common: &mut CallbackDataCommon, label_hello: Widget, config: &GeneralConfig) { let mut username = various::get_username(); // first character as uppercase if let Some(first) = username.chars().next() { @@ -37,7 +37,7 @@ fn configure_label_hello(common: &mut CallbackDataCommon, label_hello: Widget, s username.replace_range(0..1, &first); } - let translated = if !settings.home_screen.hide_username { + let translated = if !config.hide_username { common.i18n().translate_and_replace("HELLO_USER", ("{USER}", &username)) } else { common.i18n().translate("HELLO").to_string() @@ -48,7 +48,7 @@ fn configure_label_hello(common: &mut CallbackDataCommon, label_hello: Widget, s } impl TabHome { - pub fn new(frontend: &mut Frontend, parent_id: WidgetID) -> anyhow::Result { + pub fn new(frontend: &mut Frontend, parent_id: WidgetID, data: &mut T) -> anyhow::Result { let state = wgui::parser::parse_from_assets( &ParseDocumentParams { globals: frontend.layout.state.globals.clone(), @@ -61,7 +61,7 @@ impl TabHome { let mut c = frontend.layout.start_common(); let widget_label = state.fetch_widget(&c.layout.state, "label_hello")?.widget; - configure_label_hello(&mut c.common(), widget_label, frontend.settings.get_mut()); + configure_label_hello(&mut c.common(), widget_label, frontend.interface.general_config(data)); let btn_apps = state.fetch_component_as::("btn_apps")?; let btn_games = state.fetch_component_as::("btn_games")?; diff --git a/dash-frontend/src/tab/settings.rs b/dash-frontend/src/tab/settings.rs index 8fa58987..3e5c7931 100644 --- a/dash-frontend/src/tab/settings.rs +++ b/dash-frontend/src/tab/settings.rs @@ -1,21 +1,24 @@ -use std::{marker::PhantomData, rc::Rc}; +use std::{collections::HashMap, marker::PhantomData, rc::Rc}; +use strum::AsRefStr; use wgui::{ assets::AssetPath, - components::checkbox::ComponentCheckbox, - layout::WidgetID, + components::{checkbox::ComponentCheckbox, slider::ComponentSlider}, + layout::{Layout, WidgetID}, parser::{Fetchable, ParseDocumentParams, ParserState}, task::Tasks, }; +use wlx_common::config::GeneralConfig; use crate::{ frontend::{Frontend, FrontendTask}, - settings, tab::{Tab, TabType}, }; enum Task { - ToggleSetting(SettingType, bool), + UpdateBool(SettingType, bool), + UpdateFloat(SettingType, f32), + UpdateInt(SettingType, i32), } pub struct TabSettings { @@ -31,10 +34,22 @@ impl Tab for TabSettings { TabType::Settings } - fn update(&mut self, frontend: &mut Frontend, _data: &mut T) -> anyhow::Result<()> { + fn update(&mut self, frontend: &mut Frontend, data: &mut T) -> anyhow::Result<()> { + let config = frontend.interface.general_config(data); for task in self.tasks.drain() { match task { - Task::ToggleSetting(setting, n) => self.toggle_setting(frontend, setting, n), + Task::UpdateBool(setting, n) => { + setting.get_frontend_task().map(|task| frontend.tasks.push(task)); + *setting.mut_bool(config) = n; + } + Task::UpdateFloat(setting, n) => { + setting.get_frontend_task().map(|task| frontend.tasks.push(task)); + *setting.mut_f32(config) = n; + } + Task::UpdateInt(setting, n) => { + setting.get_frontend_task().map(|task| frontend.tasks.push(task)); + *setting.mut_i32(config) = n; + } } } Ok(()) @@ -42,109 +57,375 @@ impl Tab for TabSettings { } #[allow(clippy::enum_variant_names)] -#[derive(Clone)] +#[derive(Clone, Copy, AsRefStr)] enum SettingType { - DashHideUsername, - DashAmPmClock, - DashOpaqueBackground, - DashXwaylandByDefault, + AnimationSpeed, + RoundMultiplier, + InvertScrollDirectionX, + InvertScrollDirectionY, + ScrollSpeed, + LongPressDuration, + NotificationsEnabled, + NotificationsSoundEnabled, + KeyboardSoundEnabled, + UprightScreenFix, + DoubleCursorFix, + SingleSetMode, + HideGrabHelp, + XrClickSensitivity, + XrClickSensitivityRelease, + AllowSliding, + ClickFreezeTimeMs, + FocusFollowsMouseMode, + LeftHandedMouse, + BlockGameInput, + BlockGameInputIgnoreWatch, + SpaceDragMultiplier, + UseSkybox, + UsePassthrough, + ScreenRenderDown, + PointerLerpFactor, + SpaceDragUnlocked, + SpaceRotateUnlocked, + Clock12h, + HideUsername, + OpaqueBackground, + XwaylandByDefault, } impl SettingType { - fn get_bool<'a>(&self, settings: &'a mut settings::Settings) -> &'a mut bool { + pub fn mut_bool<'a>(self, config: &'a mut GeneralConfig) -> &'a mut bool { match self { - SettingType::DashHideUsername => &mut settings.home_screen.hide_username, - SettingType::DashAmPmClock => &mut settings.general.am_pm_clock, - SettingType::DashOpaqueBackground => &mut settings.general.opaque_background, - SettingType::DashXwaylandByDefault => &mut settings.tweaks.xwayland_by_default, + Self::InvertScrollDirectionX => &mut config.invert_scroll_direction_x, + Self::InvertScrollDirectionY => &mut config.invert_scroll_direction_y, + Self::NotificationsEnabled => &mut config.notifications_enabled, + Self::NotificationsSoundEnabled => &mut config.notifications_sound_enabled, + Self::KeyboardSoundEnabled => &mut config.keyboard_sound_enabled, + Self::UprightScreenFix => &mut config.upright_screen_fix, + Self::DoubleCursorFix => &mut config.double_cursor_fix, + Self::SingleSetMode => &mut config.single_set_mode, + Self::HideGrabHelp => &mut config.hide_grab_help, + Self::AllowSliding => &mut config.allow_sliding, + Self::FocusFollowsMouseMode => &mut config.focus_follows_mouse_mode, + Self::LeftHandedMouse => &mut config.left_handed_mouse, + Self::BlockGameInput => &mut config.block_game_input, + Self::BlockGameInputIgnoreWatch => &mut config.block_game_input_ignore_watch, + Self::UseSkybox => &mut config.use_skybox, + Self::UsePassthrough => &mut config.use_passthrough, + Self::ScreenRenderDown => &mut config.screen_render_down, + Self::SpaceDragUnlocked => &mut config.space_drag_unlocked, + Self::SpaceRotateUnlocked => &mut config.space_rotate_unlocked, + Self::Clock12h => &mut config.clock_12h, + Self::HideUsername => &mut config.hide_username, + Self::OpaqueBackground => &mut config.opaque_background, + Self::XwaylandByDefault => &mut config.xwayland_by_default, + _ => panic!("Requested bool for non-bool SettingType"), + } + } + + pub fn mut_f32<'a>(self, config: &'a mut GeneralConfig) -> &'a mut f32 { + match self { + Self::AnimationSpeed => &mut config.animation_speed, + Self::RoundMultiplier => &mut config.round_multiplier, + Self::ScrollSpeed => &mut config.scroll_speed, + Self::LongPressDuration => &mut config.long_press_duration, + Self::XrClickSensitivity => &mut config.xr_click_sensitivity, + Self::XrClickSensitivityRelease => &mut config.xr_click_sensitivity_release, + Self::SpaceDragMultiplier => &mut config.space_drag_multiplier, + Self::PointerLerpFactor => &mut config.pointer_lerp_factor, + _ => panic!("Requested f32 for non-f32 SettingType"), + } + } + + pub fn mut_i32<'a>(self, config: &'a mut GeneralConfig) -> &'a mut i32 { + match self { + Self::ClickFreezeTimeMs => &mut config.click_freeze_time_ms, + _ => panic!("Requested i32 for non-i32 SettingType"), + } + } + + /// Ok is translation, Err is raw text + fn get_translation(self) -> Result<&'static str, &'static str> { + match self { + Self::AnimationSpeed => Ok("APP_SETTINGS.ANIMATION_SPEED"), + Self::RoundMultiplier => Ok("APP_SETTINGS.ROUND_MULTIPLIER"), + Self::InvertScrollDirectionX => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_X"), + Self::InvertScrollDirectionY => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_Y"), + Self::ScrollSpeed => Ok("APP_SETTINGS.SCROLL_SPEED"), + Self::LongPressDuration => Ok("APP_SETTINGS.LONG_PRESS_DURATION"), + Self::NotificationsEnabled => Ok("APP_SETTINGS.NOTIFICATIONS_ENABLED"), + Self::NotificationsSoundEnabled => Ok("APP_SETTINGS.NOTIFICATIONS_SOUND_ENABLED"), + Self::KeyboardSoundEnabled => Ok("APP_SETTINGS.KEYBOARD_SOUND_ENABLED"), + Self::UprightScreenFix => Ok("APP_SETTINGS.UPRIGHT_SCREEN_FIX"), + Self::DoubleCursorFix => Ok("APP_SETTINGS.DOUBLE_CURSOR_FIX"), + Self::SingleSetMode => Ok("APP_SETTINGS.SINGLE_SET_MODE"), + Self::HideGrabHelp => Ok("APP_SETTINGS.HIDE_GRAB_HELP"), + Self::XrClickSensitivity => Ok("APP_SETTINGS.XR_CLICK_SENSITIVITY"), + Self::XrClickSensitivityRelease => Ok("APP_SETTINGS.XR_CLICK_SENSITIVITY_RELEASE"), + Self::AllowSliding => Ok("APP_SETTINGS.ALLOW_SLIDING"), + Self::ClickFreezeTimeMs => Ok("APP_SETTINGS.CLICK_FREEZE_TIME_MS"), + Self::FocusFollowsMouseMode => Ok("APP_SETTINGS.FOCUS_FOLLOWS_MOUSE_MODE"), + Self::LeftHandedMouse => Ok("APP_SETTINGS.LEFT_HANDED_MOUSE"), + Self::BlockGameInput => Ok("APP_SETTINGS.BLOCK_GAME_INPUT"), + Self::BlockGameInputIgnoreWatch => Ok("APP_SETTINGS.BLOCK_GAME_INPUT_IGNORE_WATCH"), + Self::SpaceDragMultiplier => Ok("APP_SETTINGS.SPACE_DRAG_MULTIPLIER"), + Self::UseSkybox => Ok("APP_SETTINGS.USE_SKYBOX"), + Self::UsePassthrough => Ok("APP_SETTINGS.USE_PASSTHROUGH"), + Self::ScreenRenderDown => Ok("APP_SETTINGS.SCREEN_RENDER_DOWN"), + Self::PointerLerpFactor => Ok("APP_SETTINGS.POINTER_LERP_FACTOR"), + Self::SpaceDragUnlocked => Ok("APP_SETTINGS.SPACE_DRAG_UNLOCKED"), + Self::SpaceRotateUnlocked => Ok("APP_SETTINGS.SPACE_ROTATE_UNLOCKED"), + Self::Clock12h => Ok("APP_SETTINGS.CLOCK_12H"), + Self::HideUsername => Ok("APP_SETTINGS.HIDE_USERNAME"), + Self::OpaqueBackground => Ok("APP_SETTINGS.OPAQUE_BACKGROUND"), + Self::XwaylandByDefault => Ok("APP_SETTINGS.XWAYLAND_BY_DEFAULT"), + } + } + + fn get_tooltip(self) -> Option<&'static str> { + match self { + Self::UprightScreenFix => Some("APP_SETTINGS.UPRIGHT_SCREEN_FIX_HELP"), + Self::DoubleCursorFix => Some("APP_SETTINGS.DOUBLE_CURSOR_FIX_HELP"), + Self::SingleSetMode => Some("APP_SETTINGS.SINGLE_SET_MODE_HELP"), + Self::XrClickSensitivity => Some("APP_SETTINGS.XR_CLICK_SENSITIVITY_HELP"), + Self::XrClickSensitivityRelease => Some("APP_SETTINGS.XR_CLICK_SENSITIVITY_RELEASE_HELP"), + Self::FocusFollowsMouseMode => Some("APP_SETTINGS.FOCUS_FOLLOWS_MOUSE_MODE_HELP"), + Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"), + Self::BlockGameInput => Some("APP_SETTINGS.BLOCK_GAME_INPUT_HELP"), + Self::BlockGameInputIgnoreWatch => Some("APP_SETTINGS.BLOCK_GAME_INPUT_IGNORE_WATCH_HELP"), + Self::UseSkybox => Some("APP_SETTINGS.USE_SKYBOX_HELP"), + Self::UsePassthrough => Some("APP_SETTINGS.USE_PASSTHROUGH_HELP"), + Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"), + _ => None, + } + } + + //TODO: incorporate this + fn requires_restart(self) -> bool { + match self { + Self::AnimationSpeed + | Self::RoundMultiplier + | Self::UprightScreenFix + | Self::DoubleCursorFix + | Self::SingleSetMode + | Self::UseSkybox + | Self::UsePassthrough + | Self::ScreenRenderDown => true, + _ => false, + } + } + + fn get_frontend_task(self) -> Option { + match self { + Self::Clock12h => Some(FrontendTask::RefreshClock), + Self::OpaqueBackground => Some(FrontendTask::RefreshBackground), + _ => None, } } } -fn init_setting_checkbox( - frontend: &mut Frontend, - tasks: &Tasks, - checkbox: Rc, - setting: SettingType, - additional_frontend_task: Option, -) -> anyhow::Result<()> { - let mut c = frontend.layout.start_common(); - checkbox.set_checked(&mut c.common(), *setting.get_bool(frontend.settings.get_mut())); +macro_rules! category { + ($pe:expr, $root:expr, $translation:expr, $icon:expr) => {{ + let id = $pe.idx.to_string(); + $pe.idx += 1; - let tasks = tasks.clone(); - let frontend_tasks = frontend.tasks.clone(); + let mut params: HashMap, Rc> = HashMap::new(); + params.insert(Rc::from("translation"), Rc::from($translation)); + params.insert(Rc::from("icon"), Rc::from($icon)); + params.insert(Rc::from("id"), Rc::from(id.as_ref())); - checkbox.on_toggle(Box::new(move |_common, e| { - tasks.push(Task::ToggleSetting(setting.clone(), e.checked)); + $pe + .parser_state + .instantiate_template($pe.doc_params, "SettingsGroupBox", $pe.layout, $root, params)?; - if let Some(task) = &additional_frontend_task { - frontend_tasks.push(task.clone()); + $pe.parser_state.get_widget_id(&id) + }}; +} + +macro_rules! checkbox { + ($mp:expr, $root:expr, $setting:expr) => { + let id = $mp.idx.to_string(); + $mp.idx += 1; + + let mut params: HashMap, Rc> = HashMap::new(); + params.insert(Rc::from("id"), Rc::from(id.as_ref())); + + match $setting.get_translation() { + Ok(translation) => params.insert(Rc::from("translation"), translation.into()), + Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()), + }; + + if let Some(tooltip) = $setting.get_tooltip() { + params.insert(Rc::from("tooltip"), Rc::from(tooltip)); } - Ok(()) - })); - c.finish()?; - Ok(()) + let checked = if *$setting.mut_bool($mp.config) { "1" } else { "0" }; + params.insert(Rc::from("checked"), Rc::from(checked)); + + $mp + .parser_state + .instantiate_template($mp.doc_params, "CheckBoxSetting", $mp.layout, $root, params)?; + + let checkbox = $mp.parser_state.fetch_component_as::(&id)?; + checkbox.on_toggle(Box::new({ + let tasks = $mp.tasks.clone(); + move |_common, e| { + tasks.push(Task::UpdateBool($setting, e.checked)); + Ok(()) + } + })); + }; +} + +macro_rules! slider_f32 { + ($mp:expr, $root:expr, $setting:expr, $min:expr, $max:expr, $step:expr) => { + let id = $mp.idx.to_string(); + $mp.idx += 1; + + let mut params: HashMap, Rc> = HashMap::new(); + params.insert(Rc::from("id"), Rc::from(id.as_ref())); + + match $setting.get_translation() { + Ok(translation) => params.insert(Rc::from("translation"), translation.into()), + Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()), + }; + + if let Some(tooltip) = $setting.get_tooltip() { + params.insert(Rc::from("tooltip"), Rc::from(tooltip)); + } + + let value = $setting.mut_f32($mp.config).to_string(); + params.insert(Rc::from("value"), Rc::from(value)); + params.insert(Rc::from("min"), Rc::from($min.to_string())); + params.insert(Rc::from("max"), Rc::from($max.to_string())); + params.insert(Rc::from("step"), Rc::from($step.to_string())); + + $mp + .parser_state + .instantiate_template($mp.doc_params, "SliderSetting", $mp.layout, $root, params)?; + + let slider = $mp.parser_state.fetch_component_as::(&id)?; + slider.on_value_changed(Box::new({ + let tasks = $mp.tasks.clone(); + move |_common, e| { + tasks.push(Task::UpdateFloat($setting, e.value)); + Ok(()) + } + })); + }; +} + +macro_rules! slider_i32 { + ($mp:expr, $root:expr, $setting:expr, $min:expr, $max:expr, $step:expr) => { + let id = $mp.idx.to_string(); + $mp.idx += 1; + + let mut params: HashMap, Rc> = HashMap::new(); + params.insert(Rc::from("id"), Rc::from(id.as_ref())); + + match $setting.get_translation() { + Ok(translation) => params.insert(Rc::from("translation"), translation.into()), + Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()), + }; + + if let Some(tooltip) = $setting.get_tooltip() { + params.insert(Rc::from("tooltip"), Rc::from(tooltip)); + } + + let value = $setting.mut_i32($mp.config).to_string(); + params.insert(Rc::from("value"), Rc::from(value)); + params.insert(Rc::from("min"), Rc::from($min.to_string())); + params.insert(Rc::from("max"), Rc::from($max.to_string())); + params.insert(Rc::from("step"), Rc::from($step.to_string())); + + $mp + .parser_state + .instantiate_template($mp.doc_params, "SliderSetting", $mp.layout, $root, params)?; + + let slider = $mp.parser_state.fetch_component_as::(&id)?; + slider.on_value_changed(Box::new({ + let tasks = $mp.tasks.clone(); + move |_common, e| { + tasks.push(Task::UpdateInt($setting, e.value as i32)); + Ok(()) + } + })); + }; +} + +struct MacroParams<'a> { + layout: &'a mut Layout, + parser_state: &'a mut ParserState, + doc_params: &'a ParseDocumentParams<'a>, + config: &'a mut GeneralConfig, + tasks: Tasks, + idx: usize, } impl TabSettings { - pub fn new(frontend: &mut Frontend, parent_id: WidgetID) -> anyhow::Result { - let state = wgui::parser::parse_from_assets( - &ParseDocumentParams { - globals: frontend.layout.state.globals.clone(), - path: AssetPath::BuiltIn("gui/tab/settings.xml"), - extra: Default::default(), - }, - &mut frontend.layout, - parent_id, - )?; + pub fn new(frontend: &mut Frontend, parent_id: WidgetID, data: &mut T) -> anyhow::Result { + let doc_params = ParseDocumentParams { + globals: frontend.layout.state.globals.clone(), + path: AssetPath::BuiltIn("gui/tab/settings.xml"), + extra: Default::default(), + }; + let mut parser_state = wgui::parser::parse_from_assets(&doc_params, &mut frontend.layout, parent_id)?; - let tasks = Tasks::new(); + let root = parser_state.get_widget_id("settings_root")?; - init_setting_checkbox( - frontend, - &tasks, - state.data.fetch_component_as::("cb_hide_username")?, - SettingType::DashHideUsername, - None, - )?; + let mut mp = MacroParams { + layout: &mut frontend.layout, + parser_state: &mut parser_state, + doc_params: &doc_params, + config: frontend.interface.general_config(data), + tasks: Tasks::default(), + idx: 9001, + }; - init_setting_checkbox( - frontend, - &tasks, - state.data.fetch_component_as::("cb_am_pm_clock")?, - SettingType::DashAmPmClock, - None, - )?; + let c = category!(mp, root, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/settings.svg")?; + checkbox!(mp, c, SettingType::OpaqueBackground); + checkbox!(mp, c, SettingType::HideUsername); + checkbox!(mp, c, SettingType::HideGrabHelp); + slider_f32!(mp, c, SettingType::AnimationSpeed, 0.5, 5.0, 0.1); // min, max, step + slider_f32!(mp, c, SettingType::RoundMultiplier, 0.5, 5.0, 0.1); + checkbox!(mp, c, SettingType::SingleSetMode); + checkbox!(mp, c, SettingType::UseSkybox); + checkbox!(mp, c, SettingType::UsePassthrough); + checkbox!(mp, c, SettingType::Clock12h); - init_setting_checkbox( - frontend, - &tasks, - state - .data - .fetch_component_as::("cb_opaque_background")?, - SettingType::DashOpaqueBackground, - Some(FrontendTask::RefreshBackground), - )?; + let c = category!(mp, root, "APP_SETTINGS.FEATURES", "dashboard/settings.svg")?; + checkbox!(mp, c, SettingType::NotificationsEnabled); + checkbox!(mp, c, SettingType::NotificationsSoundEnabled); + checkbox!(mp, c, SettingType::KeyboardSoundEnabled); + checkbox!(mp, c, SettingType::SpaceDragUnlocked); + checkbox!(mp, c, SettingType::SpaceRotateUnlocked); + slider_f32!(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5); + checkbox!(mp, c, SettingType::BlockGameInput); + checkbox!(mp, c, SettingType::BlockGameInputIgnoreWatch); - init_setting_checkbox( - frontend, - &tasks, - state - .data - .fetch_component_as::("cb_xwayland_by_default")?, - SettingType::DashXwaylandByDefault, - None, - )?; + let c = category!(mp, root, "APP_SETTINGS.CONTROLS", "dashboard/settings.svg")?; + checkbox!(mp, c, SettingType::FocusFollowsMouseMode); + checkbox!(mp, c, SettingType::LeftHandedMouse); + checkbox!(mp, c, SettingType::AllowSliding); + checkbox!(mp, c, SettingType::InvertScrollDirectionX); + checkbox!(mp, c, SettingType::InvertScrollDirectionY); + slider_f32!(mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1); + slider_f32!(mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1); + slider_f32!(mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1); + slider_f32!(mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1); + slider_f32!(mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1); + slider_i32!(mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50); + + let c = category!(mp, root, "APP_SETTINGS.MISC", "dashboard/settings.svg")?; + checkbox!(mp, c, SettingType::XwaylandByDefault); + checkbox!(mp, c, SettingType::UprightScreenFix); + checkbox!(mp, c, SettingType::DoubleCursorFix); + checkbox!(mp, c, SettingType::ScreenRenderDown); Ok(Self { - state, - tasks, + tasks: mp.tasks, + state: parser_state, marker: PhantomData, }) } - - fn toggle_setting(&mut self, frontend: &mut Frontend, setting: SettingType, state: bool) { - *setting.get_bool(frontend.settings.get_mut()) = state; - } } diff --git a/dash-frontend/src/util/popup_manager.rs b/dash-frontend/src/util/popup_manager.rs index 95b63ec2..22782027 100644 --- a/dash-frontend/src/util/popup_manager.rs +++ b/dash-frontend/src/util/popup_manager.rs @@ -14,11 +14,9 @@ use wgui::{ taffy::Display, widget::label::WidgetLabel, }; +use wlx_common::config::GeneralConfig; -use crate::{ - frontend::{FrontendTask, FrontendTasks}, - settings::SettingsIO, -}; +use crate::frontend::{FrontendTask, FrontendTasks}; pub struct PopupManagerParams { pub parent_id: WidgetID, @@ -58,7 +56,7 @@ pub struct PopupManager { pub struct PopupContentFuncData<'a> { pub layout: &'a mut Layout, - pub settings: &'a dyn SettingsIO, + pub config: &'a GeneralConfig, pub handle: PopupHandle, pub id_content: WidgetID, } @@ -123,10 +121,10 @@ impl PopupManager { pub fn mount_popup( &mut self, globals: WguiGlobals, - settings: &dyn SettingsIO, layout: &mut Layout, frontend_tasks: FrontendTasks, params: MountPopupParams, + config: &GeneralConfig, ) -> anyhow::Result<()> { let doc_params = &ParseDocumentParams { globals: globals.clone(), @@ -180,7 +178,7 @@ impl PopupManager { layout, handle: popup_handle.clone(), id_content, - settings, + config, })?; Ok(()) diff --git a/dash-frontend/src/views/app_launcher.rs b/dash-frontend/src/views/app_launcher.rs index 385ed79f..b84266cf 100644 --- a/dash-frontend/src/views/app_launcher.rs +++ b/dash-frontend/src/views/app_launcher.rs @@ -12,12 +12,9 @@ use wgui::{ task::Tasks, widget::label::WidgetLabel, }; -use wlx_common::{dash_interface::BoxDashInterface, desktop_finder::DesktopEntry}; +use wlx_common::{config::GeneralConfig, dash_interface::BoxDashInterface, desktop_finder::DesktopEntry}; -use crate::{ - frontend::{FrontendTask, FrontendTasks, SoundType}, - settings::SettingsIO, -}; +use crate::frontend::{FrontendTask, FrontendTasks, SoundType}; #[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)] enum ResMode { @@ -89,7 +86,7 @@ pub struct Params<'a> { pub entry: DesktopEntry, pub layout: &'a mut Layout, pub parent_id: WidgetID, - pub settings: &'a dyn SettingsIO, + pub config: &'a GeneralConfig, pub frontend_tasks: &'a FrontendTasks, pub on_launched: Box, } @@ -144,7 +141,7 @@ impl View { )?; } - let compositor_mode = if params.settings.get().tweaks.xwayland_by_default { + let compositor_mode = if params.config.xwayland_by_default { CompositorMode::Cage } else { CompositorMode::Native diff --git a/uidev/src/testbed/testbed_dashboard.rs b/uidev/src/testbed/testbed_dashboard.rs index be1ed7e6..b5e839bf 100644 --- a/uidev/src/testbed/testbed_dashboard.rs +++ b/uidev/src/testbed/testbed_dashboard.rs @@ -1,57 +1,8 @@ use crate::testbed::{Testbed, TestbedUpdateParams}; -use dash_frontend::{ - frontend::{self, FrontendUpdateParams}, - settings::{self, SettingsIO}, -}; +use dash_frontend::frontend::{self, FrontendUpdateParams}; use wgui::layout::Layout; use wlx_common::dash_interface_emulated::DashInterfaceEmulated; -struct SimpleSettingsIO { - settings: settings::Settings, -} - -impl SimpleSettingsIO { - fn new() -> Self { - let mut res = Self { - settings: settings::Settings::default(), - }; - res.read_from_disk(); - res - } -} - -// just a simple impl of a config io for dashboard frontend -// use ~/.config later -impl settings::SettingsIO for SimpleSettingsIO { - fn get_mut(&mut self) -> &mut settings::Settings { - &mut self.settings - } - - fn get(&self) -> &dash_frontend::settings::Settings { - &self.settings - } - - fn save_to_disk(&mut self) { - log::info!("saving settings"); - let data = self.settings.save(); - std::fs::write("/tmp/testbed_settings.json", data).unwrap(); - } - - fn read_from_disk(&mut self) { - log::info!("loading settings"); - if let Ok(res) = std::fs::read("/tmp/testbed_settings.json") { - let data = String::from_utf8(res).unwrap(); - self.settings = settings::Settings::load(&data).unwrap(); - } - } - - fn mark_as_dirty(&mut self) { - // just save it, at least for now - // save_to_disk should be called later in time or at exit, not instantly - self.save_to_disk(); - } -} - pub struct TestbedDashboard { frontend: frontend::Frontend<()>, } @@ -61,10 +12,12 @@ impl TestbedDashboard { let settings = SimpleSettingsIO::new(); let interface = DashInterfaceEmulated::new(); - let frontend = frontend::Frontend::new(frontend::InitParams { - settings: Box::new(settings), - interface: Box::new(interface), - })?; + let frontend = frontend::Frontend::new( + frontend::InitParams { + interface: Box::new(interface), + }, + (), + )?; Ok(Self { frontend }) } } diff --git a/wgui/src/parser/component_button.rs b/wgui/src/parser/component_button.rs index 5fc8b1fc..9857d807 100644 --- a/wgui/src/parser/component_button.rs +++ b/wgui/src/parser/component_button.rs @@ -1,13 +1,13 @@ use crate::{ assets::AssetPath, - components::{Component, button, tooltip}, + components::{button, tooltip, Component}, drawing::Color, i18n::Translation, layout::WidgetID, parser::{ - AttribPair, ParserContext, ParserFile, parse_check_f32, parse_check_i32, parse_children, parse_f32, - print_invalid_attrib, process_component, + parse_check_f32, parse_check_i32, parse_children, parse_f32, print_invalid_attrib, process_component, style::{parse_color_opt, parse_round, parse_style, parse_text_style}, + AttribPair, ParserContext, ParserFile, }, widget::util::WLength, }; @@ -41,10 +41,14 @@ pub fn parse_component_button<'a>( let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); match key { "text" => { - translation = Some(Translation::from_raw_text(value)); + if !value.is_empty() { + translation = Some(Translation::from_raw_text(value)); + } } "translation" => { - translation = Some(Translation::from_translation_key(value)); + if !value.is_empty() { + translation = Some(Translation::from_translation_key(value)); + } } "round" => { parse_round(value, &mut round, ctx.doc_params.globals.get().defaults.rounding_mult); @@ -77,8 +81,8 @@ pub fn parse_component_button<'a>( sprite_src = Some(asset_path); } } - "tooltip" => tooltip = Some(Translation::from_translation_key(value)), - "tooltip_str" => tooltip = Some(Translation::from_raw_text(value)), + "tooltip" if !value.is_empty() => tooltip = Some(Translation::from_translation_key(value)), + "tooltip_str" if !value.is_empty() => tooltip = Some(Translation::from_raw_text(value)), "tooltip_side" => { tooltip_side = match value { "left" => Some(tooltip::TooltipSide::Left), diff --git a/wgui/src/parser/component_checkbox.rs b/wgui/src/parser/component_checkbox.rs index 1ea14637..5cf7c26f 100644 --- a/wgui/src/parser/component_checkbox.rs +++ b/wgui/src/parser/component_checkbox.rs @@ -1,9 +1,9 @@ use crate::{ - components::{Component, checkbox, radio_group::ComponentRadioGroup}, + components::{checkbox, radio_group::ComponentRadioGroup, Component}, i18n::Translation, layout::WidgetID, parser::{ - AttribPair, Fetchable, ParserContext, parse_check_f32, parse_check_i32, process_component, style::parse_style, + parse_check_f32, parse_check_i32, process_component, style::parse_style, AttribPair, Fetchable, ParserContext, }, }; @@ -29,10 +29,14 @@ pub fn parse_component_checkbox( let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); match key { "text" => { - translation = Translation::from_raw_text(value); + if !value.is_empty() { + translation = Translation::from_raw_text(value); + } } "translation" => { - translation = Translation::from_translation_key(value); + if !value.is_empty() { + translation = Translation::from_translation_key(value); + } } "value" => { component_value = Some(value.into()); diff --git a/wlx-common/Cargo.toml b/wlx-common/Cargo.toml index c2a0fc07..83de42e4 100644 --- a/wlx-common/Cargo.toml +++ b/wlx-common/Cargo.toml @@ -13,6 +13,7 @@ idmap = { workspace = true, features = ["serde"] } idmap-derive.workspace = true log.workspace = true serde = { workspace = true, features = ["rc"] } +serde_json.workspace = true xdg.workspace = true chrono = "0.4.42" smol = "2.0.2" diff --git a/wlx-common/src/config.rs b/wlx-common/src/config.rs index 077a5ce8..67b47b11 100644 --- a/wlx-common/src/config.rs +++ b/wlx-common/src/config.rs @@ -23,11 +23,11 @@ pub const fn def_pw_tokens() -> PwTokenMap { AStrMap::new() } -const fn def_mouse_move_interval_ms() -> u32 { +const fn def_mouse_move_interval_ms() -> i32 { 10 // 100fps } -const fn def_click_freeze_time_ms() -> u32 { +const fn def_click_freeze_time_ms() -> i32 { 300 } @@ -110,7 +110,7 @@ pub struct GeneralConfig { pub attribs: AStrMap>, #[serde(default = "def_click_freeze_time_ms")] - pub click_freeze_time_ms: u32, + pub click_freeze_time_ms: i32, #[serde(default = "def_false")] pub invert_scroll_direction_x: bool, @@ -125,7 +125,7 @@ pub struct GeneralConfig { pub long_press_duration: f32, #[serde(default = "def_mouse_move_interval_ms")] - pub mouse_move_interval_ms: u32, + pub mouse_move_interval_ms: i32, #[serde(default = "def_true")] pub notifications_enabled: bool, @@ -240,4 +240,13 @@ pub struct GeneralConfig { #[serde(default)] pub last_set: u32, + + #[serde(default)] + pub hide_username: bool, + + #[serde(default)] + pub opaque_background: bool, + + #[serde(default)] + pub xwayland_by_default: bool, } diff --git a/wlx-common/src/dash_interface.rs b/wlx-common/src/dash_interface.rs index f9d35415..898867a3 100644 --- a/wlx-common/src/dash_interface.rs +++ b/wlx-common/src/dash_interface.rs @@ -3,7 +3,7 @@ use wayvr_ipc::{ packet_server::{WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle}, }; -use crate::desktop_finder::DesktopFinder; +use crate::{config::GeneralConfig, desktop_finder::DesktopFinder}; pub trait DashInterface { fn window_list(&mut self, data: &mut T) -> anyhow::Result>; @@ -15,6 +15,7 @@ pub trait DashInterface { fn process_terminate(&mut self, data: &mut T, handle: WvrProcessHandle) -> anyhow::Result<()>; fn recenter_playspace(&mut self, data: &mut T) -> anyhow::Result<()>; fn desktop_finder<'a>(&'a mut self, data: &'a mut T) -> &'a mut DesktopFinder; + fn general_config<'a>(&'a mut self, data: &'a mut T) -> &'a mut GeneralConfig; } pub type BoxDashInterface = Box>; diff --git a/wlx-common/src/dash_interface_emulated.rs b/wlx-common/src/dash_interface_emulated.rs index 3b44ae81..dfd40278 100644 --- a/wlx-common/src/dash_interface_emulated.rs +++ b/wlx-common/src/dash_interface_emulated.rs @@ -3,7 +3,7 @@ use wayvr_ipc::{ packet_server::{WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle}, }; -use crate::{dash_interface::DashInterface, desktop_finder::DesktopFinder, gen_id}; +use crate::{config::GeneralConfig, dash_interface::DashInterface, desktop_finder::DesktopFinder, gen_id}; #[derive(Debug)] pub struct EmuProcess { @@ -55,6 +55,7 @@ pub struct DashInterfaceEmulated { processes: EmuProcessVec, windows: EmuWindowVec, desktop_finder: DesktopFinder, + general_config: GeneralConfig, } impl DashInterfaceEmulated { @@ -73,10 +74,14 @@ impl DashInterfaceEmulated { let mut desktop_finder = DesktopFinder::new(); desktop_finder.refresh(); + // Use serde defaults + let general_config = serde_json::from_str("{}").unwrap(); + Self { processes, windows, desktop_finder, + general_config, } } } @@ -169,4 +174,8 @@ impl DashInterface<()> for DashInterfaceEmulated { fn desktop_finder<'a>(&'a mut self, _: &'a mut ()) -> &'a mut DesktopFinder { &mut self.desktop_finder } + + fn general_config<'a>(&'a mut self, _: &'a mut ()) -> &'a mut crate::config::GeneralConfig { + &mut self.general_config + } } diff --git a/wlx-overlay-s/src/backend/wayvr/mod.rs b/wlx-overlay-s/src/backend/wayvr/mod.rs index cdba7a4e..8963271c 100644 --- a/wlx-overlay-s/src/backend/wayvr/mod.rs +++ b/wlx-overlay-s/src/backend/wayvr/mod.rs @@ -107,7 +107,7 @@ impl BlitMethod { } pub struct Config { - pub click_freeze_time_ms: u32, + pub click_freeze_time_ms: i32, pub keyboard_repeat_delay_ms: u32, pub keyboard_repeat_rate: u32, pub auto_hide_delay: Option, // if None, auto-hide is disabled diff --git a/wlx-overlay-s/src/overlays/dashboard.rs b/wlx-overlay-s/src/overlays/dashboard.rs index d345cb44..b0f5336f 100644 --- a/wlx-overlay-s/src/overlays/dashboard.rs +++ b/wlx-overlay-s/src/overlays/dashboard.rs @@ -1,7 +1,4 @@ -use dash_frontend::{ - frontend::{self, FrontendTask, FrontendUpdateParams}, - settings::{self, SettingsIO}, -}; +use dash_frontend::frontend::{self, FrontendTask, FrontendUpdateParams}; use glam::{Affine2, Affine3A, Vec2, vec2, vec3}; use wayvr_ipc::{ packet_client::WvrProcessLaunchParams, @@ -26,7 +23,6 @@ use wlx_common::{ }; use crate::{ - app_misc, backend::{ input::{Haptics, HoverResult, PointerHit, PointerMode}, task::{OverlayTask, PlayspaceTask, TaskType}, @@ -53,43 +49,6 @@ pub const DASH_NAME: &str = "Dashboard"; const DASH_RES_U32A: [u32; 2] = [1920, 1080]; const DASH_RES_VEC2: Vec2 = vec2(DASH_RES_U32A[0] as _, DASH_RES_U32A[1] as _); -//FIXME: replace with proper impl -struct SimpleSettingsIO { - settings: settings::Settings, -} -impl SimpleSettingsIO { - fn new() -> Self { - let mut res = Self { - settings: settings::Settings::default(), - }; - res.read_from_disk(); - res - } -} -impl settings::SettingsIO for SimpleSettingsIO { - fn get_mut(&mut self) -> &mut settings::Settings { - &mut self.settings - } - fn get(&self) -> &dash_frontend::settings::Settings { - &self.settings - } - fn save_to_disk(&mut self) { - log::info!("saving settings"); - let data = self.settings.save(); - std::fs::write("/tmp/testbed_settings.json", data).unwrap(); - } - fn read_from_disk(&mut self) { - log::info!("loading settings"); - if let Ok(res) = std::fs::read("/tmp/testbed_settings.json") { - let data = String::from_utf8(res).unwrap(); - self.settings = settings::Settings::load(&data).unwrap(); - } - } - fn mark_as_dirty(&mut self) { - self.save_to_disk(); - } -} - pub struct DashFrontend { inner: frontend::Frontend, initialized: bool, @@ -103,13 +62,14 @@ const GUI_SCALE: f32 = 2.0; impl DashFrontend { fn new(app: &mut AppState) -> anyhow::Result { - let settings = SimpleSettingsIO::new(); let interface = DashInterfaceLive::new(); - let frontend = frontend::Frontend::new(frontend::InitParams { - settings: Box::new(settings), - interface: Box::new(interface), - })?; + let frontend = frontend::Frontend::new( + frontend::InitParams { + interface: Box::new(interface), + }, + app, + )?; frontend .tasks @@ -448,4 +408,11 @@ impl DashInterface for DashInterfaceLive { ) -> &'a mut wlx_common::desktop_finder::DesktopFinder { &mut data.desktop_finder } + + fn general_config<'a>( + &'a mut self, + data: &'a mut AppState, + ) -> &'a mut wlx_common::config::GeneralConfig { + &mut data.session.config + } } diff --git a/wlx-overlay-s/src/overlays/screen/backend.rs b/wlx-overlay-s/src/overlays/screen/backend.rs index 8c7bc6c7..0e882315 100644 --- a/wlx-overlay-s/src/overlays/screen/backend.rs +++ b/wlx-overlay-s/src/overlays/screen/backend.rs @@ -266,7 +266,7 @@ impl OverlayBackend for ScreenBackend { { let pos = self.mouse_transform.transform_point2(hit.uv); app.hid_provider.inner.mouse_move(pos); - set_next_move(u64::from(app.session.config.mouse_move_interval_ms)); + set_next_move(app.session.config.mouse_move_interval_ms as _); } HoverResult { consume: true, @@ -290,7 +290,7 @@ impl OverlayBackend for ScreenBackend { } if pressed { - set_next_move(u64::from(app.session.config.click_freeze_time_ms)); + set_next_move(app.session.config.click_freeze_time_ms as _); } app.hid_provider.inner.send_button(btn, pressed);