From ac9f3c6d231330bfff0eff9985b349e2c5d74ab4 Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:03:47 +0900 Subject: [PATCH] working bar context menus + kbd downsize --- uidev/src/testbed/testbed_generic.rs | 4 +- wgui/assets/wgui/context_menu.xml | 4 +- wgui/src/parser/mod.rs | 7 +- wgui/src/renderer_vk/text/mod.rs | 8 +- wgui/src/windowing/context_menu.rs | 47 +++-- wgui/src/windowing/window.rs | 2 +- wlx-common/src/config.rs | 3 + wlx-overlay-s/src/assets/gui/keyboard.xml | 176 +++++++++--------- wlx-overlay-s/src/assets/lang/en.json | 10 +- wlx-overlay-s/src/gui/panel/button.rs | 72 ++++++- wlx-overlay-s/src/gui/panel/mod.rs | 39 +++- .../src/overlays/keyboard/builder.rs | 7 +- 12 files changed, 237 insertions(+), 142 deletions(-) diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index fd05bc13..68168aa3 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -23,7 +23,7 @@ use wgui::{ task::Tasks, widget::{div::WidgetDiv, label::WidgetLabel, rectangle::WidgetRectangle}, windowing::{ - context_menu, + context_menu::{self, TickResult}, window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra}, }, }; @@ -285,7 +285,7 @@ impl Testbed for TestbedGeneric { let res = data .context_menu .tick(&mut self.layout, &mut self.parser_state)?; - if let Some(action_name) = res.action_name { + if let TickResult::Action(action_name) = res { log::info!("got action: {}", action_name); } diff --git a/wgui/assets/wgui/context_menu.xml b/wgui/assets/wgui/context_menu.xml index 1e83e577..4768639c 100644 --- a/wgui/assets/wgui/context_menu.xml +++ b/wgui/assets/wgui/context_menu.xml @@ -1,7 +1,7 @@ @@ -34,7 +34,7 @@ @@ -43,9 +43,9 @@ @@ -54,9 +54,9 @@ @@ -67,149 +67,139 @@
-
-
-
- + - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- -
+ +
-
+
-
+
-
-
+
+
-
-
-
- +
+
diff --git a/wlx-overlay-s/src/assets/lang/en.json b/wlx-overlay-s/src/assets/lang/en.json index 76b4d20a..5f12528c 100644 --- a/wlx-overlay-s/src/assets/lang/en.json +++ b/wlx-overlay-s/src/assets/lang/en.json @@ -3,10 +3,12 @@ "CENTER": "Center" }, "BAR": { - "HIDE": "Hide", - "TOGGLE_IN_SET": "Toggle in set", - "CLOSE": "Close", - "FORCE_CLOSE": "Force close" + "TOGGLE_VISIBILITY": "Toggle visibility", + "RESET_POSITION": "Reset position", + "RELOAD_FROM_DISK": "Reload XML from disk", + "CLOSE_MIRROR": "Close mirror", + "CLOSE_APP": "Close app", + "FORCE_CLOSE_APP": "Force close app" }, "DEFAULT": "Default", "DISABLED": "Disabled", diff --git a/wlx-overlay-s/src/gui/panel/button.rs b/wlx-overlay-s/src/gui/panel/button.rs index 7ab3c0bd..1509976e 100644 --- a/wlx-overlay-s/src/gui/panel/button.rs +++ b/wlx-overlay-s/src/gui/panel/button.rs @@ -1,5 +1,6 @@ use std::{ cell::RefCell, + collections::HashMap, process::{Child, Command, Stdio}, rc::Rc, str::FromStr, @@ -16,19 +17,21 @@ use wgui::{ }, layout::Layout, log::LogErr, - parser::{CustomAttribsInfoOwned, Fetchable, ParserState}, + parser::{self, AttribPair, CustomAttribsInfoOwned, Fetchable, ParserState}, taffy, widget::EventResult, + windowing::context_menu::{ContextMenu, OpenParams}, }; use wlx_common::overlays::ToastTopic; use crate::{ RUNNING, backend::{ + XrBackend, task::{OverlayTask, PlayspaceTask, TaskType}, wayvr::process::KillSignal, }, - overlays::{custom::create_custom, dashboard::DASH_NAME, toast::Toast, wayvr::WvrCommand}, + overlays::{custom::create_custom, toast::Toast, wayvr::WvrCommand}, state::AppState, subsystem::hid::VirtualKey, windowing::{OverlaySelector, backend::OverlayEventData, window::OverlayCategory}, @@ -186,7 +189,8 @@ pub(super) fn setup_custom_button( layout: &mut Layout, parser_state: &ParserState, attribs: &CustomAttribsInfoOwned, - _app: &AppState, + context_menu: &Rc>, + on_custom_attribs: &parser::OnCustomAttribsFunc, button: Rc, ) { for (name, kind, test_button, test_duration) in &BUTTON_EVENTS { @@ -202,6 +206,49 @@ pub(super) fn setup_custom_button( let button = button.clone(); let callback: EventCallback = match command { + "::ContextMenuOpen" => { + let Some(template_name) = args.next() else { + log::warn!( + "{command} has incorrect arguments. Should be: {command} " + ); + return; + }; + + // pass attribs with key `_context_{name}` to the context_menu template + let mut template_params = HashMap::new(); + for AttribPair { attrib, value } in &attribs.pairs { + const PREFIX: &'static str = "_context_"; + if attrib.starts_with(PREFIX) { + template_params.insert(attrib[PREFIX.len()..].into(), value.clone()); + } + } + log::warn!("Context params: {template_params:?}"); + + let template_name: Rc = template_name.into(); + let context_menu = context_menu.clone(); + let on_custom_attribs = on_custom_attribs.clone(); + + Box::new({ + move |_common, data, _app, _| { + context_menu.borrow_mut().open(OpenParams { + on_custom_attribs: Some(on_custom_attribs.clone()), + template_name: template_name.clone(), + template_params: template_params.clone(), + position: data.metadata.get_mouse_pos_absolute().unwrap(), //want panic + }); + Ok(EventResult::Consumed) + } + }) + } + "::ContextMenuClose" => { + let context_menu = context_menu.clone(); + + Box::new(move |_common, _data, _app, _| { + context_menu.borrow_mut().close(); + + Ok(EventResult::Consumed) + }) + } "::ElementSetDisplay" => { let (Some(id), Some(value)) = (args.next(), args.next()) else { log::warn!( @@ -286,6 +333,8 @@ pub(super) fn setup_custom_button( return; }; + log::warn!("{command} {arg}"); + Box::new(move |_common, data, app, _| { if !test_button(data) || !test_duration(&button, app) { return Ok(EventResult::Pass); @@ -506,6 +555,23 @@ pub(super) fn setup_custom_button( RUNNING.store(false, Ordering::Relaxed); Ok(EventResult::Consumed) }), + "::Restart" => Box::new(move |_common, data, app, _| { + if !test_button(data) || !test_duration(&button, app) { + return Ok(EventResult::Pass); + } + + let runtime = match app.xr_backend { + XrBackend::OpenVR => "--openvr", + XrBackend::OpenXR => "--openxr", + }; + + Command::new("/proc/self/exe") + .arg(runtime) // ensure same runtime + .arg("--replace") // SIGTERM the previous process + .arg("--show"); + + Ok(EventResult::Consumed) + }), "::SendKey" => { let Some(key) = args.next().and_then(|s| VirtualKey::from_str(s).ok()) else { log::error!("{command} has bad/missing arguments"); diff --git a/wlx-overlay-s/src/gui/panel/mod.rs b/wlx-overlay-s/src/gui/panel/mod.rs index c1bdbcfe..56e6dd4e 100644 --- a/wlx-overlay-s/src/gui/panel/mod.rs +++ b/wlx-overlay-s/src/gui/panel/mod.rs @@ -14,9 +14,10 @@ use wgui::{ }, gfx::cmd::WGfxClearMode, layout::{Layout, LayoutParams, LayoutUpdateParams, WidgetID}, - parser::{CustomAttribsInfoOwned, Fetchable, ParseDocumentExtra, ParserState}, + parser::{self, CustomAttribsInfoOwned, Fetchable, ParseDocumentExtra, ParserState}, renderer_vk::context::Context as WguiContext, widget::{EventResult, label::WidgetLabel}, + windowing::context_menu::{self, ContextMenu}, }; use wlx_common::overlays::{BackendAttrib, BackendAttribValue}; use wlx_common::timestep::Timestep; @@ -59,7 +60,9 @@ pub struct GuiPanel { has_focus: [bool; 2], last_content_size: Vec2, custom_elems: Rc>>, + context_menu: Rc>, on_custom_attrib: Option, + on_custom_attrib_inner: parser::OnCustomAttribsFunc, } pub type OnCustomIdFunc = Box< @@ -105,6 +108,13 @@ impl GuiPanel { ) -> anyhow::Result { let custom_elems = Rc::new(RefCell::new(vec![])); + let on_custom_attrib_inner: parser::OnCustomAttribsFunc = Rc::new({ + let custom_elems = custom_elems.clone(); + move |attribs| { + custom_elems.borrow_mut().push(attribs.to_owned()); + } + }); + let doc_params = wgui::parser::ParseDocumentParams { globals: app.wgui_globals.clone(), path: if params.external_xml { @@ -113,12 +123,7 @@ impl GuiPanel { AssetPath::FileOrBuiltIn(path) }, extra: wgui::parser::ParseDocumentExtra { - on_custom_attribs: Some(Rc::new({ - let custom_elems = custom_elems.clone(); - move |attribs| { - custom_elems.borrow_mut().push(attribs.to_owned()); - } - })), + on_custom_attribs: Some(on_custom_attrib_inner.clone()), ..Default::default() }, }; @@ -164,13 +169,16 @@ impl GuiPanel { last_content_size: Vec2::ZERO, doc_extra: Some(doc_params.extra), custom_elems, + context_menu: Default::default(), on_custom_attrib: params.on_custom_attrib, + on_custom_attrib_inner, }; me.process_custom_elems(app); Ok(me) } + /// Perform initial setup on newly added elements. pub fn process_custom_elems(&mut self, app: &mut AppState) { let mut elems = self.custom_elems.borrow_mut(); for elem in elems.iter() { @@ -186,7 +194,14 @@ impl GuiPanel { .parser_state .fetch_component_from_widget_id_as::(elem.widget_id) { - setup_custom_button::(&mut self.layout, &self.parser_state, elem, app, button); + setup_custom_button::( + &mut self.layout, + &self.parser_state, + elem, + &self.context_menu, + &self.on_custom_attrib_inner, + button, + ); } if let Some(on_custom_attrib) = &self.on_custom_attrib { @@ -268,6 +283,14 @@ impl OverlayBackend for GuiPanel { return Ok(ShouldRender::Unable); } + let tick_result = self + .context_menu + .borrow_mut() + .tick(&mut self.layout, &mut self.parser_state)?; + if matches!(tick_result, context_menu::TickResult::Opened) { + self.process_custom_elems(app); + } + if !self .last_content_size .abs_diff_eq(self.layout.content_size, 0.1 /* pixels */) diff --git a/wlx-overlay-s/src/overlays/keyboard/builder.rs b/wlx-overlay-s/src/overlays/keyboard/builder.rs index b08f1369..3c381dd2 100644 --- a/wlx-overlay-s/src/overlays/keyboard/builder.rs +++ b/wlx-overlay-s/src/overlays/keyboard/builder.rs @@ -30,7 +30,7 @@ use super::{ layout::{self, KeyCapType}, }; -const PIXELS_PER_UNIT: f32 = 80.; +const PIXELS_PER_UNIT: f32 = 60.; fn new_doc_params(panel: &mut GuiPanel) -> ParseDocumentParams<'static> { ParseDocumentParams { @@ -335,7 +335,10 @@ pub(super) fn create_keyboard_panel( ("Screen", panels_root) } OverlayCategory::Mirror => { - params.insert("display".into(), meta.name.as_ref().into()); + params.insert( + "display".into(), + (*meta.name).chars().last().unwrap().to_string().into(), + ); ("Mirror", panels_root) } OverlayCategory::Panel => ("Panel", panels_root),