diff --git a/Cargo.lock b/Cargo.lock index 79e09859..2e8f4b67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6930,6 +6930,7 @@ dependencies = [ "serde", "serde_json", "smol", + "strum", "walkdir", "wayvr-ipc", "wgui", diff --git a/dash-frontend/assets/dashboard/down.svg b/dash-frontend/assets/dashboard/down.svg new file mode 120000 index 00000000..5ab12e3e --- /dev/null +++ b/dash-frontend/assets/dashboard/down.svg @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/keyboard/down.svg \ No newline at end of file diff --git a/dash-frontend/assets/gui/t_dropdown_button.xml b/dash-frontend/assets/gui/t_dropdown_button.xml new file mode 100644 index 00000000..d9604071 --- /dev/null +++ b/dash-frontend/assets/gui/t_dropdown_button.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/dash-frontend/assets/gui/tab/settings.xml b/dash-frontend/assets/gui/tab/settings.xml index cb29c7e9..b2b635f7 100644 --- a/dash-frontend/assets/gui/tab/settings.xml +++ b/dash-frontend/assets/gui/tab/settings.xml @@ -1,6 +1,7 @@ + diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index 0516089d..77e1bb57 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -69,7 +69,16 @@ "CLEAR_SAVED_STATE_HELP": "Reset sets & overlay positions", "CLEAR_PIPEWIRE_TOKENS_HELP": "Prompt for screen selection on next start", "DELETE_ALL_CONFIGS_HELP": "Remove all configuration files from conf.d", - "RESTART_SOFTWARE_HELP": "Apply settings that require a restart" + "RESTART_SOFTWARE_HELP": "Apply settings that require a restart", + + "CAPTURE_METHOD": "Wayland screen capture", + "CAPTURE_METHOD_HELP": "Try changing this if experiencing\nblack or glitchy screens", + + "OPTION": { + "PIPEWIRE_HELP": "Fast GPU capture. Recommended", + "PW_FALLBACK_HELP": "Slow. Try in case PipeWire GPU doesn't work", + "SCREENCOPY_HELP": "Slow. Works on: Hyprland, Niri, River, Sway" + } }, "APPLICATION_LAUNCHER": "Application launcher", "APPLICATION_STARTED": "Application started", diff --git a/dash-frontend/src/tab/settings.rs b/dash-frontend/src/tab/settings.rs index f50375c2..d26bd9c2 100644 --- a/dash-frontend/src/tab/settings.rs +++ b/dash-frontend/src/tab/settings.rs @@ -1,13 +1,18 @@ -use std::{collections::HashMap, marker::PhantomData, rc::Rc}; +use std::{collections::HashMap, marker::PhantomData, rc::Rc, str::FromStr}; -use strum::AsRefStr; +use glam::Vec2; +use strum::{AsRefStr, EnumProperty, EnumString, VariantArray}; use wgui::{ assets::AssetPath, components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider}, + event::{CallbackDataCommon, EventAlterables}, + i18n::Translation, layout::{Layout, WidgetID}, log::LogErr, parser::{Fetchable, ParseDocumentParams, ParserState}, task::Tasks, + widget::label::WidgetLabel, + windowing::context_menu::{self, Blueprint, ContextMenu, TickResult}, }; use wlx_common::{config::GeneralConfig, config_io::ConfigRoot}; @@ -20,6 +25,7 @@ enum Task { UpdateBool(SettingType, bool), UpdateFloat(SettingType, f32), UpdateInt(SettingType, i32), + OpenContextMenu(Vec2, Vec), ClearPipewireTokens, ClearSavedState, DeleteAllConfigs, @@ -30,6 +36,8 @@ pub struct TabSettings { #[allow(dead_code)] pub state: ParserState, + context_menu: ContextMenu, + tasks: Tasks, marker: PhantomData, } @@ -76,17 +84,56 @@ impl Tab for TabSettings { frontend.interface.restart(data); return Ok(()); } + Task::OpenContextMenu(position, cells) => { + self.context_menu.open(context_menu::OpenParams { + on_custom_attribs: None, + position, + blueprint: Blueprint::Cells(cells), + }); + } } } + + // Dropdown handling + if let TickResult::Action(name) = self.context_menu.tick(&mut frontend.layout, &mut self.state)? { + if let (Some(setting), Some(id), Some(value), Some(text), Some(translated)) = { + let mut s = name.splitn(5, ';'); + (s.next(), s.next(), s.next(), s.next(), s.next()) + } { + let mut label = self + .state + .fetch_widget_as::(&frontend.layout.state, &format!("{id}_value"))?; + + let mut alterables = EventAlterables::default(); + let mut common = CallbackDataCommon { + alterables: &mut alterables, + state: &frontend.layout.state, + }; + + let translation = Translation { + text: text.into(), + translated: translated == "1", + }; + + label.set_text(&mut common, translation); + + let setting = SettingType::from_str(setting).expect("Invalid Enum string"); + setting.set_enum(config, value); + changed = true; + } + } + + // Notify overlays of the change if changed { frontend.interface.config_changed(data); } + Ok(()) } } #[allow(clippy::enum_variant_names)] -#[derive(Clone, Copy, AsRefStr)] +#[derive(Clone, Copy, AsRefStr, EnumString)] enum SettingType { AnimationSpeed, RoundMultiplier, @@ -120,6 +167,7 @@ enum SettingType { HideUsername, OpaqueBackground, XwaylandByDefault, + CaptureMethod, } impl SettingType { @@ -173,6 +221,40 @@ impl SettingType { } } + pub fn set_enum<'a>(self, config: &'a mut GeneralConfig, value: &str) { + match self { + Self::CaptureMethod => { + config.capture_method = wlx_common::config::CaptureMethod::from_str(value).expect("Invalid enum value!") + } + _ => panic!("Requested enum for non-enum SettingType"), + } + } + + fn get_enum_title<'a>(self, config: &'a mut GeneralConfig) -> Translation { + match self { + Self::CaptureMethod => Self::get_enum_title_inner(config.capture_method), + _ => panic!("Requested enum for non-enum SettingType"), + } + } + + fn get_enum_title_inner(value: E) -> Translation + where + E: EnumProperty + AsRef, + { + value + .get_str("Translation") + .map(|x| Translation::from_translation_key(x)) + .or_else(|| value.get_str("Text").map(|x| Translation::from_raw_text(x))) + .unwrap_or_else(|| Translation::from_raw_text(value.as_ref())) + } + + fn get_enum_tooltip_inner(value: E) -> Option + where + E: EnumProperty + AsRef, + { + value.get_str("Tooltip").map(|x| Translation::from_translation_key(x)) + } + /// Ok is translation, Err is raw text fn get_translation(self) -> Result<&'static str, &'static str> { match self { @@ -208,6 +290,7 @@ impl SettingType { Self::HideUsername => Ok("APP_SETTINGS.HIDE_USERNAME"), Self::OpaqueBackground => Ok("APP_SETTINGS.OPAQUE_BACKGROUND"), Self::XwaylandByDefault => Ok("APP_SETTINGS.XWAYLAND_BY_DEFAULT"), + Self::CaptureMethod => Ok("APP_SETTINGS.CAPTURE_METHOD"), } } @@ -224,6 +307,7 @@ impl SettingType { 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"), + Self::CaptureMethod => Some("APP_SETTINGS.CAPTURE_METHOD_HELP"), _ => None, } } @@ -381,6 +465,72 @@ macro_rules! slider_i32 { }; } +macro_rules! dropdown { + ($mp:expr, $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)); + } + + $mp + .parser_state + .instantiate_template($mp.doc_params, "DropdownButton", $mp.layout, $root, params)?; + + 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(Box::new({ + let tasks = $mp.tasks.clone(); + move |_common, e| { + 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! button { ($mp:expr, $root:expr, $translation:expr, $icon:expr, $task:expr) => { let id = $mp.idx.to_string(); @@ -474,27 +624,33 @@ impl TabSettings { checkbox!(mp, c, SettingType::UprightScreenFix); checkbox!(mp, c, SettingType::DoubleCursorFix); checkbox!(mp, c, SettingType::ScreenRenderDown); + dropdown!( + mp, + c, + SettingType::CaptureMethod, + wlx_common::config::CaptureMethod::VARIANTS + ); let c = category!(mp, root, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/blocks.svg")?; - button!( - mp, - c, - "APP_SETTINGS.CLEAR_SAVED_STATE", - "dashboard/remove_circle.svg", - Task::ClearSavedState - ); button!( mp, c, "APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS", - "dashboard/remove_circle.svg", + "dashboard/display.svg", Task::ClearPipewireTokens ); + button!( + mp, + c, + "APP_SETTINGS.CLEAR_SAVED_STATE", + "dashboard/binary.svg", + Task::ClearSavedState + ); button!( mp, c, "APP_SETTINGS.DELETE_ALL_CONFIGS", - "dashboard/remove_circle.svg", + "dashboard/circle.svg", Task::DeleteAllConfigs ); button!( @@ -509,6 +665,7 @@ impl TabSettings { tasks: mp.tasks, state: parser_state, marker: PhantomData, + context_menu: ContextMenu::default(), }) } } diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index 68168aa3..334619b0 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -259,8 +259,10 @@ impl TestbedGeneric { on_custom_attribs: Some(Rc::new(move |custom_attribs| { log::info!("custom attribs {:?}", custom_attribs.pairs); })), - template_name: "my_context_menu".into(), - template_params: Default::default(), + blueprint: context_menu::Blueprint::Template { + template_name: "my_context_menu".into(), + template_params: Default::default(), + }, position, }); } diff --git a/wgui/assets/wgui/context_menu.xml b/wgui/assets/wgui/context_menu.xml index 4768639c..0e0cd222 100644 --- a/wgui/assets/wgui/context_menu.xml +++ b/wgui/assets/wgui/context_menu.xml @@ -1,7 +1,7 @@