+
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),