watch controls for edit mode
This commit is contained in:
@@ -97,6 +97,14 @@ impl ComponentTrait for ComponentButton {
|
||||
}
|
||||
|
||||
impl ComponentButton {
|
||||
pub fn get_label(&self) -> WidgetID {
|
||||
self.data.id_label
|
||||
}
|
||||
|
||||
pub fn get_rect(&self) -> WidgetID {
|
||||
self.data.id_rect
|
||||
}
|
||||
|
||||
pub fn set_text(&self, common: &mut CallbackDataCommon, text: Translation) {
|
||||
let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(self.data.id_label) else {
|
||||
return;
|
||||
|
||||
1
wlx-overlay-s/src/assets/edit/desktop.svg
Normal file
1
wlx-overlay-s/src/assets/edit/desktop.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M6 16h9v-5H6zm10.5-3H18V8H9v1.5h7.5zM4 20q-.825 0-1.412-.587T2 18V6q0-.825.588-1.412T4 4h16q.825 0 1.413.588T22 6v12q0 .825-.587 1.413T20 20z"/></svg>
|
||||
|
After Width: | Height: | Size: 371 B |
1
wlx-overlay-s/src/assets/edit/mirror.svg
Normal file
1
wlx-overlay-s/src/assets/edit/mirror.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M2 11.6L9.6 4H4q-.825 0-1.413.588T2 6v5.6Zm0 7L16.575 4H12.4L2 14.425V18.6ZM3.4 20H20q.825 0 1.413-.588T22 18V6q0-.825-.588-1.413T20 4h-.6l-16 16Z"/></svg>
|
||||
|
After Width: | Height: | Size: 376 B |
1
wlx-overlay-s/src/assets/edit/monitor.svg
Normal file
1
wlx-overlay-s/src/assets/edit/monitor.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M8 21v-2h2v-2H4q-.825 0-1.412-.587T2 15V5q0-.825.588-1.412T4 3h16q.825 0 1.413.588T22 5v10q0 .825-.587 1.413T20 17h-6v2h2v2z"/></svg>
|
||||
|
After Width: | Height: | Size: 354 B |
1
wlx-overlay-s/src/assets/edit/window.svg
Normal file
1
wlx-overlay-s/src/assets/edit/window.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Remix Icon by Remix Design - https://github.com/Remix-Design/RemixIcon/blob/master/License --><path fill="white" d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1m17 7H4v9h16zM5 6v2h2V6zm4 0v2h2V6z"/></svg>
|
||||
|
After Width: | Height: | Size: 329 B |
@@ -6,26 +6,26 @@
|
||||
border="2" round="50%" padding="8" gradient="vertical" tooltip_side="bottom" />
|
||||
|
||||
<template name="TopButton">
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}" sticky="${sticky}" border_color="#0044CC" color="#000A1C" color2="#000002">
|
||||
<sprite id="${id}_sprite" width="48" height="48" src="${src}" _release="${release}" />
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}" _release="${release}" sticky="${sticky}" border_color="#0044CC" color="#000A1C" color2="#000002">
|
||||
<sprite id="${id}_sprite" width="48" height="48" src="${src}" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template name="TopButtonDanger">
|
||||
<Button macro="button_style" tooltip="${tooltip}" _press="${press}" border_color="#CC0000" color="#110000" color2="#020000">
|
||||
<sprite width="48" height="48" src="${src}" _release="${release}" />
|
||||
<Button macro="button_style" tooltip="${tooltip}" _press="${press}" _release="${release}" border_color="#CC0000" color="#110000" color2="#020000">
|
||||
<sprite width="48" height="48" src="${src}" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template name="TopButtonFaded">
|
||||
<Button macro="button_style" tooltip="${tooltip}" _press="${press}" border_color="#707070" color="#202020" color2="#010101">
|
||||
<sprite width="48" height="48" src="${src}" _release="${release}" />
|
||||
<Button macro="button_style" tooltip="${tooltip}" _press="${press}" _release="${release}" border_color="#707070" color="#202020" color2="#010101">
|
||||
<sprite width="48" height="48" src="${src}" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template name="PosButton">
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}" border_color="#0044CC" color="#000A1C" color2="#000002">
|
||||
<sprite id="${id}_sprite" width="40" height="40" src="${src}" _release="${release}" />
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}" _release="${release}" border_color="#0044CC" color="#000A1C" color2="#000002">
|
||||
<sprite id="${id}_sprite" width="40" height="40" src="${src}" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -19,12 +19,24 @@
|
||||
border_color="#0044CC" border="2" round="8" color="#000A1C" color2="#000002" gradient="vertical" />
|
||||
|
||||
<template name="Device">
|
||||
<sprite width="${size}" height="${size}" src="${src}" />
|
||||
<div position="absolute" margin_top="10" margin_left="9">
|
||||
<label _source="battery" _device="${device}" size="18" shadow="#000000" weight="bold" />
|
||||
<div margin="4">
|
||||
<sprite width="${size}" height="${size}" src="${src}" />
|
||||
<div margin_top="10" margin_left="-31">
|
||||
<label _source="battery" _device="${device}" size="18" shadow="#000000" weight="bold" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="SpriteButton">
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}">
|
||||
<sprite width="40" height="40" src="${src}" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template name="Screen">
|
||||
<Button macro="button_style" padding="4" id="screen_${idx}" text="DISP-${idx}" tooltip="Toggle for current set" _press="::EditModeScreenToggle ${idx}" />
|
||||
</template>
|
||||
|
||||
<template name="Set">
|
||||
<Button macro="button_style" id="set_${handle}" _press="::SetToggle ${handle}" tooltip="Switch to set" tooltip_side="top">
|
||||
<sprite width="40" height="40" color="~set_color" src="watch/set2.svg" />
|
||||
@@ -36,8 +48,8 @@
|
||||
|
||||
<elements>
|
||||
<div width="460" height="260" padding="30" interactable="0">
|
||||
<rectangle width="100%" height="100%" padding="4" box_sizing="content_box" flex_wrap="wrap" flex_direction="column" gap="4" color="~color_bg">
|
||||
<div width="100%" flex_direction="row">
|
||||
<rectangle width="100%" height="100%" padding="4" box_sizing="content_box" flex_wrap="wrap" flex_direction="column" gap="4" color="~color_bg" justify_content="space_between">
|
||||
<div width="100%" flex_direction="row" id="norm_top">
|
||||
<Device src="watch/hmd.svg" size="40" device="0" />
|
||||
<Device src="watch/controller_l.svg" size="36" device="1" />
|
||||
<Device src="watch/controller_r.svg" size="36" device="2" />
|
||||
@@ -45,7 +57,7 @@
|
||||
<Device src="watch/track3.svg" size="40" device="4" />
|
||||
<Device src="watch/track3.svg" size="40" device="5" />
|
||||
</div>
|
||||
<div flex_direction="row">
|
||||
<div width="100%" flex_direction="row" id="norm_pane">
|
||||
<div flex_direction="column" padding="4">
|
||||
<label text="23:59" _source="clock" _display="time" color="~clock0_color" size="~clock0_size" weight="bold" />
|
||||
<label text="22/2/2022" _source="clock" _display="date" color="~clock0_color" size="~clock0_date_size" weight="bold" />
|
||||
@@ -63,13 +75,34 @@
|
||||
<label text="23:59" _source="clock" _display="time" _timezone="1" color="~clock_alt2_color" size="~clock_alt_size" weight="bold" />
|
||||
</div>
|
||||
</div>
|
||||
<div width="100%" flex_direction="column" id="edit_pane" display="none" >
|
||||
<div flex_direction="column" padding="4" align_items="center" justify_content="center">
|
||||
<label text="Overlays can now be moved and tuned individually." />
|
||||
<label text="Control which overlays are visible on this set:" />
|
||||
</div>
|
||||
<div flex_direction="row" flex_wrap="wrap" padding="4" align_items="center" justify_content="center" id="toolbox">
|
||||
<Button macro="button_style" padding="4" translation="Keyboard" tooltip="Toggle for current set" _press="::OverlayToggle kbd" />
|
||||
</div>
|
||||
</div>
|
||||
<div width="100%" flex_direction="row">
|
||||
<Button macro="button_style" _press="::DashToggle" tooltip="Dashboard" tooltip_side="top">
|
||||
<sprite color="~set_color" width="40" height="40" src="watch/home.svg" />
|
||||
</Button>
|
||||
<div id="norm_dash">
|
||||
<Button macro="button_style" _press="::DashToggle" tooltip="Dashboard" tooltip_side="top" id="norm_dash">
|
||||
<sprite color="~set_color" width="40" height="40" src="watch/home.svg" />
|
||||
</Button>
|
||||
</div>
|
||||
<div id="edit_delete">
|
||||
<Button macro="button_style" _press="::EditModeDeleteDown" _release="::EditModeDeleteUp" tooltip="Long Press: Delete Set" tooltip_side="top" border_color="#CC0000" color="#110000" color2="#020000" >
|
||||
<sprite color="~set_color" width="40" height="40" src="edit/delete.svg" />
|
||||
</Button>
|
||||
</div>
|
||||
<div id="sets">
|
||||
<!-- Will populate <Set> tags at runtime -->
|
||||
</div>
|
||||
<div id="edit_add">
|
||||
<Button macro="button_style" _press="::EditModeAddSet" tooltip="Add a new set" tooltip_side="top">
|
||||
<sprite color="~set_color" width="40" height="40" src="edit/add.svg" />
|
||||
</Button>
|
||||
</div>
|
||||
<Button macro="button_style" _press="::EditToggle" tooltip="Edit mode" tooltip_side="top">
|
||||
<sprite color="~set_color" width="40" height="40" src="watch/edit.svg" />
|
||||
</Button>
|
||||
|
||||
@@ -2,38 +2,38 @@ use std::{
|
||||
collections::VecDeque,
|
||||
ops::Add,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use anyhow::{anyhow, Result};
|
||||
use ovr_overlay::{
|
||||
TrackedDeviceIndex,
|
||||
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
|
||||
TrackedDeviceIndex,
|
||||
};
|
||||
use vulkano::{Handle, VulkanObject, device::physical::PhysicalDevice};
|
||||
use vulkano::{device::physical::PhysicalDevice, Handle, VulkanObject};
|
||||
use wlx_common::overlays::ToastTopic;
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
BackendError,
|
||||
input::interact,
|
||||
openvr::{
|
||||
helpers::adjust_gain,
|
||||
input::{OpenVrInputSource, set_action_manifest},
|
||||
input::{set_action_manifest, OpenVrInputSource},
|
||||
lines::LinePool,
|
||||
manifest::{install_manifest, uninstall_manifest},
|
||||
overlay::OpenVrOverlayData,
|
||||
},
|
||||
task::{SystemTask, TaskType},
|
||||
task::{ManagerTask, SystemTask, TaskType},
|
||||
BackendError,
|
||||
},
|
||||
config::save_state,
|
||||
graphics::{GpuFutures, init_openvr_graphics},
|
||||
graphics::{init_openvr_graphics, GpuFutures},
|
||||
overlays::{
|
||||
toast::Toast,
|
||||
watch::{WATCH_NAME, watch_fade},
|
||||
watch::{watch_fade, WATCH_NAME},
|
||||
},
|
||||
state::AppState,
|
||||
subsystem::notifications::NotificationManager,
|
||||
@@ -107,7 +107,7 @@ pub fn openvr_run(
|
||||
|
||||
if show_by_default {
|
||||
app.tasks.enqueue_at(
|
||||
TaskType::System(SystemTask::ShowHide),
|
||||
TaskType::Manager(ManagerTask::ShowHide),
|
||||
Instant::now().add(Duration::from_secs(1)),
|
||||
);
|
||||
}
|
||||
@@ -254,26 +254,9 @@ pub fn openvr_run(
|
||||
SystemTask::ResetPlayspace => {
|
||||
playspace.reset_offset(&mut chaperone_mgr, &app.input_state);
|
||||
}
|
||||
SystemTask::ShowHide => {
|
||||
overlays.show_hide(&mut app);
|
||||
}
|
||||
},
|
||||
TaskType::ToggleSet(set) => {
|
||||
overlays.switch_or_toggle_set(&mut app, set);
|
||||
}
|
||||
TaskType::ToggleEditMode => {
|
||||
if !overlays.get_edit_mode() {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Edit mode enabled".into(),
|
||||
"Hover overlays to see their options".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
.submit(&mut app);
|
||||
}
|
||||
|
||||
overlays.set_edit_mode(!overlays.get_edit_mode());
|
||||
TaskType::Manager(task) => {
|
||||
overlays.handle_task(&mut app, task)?;
|
||||
}
|
||||
#[cfg(feature = "wayvr")]
|
||||
TaskType::WayVR(action) => {
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::{
|
||||
collections::VecDeque,
|
||||
ops::Add,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
@@ -18,16 +18,16 @@ use wlx_common::overlays::ToastTopic;
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
BackendError,
|
||||
input::interact,
|
||||
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
|
||||
task::{SystemTask, TaskType},
|
||||
task::{ManagerTask, SystemTask, TaskType},
|
||||
BackendError,
|
||||
},
|
||||
config::save_state,
|
||||
graphics::{GpuFutures, init_openxr_graphics},
|
||||
graphics::{init_openxr_graphics, GpuFutures},
|
||||
overlays::{
|
||||
toast::Toast,
|
||||
watch::{WATCH_NAME, watch_fade},
|
||||
watch::{watch_fade, WATCH_NAME},
|
||||
},
|
||||
state::AppState,
|
||||
subsystem::notifications::NotificationManager,
|
||||
@@ -95,7 +95,7 @@ pub fn openxr_run(
|
||||
|
||||
if show_by_default {
|
||||
app.tasks.enqueue_at(
|
||||
TaskType::System(SystemTask::ShowHide),
|
||||
TaskType::Manager(ManagerTask::ShowHide),
|
||||
Instant::now().add(Duration::from_secs(1)),
|
||||
);
|
||||
}
|
||||
@@ -537,27 +537,10 @@ pub fn openxr_run(
|
||||
playspace.reset_offset(monado.as_mut().unwrap()); // safe
|
||||
}
|
||||
}
|
||||
SystemTask::ShowHide => {
|
||||
overlays.show_hide(&mut app);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
TaskType::ToggleSet(set) => {
|
||||
overlays.switch_or_toggle_set(&mut app, set);
|
||||
}
|
||||
TaskType::ToggleEditMode => {
|
||||
if !overlays.get_edit_mode() {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Edit mode enabled".into(),
|
||||
"Hover overlays to see their options".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
.submit(&mut app);
|
||||
}
|
||||
|
||||
overlays.set_edit_mode(!overlays.get_edit_mode());
|
||||
TaskType::Manager(task) => {
|
||||
overlays.handle_task(&mut app, task)?;
|
||||
}
|
||||
#[cfg(feature = "wayvr")]
|
||||
TaskType::WayVR(action) => {
|
||||
|
||||
@@ -47,18 +47,23 @@ pub enum SystemTask {
|
||||
ColorGain(ColorChannel, f32),
|
||||
ResetPlayspace,
|
||||
FixFloor,
|
||||
ShowHide,
|
||||
}
|
||||
|
||||
pub type OverlayTask = dyn FnOnce(&mut AppState, &mut OverlayWindowConfig) + Send;
|
||||
pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<OverlayWindowConfig> + Send;
|
||||
pub enum ManagerTask {
|
||||
AddSet,
|
||||
ToggleSet(usize),
|
||||
DeleteActiveSet,
|
||||
ToggleEditMode,
|
||||
ShowHide,
|
||||
}
|
||||
|
||||
pub enum TaskType {
|
||||
Overlay(OverlaySelector, Box<OverlayTask>),
|
||||
CreateOverlay(OverlaySelector, Box<CreateOverlayTask>),
|
||||
DropOverlay(OverlaySelector),
|
||||
ToggleSet(usize),
|
||||
ToggleEditMode,
|
||||
Manager(ManagerTask),
|
||||
System(SystemTask),
|
||||
#[cfg(feature = "wayvr")]
|
||||
WayVR(WayVRAction),
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{
|
||||
cell::RefCell,
|
||||
io::BufReader,
|
||||
process::{Child, ChildStdout},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use wgui::{
|
||||
@@ -11,7 +12,7 @@ use wgui::{
|
||||
widget::EventResult,
|
||||
};
|
||||
|
||||
use crate::state::AppState;
|
||||
use crate::{backend::task::ManagerTask, state::AppState, windowing::OverlaySelector};
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
use crate::backend::{task::TaskType, wayvr::WayVRAction};
|
||||
@@ -48,21 +49,42 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
"::SetToggle" => {
|
||||
let arg = args.next().unwrap_or_default();
|
||||
let Ok(set_idx) = arg.parse() else {
|
||||
log::error!("::SetToggle has invalid argument: \"{arg}\"");
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
app.tasks.enqueue(TaskType::ToggleSet(set_idx));
|
||||
app.tasks
|
||||
.enqueue(TaskType::Manager(ManagerTask::ToggleSet(set_idx)));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::OverlayToggle" => {
|
||||
let Some(arg): Option<Arc<str>> = args.next().map(|a| a.into()) else {
|
||||
log::error!("{command} has missing arguments");
|
||||
return;
|
||||
};
|
||||
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Name(arg.clone()),
|
||||
Box::new(move |app, owc| {
|
||||
if owc.active_state.is_none() {
|
||||
owc.activate(app);
|
||||
} else {
|
||||
owc.deactivate();
|
||||
}
|
||||
}),
|
||||
));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::WatchHide" => todo!(),
|
||||
"::WatchSwapHand" => todo!(),
|
||||
// TODO
|
||||
"::EditToggle" => Box::new(move |_common, _data, app, _| {
|
||||
app.tasks.enqueue(TaskType::ToggleEditMode);
|
||||
app.tasks
|
||||
.enqueue(TaskType::Manager(ManagerTask::ToggleEditMode));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::WatchHide" => todo!(),
|
||||
"::WatchSwapHand" => todo!(),
|
||||
// TODO
|
||||
#[allow(clippy::match_same_arms)]
|
||||
"::OscSend" => return,
|
||||
|
||||
@@ -7,7 +7,7 @@ use wgui::{
|
||||
assets::AssetPath,
|
||||
drawing,
|
||||
event::{
|
||||
Event as WguiEvent, EventCallback, EventListenerID, EventListenerKind,
|
||||
Event as WguiEvent, EventAlterables, EventCallback, EventListenerID, EventListenerKind,
|
||||
InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent,
|
||||
MouseMotionEvent, MouseUpEvent, MouseWheelEvent,
|
||||
},
|
||||
@@ -15,6 +15,7 @@ use wgui::{
|
||||
layout::{Layout, LayoutParams, WidgetID},
|
||||
parser::{CustomAttribsInfoOwned, ParserState},
|
||||
renderer_vk::context::Context as WguiContext,
|
||||
taffy,
|
||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle, EventResult},
|
||||
};
|
||||
|
||||
@@ -220,6 +221,22 @@ impl<S: 'static> GuiPanel<S> {
|
||||
) -> Option<EventListenerID> {
|
||||
self.layout.add_event_listener(widget_id, kind, callback)
|
||||
}
|
||||
|
||||
pub fn widget_set_display(
|
||||
&self,
|
||||
widget_id: WidgetID,
|
||||
display: taffy::Display,
|
||||
alterables: &mut EventAlterables,
|
||||
) {
|
||||
let mut style = self
|
||||
.layout
|
||||
.state
|
||||
.get_widget_style(widget_id)
|
||||
.unwrap_or(&taffy::Style::DEFAULT)
|
||||
.clone();
|
||||
style.display = display;
|
||||
alterables.set_style(widget_id, style);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
||||
|
||||
@@ -34,10 +34,17 @@ use crate::{
|
||||
|
||||
mod lock;
|
||||
mod pos;
|
||||
mod tab;
|
||||
pub(crate) mod tab;
|
||||
|
||||
struct LongPressButtonState {
|
||||
pressed: Instant,
|
||||
pub(super) struct LongPressButtonState {
|
||||
pub(super) pressed: Instant,
|
||||
}
|
||||
impl Default for LongPressButtonState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pressed: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EditModeState {
|
||||
@@ -264,7 +271,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeleteRelease" => Box::new(move |_common, _data, app, state| {
|
||||
if state.delete.pressed.elapsed() > Duration::from_secs(2) {
|
||||
if state.delete.pressed.elapsed() < Duration::from_secs(2) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
@@ -296,7 +303,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
|
||||
panel.state.pos = PositioningHandler::new(&mut panel)?;
|
||||
panel.state.lock = InteractLockHandler::new(&mut panel)?;
|
||||
panel.state.tabs = ButtonPaneTabSwitcher::new(&mut panel)?;
|
||||
panel.state.tabs = ButtonPaneTabSwitcher::new(&mut panel, &["none", "pos", "alpha", "curve"])?;
|
||||
|
||||
set_up_checkbox(&mut panel, "additive_box", cb_assign_additive)?;
|
||||
set_up_slider(&mut panel, "alpha_slider", cb_assign_alpha)?;
|
||||
|
||||
@@ -5,9 +5,8 @@ use wgui::{
|
||||
parser::Fetchable, taffy,
|
||||
};
|
||||
|
||||
use crate::overlays::edit::EditModeWrapPanel;
|
||||
use crate::gui::panel::GuiPanel;
|
||||
|
||||
static TABS: [&str; 4] = ["none", "pos", "alpha", "curve"];
|
||||
static BUTTON_PREFIX: &str = "top_";
|
||||
static PANE_PREFIX: &str = "tab_";
|
||||
|
||||
@@ -19,16 +18,16 @@ struct TabData {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct ButtonPaneTabSwitcher {
|
||||
pub struct ButtonPaneTabSwitcher {
|
||||
tabs: HashMap<&'static str, Rc<TabData>>,
|
||||
active_tab: Option<Rc<TabData>>,
|
||||
}
|
||||
|
||||
impl ButtonPaneTabSwitcher {
|
||||
pub fn new(panel: &mut EditModeWrapPanel) -> anyhow::Result<Self> {
|
||||
pub fn new<S>(panel: &mut GuiPanel<S>, tab_names: &[&'static str]) -> anyhow::Result<Self> {
|
||||
let mut tabs = HashMap::new();
|
||||
|
||||
for tab_name in &TABS {
|
||||
for tab_name in tab_names {
|
||||
let name = format!("{BUTTON_PREFIX}{tab_name}");
|
||||
let button = panel.parser_state.fetch_component_as(&name).ok();
|
||||
|
||||
|
||||
@@ -1,23 +1,34 @@
|
||||
use std::{collections::HashMap, rc::Rc, time::Duration};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use glam::{Affine3A, Vec3, Vec3A};
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
event::{CallbackDataCommon, EventAlterables},
|
||||
event::{CallbackDataCommon, EventAlterables, EventCallback},
|
||||
i18n::Translation,
|
||||
layout::WidgetID,
|
||||
parser::Fetchable,
|
||||
taffy,
|
||||
widget::EventResult,
|
||||
};
|
||||
use wlx_common::windowing::{OverlayWindowState, Positioning};
|
||||
|
||||
use crate::{
|
||||
backend::task::{ManagerTask, TaskType},
|
||||
gui::{
|
||||
panel::{GuiPanel, NewGuiPanelParams},
|
||||
panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc},
|
||||
timer::GuiTimer,
|
||||
},
|
||||
overlays::edit::LongPressButtonState,
|
||||
state::AppState,
|
||||
windowing::{
|
||||
Z_ORDER_WATCH,
|
||||
backend::OverlayEventData,
|
||||
manager::MAX_OVERLAY_SETS,
|
||||
window::{OverlayWindowConfig, OverlayWindowData},
|
||||
OverlaySelector, Z_ORDER_WATCH,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,11 +38,78 @@ pub const WATCH_NAME: &str = "watch";
|
||||
struct WatchState {
|
||||
current_set: Option<usize>,
|
||||
set_buttons: Vec<Rc<ComponentButton>>,
|
||||
screen_buttons: Vec<Rc<ComponentButton>>,
|
||||
edit_mode_widgets: Vec<(WidgetID, bool)>,
|
||||
edit_add_widget: WidgetID,
|
||||
num_sets: usize,
|
||||
delete: LongPressButtonState,
|
||||
}
|
||||
|
||||
#[allow(clippy::significant_drop_tightening)]
|
||||
pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<OverlayWindowConfig> {
|
||||
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) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut args = action.split_whitespace();
|
||||
let Some(command) = args.next() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let callback: EventCallback<AppState, WatchState> = match command {
|
||||
"::EditModeDeleteDown" => Box::new(move |_common, _data, _app, state| {
|
||||
state.delete.pressed = Instant::now();
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeleteUp" => Box::new(move |_common, _data, app, state| {
|
||||
if state.delete.pressed.elapsed() < Duration::from_secs(2) {
|
||||
return Ok(EventResult::Consumed);
|
||||
}
|
||||
app.tasks
|
||||
.enqueue(TaskType::Manager(ManagerTask::DeleteActiveSet));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeAddSet" => Box::new(move |_common, _data, app, _state| {
|
||||
app.tasks.enqueue(TaskType::Manager(ManagerTask::AddSet));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeScreenToggle" => {
|
||||
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(screen) = app.screens.get(idx) else {
|
||||
log::error!("No screen at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
};
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Name(screen.name.clone()),
|
||||
Box::new(move |app, owc| {
|
||||
if owc.active_state.is_none() {
|
||||
owc.activate(app);
|
||||
} else {
|
||||
owc.deactivate();
|
||||
}
|
||||
}),
|
||||
));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let id = layout.add_event_listener(attribs.widget_id, *kind, callback);
|
||||
log::debug!("Registered {action} on {:?} as {id:?}", attribs.widget_id);
|
||||
}
|
||||
});
|
||||
|
||||
let mut panel = GuiPanel::new_from_template(
|
||||
app,
|
||||
"gui/watch.xml",
|
||||
@@ -39,37 +117,56 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
|
||||
NewGuiPanelParams {
|
||||
on_custom_id: Some(Box::new(
|
||||
move |id, widget, doc_params, layout, parser_state, state| {
|
||||
if &*id != "sets" {
|
||||
return Ok(());
|
||||
}
|
||||
if id.starts_with("norm_") {
|
||||
state.edit_mode_widgets.push((widget, false));
|
||||
} else if &*id == "edit_add" {
|
||||
state.edit_add_widget = widget;
|
||||
} else if id.starts_with("edit_") {
|
||||
state.edit_mode_widgets.push((widget, true));
|
||||
} else if &*id == "sets" {
|
||||
for idx in 0..MAX_OVERLAY_SETS {
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert("display".into(), (idx + 1).to_string().into());
|
||||
params.insert("handle".into(), idx.to_string().into());
|
||||
parser_state
|
||||
.instantiate_template(doc_params, "Set", layout, widget, params)?;
|
||||
|
||||
for idx in 0..num_sets {
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert("display".into(), (idx + 1).to_string().into());
|
||||
params.insert("handle".into(), idx.to_string().into());
|
||||
parser_state
|
||||
.instantiate_template(doc_params, "Set", layout, widget, params)?;
|
||||
let button_id = format!("set_{idx}");
|
||||
let component =
|
||||
parser_state.fetch_component_as::<ComponentButton>(&button_id)?;
|
||||
state.set_buttons.push(component);
|
||||
}
|
||||
} else if &*id == "toolbox" {
|
||||
for idx in 0..9 {
|
||||
let screen_id = format!("screen_{idx}");
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert("idx".into(), idx.to_string().into());
|
||||
parser_state.instantiate_template(
|
||||
doc_params, "Screen", layout, widget, params,
|
||||
)?;
|
||||
|
||||
let button_id = format!("set_{idx}");
|
||||
let component =
|
||||
parser_state.fetch_component_as::<ComponentButton>(&button_id)?;
|
||||
state.set_buttons.push(component);
|
||||
let component =
|
||||
parser_state.fetch_component_as::<ComponentButton>(&screen_id)?;
|
||||
state.screen_buttons.push(component);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)),
|
||||
on_custom_attrib: Some(on_custom_attrib),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
panel.on_notify = Some(Box::new(|panel, _app, event_data| {
|
||||
panel.on_notify = Some(Box::new(|panel, app, event_data| {
|
||||
let mut alterables = EventAlterables::default();
|
||||
let mut common = CallbackDataCommon {
|
||||
alterables: &mut alterables,
|
||||
state: &panel.layout.state,
|
||||
};
|
||||
|
||||
match event_data {
|
||||
OverlayEventData::SetChanged(current_set) => {
|
||||
let mut alterables = EventAlterables::default();
|
||||
let mut common = CallbackDataCommon {
|
||||
alterables: &mut alterables,
|
||||
state: &panel.layout.state,
|
||||
};
|
||||
OverlayEventData::ActiveSetChanged(current_set) => {
|
||||
if let Some(old_set) = panel.state.current_set.take() {
|
||||
panel.state.set_buttons[old_set].set_sticky_state(&mut common, false);
|
||||
}
|
||||
@@ -77,9 +174,64 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
|
||||
panel.state.set_buttons[new_set].set_sticky_state(&mut common, true);
|
||||
}
|
||||
panel.state.current_set = current_set;
|
||||
panel.layout.process_alterables(alterables)?;
|
||||
}
|
||||
OverlayEventData::NumSetsChanged(num_sets) => {
|
||||
panel.state.num_sets = num_sets;
|
||||
for i in 0..MAX_OVERLAY_SETS {
|
||||
let comp = panel.state.set_buttons[i].clone();
|
||||
let rect_id = comp.get_rect();
|
||||
let display = if i < num_sets {
|
||||
taffy::Display::Flex
|
||||
} else {
|
||||
taffy::Display::None
|
||||
};
|
||||
panel.widget_set_display(rect_id, display, &mut common.alterables);
|
||||
}
|
||||
let display = if num_sets < 7 {
|
||||
taffy::Display::Flex
|
||||
} else {
|
||||
taffy::Display::None
|
||||
};
|
||||
panel.widget_set_display(
|
||||
panel.state.edit_add_widget,
|
||||
display,
|
||||
&mut common.alterables,
|
||||
);
|
||||
}
|
||||
OverlayEventData::EditModeChanged(edit_mode) => {
|
||||
for (w, e) in panel.state.edit_mode_widgets.iter() {
|
||||
let display = if *e == edit_mode {
|
||||
taffy::Display::Flex
|
||||
} else {
|
||||
taffy::Display::None
|
||||
};
|
||||
panel.widget_set_display(*w, display, &mut common.alterables);
|
||||
}
|
||||
let display = if edit_mode && panel.state.num_sets < 7 {
|
||||
taffy::Display::Flex
|
||||
} else {
|
||||
taffy::Display::None
|
||||
};
|
||||
panel.widget_set_display(
|
||||
panel.state.edit_add_widget,
|
||||
display,
|
||||
&mut common.alterables,
|
||||
);
|
||||
}
|
||||
OverlayEventData::ScreensChanged => {
|
||||
for (idx, btn) in panel.state.screen_buttons.iter().enumerate() {
|
||||
let display = if let Some(screen) = app.screens.get(idx) {
|
||||
btn.set_text(&mut common, Translation::from_raw_text(&screen.name));
|
||||
taffy::Display::Flex
|
||||
} else {
|
||||
taffy::Display::None
|
||||
};
|
||||
panel.widget_set_display(btn.get_rect(), display, &mut common.alterables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panel.layout.process_alterables(alterables)?;
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
|
||||
@@ -65,7 +65,11 @@ impl RenderResources {
|
||||
}
|
||||
|
||||
pub enum OverlayEventData {
|
||||
SetChanged(Option<usize>),
|
||||
ActiveSetChanged(Option<usize>),
|
||||
NumSetsChanged(usize),
|
||||
EditModeChanged(bool),
|
||||
///TODO: this only gets fired at startup
|
||||
ScreensChanged,
|
||||
}
|
||||
|
||||
pub trait OverlayBackend: Any {
|
||||
|
||||
@@ -2,20 +2,23 @@ use std::collections::HashMap;
|
||||
|
||||
use glam::{Affine3A, Vec3, Vec3A};
|
||||
use slotmap::{HopSlotMap, Key, SecondaryMap};
|
||||
use wlx_common::config::SerializedWindowSet;
|
||||
use wlx_common::{config::SerializedWindowSet, overlays::ToastTopic};
|
||||
|
||||
use crate::{
|
||||
backend::task::ManagerTask,
|
||||
overlays::{
|
||||
anchor::create_anchor, edit::EditWrapperManager, keyboard::builder::create_keyboard,
|
||||
screen::create_screens, watch::create_watch,
|
||||
screen::create_screens, toast::Toast, watch::create_watch,
|
||||
},
|
||||
state::AppState,
|
||||
windowing::{
|
||||
OverlayID, OverlaySelector, backend::OverlayEventData, set::OverlayWindowSet, snap_upright,
|
||||
window::OverlayWindowData,
|
||||
backend::OverlayEventData, set::OverlayWindowSet, snap_upright, window::OverlayWindowData,
|
||||
OverlayID, OverlaySelector,
|
||||
},
|
||||
};
|
||||
|
||||
pub const MAX_OVERLAY_SETS: usize = 7;
|
||||
|
||||
pub struct OverlayWindowManager<T> {
|
||||
wrappers: EditWrapperManager,
|
||||
overlays: HopSlotMap<OverlayID, OverlayWindowData<T>>,
|
||||
@@ -95,7 +98,15 @@ where
|
||||
let anchor = OverlayWindowData::from_config(create_anchor(app)?);
|
||||
me.add(anchor, app);
|
||||
|
||||
let watch = OverlayWindowData::from_config(create_watch(app, me.sets.len())?);
|
||||
let mut watch = OverlayWindowData::from_config(create_watch(app)?);
|
||||
|
||||
for ev in [
|
||||
OverlayEventData::ScreensChanged,
|
||||
OverlayEventData::NumSetsChanged(me.sets.len()),
|
||||
OverlayEventData::EditModeChanged(false),
|
||||
] {
|
||||
watch.config.backend.notify(app, ev)?;
|
||||
}
|
||||
me.watch_id = me.add(watch, app);
|
||||
|
||||
// overwrite default layout with saved layout, if exists
|
||||
@@ -174,14 +185,21 @@ impl<T> OverlayWindowManager<T> {
|
||||
self.edit_mode
|
||||
}
|
||||
|
||||
pub fn set_edit_mode(&mut self, enabled: bool) {
|
||||
pub fn set_edit_mode(&mut self, enabled: bool, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let changed = enabled != self.edit_mode;
|
||||
self.edit_mode = enabled;
|
||||
if enabled {
|
||||
return;
|
||||
if !enabled {
|
||||
for o in self.overlays.values_mut() {
|
||||
self.wrappers.unwrap_edit_mode(&mut o.config);
|
||||
}
|
||||
}
|
||||
for o in self.overlays.values_mut() {
|
||||
self.wrappers.unwrap_edit_mode(&mut o.config);
|
||||
if changed && let Some(watch) = self.mut_by_id(self.watch_id) {
|
||||
watch
|
||||
.config
|
||||
.backend
|
||||
.notify(app, OverlayEventData::EditModeChanged(enabled))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn edit_overlay(&mut self, id: OverlayID, enabled: bool, app: &mut AppState) {
|
||||
@@ -324,7 +342,7 @@ impl<T> OverlayWindowManager<T> {
|
||||
watch
|
||||
.config
|
||||
.backend
|
||||
.notify(app, OverlayEventData::SetChanged(new_set))
|
||||
.notify(app, OverlayEventData::ActiveSetChanged(new_set))
|
||||
.unwrap(); // TODO: handle this
|
||||
}
|
||||
}
|
||||
@@ -342,4 +360,63 @@ impl<T> OverlayWindowManager<T> {
|
||||
// toggle watch back on if it was hidden
|
||||
self.mut_by_id(self.watch_id).unwrap().config.activate(app);
|
||||
}
|
||||
|
||||
pub fn handle_task(&mut self, app: &mut AppState, task: ManagerTask) -> anyhow::Result<()> {
|
||||
match task {
|
||||
ManagerTask::ShowHide => self.show_hide(app),
|
||||
ManagerTask::ToggleSet(set) => {
|
||||
self.switch_or_toggle_set(app, set);
|
||||
}
|
||||
ManagerTask::ToggleEditMode => {
|
||||
self.set_edit_mode(!self.edit_mode, app)?;
|
||||
}
|
||||
ManagerTask::AddSet => {
|
||||
self.sets.push(OverlayWindowSet::default());
|
||||
let len = self.sets.len();
|
||||
if let Some(watch) = self.mut_by_id(self.watch_id) {
|
||||
watch
|
||||
.config
|
||||
.backend
|
||||
.notify(app, OverlayEventData::NumSetsChanged(len))?;
|
||||
}
|
||||
}
|
||||
ManagerTask::DeleteActiveSet => {
|
||||
let Some(set) = self.current_set else {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Can't remove set".into(),
|
||||
"No set is selected!".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
.submit(app);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if self.sets.len() <= 1 {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Can't remove set".into(),
|
||||
"This is the last existing set!".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
.submit(app);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.switch_to_set(app, None);
|
||||
self.sets.remove(set);
|
||||
let len = self.sets.len();
|
||||
if let Some(watch) = self.mut_by_id(self.watch_id) {
|
||||
watch
|
||||
.config
|
||||
.backend
|
||||
.notify(app, OverlayEventData::NumSetsChanged(len))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user