add universal _long_release and variants

This commit is contained in:
galister
2025-12-22 17:47:46 +09:00
parent a0bc4001c0
commit b90b7336e0
13 changed files with 223 additions and 104 deletions

View File

@@ -23,7 +23,11 @@ use crate::{
},
};
use glam::{Mat4, Vec3};
use std::{cell::RefCell, rc::Rc};
use std::{
cell::RefCell,
rc::Rc,
time::{Duration, Instant},
};
use taffy::{prelude::length, AlignItems, JustifyContent};
pub struct Params<'a> {
@@ -42,6 +46,7 @@ pub struct Params<'a> {
/// until "un-clicked". this is visual only.
/// set the initial state using `set_sticky_state`
pub sticky: bool,
pub long_press_time: f32,
}
impl Default for Params<'_> {
@@ -59,6 +64,7 @@ impl Default for Params<'_> {
text_style: TextStyle::default(),
tooltip: None,
sticky: false,
long_press_time: 0.0,
}
}
}
@@ -80,6 +86,7 @@ struct State {
on_click: Option<ButtonClickCallback>,
active_tooltip: Option<Rc<ComponentTooltip>>,
colors: Colors,
last_pressed: Instant,
}
struct Data {
@@ -152,6 +159,10 @@ impl ComponentButton {
rect.params.color2 = get_color2(&color);
}
pub fn get_time_since_last_pressed(&self) -> Duration {
self.state.borrow().last_pressed.elapsed()
}
pub fn on_click(&self, func: ButtonClickCallback) {
self.state.borrow_mut().on_click = Some(func);
}
@@ -329,6 +340,7 @@ fn register_event_mouse_press(state: Rc<RefCell<State>>, listeners: &mut EventLi
if state.hovered {
state.down = true;
state.last_pressed = Instant::now();
Ok(EventResult::Consumed)
} else {
Ok(EventResult::Pass)
@@ -357,7 +369,6 @@ fn register_event_mouse_release(
if state.down {
state.down = false;
if state.hovered
&& let Some(on_click) = &state.on_click
{
@@ -507,6 +518,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
on_click: None,
active_tooltip: None,
sticky_down: false,
last_pressed: Instant::now(),
colors: Colors {
color,
border_color,

View File

@@ -5,7 +5,7 @@ use crate::{
i18n::Translation,
layout::WidgetID,
parser::{
parse_check_f32, parse_check_i32, parse_children, print_invalid_attrib, process_component,
parse_check_f32, parse_check_i32, parse_children, parse_f32, print_invalid_attrib, process_component,
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
AttribPair, ParserContext, ParserFile,
},
@@ -28,6 +28,7 @@ pub fn parse_component_button<'a>(
let mut tooltip: Option<String> = None;
let mut tooltip_side: Option<tooltip::TooltipSide> = None;
let mut sticky: bool = false;
let mut long_press_time = 0.0;
let mut sprite_src: Option<AssetPath> = None;
let mut translation: Option<Translation> = None;
@@ -92,6 +93,9 @@ pub fn parse_component_button<'a>(
let mut sticky_i32 = 0;
sticky = parse_check_i32(value, &mut sticky_i32) && sticky_i32 == 1;
}
"long_press_time" => {
long_press_time = parse_f32(value).unwrap_or(long_press_time);
}
_ => {}
}
}
@@ -113,6 +117,7 @@ pub fn parse_component_button<'a>(
text: Translation::from_translation_key(&t),
}),
sticky,
long_press_time,
sprite_src,
},
)?;

View File

@@ -205,7 +205,11 @@ impl EventResult {
#[must_use]
pub fn merge(self, other: Self) -> Self {
if self > other { self } else { other }
if self > other {
self
} else {
other
}
}
}

View File

@@ -121,6 +121,9 @@ pub struct GeneralConfig {
#[serde(default = "def_one")]
pub scroll_speed: f32,
#[serde(default = "def_one")]
pub long_press_duration: f32,
#[serde(default = "def_mouse_move_interval_ms")]
pub mouse_move_interval_ms: u32,

View File

@@ -15,7 +15,7 @@
</template>
<template name="TopButtonDanger">
<Button macro="button_style" tooltip="${tooltip}" _press="${press}" _release="${release}" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
<Button macro="button_style" tooltip="${tooltip}" _long_release="${long_release}" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
<sprite width="48" height="48" src="${src}" />
</Button>
</template>
@@ -48,7 +48,7 @@
<TopButton sticky="0" id="top_mouse" src="edit/normal.svg" tooltip="EDIT_MODE.MOUSE.TITLE" press="::EditModeTab mouse" />
<!-- TopButton sticky="0" id="top_move" src="edit/move-all.svg" tooltip="EDIT_MODE.MOVE_PRESS_AND_DRAG" / -->
<!-- TopButton sticky="0" id="top_resize" src="edit/resize.svg" tooltip="EDIT_MODE.RESIZE_PRESS_AND_DRAG" / -->
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" long_release="::EditModeDelete" />
<div width="8" height="100%" />
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" press="::EditToggle" />
</div>

View File

@@ -35,7 +35,7 @@ All overlays are listed on bottom row.
<template name="Overlay">
<Button macro="button_style" id="overlay_${idx}"
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}"
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}" _long_release="::SingleSetOverlayReset ${idx}"
align_items="center"
height="40">
<sprite id="overlay_${idx}_sprite" src_builtin="${src}" width="32" height="32" />

View File

@@ -140,7 +140,7 @@
<sprite color="~set_color" width="40" height="40" src="watch/wayvr_dashboard_mono.svg" />
</Button>
<div id="edit_delete" display="none">
<Button macro="button_style" _press="::EditModeDeleteDown" _release="::EditModeDeleteUp" tooltip="WATCH.LONG_PRESS_TO_DELETE_SET" tooltip_side="top" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
<Button macro="button_style" _long_release="::EditModeDeleteSet" tooltip="WATCH.LONG_PRESS_TO_DELETE_SET" tooltip_side="top" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
<sprite color="~set_color" width="40" height="40" src="edit/delete.svg" />
</Button>
</div>

View File

@@ -81,11 +81,17 @@ Supported events:
<button _press="..." _release="..." />
```
Laser-color-specific variants are also available:
Laser-color-specific variants are also available
- `_press_left` & `_release_left` for blue laser
- `_press_right` & `_release_right` for orange laser
- `_press_middle` & `_release_middle` for purple laser
Release after short/long press (length controlled by config `long_press_duration`)
- `_short_release` & `_long_release` for any laser
- `_short_release_left` & `_long_release_left` for blue laser
- `_short_release_right` & `_long_release_right` for orange laser
- `_short_release_middle` & `_long_release_middle` for purple laser
#### Supported button actions
##### `::ShellExec <command> [args ..]`

View File

@@ -33,38 +33,115 @@ use crate::{
#[cfg(feature = "wayvr")]
use crate::backend::wayvr::WayVRAction;
pub const BUTTON_EVENTS: [(&str, EventListenerKind, fn(&mut CallbackData) -> bool); 8] = [
("_press", EventListenerKind::MousePress, any_button),
("_release", EventListenerKind::MouseRelease, any_button),
("_press_left", EventListenerKind::MousePress, left_button),
pub const BUTTON_EVENTS: [(
&str,
EventListenerKind,
fn(&mut CallbackData) -> bool,
fn(&ComponentButton, &AppState) -> bool,
); 16] = [
(
"_press",
EventListenerKind::MousePress,
button_any,
short_duration,
),
(
"_release",
EventListenerKind::MouseRelease,
button_any,
ignore_duration,
),
(
"_press_left",
EventListenerKind::MousePress,
button_left,
ignore_duration,
),
(
"_release_left",
EventListenerKind::MouseRelease,
left_button,
button_left,
ignore_duration,
),
(
"_press_right",
EventListenerKind::MousePress,
button_right,
ignore_duration,
),
("_press_right", EventListenerKind::MousePress, right_button),
(
"_release_right",
EventListenerKind::MouseRelease,
right_button,
button_right,
ignore_duration,
),
(
"_press_middle",
EventListenerKind::MousePress,
middle_button,
button_middle,
ignore_duration,
),
(
"_release_middle",
EventListenerKind::MouseRelease,
middle_button,
button_middle,
ignore_duration,
),
(
"_short_release",
EventListenerKind::MouseRelease,
button_any,
short_duration,
),
(
"_short_release_left",
EventListenerKind::MouseRelease,
button_left,
short_duration,
),
(
"_short_release_right",
EventListenerKind::MouseRelease,
button_right,
short_duration,
),
(
"_short_release_middle",
EventListenerKind::MouseRelease,
button_middle,
short_duration,
),
(
"_long_release",
EventListenerKind::MouseRelease,
button_any,
long_duration,
),
(
"_long_release_left",
EventListenerKind::MouseRelease,
button_left,
long_duration,
),
(
"_long_release_right",
EventListenerKind::MouseRelease,
button_right,
long_duration,
),
(
"_long_release_middle",
EventListenerKind::MouseRelease,
button_middle,
long_duration,
),
];
fn any_button(_: &mut CallbackData) -> bool {
fn button_any(_: &mut CallbackData) -> bool {
true
}
fn left_button(data: &mut CallbackData) -> bool {
fn button_left(data: &mut CallbackData) -> bool {
if let CallbackMetadata::MouseButton(b) = data.metadata
&& let MouseButtonIndex::Left = b.index
{
@@ -73,7 +150,7 @@ fn left_button(data: &mut CallbackData) -> bool {
false
}
}
fn right_button(data: &mut CallbackData) -> bool {
fn button_right(data: &mut CallbackData) -> bool {
if let CallbackMetadata::MouseButton(b) = data.metadata
&& let MouseButtonIndex::Right = b.index
{
@@ -82,7 +159,7 @@ fn right_button(data: &mut CallbackData) -> bool {
false
}
}
fn middle_button(data: &mut CallbackData) -> bool {
fn button_middle(data: &mut CallbackData) -> bool {
if let CallbackMetadata::MouseButton(b) = data.metadata
&& let MouseButtonIndex::Middle = b.index
{
@@ -92,13 +169,23 @@ fn middle_button(data: &mut CallbackData) -> bool {
}
}
fn ignore_duration(_btn: &ComponentButton, _app: &AppState) -> bool {
true
}
fn long_duration(btn: &ComponentButton, app: &AppState) -> bool {
btn.get_time_since_last_pressed().as_secs_f32() > app.session.config.long_press_duration
}
fn short_duration(btn: &ComponentButton, app: &AppState) -> bool {
btn.get_time_since_last_pressed().as_secs_f32() < app.session.config.long_press_duration
}
pub(super) fn setup_custom_button<S: 'static>(
layout: &mut Layout,
attribs: &CustomAttribsInfoOwned,
_app: &AppState,
button: Rc<ComponentButton>,
) {
for (name, kind, test_btn) in &BUTTON_EVENTS {
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
let Some(action) = attribs.get_value(name) else {
continue;
};
@@ -108,10 +195,12 @@ pub(super) fn setup_custom_button<S: 'static>(
continue;
};
let button = button.clone();
let callback: EventCallback<AppState, S> = match command {
#[cfg(feature = "wayvr")]
"::DashToggle" => Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -126,7 +215,7 @@ pub(super) fn setup_custom_button<S: 'static>(
return;
};
Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -142,7 +231,7 @@ pub(super) fn setup_custom_button<S: 'static>(
};
Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -160,7 +249,7 @@ pub(super) fn setup_custom_button<S: 'static>(
})
}
"::EditToggle" => Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -170,7 +259,7 @@ pub(super) fn setup_custom_button<S: 'static>(
}),
#[cfg(feature = "wayland")]
"::NewMirror" => Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -187,7 +276,7 @@ pub(super) fn setup_custom_button<S: 'static>(
Ok(EventResult::Consumed)
}),
"::CleanupMirrors" => Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -196,7 +285,7 @@ pub(super) fn setup_custom_button<S: 'static>(
Ok(EventResult::Consumed)
}),
"::PlayspaceReset" => Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -204,7 +293,7 @@ pub(super) fn setup_custom_button<S: 'static>(
Ok(EventResult::Consumed)
}),
"::PlayspaceRecenter" => Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -213,7 +302,7 @@ pub(super) fn setup_custom_button<S: 'static>(
Ok(EventResult::Consumed)
}),
"::PlayspaceFixFloor" => Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -233,8 +322,8 @@ pub(super) fn setup_custom_button<S: 'static>(
);
Ok(EventResult::Consumed)
}),
"::Shutdown" => Box::new(move |_common, data, _app, _| {
if !test_btn(data) {
"::Shutdown" => Box::new(move |_common, data, app, _| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -255,7 +344,7 @@ pub(super) fn setup_custom_button<S: 'static>(
return;
};
Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -285,8 +374,8 @@ pub(super) fn setup_custom_button<S: 'static>(
}),
);
Box::new(move |_common, data, _app, _| {
if !test_btn(data) {
Box::new(move |_common, data, app, _| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -314,7 +403,7 @@ pub(super) fn setup_custom_button<S: 'static>(
}
Box::new(move |_common, data, app, _| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}

View File

@@ -70,7 +70,8 @@ pub type OnCustomIdFunc<S> = Box<
) -> anyhow::Result<()>,
>;
pub type OnCustomAttribFunc = Box<dyn Fn(&mut Layout, &CustomAttribsInfoOwned, &AppState)>;
pub type OnCustomAttribFunc =
Box<dyn Fn(&mut Layout, &ParserState, &CustomAttribsInfoOwned, &AppState)>;
pub struct NewGuiPanelParams<S> {
pub on_custom_id: Option<OnCustomIdFunc<S>>, // used only in `new_from_template`
@@ -155,7 +156,7 @@ impl<S: 'static> GuiPanel<S> {
}
if let Some(on_custom_attrib) = &params.on_custom_attrib {
on_custom_attrib(&mut layout, elem, app);
on_custom_attrib(&mut layout, &parser_state, elem, app);
}
}

View File

@@ -47,21 +47,9 @@ mod sprite_tab;
mod stereo;
pub mod tab;
pub(super) struct LongPressButtonState {
pub(super) pressed: Instant,
}
impl Default for LongPressButtonState {
fn default() -> Self {
Self {
pressed: Instant::now(),
}
}
}
struct EditModeState {
tasks: Rc<RefCell<TaskContainer>>,
id: Rc<RefCell<OverlayID>>,
delete: LongPressButtonState,
tabs: ButtonPaneTabSwitcher,
lock: InteractLockHandler,
pos: SpriteTabHandler<PosTabState>,
@@ -272,9 +260,6 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
let state = EditModeState {
id: Rc::new(RefCell::new(OverlayID::null())),
tasks: Rc::new(RefCell::new(TaskContainer::new())),
delete: LongPressButtonState {
pressed: Instant::now(),
},
tabs: ButtonPaneTabSwitcher::default(),
lock: InteractLockHandler::default(),
pos: SpriteTabHandler::default(),
@@ -284,8 +269,14 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
let anim_mult = app.wgui_globals.defaults().animation_mult;
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
for (name, kind, test_btn) in &BUTTON_EVENTS {
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| {
let Ok(button) =
parser.fetch_component_from_widget_id_as::<ComponentButton>(attribs.widget_id)
else {
return;
};
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
let Some(action) = attribs.get_value(name) else {
continue;
};
@@ -295,9 +286,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
continue;
};
let button = button.clone();
let callback: EventCallback<AppState, EditModeState> = match command {
"::EditModeToggleLock" => Box::new(move |common, data, app, state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -308,7 +301,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
Ok(EventResult::Consumed)
}),
"::EditModeToggleGrab" => Box::new(move |_common, data, app, state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -324,8 +317,8 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
}),
"::EditModeTab" => {
let tab_name = args.next().unwrap().to_owned();
Box::new(move |common, data, _app, state| {
if !test_btn(data) {
Box::new(move |common, data, app, state| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -336,7 +329,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
"::EditModeSetPos" => {
let key = args.next().unwrap().to_owned();
Box::new(move |common, data, app, state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -350,7 +343,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
"::EditModeSetStereo" => {
let key = args.next().unwrap().to_owned();
Box::new(move |common, data, app, state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -364,7 +357,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
"::EditModeSetMouse" => {
let key = args.next().unwrap().to_owned();
Box::new(move |common, data, app, state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -375,23 +368,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
Ok(EventResult::Consumed)
})
}
"::EditModeDeletePress" => Box::new(move |_common, data, _app, state| {
if !test_btn(data) {
"::EditModeDelete" => Box::new(move |_common, data, app, state| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
state.delete.pressed = Instant::now();
// TODO: animate to light up button after 2s
Ok(EventResult::Consumed)
}),
"::EditModeDeleteRelease" => Box::new(move |_common, data, app, state| {
if !test_btn(data) {
return Ok(EventResult::Pass);
}
if state.delete.pressed.elapsed() < Duration::from_secs(1) {
return Ok(EventResult::Pass);
}
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(*state.id.borrow()),
Box::new(move |_app, owc| {

View File

@@ -1,8 +1,4 @@
use std::{
collections::HashMap,
rc::Rc,
time::{Duration, Instant},
};
use std::{collections::HashMap, rc::Rc, time::Duration};
use glam::{Affine3A, Quat, Vec3, Vec3A, vec3};
use idmap::DirectIdMap;
@@ -31,7 +27,6 @@ use crate::{
panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS},
timer::GuiTimer,
},
overlays::edit::LongPressButtonState,
state::AppState,
windowing::{
OverlayID, OverlaySelector, Z_ORDER_WATCH,
@@ -70,7 +65,6 @@ struct WatchState {
keyboard_oid: OverlayID,
dashboard_oid: OverlayID,
num_sets: usize,
delete: LongPressButtonState,
}
#[allow(clippy::significant_drop_tightening)]
@@ -78,8 +72,14 @@ struct WatchState {
pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
let state = WatchState::default();
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
for (name, kind, test_btn) in &BUTTON_EVENTS {
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| {
let Ok(button) =
parser.fetch_component_from_widget_id_as::<ComponentButton>(attribs.widget_id)
else {
return;
};
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
let Some(action) = attribs.get_value(name) else {
continue;
};
@@ -89,29 +89,20 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
continue;
};
let button = button.clone();
let callback: EventCallback<AppState, WatchState> = match command {
"::EditModeDeleteDown" => Box::new(move |_common, data, _app, state| {
if !test_btn(data) {
"::EditModeDeleteSet" => Box::new(move |_common, data, app, _state| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
state.delete.pressed = Instant::now();
Ok(EventResult::Consumed)
}),
"::EditModeDeleteUp" => Box::new(move |_common, data, app, state| {
if !test_btn(data) {
return Ok(EventResult::Pass);
}
if state.delete.pressed.elapsed() < Duration::from_secs(1) {
return Ok(EventResult::Consumed);
}
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::DeleteActiveSet));
Ok(EventResult::Consumed)
}),
"::EditModeAddSet" => Box::new(move |_common, data, app, _state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -125,7 +116,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
return;
};
Box::new(move |_common, data, app, state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -154,7 +145,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
return;
};
Box::new(move |_common, data, app, state| {
if !test_btn(data) {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
@@ -170,6 +161,29 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
Ok(EventResult::Consumed)
})
}
"::SingleSetOverlayReset" => {
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| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
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::Modify(
OverlaySelector::Id(overlay.id),
Box::new(|app, owc| owc.activate(app)),
)));
Ok(EventResult::Consumed)
})
}
_ => return,
};

View File

@@ -95,7 +95,7 @@
#color_accent: "#008cff"
#color_danger: "#ff3300"
#color_faded: "#668299"
#color_background: "#010206",
#color_background: "#010206"
## Multiplier for animation speed. 2.0 → double speed, 0.5 → half speed
#animation_speed: 1.0
@@ -175,6 +175,10 @@
#xr_click_sensitivity: 0.7
#xr_click_sensitivity_release: 0.5
## How many seconds to buttons need to be held
## before it's considered a long press
#long_press_duration: 1.0
## Change speed of scrolling
# 0.5 → half the speed
# 2.0 → twice the speed