working bar context menus + kbd downsize
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(¶ms.template_name, ¶ms.template_params, params.position)?;
|
||||
let blueprint = parser_state.context_menu_create_blueprint(¶ms.template_name, ¶ms.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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -249,4 +249,7 @@ pub struct GeneralConfig {
|
||||
|
||||
#[serde(default)]
|
||||
pub xwayland_by_default: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub context_menu_hold_and_release: bool,
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 */)
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user