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

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