single set mode
This commit is contained in:
@@ -187,6 +187,9 @@ pub struct GeneralConfig {
|
|||||||
#[serde(default = "def_false")]
|
#[serde(default = "def_false")]
|
||||||
pub double_cursor_fix: bool,
|
pub double_cursor_fix: bool,
|
||||||
|
|
||||||
|
#[serde(default = "def_false")]
|
||||||
|
pub single_set_mode: bool,
|
||||||
|
|
||||||
#[serde(default = "def_astrset_empty")]
|
#[serde(default = "def_astrset_empty")]
|
||||||
pub custom_panels: AStrSet,
|
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::f32::consts::PI;
|
||||||
use std::process::{Child, Command};
|
use std::process::{Child, Command};
|
||||||
use std::{collections::VecDeque, time::Instant};
|
use std::time::Instant;
|
||||||
|
|
||||||
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
|
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
|
||||||
|
|
||||||
@@ -192,7 +192,6 @@ pub struct InteractionState {
|
|||||||
pub grabbed: Option<GrabData>,
|
pub grabbed: Option<GrabData>,
|
||||||
pub clicked_id: Option<OverlayID>,
|
pub clicked_id: Option<OverlayID>,
|
||||||
pub hovered_id: Option<OverlayID>,
|
pub hovered_id: Option<OverlayID>,
|
||||||
pub release_actions: VecDeque<Box<dyn Fn()>>,
|
|
||||||
pub next_push: Instant,
|
pub next_push: Instant,
|
||||||
pub haptics: Option<f32>,
|
pub haptics: Option<f32>,
|
||||||
}
|
}
|
||||||
@@ -204,7 +203,6 @@ impl Default for InteractionState {
|
|||||||
grabbed: None,
|
grabbed: None,
|
||||||
clicked_id: None,
|
clicked_id: None,
|
||||||
hovered_id: None,
|
hovered_id: None,
|
||||||
release_actions: VecDeque::new(),
|
|
||||||
next_push: Instant::now(),
|
next_push: Instant::now(),
|
||||||
haptics: None,
|
haptics: None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<OverlayWindowCo
|
|||||||
pub enum OverlayTask {
|
pub enum OverlayTask {
|
||||||
AddSet,
|
AddSet,
|
||||||
ToggleSet(usize),
|
ToggleSet(usize),
|
||||||
|
SoftToggleOverlay(OverlaySelector),
|
||||||
DeleteActiveSet,
|
DeleteActiveSet,
|
||||||
ToggleEditMode,
|
ToggleEditMode,
|
||||||
ShowHide,
|
ShowHide,
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ struct OverlayButton {
|
|||||||
button: Rc<ComponentButton>,
|
button: Rc<ComponentButton>,
|
||||||
label: WidgetID,
|
label: WidgetID,
|
||||||
sprite: WidgetID,
|
sprite: WidgetID,
|
||||||
|
condensed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -126,6 +127,25 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
Ok(EventResult::Consumed)
|
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,
|
_ => 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(
|
let mut panel = GuiPanel::new_from_template(
|
||||||
app,
|
app,
|
||||||
"gui/watch.xml",
|
watch_xml,
|
||||||
state,
|
state,
|
||||||
NewGuiPanelParams {
|
NewGuiPanelParams {
|
||||||
on_custom_id: Some(Box::new(
|
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}"))?;
|
.fetch_component_as::<ComponentButton>(&format!("set_{idx}"))?;
|
||||||
state.set_buttons.push(comp);
|
state.set_buttons.push(comp);
|
||||||
}
|
}
|
||||||
} else if &*id == "toolbox" {
|
} else if &*id == "toolbox" || &*id == "toolbox-condensed" {
|
||||||
for idx in 0..MAX_TOOLBOX_BUTTONS {
|
for idx in 0..MAX_TOOLBOX_BUTTONS {
|
||||||
let id_str = format!("overlay_{idx}");
|
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"))
|
.get_widget_id(&format!("overlay_{idx}_sprite"))
|
||||||
.inspect_err(|e| log::warn!("{e:?}"))
|
.inspect_err(|e| log::warn!("{e:?}"))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
|
condensed: id.ends_with("-condensed"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if id.starts_with("overlay_") && id.ends_with("_sprite") {
|
} 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 {
|
match event_data {
|
||||||
OverlayEventData::ActiveSetChanged(current_set) => {
|
OverlayEventData::ActiveSetChanged(current_set) => {
|
||||||
if let Some(old_set) = panel.state.current_set.take() {
|
if let Some(old_set) = panel.state.current_set.take()
|
||||||
panel.state.set_buttons[old_set].set_sticky_state(&mut com, false);
|
&& 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 {
|
if let Some(new_set) = current_set
|
||||||
panel.state.set_buttons[new_set].set_sticky_state(&mut com, true);
|
&& 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;
|
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;
|
panel.state.overlay_metas = metas;
|
||||||
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 = 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) =
|
if let Some(mut label) =
|
||||||
panel.layout.state.widgets.get_as::<WidgetLabel>(btn.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> {
|
fn sanitize_overlay_name(str: &str) -> Rc<str> {
|
||||||
str.replace("-wvr", "").into()
|
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) => {
|
OverlayTask::ToggleSet(set) => {
|
||||||
self.switch_or_toggle_set(app, 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 => {
|
OverlayTask::ToggleEditMode => {
|
||||||
self.set_edit_mode(!self.edit_mode, app)?;
|
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(
|
pub fn mut_by_selector(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &OverlaySelector,
|
selector: &OverlaySelector,
|
||||||
) -> Option<&mut OverlayWindowData<T>> {
|
) -> Option<&mut OverlayWindowData<T>> {
|
||||||
match selector {
|
self.id_by_selector(selector)
|
||||||
OverlaySelector::Id(id) => self.mut_by_id(*id),
|
.and_then(|id| self.mut_by_id(id))
|
||||||
OverlaySelector::Name(name) => self.lookup(name).and_then(|id| self.mut_by_id(id)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_by_selector(
|
fn remove_by_selector(
|
||||||
@@ -559,7 +589,6 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
|
|
||||||
if let Some(current_set) = self.current_set.as_ref() {
|
if let Some(current_set) = self.current_set.as_ref() {
|
||||||
let ws = &mut self.sets[*current_set];
|
let ws = &mut self.sets[*current_set];
|
||||||
ws.overlays.clear();
|
|
||||||
for (id, data) in self.overlays.iter_mut().filter(|(_, d)| !d.config.global) {
|
for (id, data) in self.overlays.iter_mut().filter(|(_, d)| !d.config.global) {
|
||||||
if let Some(state) = data.config.active_state.take() {
|
if let Some(state) = data.config.active_state.take() {
|
||||||
log::debug!("{}: active_state → ws{}", data.config.name, current_set);
|
log::debug!("{}: active_state → ws{}", data.config.name, current_set);
|
||||||
@@ -582,6 +611,7 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
data.config.reset(app, false);
|
data.config.reset(app, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ws.overlays.clear();
|
||||||
self.restore_set = new_set;
|
self.restore_set = new_set;
|
||||||
}
|
}
|
||||||
self.current_set = new_set;
|
self.current_set = new_set;
|
||||||
|
|||||||
Reference in New Issue
Block a user