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

View File

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

View File

@@ -205,7 +205,11 @@ impl EventResult {
#[must_use] #[must_use]
pub fn merge(self, other: Self) -> Self { 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")] #[serde(default = "def_one")]
pub scroll_speed: f32, pub scroll_speed: f32,
#[serde(default = "def_one")]
pub long_press_duration: f32,
#[serde(default = "def_mouse_move_interval_ms")] #[serde(default = "def_mouse_move_interval_ms")]
pub mouse_move_interval_ms: u32, pub mouse_move_interval_ms: u32,

View File

@@ -15,7 +15,7 @@
</template> </template>
<template name="TopButtonDanger"> <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}" /> <sprite width="48" height="48" src="${src}" />
</Button> </Button>
</template> </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_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_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" / --> <!-- 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%" /> <div width="8" height="100%" />
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" press="::EditToggle" /> <TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" press="::EditToggle" />
</div> </div>

View File

@@ -35,7 +35,7 @@ All overlays are listed on bottom row.
<template name="Overlay"> <template name="Overlay">
<Button macro="button_style" id="overlay_${idx}" <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" align_items="center"
height="40"> height="40">
<sprite id="overlay_${idx}_sprite" src_builtin="${src}" width="32" height="32" /> <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" /> <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">
<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" /> <sprite color="~set_color" width="40" height="40" src="edit/delete.svg" />
</Button> </Button>
</div> </div>

View File

@@ -81,11 +81,17 @@ Supported events:
<button _press="..." _release="..." /> <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_left` & `_release_left` for blue laser
- `_press_right` & `_release_right` for orange laser - `_press_right` & `_release_right` for orange laser
- `_press_middle` & `_release_middle` for purple 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 #### Supported button actions
##### `::ShellExec <command> [args ..]` ##### `::ShellExec <command> [args ..]`

View File

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

View File

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

View File

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

View File

@@ -95,7 +95,7 @@
#color_accent: "#008cff" #color_accent: "#008cff"
#color_danger: "#ff3300" #color_danger: "#ff3300"
#color_faded: "#668299" #color_faded: "#668299"
#color_background: "#010206", #color_background: "#010206"
## Multiplier for animation speed. 2.0 → double speed, 0.5 → half speed ## Multiplier for animation speed. 2.0 → double speed, 0.5 → half speed
#animation_speed: 1.0 #animation_speed: 1.0
@@ -175,6 +175,10 @@
#xr_click_sensitivity: 0.7 #xr_click_sensitivity: 0.7
#xr_click_sensitivity_release: 0.5 #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 ## Change speed of scrolling
# 0.5 → half the speed # 0.5 → half the speed
# 2.0 → twice the speed # 2.0 → twice the speed