From 18f99b5dafb66cf7f166840ac6ee348c80851bac Mon Sep 17 00:00:00 2001 From: Aleksander Date: Fri, 3 Apr 2026 00:11:26 +0200 Subject: [PATCH] dash-frontend: settings: use generics instead of macros, separate each settings tab by a separate file --- dash-frontend/src/tab/settings/macros.rs | 323 ++++++++++++++ .../src/tab/{settings.rs => settings/mod.rs} | 413 +----------------- .../src/tab/settings/tab_autostart_apps.rs | 19 + .../src/tab/settings/tab_controls.rs | 23 + .../src/tab/settings/tab_features.rs | 19 + .../src/tab/settings/tab_look_and_feel.rs | 22 + dash-frontend/src/tab/settings/tab_misc.rs | 15 + .../src/tab/settings/tab_troubleshooting.rs | 45 ++ wgui/src/windowing/context_menu.rs | 1 + 9 files changed, 486 insertions(+), 394 deletions(-) create mode 100644 dash-frontend/src/tab/settings/macros.rs rename dash-frontend/src/tab/{settings.rs => settings/mod.rs} (59%) create mode 100644 dash-frontend/src/tab/settings/tab_autostart_apps.rs create mode 100644 dash-frontend/src/tab/settings/tab_controls.rs create mode 100644 dash-frontend/src/tab/settings/tab_features.rs create mode 100644 dash-frontend/src/tab/settings/tab_look_and_feel.rs create mode 100644 dash-frontend/src/tab/settings/tab_misc.rs create mode 100644 dash-frontend/src/tab/settings/tab_troubleshooting.rs diff --git a/dash-frontend/src/tab/settings/macros.rs b/dash-frontend/src/tab/settings/macros.rs new file mode 100644 index 00000000..8c761412 --- /dev/null +++ b/dash-frontend/src/tab/settings/macros.rs @@ -0,0 +1,323 @@ +use std::{collections::HashMap, rc::Rc}; + +use crate::tab::settings::{self, SettingType, Task, horiz_cell, mount_requires_restart}; +use wgui::{ + components::{ + button::{ButtonClickEvent, ComponentButton}, + checkbox::ComponentCheckbox, + slider::ComponentSlider, + }, + layout::{Layout, WidgetID}, + parser::{Fetchable, ParseDocumentParams, ParserState}, + task::Tasks, + widget::label::WidgetLabel, + windowing::context_menu, +}; +use wlx_common::config::GeneralConfig; + +pub fn options_category( + mp: &mut MacroParams, + parent: WidgetID, + translation: &str, + icon: &str, +) -> anyhow::Result { + let id = mp.idx.to_string(); + mp.idx += 1; + + 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())); + + mp.parser_state + .instantiate_template(mp.doc_params, "SettingsGroupBox", mp.layout, parent, params)?; + + mp.parser_state.get_widget_id(&id) +} + +pub fn options_checkbox(mp: &mut MacroParams, parent: WidgetID, setting: SettingType) -> anyhow::Result<()> { + 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 checked = if *setting.mut_bool(mp.config) { "1" } else { "0" }; + params.insert(Rc::from("checked"), Rc::from(checked)); + + let id_cell = horiz_cell(mp.layout, parent)?; + + mp.parser_state + .instantiate_template(mp.doc_params, "CheckBoxSetting", mp.layout, id_cell, params)?; + + if setting.requires_restart() { + mount_requires_restart(mp.layout, id_cell)?; + } + + 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(()) + } + })); + + Ok(()) +} + +pub fn options_slider_f32( + mp: &mut MacroParams, + parent: WidgetID, + setting: SettingType, + min: f32, + max: f32, + step: f32, +) -> anyhow::Result<()> { + 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())); + + let id_cell = horiz_cell(mp.layout, parent)?; + + mp.parser_state + .instantiate_template(mp.doc_params, "SliderSetting", mp.layout, id_cell, params)?; + + if setting.requires_restart() { + mount_requires_restart(mp.layout, id_cell)?; + } + + 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(()) + } + })); + + Ok(()) +} + +pub fn options_slider_i32( + mp: &mut MacroParams, + parent: WidgetID, + setting: SettingType, + min: i32, + max: i32, + step: i32, +) -> anyhow::Result<()> { + 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 id_cell = horiz_cell(mp.layout, parent)?; + + 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, id_cell, params)?; + + if setting.requires_restart() { + mount_requires_restart(mp.layout, id_cell)?; + } + + 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(()) + } + })); + Ok(()) +} + +pub fn options_dropdown( + mp: &mut MacroParams, + parent: WidgetID, + setting: &'static SettingType, +) -> anyhow::Result<()> +where + EnumType: strum::VariantArray + strum::EnumProperty + std::convert::AsRef + Copy + 'static, +{ + 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 id_cell = horiz_cell(mp.layout, parent)?; + + mp.parser_state + .instantiate_template(mp.doc_params, "DropdownButton", mp.layout, id_cell, params)?; + + if setting.requires_restart() { + mount_requires_restart(mp.layout, id_cell)?; + } + + let setting_str = setting.as_ref(); + let title = setting.get_enum_title(mp.config); + + { + let mut label = mp + .parser_state + .fetch_widget_as::(&mp.layout.state, &format!("{id}_value"))?; + label.set_text_simple(&mut mp.layout.state.globals.get(), title); + } + + let btn = mp.parser_state.fetch_component_as::(&id)?; + btn.on_click(Rc::new({ + let tasks = mp.tasks.clone(); + move |_common, e: ButtonClickEvent| { + tasks.push(Task::OpenContextMenu( + e.mouse_pos_absolute.unwrap_or_default(), + EnumType::VARIANTS + .iter() + .filter_map(|item| { + if item.get_bool("Hidden").unwrap_or(false) { + return None; + } + + let value = item.as_ref(); + let title = SettingType::get_enum_title_inner(*item); + let tooltip = SettingType::get_enum_tooltip_inner(*item); + + let text = &title.text; + let translated = if title.translated { "1" } else { "0" }; + + Some(context_menu::Cell { + action_name: Some(format!("{setting_str};{id};{value};{text};{translated}").into()), + title, + tooltip, + attribs: vec![], + }) + }) + .collect(), + )); + Ok(()) + } + })); + + Ok(()) +} + +pub fn options_danger_button( + mp: &mut MacroParams, + parent: WidgetID, + translation: &str, + icon: &str, + task: Task, +) -> anyhow::Result<()> { + 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())); + params.insert(Rc::from("translation"), Rc::from(translation)); + params.insert(Rc::from("icon"), Rc::from(icon)); + + mp.parser_state + .instantiate_template(mp.doc_params, "DangerButton", mp.layout, parent, params)?; + + let btn = mp.parser_state.fetch_component_as::(&id)?; + btn.on_click(Rc::new({ + let tasks = mp.tasks.clone(); + move |_common, _e| { + tasks.push(task.clone()); + Ok(()) + } + })); + + Ok(()) +} + +pub fn options_autostart_app( + mp: &mut MacroParams, + parent: WidgetID, + text: &str, + ids: &mut Vec>, +) -> anyhow::Result<()> { + 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())); + params.insert(Rc::from("text"), Rc::from(text)); + + mp.parser_state + .instantiate_template(mp.doc_params, "AutostartApp", mp.layout, parent, params)?; + + let btn = mp.parser_state.fetch_component_as::(&id)?; + let id: Rc = Rc::from(id); + + ids.push(id.clone()); + + btn.on_click(Rc::new({ + let tasks = mp.tasks.clone(); + move |_common, _e| { + tasks.push(Task::RemoveAutostartApp(id.clone())); + Ok(()) + } + })); + Ok(()) +} + +pub struct MacroParams<'a> { + pub layout: &'a mut Layout, + pub parser_state: &'a mut ParserState, + pub doc_params: &'a ParseDocumentParams<'a>, + pub config: &'a mut GeneralConfig, + pub tasks: Tasks, + pub idx: usize, +} diff --git a/dash-frontend/src/tab/settings.rs b/dash-frontend/src/tab/settings/mod.rs similarity index 59% rename from dash-frontend/src/tab/settings.rs rename to dash-frontend/src/tab/settings/mod.rs index 32a1a2c4..bcd79abf 100644 --- a/dash-frontend/src/tab/settings.rs +++ b/dash-frontend/src/tab/settings/mod.rs @@ -1,15 +1,9 @@ -use std::{collections::HashMap, marker::PhantomData, rc::Rc, str::FromStr}; - use glam::Vec2; -use strum::{AsRefStr, EnumProperty, EnumString, VariantArray}; +use std::{marker::PhantomData, rc::Rc, str::FromStr}; +use strum::{AsRefStr, EnumProperty, EnumString}; use wgui::{ assets::AssetPath, - components::{ - button::{ButtonClickEvent, ComponentButton}, - checkbox::ComponentCheckbox, - slider::ComponentSlider, - tabs::ComponentTabs, - }, + components::tabs::ComponentTabs, drawing, event::{CallbackDataCommon, EventAlterables}, globals::WguiGlobals, @@ -30,9 +24,17 @@ use wlx_common::{config::GeneralConfig, config_io::ConfigRoot, dash_interface::R use crate::{ frontend::{Frontend, FrontendTask}, - tab::{Tab, TabType}, + tab::{Tab, TabType, settings::macros::MacroParams}, }; +mod macros; +mod tab_autostart_apps; +mod tab_controls; +mod tab_features; +mod tab_look_and_feel; +mod tab_misc; +mod tab_troubleshooting; + #[derive(Clone)] enum TabNameEnum { LookAndFeel, @@ -57,6 +59,7 @@ impl TabNameEnum { } } +#[derive(Clone)] enum Task { UpdateBool(SettingType, bool), UpdateFloat(SettingType, f32), @@ -484,287 +487,6 @@ fn mount_requires_restart(layout: &mut Layout, parent: WidgetID) -> anyhow::Resu Ok(()) } -macro_rules! category { - ($pe:expr, $root:expr, $translation:expr, $icon:expr) => {{ - let id = $pe.idx.to_string(); - $pe.idx += 1; - - 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())); - - $pe - .parser_state - .instantiate_template($pe.doc_params, "SettingsGroupBox", $pe.layout, $root, params)?; - - $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)); - } - - let checked = if *$setting.mut_bool($mp.config) { "1" } else { "0" }; - params.insert(Rc::from("checked"), Rc::from(checked)); - - let id_cell = horiz_cell($mp.layout, $root)?; - - $mp - .parser_state - .instantiate_template($mp.doc_params, "CheckBoxSetting", $mp.layout, id_cell, params)?; - - if $setting.requires_restart() { - mount_requires_restart($mp.layout, id_cell)?; - } - - 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())); - - let id_cell = horiz_cell($mp.layout, $root)?; - - $mp - .parser_state - .instantiate_template($mp.doc_params, "SliderSetting", $mp.layout, id_cell, params)?; - - if $setting.requires_restart() { - mount_requires_restart($mp.layout, id_cell)?; - } - - 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 id_cell = horiz_cell($mp.layout, $root)?; - - 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, id_cell, params)?; - - if $setting.requires_restart() { - mount_requires_restart($mp.layout, id_cell)?; - } - - 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(()) - } - })); - }; -} - -macro_rules! dropdown { - ($mp:expr /* `MacroParams` struct */, $root:expr, $setting:expr, $options: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 id_cell = horiz_cell($mp.layout, $root)?; - - $mp - .parser_state - .instantiate_template($mp.doc_params, "DropdownButton", $mp.layout, id_cell, params)?; - - if $setting.requires_restart() { - mount_requires_restart($mp.layout, id_cell)?; - } - - let setting_str = $setting.as_ref(); - let title = $setting.get_enum_title($mp.config); - - { - let mut label = $mp - .parser_state - .fetch_widget_as::(&$mp.layout.state, &format!("{id}_value"))?; - label.set_text_simple(&mut $mp.layout.state.globals.get(), title); - } - - let btn = $mp.parser_state.fetch_component_as::(&id)?; - btn.on_click(Rc::new({ - let tasks = $mp.tasks.clone(); - move |_common, e: ButtonClickEvent| { - tasks.push(Task::OpenContextMenu( - e.mouse_pos_absolute.unwrap_or_default(), - $options - .iter() - .filter_map(|item| { - if item.get_bool("Hidden").unwrap_or(false) { - return None; - } - - let value = item.as_ref(); - let title = SettingType::get_enum_title_inner(*item); - let tooltip = SettingType::get_enum_tooltip_inner(*item); - - let text = &title.text; - let translated = if title.translated { "1" } else { "0" }; - - Some(context_menu::Cell { - action_name: Some(format!("{setting_str};{id};{value};{text};{translated}").into()), - title, - tooltip, - attribs: vec![], - }) - }) - .collect(), - )); - Ok(()) - } - })); - }; -} - -macro_rules! danger_button { - ($mp:expr, $root:expr, $translation:expr, $icon:expr, $task: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())); - params.insert(Rc::from("translation"), Rc::from($translation)); - params.insert(Rc::from("icon"), Rc::from($icon)); - - $mp - .parser_state - .instantiate_template($mp.doc_params, "DangerButton", $mp.layout, $root, params)?; - - let btn = $mp.parser_state.fetch_component_as::(&id)?; - btn.on_click(Rc::new({ - let tasks = $mp.tasks.clone(); - move |_common, _e| { - tasks.push($task); - Ok(()) - } - })); - }; -} - -macro_rules! autostart_app { - ($mp:expr, $root:expr, $text:expr, $ids: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())); - params.insert(Rc::from("text"), Rc::from($text.as_str())); - - $mp - .parser_state - .instantiate_template($mp.doc_params, "AutostartApp", $mp.layout, $root, params)?; - - let btn = $mp.parser_state.fetch_component_as::(&id)?; - let id: Rc = Rc::from(id); - - $ids.push(id.clone()); - - btn.on_click(Rc::new({ - let tasks = $mp.tasks.clone(); - move |_common, _e| { - tasks.push(Task::RemoveAutostartApp(id.clone())); - 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, -} - fn doc_params(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> { ParseDocumentParams { globals: globals.clone(), @@ -790,119 +512,22 @@ impl TabSettings { match name { TabNameEnum::LookAndFeel => { - let c = category!(mp, root, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/palette.svg")?; - dropdown!(mp, c, SettingType::Language, wlx_common::locale::Language::VARIANTS); - checkbox!(mp, c, SettingType::OpaqueBackground); - checkbox!(mp, c, SettingType::HideUsername); - checkbox!(mp, c, SettingType::HideGrabHelp); - slider_f32!(mp, c, SettingType::UiAnimationSpeed, 0.5, 5.0, 0.1); // min, max, step - slider_f32!(mp, c, SettingType::UiGradientIntensity, 0.0, 1.0, 0.05); // min, max, step - slider_f32!(mp, c, SettingType::UiRoundMultiplier, 0.5, 5.0, 0.1); - checkbox!(mp, c, SettingType::SetsOnWatch); - checkbox!(mp, c, SettingType::UseSkybox); - slider_f32!(mp, c, SettingType::GridOpacity, 0.0, 1.0, 0.05); // min, max, step - checkbox!(mp, c, SettingType::UsePassthrough); - checkbox!(mp, c, SettingType::Clock12h); + tab_look_and_feel::mount(&mut mp, root)?; } TabNameEnum::Features => { - let c = category!(mp, root, "APP_SETTINGS.FEATURES", "dashboard/options.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); - checkbox!(mp, c, SettingType::BlockPosesOnKbdInteraction); + tab_features::mount(&mut mp, root)?; } TabNameEnum::Controls => { - let c = category!(mp, root, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?; - dropdown!( - mp, - c, - SettingType::KeyboardMiddleClick, - wlx_common::config::AltModifier::VARIANTS - ); - dropdown!( - mp, - c, - SettingType::HandsfreePointer, - wlx_common::config::HandsfreePointer::VARIANTS - ); - 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); + tab_controls::mount(&mut mp, root)?; } TabNameEnum::Misc => { - let c = category!(mp, root, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?; - dropdown!( - mp, - c, - SettingType::CaptureMethod, - wlx_common::config::CaptureMethod::VARIANTS - ); - checkbox!(mp, c, SettingType::XwaylandByDefault); - checkbox!(mp, c, SettingType::UprightScreenFix); - checkbox!(mp, c, SettingType::DoubleCursorFix); - checkbox!(mp, c, SettingType::ScreenRenderDown); + tab_misc::mount(&mut mp, root)?; } TabNameEnum::AutostartApps => { - self.app_button_ids = vec![]; - - if !mp.config.autostart_apps.is_empty() { - let c = category!(mp, root, "APP_SETTINGS.AUTOSTART_APPS", "dashboard/apps.svg")?; - - for app in &mp.config.autostart_apps { - autostart_app!(mp, c, app.name, self.app_button_ids); - } - } + tab_autostart_apps::mount(&mut mp, root, &mut self.app_button_ids)?; } TabNameEnum::Troubleshooting => { - let c = category!(mp, root, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/cpu.svg")?; - danger_button!( - mp, - c, - "APP_SETTINGS.RESET_PLAYSPACE", - "dashboard/recenter.svg", - Task::ResetPlayspace - ); - danger_button!( - mp, - c, - "APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS", - "dashboard/display.svg", - Task::ClearPipewireTokens - ); - danger_button!( - mp, - c, - "APP_SETTINGS.CLEAR_SAVED_STATE", - "dashboard/binary.svg", - Task::ClearSavedState - ); - danger_button!( - mp, - c, - "APP_SETTINGS.DELETE_ALL_CONFIGS", - "dashboard/circle.svg", - Task::DeleteAllConfigs - ); - danger_button!( - mp, - c, - "APP_SETTINGS.RESTART_SOFTWARE", - "dashboard/refresh.svg", - Task::RestartSoftware - ); + tab_troubleshooting::mount(&mut mp, root)?; } } diff --git a/dash-frontend/src/tab/settings/tab_autostart_apps.rs b/dash-frontend/src/tab/settings/tab_autostart_apps.rs new file mode 100644 index 00000000..6483fc94 --- /dev/null +++ b/dash-frontend/src/tab/settings/tab_autostart_apps.rs @@ -0,0 +1,19 @@ +use std::rc::Rc; + +use crate::tab::settings::macros::{MacroParams, options_autostart_app, options_category}; +use wgui::layout::WidgetID; + +pub fn mount(mp: &mut MacroParams, parent: WidgetID, app_button_ids: &mut Vec>) -> anyhow::Result<()> { + *app_button_ids = Vec::new(); + + if !mp.config.autostart_apps.is_empty() { + let c = options_category(mp, parent, "APP_SETTINGS.AUTOSTART_APPS", "dashboard/apps.svg")?; + + // todo: prevent clone + let autostart_apps = mp.config.autostart_apps.clone(); + for app in autostart_apps { + options_autostart_app(mp, c, &app.name, app_button_ids)?; + } + } + Ok(()) +} diff --git a/dash-frontend/src/tab/settings/tab_controls.rs b/dash-frontend/src/tab/settings/tab_controls.rs new file mode 100644 index 00000000..27b1f10b --- /dev/null +++ b/dash-frontend/src/tab/settings/tab_controls.rs @@ -0,0 +1,23 @@ +use crate::tab::settings::{ + SettingType, + macros::{MacroParams, options_category, options_checkbox, options_dropdown, options_slider_f32, options_slider_i32}, +}; +use wgui::layout::WidgetID; + +pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> { + let c = options_category(mp, parent, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?; + options_dropdown::(mp, c, &SettingType::KeyboardMiddleClick)?; + options_dropdown::(mp, c, &SettingType::HandsfreePointer)?; + options_checkbox(mp, c, SettingType::FocusFollowsMouseMode)?; + options_checkbox(mp, c, SettingType::LeftHandedMouse)?; + options_checkbox(mp, c, SettingType::AllowSliding)?; + options_checkbox(mp, c, SettingType::InvertScrollDirectionX)?; + options_checkbox(mp, c, SettingType::InvertScrollDirectionY)?; + options_slider_f32(mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1)?; + options_slider_f32(mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1)?; + options_slider_f32(mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1)?; + options_slider_f32(mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1)?; + options_slider_f32(mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1)?; + options_slider_i32(mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50)?; + Ok(()) +} diff --git a/dash-frontend/src/tab/settings/tab_features.rs b/dash-frontend/src/tab/settings/tab_features.rs new file mode 100644 index 00000000..eb0e9865 --- /dev/null +++ b/dash-frontend/src/tab/settings/tab_features.rs @@ -0,0 +1,19 @@ +use crate::tab::settings::{ + SettingType, + macros::{MacroParams, options_category, options_checkbox, options_slider_f32}, +}; +use wgui::layout::WidgetID; + +pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> { + let c = options_category(mp, parent, "APP_SETTINGS.FEATURES", "dashboard/options.svg")?; + options_checkbox(mp, c, SettingType::NotificationsEnabled)?; + options_checkbox(mp, c, SettingType::NotificationsSoundEnabled)?; + options_checkbox(mp, c, SettingType::KeyboardSoundEnabled)?; + options_checkbox(mp, c, SettingType::SpaceDragUnlocked)?; + options_checkbox(mp, c, SettingType::SpaceRotateUnlocked)?; + options_slider_f32(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5)?; + options_checkbox(mp, c, SettingType::BlockGameInput)?; + options_checkbox(mp, c, SettingType::BlockGameInputIgnoreWatch)?; + options_checkbox(mp, c, SettingType::BlockPosesOnKbdInteraction)?; + Ok(()) +} diff --git a/dash-frontend/src/tab/settings/tab_look_and_feel.rs b/dash-frontend/src/tab/settings/tab_look_and_feel.rs new file mode 100644 index 00000000..6fe40420 --- /dev/null +++ b/dash-frontend/src/tab/settings/tab_look_and_feel.rs @@ -0,0 +1,22 @@ +use crate::tab::settings::{ + SettingType, + macros::{MacroParams, options_category, options_checkbox, options_dropdown, options_slider_f32}, +}; +use wgui::layout::WidgetID; + +pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> { + let c = options_category(mp, parent, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/palette.svg")?; + options_dropdown::(mp, c, &SettingType::Language)?; + options_checkbox(mp, c, SettingType::OpaqueBackground)?; + options_checkbox(mp, c, SettingType::HideUsername)?; + options_checkbox(mp, c, SettingType::HideGrabHelp)?; + options_slider_f32(mp, c, SettingType::UiAnimationSpeed, 0.5, 5.0, 0.1)?; // min, max, step + options_slider_f32(mp, c, SettingType::UiGradientIntensity, 0.0, 1.0, 0.05)?; // min, max, step + options_slider_f32(mp, c, SettingType::UiRoundMultiplier, 0.5, 5.0, 0.1)?; + options_checkbox(mp, c, SettingType::SetsOnWatch)?; + options_checkbox(mp, c, SettingType::UseSkybox)?; + options_slider_f32(mp, c, SettingType::GridOpacity, 0.0, 1.0, 0.05)?; // min, max, step + options_checkbox(mp, c, SettingType::UsePassthrough)?; + options_checkbox(mp, c, SettingType::Clock12h)?; + Ok(()) +} diff --git a/dash-frontend/src/tab/settings/tab_misc.rs b/dash-frontend/src/tab/settings/tab_misc.rs new file mode 100644 index 00000000..9fa6f429 --- /dev/null +++ b/dash-frontend/src/tab/settings/tab_misc.rs @@ -0,0 +1,15 @@ +use crate::tab::settings::{ + SettingType, + macros::{MacroParams, options_category, options_checkbox, options_dropdown}, +}; +use wgui::layout::WidgetID; + +pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> { + let c = options_category(mp, parent, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?; + options_dropdown::(mp, c, &SettingType::CaptureMethod)?; + options_checkbox(mp, c, SettingType::XwaylandByDefault)?; + options_checkbox(mp, c, SettingType::UprightScreenFix)?; + options_checkbox(mp, c, SettingType::DoubleCursorFix)?; + options_checkbox(mp, c, SettingType::ScreenRenderDown)?; + Ok(()) +} diff --git a/dash-frontend/src/tab/settings/tab_troubleshooting.rs b/dash-frontend/src/tab/settings/tab_troubleshooting.rs new file mode 100644 index 00000000..9e16fd4b --- /dev/null +++ b/dash-frontend/src/tab/settings/tab_troubleshooting.rs @@ -0,0 +1,45 @@ +use crate::tab::settings::{ + Task, + macros::{MacroParams, options_category, options_danger_button}, +}; +use wgui::layout::WidgetID; + +pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> { + let c = options_category(mp, parent, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/cpu.svg")?; + options_danger_button( + mp, + c, + "APP_SETTINGS.RESET_PLAYSPACE", + "dashboard/recenter.svg", + Task::ResetPlayspace, + )?; + options_danger_button( + mp, + c, + "APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS", + "dashboard/display.svg", + Task::ClearPipewireTokens, + )?; + options_danger_button( + mp, + c, + "APP_SETTINGS.CLEAR_SAVED_STATE", + "dashboard/binary.svg", + Task::ClearSavedState, + )?; + options_danger_button( + mp, + c, + "APP_SETTINGS.DELETE_ALL_CONFIGS", + "dashboard/circle.svg", + Task::DeleteAllConfigs, + )?; + options_danger_button( + mp, + c, + "APP_SETTINGS.RESTART_SOFTWARE", + "dashboard/refresh.svg", + Task::RestartSoftware, + )?; + Ok(()) +} diff --git a/wgui/src/windowing/context_menu.rs b/wgui/src/windowing/context_menu.rs index 15982d7d..6852604e 100644 --- a/wgui/src/windowing/context_menu.rs +++ b/wgui/src/windowing/context_menu.rs @@ -13,6 +13,7 @@ use crate::{ windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra}, }; +#[derive(Clone)] pub struct Cell { pub title: Translation, pub tooltip: Option,