watch: highlight buttons of active overlays

This commit is contained in:
galister
2025-12-21 21:53:25 +09:00
parent d61b056db5
commit e04edec586
7 changed files with 130 additions and 20 deletions

View File

@@ -34,7 +34,7 @@ All overlays are listed on bottom row.
</template> </template>
<template name="Overlay"> <template name="Overlay">
<Button macro="button_style" id="overlay_${idx}" <Button macro="button_style" id="overlay_${idx}" sticky="1"
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}"
align_items="center" align_items="center"
height="40"> height="40">
@@ -111,13 +111,13 @@ All overlays are listed on bottom row.
<!-- Bottom buttons --> <!-- Bottom buttons -->
<div id="toolbox-condensed" gap="6" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap"> <div id="toolbox-condensed" gap="6" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap">
<Button height="40" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top"> <Button id="btn_dashboard" height="40" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" sticky="1" >
<sprite color="~set_color" width="32" height="32" src="watch/wayvr_dashboard_mono.svg" /> <sprite color="~set_color" width="32" height="32" src="watch/wayvr_dashboard_mono.svg" />
</Button> </Button>
<Button id="btn_edit_mode" height="40" macro="button_style" _press="::EditToggle" tooltip="WATCH.EDIT_MODE" tooltip_side="top"> <Button id="btn_edit_mode" height="40" macro="button_style" _press="::EditToggle" tooltip="WATCH.EDIT_MODE" tooltip_side="top">
<sprite color="~set_color" width="32" height="32" src="watch/edit.svg" /> <sprite color="~set_color" width="32" height="32" src="watch/edit.svg" />
</Button> </Button>
<Button height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd"> <Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" sticky="1" >
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" /> <sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
</Button> </Button>
<!-- Src here may be changed, but maintain `OverlayCategory` order: Panel, Screen, Mirror, WayVR --> <!-- Src here may be changed, but maintain `OverlayCategory` order: Panel, Screen, Mirror, WayVR -->

View File

@@ -30,7 +30,7 @@
</template> </template>
<template name="Overlay"> <template name="Overlay">
<Button macro="button_style" id="overlay_${idx}" <Button macro="button_style" id="overlay_${idx}" sticky="1"
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::EditModeOverlayToggle ${idx}" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::EditModeOverlayToggle ${idx}"
align_items="center" align_items="center"
height="40"> height="40">
@@ -120,7 +120,7 @@
</div> </div>
<div flex_direction="column" align_items="center" justify_content="center"> <div flex_direction="column" align_items="center" justify_content="center">
<div id="toolbox" gap="8" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap"> <div id="toolbox" gap="8" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap">
<Button height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd"> <Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" sticky="1" >
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" /> <sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
<label translation="EDIT_MODE.KEYBOARD" size="18" /> <label translation="EDIT_MODE.KEYBOARD" size="18" />
</Button> </Button>
@@ -136,7 +136,7 @@
<!-- Bottom buttons --> <!-- Bottom buttons -->
<div flex_direction="row" gap="4"> <div flex_direction="row" gap="4">
<Button macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top"> <Button id="btn_dashboard" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" sticky="1" >
<sprite color="~set_color" width="40" height="40" src="watch/wayvr_dashboard_mono.svg" /> <sprite color="~set_color" width="40" height="40" src="watch/wayvr_dashboard_mono.svg" />
</Button> </Button>
<div id="edit_delete" display="none"> <div id="edit_delete" display="none">

View File

@@ -5,7 +5,8 @@ use std::{
}; };
use glam::{Affine3A, Quat, Vec3, Vec3A, vec3}; use glam::{Affine3A, Quat, Vec3, Vec3A, vec3};
use idmap::DirectIdMap; use idmap::{DirectIdMap, ordered::Keys};
use slotmap::SecondaryMap;
use wgui::{ use wgui::{
components::button::ComponentButton, components::button::ComponentButton,
event::{CallbackDataCommon, EventAlterables, EventCallback, StyleSetRequest}, event::{CallbackDataCommon, EventAlterables, EventCallback, StyleSetRequest},
@@ -33,7 +34,7 @@ use crate::{
overlays::edit::LongPressButtonState, overlays::edit::LongPressButtonState,
state::AppState, state::AppState,
windowing::{ windowing::{
OverlaySelector, Z_ORDER_WATCH, OverlayID, OverlaySelector, Z_ORDER_WATCH,
backend::{OverlayEventData, OverlayMeta}, backend::{OverlayEventData, OverlayMeta},
manager::MAX_OVERLAY_SETS, manager::MAX_OVERLAY_SETS,
window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData}, window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData},
@@ -60,11 +61,14 @@ struct WatchState {
set_buttons: Vec<Rc<ComponentButton>>, set_buttons: Vec<Rc<ComponentButton>>,
overlay_buttons: Vec<OverlayButton>, overlay_buttons: Vec<OverlayButton>,
overlay_metas: Vec<OverlayMeta>, overlay_metas: Vec<OverlayMeta>,
overlay_indices: SecondaryMap<OverlayID, usize>,
edit_mode_widgets: Vec<(WidgetID, bool)>, edit_mode_widgets: Vec<(WidgetID, bool)>,
edit_add_widget: WidgetID, edit_add_widget: WidgetID,
device_role_icons: DirectIdMap<TrackedDeviceRole, CustomGlyphData>, device_role_icons: DirectIdMap<TrackedDeviceRole, CustomGlyphData>,
overlay_cat_icons: DirectIdMap<OverlayCategory, CustomGlyphData>, overlay_cat_icons: DirectIdMap<OverlayCategory, CustomGlyphData>,
devices: Vec<(WidgetID, WidgetID)>, devices: Vec<(WidgetID, WidgetID)>,
keyboard_oid: OverlayID,
dashboard_oid: OverlayID,
num_sets: usize, num_sets: usize,
delete: LongPressButtonState, delete: LongPressButtonState,
} }
@@ -310,6 +314,14 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
.parser_state .parser_state
.fetch_component_as::<ComponentButton>("btn_edit_mode") .fetch_component_as::<ComponentButton>("btn_edit_mode")
.ok(); .ok();
let btn_keyboard = panel
.parser_state
.fetch_component_as::<ComponentButton>("btn_keyboard")
.ok();
let btn_dashboard = panel
.parser_state
.fetch_component_as::<ComponentButton>("btn_dashboard")
.ok();
panel.on_notify = Some(Box::new(move |panel, app, event_data| { panel.on_notify = Some(Box::new(move |panel, app, event_data| {
let mut alterables = EventAlterables::default(); let mut alterables = EventAlterables::default();
@@ -379,7 +391,31 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
} }
} }
OverlayEventData::OverlaysChanged(metas) => { OverlayEventData::OverlaysChanged(metas) => {
panel.state.overlay_metas = metas; panel.state.overlay_metas.clear();
for meta in metas {
match meta.category {
OverlayCategory::Keyboard => {
panel.state.keyboard_oid = meta.id;
if let Some(btn_keyboard) = btn_keyboard.as_ref() {
btn_keyboard.set_sticky_state(&mut com, meta.visible);
}
}
OverlayCategory::Dashboard => {
if let Some(btn_dashboard) = btn_dashboard.as_ref() {
btn_dashboard.set_sticky_state(&mut com, meta.visible);
}
panel.state.dashboard_oid = meta.id
}
OverlayCategory::Internal => {}
_ => panel.state.overlay_metas.push(meta),
}
}
panel.state.overlay_indices.clear();
for (idx, meta) in panel.state.overlay_metas.iter().enumerate() {
panel.state.overlay_indices.insert(meta.id, idx);
}
for (idx, btn) in panel.state.overlay_buttons.iter().enumerate() { for (idx, btn) in panel.state.overlay_buttons.iter().enumerate() {
let display = if let Some(meta) = panel.state.overlay_metas.get(idx) { let display = if let Some(meta) = panel.state.overlay_metas.get(idx) {
let name = btn let name = btn
@@ -406,6 +442,8 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
sprite.set_content(Some(glyph.clone())); sprite.set_content(Some(glyph.clone()));
} }
btn.button.set_sticky_state(&mut com, meta.visible);
taffy::Display::Flex taffy::Display::Flex
} else { } else {
taffy::Display::None taffy::Display::None
@@ -414,6 +452,39 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
.set_style(btn.button.get_rect(), StyleSetRequest::Display(display)); .set_style(btn.button.get_rect(), StyleSetRequest::Display(display));
} }
} }
OverlayEventData::VisibleOverlaysChanged(overlays) => {
for meta in panel.state.overlay_metas.iter_mut() {
meta.visible = false;
}
let mut keyboard_visible = false;
let mut dashboard_visible = false;
for visible in overlays.iter() {
if let Some(idx) = panel.state.overlay_indices.get(*visible)
&& let Some(o) = panel.state.overlay_metas.get_mut(*idx)
{
o.visible = true;
} else if panel.state.keyboard_oid == *visible {
keyboard_visible = true;
} else if panel.state.dashboard_oid == *visible {
dashboard_visible = true;
}
}
for (idx, btn) in panel.state.overlay_buttons.iter().enumerate() {
let Some(meta) = panel.state.overlay_metas.get(idx) else {
continue;
};
btn.button.set_sticky_state(&mut com, meta.visible);
}
if let Some(btn_keyboard) = btn_keyboard.as_ref() {
btn_keyboard.set_sticky_state(&mut com, keyboard_visible);
}
if let Some(btn_dashboard) = btn_dashboard.as_ref() {
btn_dashboard.set_sticky_state(&mut com, dashboard_visible);
}
}
OverlayEventData::DevicesChanged => { OverlayEventData::DevicesChanged => {
for (i, (div, s)) in panel.state.devices.iter().enumerate() { for (i, (div, s)) in panel.state.devices.iter().enumerate() {
if let Some(dev) = app.input_state.devices.get(i) if let Some(dev) = app.input_state.devices.get(i)

View File

@@ -811,7 +811,7 @@ pub fn create_wayvr_display_overlay(
)?); )?);
let category = if name == DASHBOARD_DISPLAY_NAME { let category = if name == DASHBOARD_DISPLAY_NAME {
OverlayCategory::Internal OverlayCategory::Dashboard
} else { } else {
OverlayCategory::WayVR OverlayCategory::WayVR
}; };

View File

@@ -106,6 +106,7 @@ pub struct OverlayMeta {
pub id: OverlayID, pub id: OverlayID,
pub name: Arc<str>, pub name: Arc<str>,
pub category: OverlayCategory, pub category: OverlayCategory,
pub visible: bool,
} }
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
@@ -114,6 +115,7 @@ pub enum OverlayEventData {
NumSetsChanged(usize), NumSetsChanged(usize),
EditModeChanged(bool), EditModeChanged(bool),
OverlaysChanged(Vec<OverlayMeta>), OverlaysChanged(Vec<OverlayMeta>),
VisibleOverlaysChanged(Vec<OverlayID>),
DevicesChanged, DevicesChanged,
OverlayGrabbed { OverlayGrabbed {
name: Arc<str>, name: Arc<str>,

View File

@@ -248,7 +248,12 @@ where
} }
OverlayTask::Modify(sel, f) => { OverlayTask::Modify(sel, f) => {
if let Some(o) = self.mut_by_selector(&sel) { if let Some(o) = self.mut_by_selector(&sel) {
let was_visible = o.config.is_active();
f(app, &mut o.config); f(app, &mut o.config);
if was_visible != o.config.is_active() {
let _ = self.visible_overlays_changed(app);
}
} else { } else {
log::warn!("Overlay not found for task: {sel:?}"); log::warn!("Overlay not found for task: {sel:?}");
} }
@@ -477,7 +482,10 @@ impl<T> OverlayWindowManager<T> {
return; return;
}; };
if matches!(overlay.config.category, OverlayCategory::Internal) { if matches!(
overlay.config.category,
OverlayCategory::Internal | OverlayCategory::Dashboard
) {
// watch, anchor, toast, dashboard // watch, anchor, toast, dashboard
return; return;
} }
@@ -524,7 +532,7 @@ impl<T> OverlayWindowManager<T> {
let internal = ret_val.as_ref().is_some_and(|o| { let internal = ret_val.as_ref().is_some_and(|o| {
matches!( matches!(
o.config.category, o.config.category,
OverlayCategory::Internal | OverlayCategory::Keyboard OverlayCategory::Internal | OverlayCategory::Keyboard | OverlayCategory::Dashboard
) )
}); });
@@ -577,10 +585,7 @@ impl<T> OverlayWindowManager<T> {
let name = overlay.config.name.clone(); let name = overlay.config.name.clone();
let global = overlay.config.global; let global = overlay.config.global;
let internal = matches!( let internal = matches!(overlay.config.category, OverlayCategory::Internal);
overlay.config.category,
OverlayCategory::Internal | OverlayCategory::Keyboard
);
let show_on_spawn = overlay.config.show_on_spawn; let show_on_spawn = overlay.config.show_on_spawn;
let oid = self.overlays.insert(overlay); let oid = self.overlays.insert(overlay);
@@ -611,6 +616,9 @@ impl<T> OverlayWindowManager<T> {
if !internal && let Err(e) = self.overlays_changed(app) { if !internal && let Err(e) = self.overlays_changed(app) {
log::error!("Error while adding overlay: {e:?}"); log::error!("Error while adding overlay: {e:?}");
} }
if !internal && let Err(e) = self.visible_overlays_changed(app) {
log::error!("Error while adding overlay: {e:?}");
}
oid oid
} }
@@ -671,6 +679,10 @@ impl<T> OverlayWindowManager<T> {
.backend .backend
.notify(app, OverlayEventData::ActiveSetChanged(new_set)) .notify(app, OverlayEventData::ActiveSetChanged(new_set))
.unwrap(); // TODO: handle this .unwrap(); // TODO: handle this
let _ = self
.visible_overlays_changed(app)
.inspect_err(|e| log::error!("VisibleOverlaysChanged: {e:?}"));
} }
} }
@@ -683,21 +695,23 @@ impl<T> OverlayWindowManager<T> {
} else { } else {
self.switch_to_set(app, None, false); self.switch_to_set(app, None, false);
} }
let _ = self
.visible_overlays_changed(app)
.inspect_err(|e| log::error!("VisibleOverlaysChanged: {e:?}"));
} }
fn overlays_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> { fn overlays_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> {
let mut meta = Vec::with_capacity(self.overlays.len()); let mut meta = Vec::with_capacity(self.overlays.len());
for (id, data) in &self.overlays { for (id, data) in &self.overlays {
if matches!( if matches!(data.config.category, OverlayCategory::Internal) {
data.config.category,
OverlayCategory::Internal | OverlayCategory::Keyboard
) {
continue; continue;
} }
meta.push(OverlayMeta { meta.push(OverlayMeta {
id, id,
name: data.config.name.clone(), name: data.config.name.clone(),
category: data.config.category, category: data.config.category,
visible: data.config.is_active(),
}); });
} }
@@ -711,6 +725,28 @@ impl<T> OverlayWindowManager<T> {
Ok(()) Ok(())
} }
fn visible_overlays_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> {
let mut vis = Vec::with_capacity(self.overlays.len());
for (id, data) in &self.overlays {
if data.config.active_state.is_none()
|| matches!(data.config.category, OverlayCategory::Internal)
{
continue;
}
vis.push(id);
}
if let Some(watch) = self.mut_by_id(self.watch_id) {
watch
.config
.backend
.notify(app, OverlayEventData::VisibleOverlaysChanged(vis))?;
}
Ok(())
}
pub fn devices_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> { pub fn devices_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> {
if let Some(watch) = self.mut_by_id(self.watch_id) { if let Some(watch) = self.mut_by_id(self.watch_id) {
watch watch

View File

@@ -56,6 +56,7 @@ impl<T> OverlayWindowData<T> {
pub enum OverlayCategory { pub enum OverlayCategory {
Internal, Internal,
Keyboard, Keyboard,
Dashboard,
Panel, Panel,
Screen, Screen,
Mirror, Mirror,