working bar context menus + kbd downsize

This commit is contained in:
galister
2026-01-07 19:03:47 +09:00
parent 165070da51
commit ac9f3c6d23
12 changed files with 237 additions and 142 deletions

View File

@@ -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);
}

View File

@@ -1,7 +1,7 @@
<layout>
<!-- text: str -->
<template name="Cell">
<Button id="button" text="${text}" weight="bold" border="0" color="#FFFFFF00" />
<Button id="button" text="${text}" weight="bold" border="0" padding="4" color="#FFFFFF00" />
</template>
<template name="Separator">
@@ -13,4 +13,4 @@
</rectangle>
</elements>
</layout>
</layout>

View File

@@ -32,7 +32,6 @@ use crate::{
windowing::context_menu,
};
use anyhow::Context;
use glam::Vec2;
use ouroboros::self_referencing;
use smallvec::SmallVec;
use std::{cell::RefMut, collections::HashMap, path::Path, rc::Rc};
@@ -97,7 +96,7 @@ pub trait Fetchable {
}
impl ParserData {
fn take_results_from(&mut self, from: &mut Self) {
pub(crate) fn take_results_from(&mut self, from: &mut Self) {
let ids = std::mem::take(&mut from.ids);
let components = std::mem::take(&mut from.components);
let components_by_id = std::mem::take(&mut from.components_by_id);
@@ -140,7 +139,7 @@ impl Fetchable for ParserData {
};
let Some(component) = weak.upgrade() else {
anyhow::bail!("Component by widget ID \"{widget_id:?}\" doesn't exist");
anyhow::bail!("Component by widget ID \"{widget_id:?}\" has disappeared");
};
Ok(Component(component))
@@ -264,7 +263,6 @@ impl ParserState {
&mut self,
template_name: &str,
template_params: &HashMap<Rc<str>, Rc<str>>,
position: Vec2,
) -> anyhow::Result<context_menu::Blueprint> {
let Some(template) = self.data.templates.get(template_name) else {
anyhow::bail!("no template named \"{template_name}\" found");
@@ -320,7 +318,6 @@ impl ParserState {
Ok(
context_menu::Blueprint {
cells,
position,
}
)
}

View File

@@ -16,7 +16,7 @@ use crate::drawing::{self};
pub static SWASH_CACHE: LazyLock<Mutex<SwashCache>> = LazyLock::new(|| Mutex::new(SwashCache::new()));
/// Used in case no `font_size` is defined
const DEFAULT_FONT_SIZE: f32 = 14.;
pub(crate) const DEFAULT_FONT_SIZE: f32 = 14.;
/// In case no `line_height` is defined, use `font_size` * `DEFAULT_LINE_HEIGHT_RATIO`
const DEFAULT_LINE_HEIGHT_RATIO: f32 = 1.43;
@@ -77,7 +77,11 @@ impl From<&TextStyle> for Metrics {
impl From<&TextStyle> for Wrap {
fn from(value: &TextStyle) -> Self {
if value.wrap { Self::WordOrGlyph } else { Self::None }
if value.wrap {
Self::WordOrGlyph
} else {
Self::None
}
}
}

View File

@@ -20,7 +20,6 @@ pub struct Cell {
}
pub(crate) struct Blueprint {
pub position: Vec2,
pub cells: Vec<Cell>,
}
@@ -43,23 +42,25 @@ pub struct ContextMenu {
tasks: Tasks<Task>,
}
fn doc_params<'a>(
globals: &WguiGlobals,
on_custom_attribs: Option<parser::OnCustomAttribsFunc>,
) -> parser::ParseDocumentParams<'a> {
fn doc_params<'a>(globals: &WguiGlobals) -> parser::ParseDocumentParams<'a> {
parser::ParseDocumentParams {
globals: globals.clone(),
path: AssetPath::WguiInternal("wgui/context_menu.xml"),
extra: parser::ParseDocumentExtra {
on_custom_attribs,
..Default::default()
},
extra: Default::default(),
}
}
#[derive(Default)]
pub struct TickResult {
pub action_name: Option<Rc<str>>,
pub enum TickResult {
/// Nothing happened
#[default]
None,
/// The context menu was opened.
Opened,
/// User has selected an action.
Action(Rc<str>),
/// The context menu was closed without an action.
Closed,
}
impl ContextMenu {
@@ -77,8 +78,7 @@ impl ContextMenu {
layout: &mut Layout,
parser_state: &mut ParserState,
) -> anyhow::Result<()> {
let blueprint =
parser_state.context_menu_create_blueprint(&params.template_name, &params.template_params, params.position)?;
let blueprint = parser_state.context_menu_create_blueprint(&params.template_name, &params.template_params)?;
let globals = layout.state.globals.clone();
@@ -94,19 +94,20 @@ impl ContextMenu {
})?;
let content = self.window.get_content();
let doc_params = doc_params(&globals, params.on_custom_attribs.clone());
let doc_params = doc_params(&globals);
let mut state = parser::parse_from_assets(&doc_params, layout, content.id)?;
let mut inner_parser = parser::parse_from_assets(&doc_params, layout, content.id)?;
let id_buttons = state.get_widget_id("buttons")?;
let id_buttons = inner_parser.get_widget_id("buttons")?;
for (idx, cell) in blueprint.cells.iter().enumerate() {
let mut par = HashMap::new();
par.insert(Rc::from("text"), cell.title.generate(&mut globals.i18n()));
let data_cell = state.parse_template(&doc_params, "Cell", layout, id_buttons, par)?;
let mut data_cell = inner_parser.parse_template(&doc_params, "Cell", layout, id_buttons, par)?;
let button = data_cell.fetch_component_as::<ComponentButton>("button")?;
let button_id = button.base().get_id();
parser_state.data.take_results_from(&mut data_cell);
self
.tasks
.handle_button(&button, Task::ActionClicked(cell.action_name.clone()));
@@ -121,7 +122,7 @@ impl ContextMenu {
}
if idx < blueprint.cells.len() - 1 {
state.parse_template(&doc_params, "Separator", layout, id_buttons, Default::default())?;
inner_parser.parse_template(&doc_params, "Separator", layout, id_buttons, Default::default())?;
}
}
Ok(())
@@ -130,14 +131,20 @@ impl ContextMenu {
pub fn tick(&mut self, layout: &mut Layout, parser_state: &mut ParserState) -> anyhow::Result<TickResult> {
if let Some(mut p) = self.pending_open.take() {
self.open_process(&mut p, layout, parser_state)?;
let _ = self.tasks.drain();
return Ok(TickResult::Opened);
}
let mut result = TickResult::default();
for task in self.tasks.drain() {
match task {
Task::ActionClicked(action_name) => {
result.action_name = action_name;
Task::ActionClicked(Some(action_name)) => {
result = TickResult::Action(action_name);
self.close();
}
Task::ActionClicked(None) => {
result = TickResult::Closed;
self.close();
}
}

View File

@@ -14,10 +14,10 @@ use crate::{
layout::{Layout, LayoutTask, LayoutTasks, WidgetPair},
parser::{self, Fetchable, ParserState},
widget::{
EventResult,
div::WidgetDiv,
label::WidgetLabel,
rectangle::{WidgetRectangle, WidgetRectangleParams},
EventResult,
},
};

View File

@@ -249,4 +249,7 @@ pub struct GeneralConfig {
#[serde(default)]
pub xwayland_by_default: bool,
#[serde(default)]
pub context_menu_hold_and_release: bool,
}

View File

@@ -1,12 +1,12 @@
<layout>
<macro name="keycap_rect"
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
border_color="~color_accent_translucent" border="2" round="8" color="~color_accent_40" color2="~color_accent_10" gradient="vertical"
border_color="~color_accent_translucent" border="2" round="6" color="~color_accent_40" color2="~color_accent_10" gradient="vertical"
align_items="center" justify_content="center" />
<macro name="tray_rect"
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
border_color="~color_accent_translucent" border="2" round="8" color="~color_bg" color2="~color_accent_10" gradient="vertical"
border_color="~color_accent_translucent" border="2" round="6" color="~color_bg" color2="~color_accent_10" gradient="vertical"
align_items="center" justify_content="center" />
<macro name="keycap_div"
@@ -24,7 +24,7 @@
<template name="KeySpecial">
<div macro="keycap_div">
<rectangle id="${id}" macro="keycap_rect">
<sprite color="~color_text" width="32" height="32" src="keyboard/${text}.svg" />
<sprite color="~color_text" width="21" height="21" src="keyboard/${text}.svg" />
</rectangle>
</div>
</template>
@@ -34,7 +34,7 @@
<template name="KeyLetter">
<div macro="keycap_div">
<rectangle id="${id}" macro="keycap_rect">
<label text="${text}" size="24" />
<label text="${text}" size="16" />
</rectangle>
</div>
</template>
@@ -43,9 +43,9 @@
<!-- Used for letter keys on layouts with AltGr. -->
<template name="KeyLetterAltGr">
<div macro="keycap_div">
<rectangle id="${id}" macro="keycap_rect" gap="4">
<label text="${text}" size="24" />
<label color="~color_text_translucent" text="${text_altgr}" size="24" />
<rectangle id="${id}" macro="keycap_rect" gap="3">
<label text="${text}" size="16" />
<label color="~color_text_translucent" text="${text_altgr}" size="16" />
</rectangle>
</div>
</template>
@@ -54,9 +54,9 @@
<!-- Used for number & symbol keys on layouts without AltGr. -->
<template name="KeySymbol">
<div macro="keycap_div">
<rectangle id="${id}" macro="keycap_rect" gap="4">
<label color="~color_text_translucent" text="${text_shift}" size="24" />
<label text="${text}" size="24" />
<rectangle id="${id}" macro="keycap_rect" gap="3">
<label color="~color_text_translucent" text="${text_shift}" size="16" />
<label text="${text}" size="16" />
</rectangle>
</div>
</template>
@@ -67,149 +67,139 @@
<div macro="keycap_div">
<rectangle id="${id}" macro="keycap_rect" flex_direction="row" flex_wrap="wrap">
<div width="50%" height="50%" align_items="center" justify_content="center">
<label color="~color_text_translucent" text="${text_shift}" size="24" />
<label color="~color_text_translucent" text="${text_shift}" size="16" />
</div>
<div width="50%" height="50%" align_items="center" justify_content="center" />
<div width="50%" height="50%" align_items="center" justify_content="center">
<label text="${text}" size="24" />
<label text="${text}" size="16" />
</div>
<div width="50%" height="50%" align_items="center" justify_content="center">
<label color="~color_text_translucent" text="${text_altgr}" size="24" />
<label color="~color_text_translucent" text="${text_altgr}" size="16" />
</div>
</rectangle>
</div>
</template>
<macro name="button_style" border="2" border_color="~color_accent_translucent" color="~color_bg" round="8"
align_items="center" justify_content="center" padding="8" width="80" height="80" overflow="visible"/>
<macro name="button_style" border="2" border_color="~color_accent_translucent" color="~color_bg" round="6"
align_items="center" justify_content="center" padding="6" width="60" height="60" overflow="visible"/>
<macro name="menu_button_style" border="2" border_color="~color_accent_translucent" color="~color_bg" round="8"
align_items="center" justify_content="center" padding="8" width="100%" height="60" />
<macro name="bg_rect" width="100%" color="~color_bg" round="10" border="2" border_color="~color_accent" />
<macro name="bg_rect" width="100%" color="~color_bg" round="16" border="2" border_color="~color_accent" />
<macro name="dropdown_root" new_pass="1" width="200" color="~color_bg" flex_direction="column" position="absolute" margin_top="80" display="none" />
<macro name="dropdown_title" color="~color_faded_50" width="100%" padding="16" overflow="hidden" />
<template name="MenuButton">
<Button macro="menu_button_style" _release="${action}">
<label translation="${translation}" size="24" />
</Button>
</template>
<blueprint name="menu_app">
<context_menu >
<!-- title text="${name}" /-->
<cell translation="BAR.TOGGLE_VISIBILITY" _release="::OverlayToggle ${name}" />
<cell translation="BAR.RESET_POSITION" _release="::OverlayReset ${name}" />
<cell translation="BAR.CLOSE_APP" _release="::WvrOverlayTermProcess ${name}" />
<cell translation="BAR.FORCE_CLOSE_APP" _release="::WvrOverlayKillProcess ${name}" />
</context_menu>
</blueprint>
<!-- An app with a single icon. -->
<template name="App">
<div>
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
<sprite width="56" height="56" color="~text_color" src_ext="${icon}" />
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
<MenuButton translation="BAR.CLOSE" action="::WvrOverlayTermProcess ${name}" />
<MenuButton translation="BAR.FORCE_CLOSE" action="::WvrOverlayKillProcess ${name}" />
</div>
</div>
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::ContextMenuOpen menu_app">
<sprite width="38" height="38" color="~text_color" src_ext="${icon}" />
</Button>
</template>
<blueprint name="menu_screen">
<context_menu >
<!-- title text="${name}" /-->
<cell translation="BAR.TOGGLE_VISIBILITY" _press="::OverlayToggle ${name}" />
<cell translation="BAR.RESET_POSITION" _press="::OverlayReset ${name}" />
</context_menu>
</blueprint>
<!-- A screen with a shortened connector name, e.g. "H1" for HDMI-A-1 or "D2" for DP-2 -->
<template name="Screen">
<div>
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
<sprite width="56" height="56" color="~text_color" src_builtin="edit/screen.svg" />
<div position="absolute" margin_top="-10">
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
</div>
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_screen">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/screen.svg" />
<div position="absolute" margin_top="-7" margin_left="-1">
<label text="${display}" size="18" color="~color_faded_20" weight="bold" />
</div>
</div>
</Button>
</template>
<blueprint name="menu_panel">
<context_menu >
<!-- title text="${name}" /-->
<cell translation="BAR.TOGGLE_VISIBILITY" _release="::OverlayToggle ${name}" />
<cell translation="BAR.RESET_POSITION" _release="::OverlayReset ${name}" />
<cell translation="BAR.RELOAD_FROM_DISK" _release="::CustomOverlayReload ${name}" />
</context_menu>
</blueprint>
<template name="Panel">
<div>
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
<sprite width="56" height="56" color="~text_color" src_builtin="edit/panel.svg" />
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
</div>
</div>
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::ContextMenuOpen menu_panel">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/panel.svg" />
</Button>
</template>
<blueprint name="menu_mirror">
<context_menu >
<!-- title text="${name}" /-->
<cell translation="BAR.TOGGLE_VISIBILITY" _release="::OverlayToggle ${name}" />
<cell translation="BAR.RESET_POSITION" _release="::OverlayReset ${name}" />
<cell translation="BAR.CLOSE_MIRROR" _release="::OverlayDrop ${name}" />
</context_menu>
</blueprint>
<template name="Mirror">
<div>
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
<sprite width="56" height="56" color="~text_color" src_builtin="edit/mirror.svg" />
<div position="absolute" margin_top="7" margin_left="20">
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
</div>
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
<MenuButton translation="BAR.CLOSE" action="::OverlayDrop ${name}" />
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::ContextMenuOpen menu_mirror">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/mirror.svg" />
<div position="absolute" margin_top="5" margin_left="13">
<label text="${display}" size="20" color="~color_faded_20" weight="bold" />
</div>
</div>
</Button>
</template>
<template name="Set">
<Button macro="button_style" id="set_${idx}" _press="::SetSwitch ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="bottom">
<sprite width="56" height="56" color="~text_color" src_builtin="watch/set2.svg" />
<div position="absolute" margin_top="16" margin_left="-8">
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
<sprite width="38" height="38" color="~text_color" src_builtin="watch/set2.svg" />
<div position="absolute" margin_top="10" margin_left="-7">
<label text="${display}" size="20" color="~color_faded_20" weight="bold" />
</div>
</Button>
</template>
<elements>
<div flex_direction="column" interactable="0">
<rectangle macro="bg_rect" padding="16" align_items="center" justify_content="space_between">
<div gap="16">
<rectangle macro="bg_rect" padding="10" align_items="center" justify_content="space_between">
<div gap="10">
<Button macro="button_style" id="btn_dashboard" _press="::DashToggle">
<sprite width="56" height="56" color="~text_color" src="watch/wayvr_dashboard_mono.svg" />
<sprite width="38" height="38" color="~text_color" src="watch/wayvr_dashboard_mono.svg" />
</Button>
<VerticalSeparator />
<div id="panels_root" gap="8">
<div id="panels_root" gap="6">
<Screen idx="0" display="H1" name="HDMI-A-1" />
<Screen idx="1" display="D2" name="Screen: DP-2" />
<Mirror idx="1" display="1" name="M1" />
<Panel idx="1" display="Test" name="Test" />
</div>
<VerticalSeparator />
<div id="apps_root" gap="8">
<div id="apps_root" gap="6">
<App id="test1" name="Blender" icon="/usr/share/icons/hicolor/scalable/apps/blender-5.0.svg" />
<App id="test2" name="Inkscape" icon="/usr/share/icons/hicolor/scalable/apps/org.inkscape.Inkscape.svg" />
<App id="test3" name="GIMP" icon="/usr/share/icons/hicolor/scalable/apps/gimp.svg" />
</div>
</div>
<div id="tray_root" flex_direction="row" gap="16">
<div id="sets_root" flex_direction="row" gap="8">
<div id="tray_root" flex_direction="row" gap="10">
<div id="sets_root" flex_direction="row" gap="6">
<Set idx="0" display="1" />
<Set idx="1" display="2" />
</div>
<VerticalSeparator />
<div flex_direction="column" gap="4" align_items="center">
<label text="23:59" _source="clock" _display="time" size="32" weight="bold" />
<label text="Tuesday" _source="clock" _display="dow" size="22" />
<label text="22/2/2022" _source="clock" _display="date" size="22" />
<div flex_direction="column" gap="3" align_items="center">
<label text="23:59" _source="clock" _display="time" size="21" weight="bold" />
<label text="Tuesday" _source="clock" _display="dow" size="15" />
<label text="22/2/2022" _source="clock" _display="date" size="15" />
</div>
</div>
</rectangle>
<div width="100%" height="20" interactable="0" />
<rectangle id="keyboard_root" macro="bg_rect" flex_direction="column" padding="16">
<div width="100%" height="13" interactable="0" />
<rectangle id="keyboard_root" macro="bg_rect" flex_direction="column" padding="10">
</rectangle>
</div>
</elements>

View File

@@ -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",

View File

@@ -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<S: 'static>(
layout: &mut Layout,
parser_state: &ParserState,
attribs: &CustomAttribsInfoOwned,
_app: &AppState,
context_menu: &Rc<RefCell<ContextMenu>>,
on_custom_attribs: &parser::OnCustomAttribsFunc,
button: Rc<ComponentButton>,
) {
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
@@ -202,6 +206,49 @@ pub(super) fn setup_custom_button<S: 'static>(
let button = button.clone();
let callback: EventCallback<AppState, S> = match command {
"::ContextMenuOpen" => {
let Some(template_name) = args.next() else {
log::warn!(
"{command} has incorrect arguments. Should be: {command} <context_menu>"
);
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<str> = 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<S: 'static>(
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<S: 'static>(
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");

View File

@@ -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<S> {
has_focus: [bool; 2],
last_content_size: Vec2,
custom_elems: Rc<RefCell<Vec<CustomAttribsInfoOwned>>>,
context_menu: Rc<RefCell<ContextMenu>>,
on_custom_attrib: Option<OnCustomAttribFunc>,
on_custom_attrib_inner: parser::OnCustomAttribsFunc,
}
pub type OnCustomIdFunc<S> = Box<
@@ -105,6 +108,13 @@ impl<S: 'static> GuiPanel<S> {
) -> anyhow::Result<Self> {
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<S: 'static> GuiPanel<S> {
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<S: 'static> GuiPanel<S> {
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<S: 'static> GuiPanel<S> {
.parser_state
.fetch_component_from_widget_id_as::<ComponentButton>(elem.widget_id)
{
setup_custom_button::<S>(&mut self.layout, &self.parser_state, elem, app, button);
setup_custom_button::<S>(
&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<S: 'static> OverlayBackend for GuiPanel<S> {
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 */)

View File

@@ -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<KeyboardState>) -> 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),