single set mode
This commit is contained in:
@@ -187,6 +187,9 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "def_false")]
|
||||
pub double_cursor_fix: bool,
|
||||
|
||||
#[serde(default = "def_false")]
|
||||
pub single_set_mode: bool,
|
||||
|
||||
#[serde(default = "def_astrset_empty")]
|
||||
pub custom_panels: AStrSet,
|
||||
|
||||
|
||||
133
wlx-overlay-s/src/assets/gui/watch-noset.xml
Normal file
133
wlx-overlay-s/src/assets/gui/watch-noset.xml
Normal file
@@ -0,0 +1,133 @@
|
||||
<!--
|
||||
Variant of the watch with no sets.
|
||||
All overlays are listed on bottom row.
|
||||
-->
|
||||
<layout>
|
||||
<theme>
|
||||
<var key="set_color" value="#cad3f5" />
|
||||
<var key="bgcolor" value="#010206d5" />
|
||||
|
||||
<var key="clock0_color" value="#cad3f5" />
|
||||
<var key="clock0_size" value="46" />
|
||||
<var key="clock0_date_size" value="16" />
|
||||
<var key="clock0_dow_size" value="16" />
|
||||
<var key="clock_alt1_color" value="#8bd5ca" />
|
||||
<var key="clock_alt2_color" value="#b7bdf8" />
|
||||
<var key="clock_alt_size" value="24" />
|
||||
<var key="clock_alt_tz_size" value="14" />
|
||||
</theme>
|
||||
|
||||
<macro name="decorative_rect"
|
||||
padding="8" color="~bgcolor"
|
||||
border="2" border_color="~color_accent" round="8" />
|
||||
|
||||
<macro name="button_style"
|
||||
padding="8"
|
||||
border_color="~color_accent_translucent" border="2" round="8" color="~color_accent_5" color2="~color_accent_1" gradient="vertical"
|
||||
align_items="center" justify_content="center" />
|
||||
|
||||
<template name="Device">
|
||||
<rectangle id="dev_${idx}" macro="decorative_rect" padding_top="4" padding_bottom="4" display="none" align_items="center" gap="8">
|
||||
<sprite id="dev_${idx}_sprite" width="32" height="32" src_builtin="${src}" />
|
||||
<label _source="battery" _device="${idx}" size="24" weight="bold" />
|
||||
</rectangle>
|
||||
</template>
|
||||
|
||||
<template name="Overlay">
|
||||
<Button macro="button_style" id="overlay_${idx}"
|
||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}"
|
||||
align_items="center"
|
||||
height="40">
|
||||
<sprite id="overlay_${idx}_sprite" src_builtin="${src}" width="32" height="32" />
|
||||
<label id="overlay_${idx}_label" text="WLX-${idx}" size="18" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
[!!!!!!!!] Disclaimer [!!!!!!!!]
|
||||
Elements with id="norm_*" show in normal mode.
|
||||
Elements with id="edit_*" show in edit mode.
|
||||
-->
|
||||
<elements>
|
||||
<!-- padding="32" is required there (to make room for tooltips) -->
|
||||
<div
|
||||
padding="32" interactable="0"
|
||||
flex_direction="column" gap="8">
|
||||
<!-- Top elements (device battery levels) -->
|
||||
<div flex_direction="row" id="devices" interactable="0" gap="8">
|
||||
<!-- Src here may be changed, but maintain `TrackedDeviceRole` order: HMD, Left, Right, Tracker -->
|
||||
<Device src="watch/hmd.svg" idx="0" />
|
||||
<Device src="watch/controller_l.svg" idx="1" />
|
||||
<Device src="watch/controller_r.svg" idx="2" />
|
||||
<Device src="watch/track.svg" idx="3" />
|
||||
<!-- Will populate additional <Device> tags at runtime -->
|
||||
</div>
|
||||
|
||||
<!-- All other elements inside the container -->
|
||||
<div flex_direction="column" gap="8">
|
||||
<rectangle macro="decorative_rect" flex_direction="row" gap="8">
|
||||
<!-- Clock, date and various timezones -->
|
||||
<div gap="8">
|
||||
<div flex_direction="column">
|
||||
<label text="23:59" _source="clock" _display="time" color="~clock0_color" size="~clock0_size" weight="bold" />
|
||||
<div padding="2" gap="2" flex_direction="column">
|
||||
<label text="22/2/2022" _source="clock" _display="date" color="~clock0_color" size="~clock0_date_size" weight="bold" />
|
||||
<label text="Tuesday" _source="clock" _display="dow" color="~clock0_color" size="~clock0_dow_size" weight="bold" />
|
||||
</div>
|
||||
</div>
|
||||
<div flex_direction="column" gap="8">
|
||||
<!-- Timezone names here are only placeholders. Set your timezones via ~/.config/wlxoverlay/conf.d -->
|
||||
<div flex_direction="column">
|
||||
<label text="Paris" _source="clock" _display="name" _timezone="0" color="~clock_alt1_color" size="~clock_alt_tz_size" weight="bold" />
|
||||
<label text="23:59" _source="clock" _display="time" _timezone="0" color="~clock_alt1_color" size="~clock_alt_size" weight="bold" />
|
||||
</div>
|
||||
<div flex_direction="column">
|
||||
<label text="New York" _source="clock" _display="name" _timezone="1" color="~clock_alt2_color" size="~clock_alt_tz_size" weight="bold" />
|
||||
<label text="23:59" _source="clock" _display="time" _timezone="1" color="~clock_alt2_color" size="~clock_alt_size" weight="bold" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Four buttons -->
|
||||
<div flex_direction="column" gap="8">
|
||||
<div gap="8">
|
||||
<Button macro="button_style" _press="::NewMirror" tooltip="WATCH.MIRROR" tooltip_side="left">
|
||||
<sprite width="40" height="40" color="~set_color" src="edit/mirror.svg" />
|
||||
</Button>
|
||||
<Button macro="button_style" _press="::CleanupMirrors" tooltip="WATCH.CLEANUP_MIRRORS" tooltip_side="left">
|
||||
<sprite width="40" height="40" color="~set_color" src="watch/mirror-off.svg" />
|
||||
</Button>
|
||||
</div>
|
||||
<div gap="8">
|
||||
<Button macro="button_style" _press="::PlayspaceRecenter" tooltip="WATCH.RECENTER" tooltip_side="left">
|
||||
<sprite width="40" height="40" color="~set_color" src="watch/recenter.svg" />
|
||||
</Button>
|
||||
<Button macro="button_style" _press="::PlayspaceFixFloor" tooltip="WATCH.FIX_FLOOR" tooltip_side="left">
|
||||
<sprite width="40" height="40" color="~set_color" src="watch/fix-floor.svg" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</rectangle>
|
||||
|
||||
<!-- Bottom buttons -->
|
||||
<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">
|
||||
<sprite color="~set_color" width="32" height="32" src="watch/wayvr_dashboard_mono.svg" />
|
||||
</Button>
|
||||
<Button 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" />
|
||||
</Button>
|
||||
<Button height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd">
|
||||
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
|
||||
</Button>
|
||||
<!-- Src here may be changed, but maintain `OverlayCategory` order: Panel, Screen, Mirror, WayVR -->
|
||||
<Overlay src="edit/panel.svg" idx="0" />
|
||||
<Overlay src="edit/screen.svg" idx="1" />
|
||||
<Overlay src="edit/mirror.svg" idx="2" />
|
||||
<Overlay src="edit/wayvr.svg" idx="3" />
|
||||
<!-- Will populate additional <Overlay> tags at runtime -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::f32::consts::PI;
|
||||
use std::process::{Child, Command};
|
||||
use std::{collections::VecDeque, time::Instant};
|
||||
use std::time::Instant;
|
||||
|
||||
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
|
||||
|
||||
@@ -192,7 +192,6 @@ pub struct InteractionState {
|
||||
pub grabbed: Option<GrabData>,
|
||||
pub clicked_id: Option<OverlayID>,
|
||||
pub hovered_id: Option<OverlayID>,
|
||||
pub release_actions: VecDeque<Box<dyn Fn()>>,
|
||||
pub next_push: Instant,
|
||||
pub haptics: Option<f32>,
|
||||
}
|
||||
@@ -204,7 +203,6 @@ impl Default for InteractionState {
|
||||
grabbed: None,
|
||||
clicked_id: None,
|
||||
hovered_id: None,
|
||||
release_actions: VecDeque::new(),
|
||||
next_push: Instant::now(),
|
||||
haptics: None,
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<OverlayWindowCo
|
||||
pub enum OverlayTask {
|
||||
AddSet,
|
||||
ToggleSet(usize),
|
||||
SoftToggleOverlay(OverlaySelector),
|
||||
DeleteActiveSet,
|
||||
ToggleEditMode,
|
||||
ShowHide,
|
||||
|
||||
@@ -51,6 +51,7 @@ struct OverlayButton {
|
||||
button: Rc<ComponentButton>,
|
||||
label: WidgetID,
|
||||
sprite: WidgetID,
|
||||
condensed: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -126,6 +127,25 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::SingleSetOverlayToggle" => {
|
||||
let arg = args.next().unwrap_or_default();
|
||||
let Ok(idx) = arg.parse::<usize>() else {
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, state| {
|
||||
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||
log::error!("No overlay at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
};
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::SoftToggleOverlay(
|
||||
OverlaySelector::Id(overlay.id),
|
||||
)));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
@@ -134,9 +154,16 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
}
|
||||
});
|
||||
|
||||
let watch_xml = app
|
||||
.session
|
||||
.config
|
||||
.single_set_mode
|
||||
.then_some("gui/watch-noset.xml")
|
||||
.unwrap_or("gui/watch.xml");
|
||||
|
||||
let mut panel = GuiPanel::new_from_template(
|
||||
app,
|
||||
"gui/watch.xml",
|
||||
watch_xml,
|
||||
state,
|
||||
NewGuiPanelParams {
|
||||
on_custom_id: Some(Box::new(
|
||||
@@ -165,7 +192,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
.fetch_component_as::<ComponentButton>(&format!("set_{idx}"))?;
|
||||
state.set_buttons.push(comp);
|
||||
}
|
||||
} else if &*id == "toolbox" {
|
||||
} else if &*id == "toolbox" || &*id == "toolbox-condensed" {
|
||||
for idx in 0..MAX_TOOLBOX_BUTTONS {
|
||||
let id_str = format!("overlay_{idx}");
|
||||
|
||||
@@ -193,6 +220,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
.get_widget_id(&format!("overlay_{idx}_sprite"))
|
||||
.inspect_err(|e| log::warn!("{e:?}"))
|
||||
.unwrap_or_default(),
|
||||
condensed: id.ends_with("-condensed"),
|
||||
});
|
||||
}
|
||||
} else if id.starts_with("overlay_") && id.ends_with("_sprite") {
|
||||
@@ -287,11 +315,15 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
|
||||
match event_data {
|
||||
OverlayEventData::ActiveSetChanged(current_set) => {
|
||||
if let Some(old_set) = panel.state.current_set.take() {
|
||||
panel.state.set_buttons[old_set].set_sticky_state(&mut com, false);
|
||||
if let Some(old_set) = panel.state.current_set.take()
|
||||
&& let Some(old_set) = panel.state.set_buttons.get_mut(old_set)
|
||||
{
|
||||
old_set.set_sticky_state(&mut com, false);
|
||||
}
|
||||
if let Some(new_set) = current_set {
|
||||
panel.state.set_buttons[new_set].set_sticky_state(&mut com, true);
|
||||
if let Some(new_set) = current_set
|
||||
&& let Some(new_set) = panel.state.set_buttons.get_mut(new_set)
|
||||
{
|
||||
new_set.set_sticky_state(&mut com, true);
|
||||
}
|
||||
panel.state.current_set = current_set;
|
||||
}
|
||||
@@ -341,7 +373,11 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
panel.state.overlay_metas = metas;
|
||||
for (idx, btn) in panel.state.overlay_buttons.iter().enumerate() {
|
||||
let display = if let Some(meta) = panel.state.overlay_metas.get(idx) {
|
||||
let name = sanitize_overlay_name(&meta.name);
|
||||
let name = btn
|
||||
.condensed
|
||||
.then(|| condense_overlay_name(&meta.name))
|
||||
.unwrap_or_else(|| sanitize_overlay_name(&meta.name));
|
||||
|
||||
if let Some(mut label) =
|
||||
panel.layout.state.widgets.get_as::<WidgetLabel>(btn.label)
|
||||
{
|
||||
@@ -438,3 +474,12 @@ pub fn watch_fade<D>(app: &mut AppState, watch: &mut OverlayWindowData<D>) {
|
||||
fn sanitize_overlay_name(str: &str) -> Rc<str> {
|
||||
str.replace("-wvr", "").into()
|
||||
}
|
||||
|
||||
fn condense_overlay_name(str: &str) -> Rc<str> {
|
||||
str.replace("DP-", "D")
|
||||
.replace("HDMI-A-", "H")
|
||||
.replace("WVR-wvr_", "W")
|
||||
.replace("WVR-wvr", "W0")
|
||||
.replace("Keyboard", "")
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -148,6 +148,31 @@ where
|
||||
OverlayTask::ToggleSet(set) => {
|
||||
self.switch_or_toggle_set(app, set);
|
||||
}
|
||||
OverlayTask::SoftToggleOverlay(sel) => {
|
||||
let Some(id) = self.id_by_selector(&sel) else {
|
||||
log::warn!("Overlay not found for task: {sel:?}");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let o = &mut self.overlays[id];
|
||||
if let Some(active_state) = o.config.active_state.take() {
|
||||
log::debug!("{}: soft-toggle off", o.config.name);
|
||||
self.sets[self.restore_set]
|
||||
.overlays
|
||||
.insert(id, active_state);
|
||||
} else if let Some(state) = self.sets[self.restore_set].overlays.remove(id) {
|
||||
let o = &mut self.overlays[id];
|
||||
log::debug!("{}: soft-toggle on", o.config.name);
|
||||
o.config.dirty = true;
|
||||
o.config.active_state = Some(state);
|
||||
o.config.reset(app, false);
|
||||
} else {
|
||||
// no saved state
|
||||
o.config.activate(app);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
OverlayTask::ToggleEditMode => {
|
||||
self.set_edit_mode(!self.edit_mode, app)?;
|
||||
}
|
||||
@@ -428,14 +453,19 @@ impl<T> OverlayWindowManager<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id_by_selector(&self, selector: &OverlaySelector) -> Option<OverlayID> {
|
||||
match selector {
|
||||
OverlaySelector::Id(id) => Some(*id),
|
||||
OverlaySelector::Name(name) => self.lookup(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mut_by_selector(
|
||||
&mut self,
|
||||
selector: &OverlaySelector,
|
||||
) -> Option<&mut OverlayWindowData<T>> {
|
||||
match selector {
|
||||
OverlaySelector::Id(id) => self.mut_by_id(*id),
|
||||
OverlaySelector::Name(name) => self.lookup(name).and_then(|id| self.mut_by_id(id)),
|
||||
}
|
||||
self.id_by_selector(selector)
|
||||
.and_then(|id| self.mut_by_id(id))
|
||||
}
|
||||
|
||||
fn remove_by_selector(
|
||||
@@ -559,7 +589,6 @@ impl<T> OverlayWindowManager<T> {
|
||||
|
||||
if let Some(current_set) = self.current_set.as_ref() {
|
||||
let ws = &mut self.sets[*current_set];
|
||||
ws.overlays.clear();
|
||||
for (id, data) in self.overlays.iter_mut().filter(|(_, d)| !d.config.global) {
|
||||
if let Some(state) = data.config.active_state.take() {
|
||||
log::debug!("{}: active_state → ws{}", data.config.name, current_set);
|
||||
@@ -582,6 +611,7 @@ impl<T> OverlayWindowManager<T> {
|
||||
data.config.reset(app, false);
|
||||
}
|
||||
}
|
||||
ws.overlays.clear();
|
||||
self.restore_set = new_set;
|
||||
}
|
||||
self.current_set = new_set;
|
||||
|
||||
Reference in New Issue
Block a user