watch toolbox to support various overlay types
This commit is contained in:
@@ -33,8 +33,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="Screen">
|
<template name="Overlay">
|
||||||
<Button macro="button_style" padding="4" id="screen_${idx}" text="DISP-${idx}" tooltip="Toggle for current set" _press="::EditModeScreenToggle ${idx}" />
|
<Button macro="button_style" padding="4" id="overlay_${idx}" text="WLX-${idx}" tooltip="Toggle for current set" _press="::EditModeOverlayToggle ${idx}" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="Set">
|
<template name="Set">
|
||||||
@@ -82,6 +82,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div flex_direction="row" flex_wrap="wrap" padding="4" align_items="center" justify_content="center" id="toolbox">
|
<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" />
|
<Button macro="button_style" padding="4" translation="Keyboard" tooltip="Toggle for current set" _press="::OverlayToggle kbd" />
|
||||||
|
<!-- Will populate <Overlay> tags at runtime -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div width="100%" flex_direction="row">
|
<div width="100%" flex_direction="row">
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ pub fn openvr_run(
|
|||||||
&& o.birthframe < cur_frame
|
&& o.birthframe < cur_frame
|
||||||
{
|
{
|
||||||
o.destroy(&mut overlay_mgr);
|
o.destroy(&mut overlay_mgr);
|
||||||
overlays.remove_by_selector(&sel);
|
overlays.remove_by_selector(&sel, &mut app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TaskType::System(task) => match task {
|
TaskType::System(task) => match task {
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ pub fn openxr_run(
|
|||||||
&& o.birthframe < cur_frame
|
&& o.birthframe < cur_frame
|
||||||
{
|
{
|
||||||
log::debug!("{}: destroy", o.config.name);
|
log::debug!("{}: destroy", o.config.name);
|
||||||
if let Some(o) = overlays.remove_by_selector(&sel) {
|
if let Some(o) = overlays.remove_by_selector(&sel, &mut app) {
|
||||||
// set for deletion after all images are done showing
|
// set for deletion after all images are done showing
|
||||||
delete_queue.push((o, cur_frame + 5));
|
delete_queue.push((o, cur_frame + 5));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
use glam::{vec3, Affine3A, Quat, Vec3};
|
||||||
use wlx_common::windowing::OverlayWindowState;
|
use wlx_common::windowing::OverlayWindowState;
|
||||||
|
|
||||||
use crate::{gui::panel::GuiPanel, state::AppState, windowing::window::OverlayWindowConfig};
|
use crate::{
|
||||||
|
gui::panel::GuiPanel,
|
||||||
|
state::AppState,
|
||||||
|
windowing::window::{OverlayCategory, OverlayWindowConfig},
|
||||||
|
};
|
||||||
|
|
||||||
const SETTINGS_NAME: &str = "settings";
|
const SETTINGS_NAME: &str = "settings";
|
||||||
|
|
||||||
@@ -20,6 +24,7 @@ pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindow
|
|||||||
|
|
||||||
Some(OverlayWindowConfig {
|
Some(OverlayWindowConfig {
|
||||||
name,
|
name,
|
||||||
|
category: OverlayCategory::PanelCustom,
|
||||||
default_state: OverlayWindowState {
|
default_state: OverlayWindowState {
|
||||||
interactable: true,
|
interactable: true,
|
||||||
grabbable: true,
|
grabbable: true,
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
|||||||
Ok(EventResult::Consumed)
|
Ok(EventResult::Consumed)
|
||||||
}),
|
}),
|
||||||
"::EditModeDeleteRelease" => Box::new(move |_common, _data, app, state| {
|
"::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(1) {
|
||||||
return Ok(EventResult::Pass);
|
return Ok(EventResult::Pass);
|
||||||
}
|
}
|
||||||
app.tasks.enqueue(TaskType::Overlay(
|
app.tasks.enqueue(TaskType::Overlay(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use futures::{Future, FutureExt};
|
use futures::{Future, FutureExt};
|
||||||
use glam::{Affine2, Affine3A, Vec3};
|
use glam::{Affine2, Affine3A, Vec3};
|
||||||
use wlx_capture::pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen};
|
use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult};
|
||||||
use wlx_common::windowing::OverlayWindowState;
|
use wlx_common::windowing::OverlayWindowState;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -16,12 +16,12 @@ use crate::{
|
|||||||
state::{AppSession, AppState},
|
state::{AppSession, AppState},
|
||||||
subsystem::hid::WheelDelta,
|
subsystem::hid::WheelDelta,
|
||||||
windowing::{
|
windowing::{
|
||||||
OverlaySelector,
|
|
||||||
backend::{
|
backend::{
|
||||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
|
ui_transform, FrameMeta, OverlayBackend, OverlayEventData, RenderResources,
|
||||||
ui_transform,
|
ShouldRender,
|
||||||
},
|
},
|
||||||
window::OverlayWindowConfig,
|
window::{OverlayCategory, OverlayWindowConfig},
|
||||||
|
OverlaySelector,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -152,6 +152,7 @@ impl OverlayBackend for MirrorBackend {
|
|||||||
pub fn new_mirror(name: Arc<str>, session: &AppSession) -> OverlayWindowConfig {
|
pub fn new_mirror(name: Arc<str>, session: &AppSession) -> OverlayWindowConfig {
|
||||||
OverlayWindowConfig {
|
OverlayWindowConfig {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
|
category: OverlayCategory::Mirror,
|
||||||
default_state: OverlayWindowState {
|
default_state: OverlayWindowState {
|
||||||
interactable: true,
|
interactable: true,
|
||||||
grabbable: true,
|
grabbable: true,
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
use std::{f32::consts::PI, sync::Arc};
|
use std::{f32::consts::PI, sync::Arc};
|
||||||
|
|
||||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
use glam::{vec3, Affine3A, Quat, Vec3};
|
||||||
use wlx_capture::frame::Transform;
|
use wlx_capture::frame::Transform;
|
||||||
use wlx_common::windowing::{OverlayWindowState, Positioning};
|
use wlx_common::windowing::{OverlayWindowState, Positioning};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
state::{AppSession, AppState, ScreenMeta},
|
state::{AppSession, AppState, ScreenMeta},
|
||||||
subsystem::{hid::XkbKeymap, input::KeyboardFocus},
|
subsystem::{hid::XkbKeymap, input::KeyboardFocus},
|
||||||
windowing::{backend::OverlayBackend, window::OverlayWindowConfig},
|
windowing::{
|
||||||
|
backend::OverlayBackend,
|
||||||
|
window::{OverlayCategory, OverlayWindowConfig},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
@@ -38,6 +41,7 @@ fn create_screen_from_backend(
|
|||||||
|
|
||||||
OverlayWindowConfig {
|
OverlayWindowConfig {
|
||||||
name,
|
name,
|
||||||
|
category: OverlayCategory::Screen,
|
||||||
default_state: OverlayWindowState {
|
default_state: OverlayWindowState {
|
||||||
grabbable: true,
|
grabbable: true,
|
||||||
positioning: Positioning::Anchored,
|
positioning: Positioning::Anchored,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use crate::{
|
|||||||
overlays::edit::LongPressButtonState,
|
overlays::edit::LongPressButtonState,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::{
|
windowing::{
|
||||||
backend::OverlayEventData,
|
backend::{OverlayEventData, OverlayMeta},
|
||||||
manager::MAX_OVERLAY_SETS,
|
manager::MAX_OVERLAY_SETS,
|
||||||
window::{OverlayWindowConfig, OverlayWindowData},
|
window::{OverlayWindowConfig, OverlayWindowData},
|
||||||
OverlaySelector, Z_ORDER_WATCH,
|
OverlaySelector, Z_ORDER_WATCH,
|
||||||
@@ -33,12 +33,14 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const WATCH_NAME: &str = "watch";
|
pub const WATCH_NAME: &str = "watch";
|
||||||
|
const MAX_TOOLBOX_BUTTONS: usize = 16;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct WatchState {
|
struct WatchState {
|
||||||
current_set: Option<usize>,
|
current_set: Option<usize>,
|
||||||
set_buttons: Vec<Rc<ComponentButton>>,
|
set_buttons: Vec<Rc<ComponentButton>>,
|
||||||
screen_buttons: Vec<Rc<ComponentButton>>,
|
overlay_buttons: Vec<Rc<ComponentButton>>,
|
||||||
|
overlay_metas: Vec<OverlayMeta>,
|
||||||
edit_mode_widgets: Vec<(WidgetID, bool)>,
|
edit_mode_widgets: Vec<(WidgetID, bool)>,
|
||||||
edit_add_widget: WidgetID,
|
edit_add_widget: WidgetID,
|
||||||
num_sets: usize,
|
num_sets: usize,
|
||||||
@@ -66,7 +68,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
Ok(EventResult::Consumed)
|
Ok(EventResult::Consumed)
|
||||||
}),
|
}),
|
||||||
"::EditModeDeleteUp" => Box::new(move |_common, _data, app, state| {
|
"::EditModeDeleteUp" => Box::new(move |_common, _data, app, state| {
|
||||||
if state.delete.pressed.elapsed() < Duration::from_secs(2) {
|
if state.delete.pressed.elapsed() < Duration::from_secs(1) {
|
||||||
return Ok(EventResult::Consumed);
|
return Ok(EventResult::Consumed);
|
||||||
}
|
}
|
||||||
app.tasks
|
app.tasks
|
||||||
@@ -77,20 +79,20 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
app.tasks.enqueue(TaskType::Manager(ManagerTask::AddSet));
|
app.tasks.enqueue(TaskType::Manager(ManagerTask::AddSet));
|
||||||
Ok(EventResult::Consumed)
|
Ok(EventResult::Consumed)
|
||||||
}),
|
}),
|
||||||
"::EditModeScreenToggle" => {
|
"::EditModeOverlayToggle" => {
|
||||||
let arg = args.next().unwrap_or_default();
|
let arg = args.next().unwrap_or_default();
|
||||||
let Ok(idx) = arg.parse::<usize>() else {
|
let Ok(idx) = arg.parse::<usize>() else {
|
||||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
Box::new(move |_common, _data, app, _state| {
|
Box::new(move |_common, _data, app, state| {
|
||||||
let Some(screen) = app.screens.get(idx) else {
|
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||||
log::error!("No screen at index {idx}.");
|
log::error!("No overlay at index {idx}.");
|
||||||
return Ok(EventResult::Consumed);
|
return Ok(EventResult::Consumed);
|
||||||
};
|
};
|
||||||
|
|
||||||
app.tasks.enqueue(TaskType::Overlay(
|
app.tasks.enqueue(TaskType::Overlay(
|
||||||
OverlaySelector::Name(screen.name.clone()),
|
OverlaySelector::Id(overlay.id.clone()),
|
||||||
Box::new(move |app, owc| {
|
Box::new(move |app, owc| {
|
||||||
if owc.active_state.is_none() {
|
if owc.active_state.is_none() {
|
||||||
owc.activate(app);
|
owc.activate(app);
|
||||||
@@ -137,17 +139,17 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
state.set_buttons.push(component);
|
state.set_buttons.push(component);
|
||||||
}
|
}
|
||||||
} else if &*id == "toolbox" {
|
} else if &*id == "toolbox" {
|
||||||
for idx in 0..9 {
|
for idx in 0..MAX_TOOLBOX_BUTTONS {
|
||||||
let screen_id = format!("screen_{idx}");
|
let overlay_id = format!("overlay_{idx}");
|
||||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||||
params.insert("idx".into(), idx.to_string().into());
|
params.insert("idx".into(), idx.to_string().into());
|
||||||
parser_state.instantiate_template(
|
parser_state.instantiate_template(
|
||||||
doc_params, "Screen", layout, widget, params,
|
doc_params, "Overlay", layout, widget, params,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let component =
|
let component =
|
||||||
parser_state.fetch_component_as::<ComponentButton>(&screen_id)?;
|
parser_state.fetch_component_as::<ComponentButton>(&overlay_id)?;
|
||||||
state.screen_buttons.push(component);
|
state.overlay_buttons.push(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -158,7 +160,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
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 alterables = EventAlterables::default();
|
||||||
let mut common = CallbackDataCommon {
|
let mut common = CallbackDataCommon {
|
||||||
alterables: &mut alterables,
|
alterables: &mut alterables,
|
||||||
@@ -218,10 +220,12 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
&mut common.alterables,
|
&mut common.alterables,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
OverlayEventData::ScreensChanged => {
|
OverlayEventData::OverlaysChanged(metas) => {
|
||||||
for (idx, btn) in panel.state.screen_buttons.iter().enumerate() {
|
panel.state.overlay_metas = metas;
|
||||||
let display = if let Some(screen) = app.screens.get(idx) {
|
for (idx, btn) in panel.state.overlay_buttons.iter().enumerate() {
|
||||||
btn.set_text(&mut common, Translation::from_raw_text(&screen.name));
|
let display = if let Some(meta) = panel.state.overlay_metas.get(idx) {
|
||||||
|
btn.set_text(&mut common, Translation::from_raw_text(&meta.name));
|
||||||
|
//TODO: add category icons
|
||||||
taffy::Display::Flex
|
taffy::Display::Flex
|
||||||
} else {
|
} else {
|
||||||
taffy::Display::None
|
taffy::Display::None
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
use glam::{Affine2, Affine3A, Quat, Vec3, vec3};
|
use glam::{vec3, Affine2, Affine3A, Quat, Vec3};
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
buffer::{BufferUsage, Subbuffer},
|
buffer::{BufferUsage, Subbuffer},
|
||||||
command_buffer::CommandBufferUsage,
|
command_buffer::CommandBufferUsage,
|
||||||
format::Format,
|
format::Format,
|
||||||
image::{Image, ImageTiling, SubresourceLayout, view::ImageView},
|
image::{view::ImageView, Image, ImageTiling, SubresourceLayout},
|
||||||
};
|
};
|
||||||
use wayvr_ipc::packet_server::{self, PacketServer, WvrStateChanged};
|
use wayvr_ipc::packet_server::{self, PacketServer, WvrStateChanged};
|
||||||
use wgui::gfx::{
|
use wgui::gfx::{
|
||||||
WGfx,
|
|
||||||
pass::WGfxPass,
|
pass::WGfxPass,
|
||||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||||
|
WGfx,
|
||||||
};
|
};
|
||||||
use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane};
|
use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane};
|
||||||
use wlx_common::windowing::OverlayWindowState;
|
use wlx_common::windowing::OverlayWindowState;
|
||||||
@@ -21,22 +21,23 @@ use crate::{
|
|||||||
input::{self, HoverResult},
|
input::{self, HoverResult},
|
||||||
task::TaskType,
|
task::TaskType,
|
||||||
wayvr::{
|
wayvr::{
|
||||||
self, WayVR, WayVRAction, WayVRDisplayClickAction, display,
|
self, display,
|
||||||
server_ipc::{gen_args_vec, gen_env_vec},
|
server_ipc::{gen_args_vec, gen_env_vec},
|
||||||
|
WayVR, WayVRAction, WayVRDisplayClickAction,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config_wayvr,
|
config_wayvr,
|
||||||
graphics::{Vert2Uv, dmabuf::WGfxDmabuf},
|
graphics::{dmabuf::WGfxDmabuf, Vert2Uv},
|
||||||
state::{self, AppState},
|
state::{self, AppState},
|
||||||
subsystem::{hid::WheelDelta, input::KeyboardFocus},
|
subsystem::{hid::WheelDelta, input::KeyboardFocus},
|
||||||
windowing::{
|
windowing::{
|
||||||
OverlayID, OverlaySelector, Z_ORDER_DASHBOARD,
|
|
||||||
backend::{
|
backend::{
|
||||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
|
ui_transform, FrameMeta, OverlayBackend, OverlayEventData, RenderResources,
|
||||||
ui_transform,
|
ShouldRender,
|
||||||
},
|
},
|
||||||
manager::OverlayWindowManager,
|
manager::OverlayWindowManager,
|
||||||
window::{OverlayWindowConfig, OverlayWindowData},
|
window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData},
|
||||||
|
OverlayID, OverlaySelector, Z_ORDER_DASHBOARD,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ use super::toast::error_toast;
|
|||||||
// Hard-coded for now
|
// Hard-coded for now
|
||||||
const DASHBOARD_WIDTH: u16 = 1920;
|
const DASHBOARD_WIDTH: u16 = 1920;
|
||||||
const DASHBOARD_HEIGHT: u16 = 1080;
|
const DASHBOARD_HEIGHT: u16 = 1080;
|
||||||
const DASHBOARD_DISPLAY_NAME: &str = "_DASHBOARD";
|
const DASHBOARD_DISPLAY_NAME: &str = "DASHBOARD";
|
||||||
|
|
||||||
pub struct WayVRContext {
|
pub struct WayVRContext {
|
||||||
wayvr: Rc<RefCell<WayVRData>>,
|
wayvr: Rc<RefCell<WayVRData>>,
|
||||||
@@ -794,9 +795,16 @@ pub fn create_wayvr_display_overlay(
|
|||||||
[display_width, display_height],
|
[display_width, display_height],
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
|
let category = if name == DASHBOARD_DISPLAY_NAME {
|
||||||
|
OverlayCategory::Internal
|
||||||
|
} else {
|
||||||
|
OverlayCategory::WayVR
|
||||||
|
};
|
||||||
|
|
||||||
Ok(OverlayWindowConfig {
|
Ok(OverlayWindowConfig {
|
||||||
name: format!("WayVR - {name}").into(),
|
name: format!("WVR-{name}").into(),
|
||||||
keyboard_focus: Some(KeyboardFocus::WayVR),
|
keyboard_focus: Some(KeyboardFocus::WayVR),
|
||||||
|
category,
|
||||||
default_state: OverlayWindowState {
|
default_state: OverlayWindowState {
|
||||||
interactable: true,
|
interactable: true,
|
||||||
grabbable: true,
|
grabbable: true,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::{
|
|||||||
graphics::ExtentExt,
|
graphics::ExtentExt,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::WheelDelta,
|
subsystem::hid::WheelDelta,
|
||||||
|
windowing::{window::OverlayCategory, OverlayID},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy)]
|
#[derive(Default, Clone, Copy)]
|
||||||
@@ -64,12 +65,17 @@ impl RenderResources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct OverlayMeta {
|
||||||
|
pub id: OverlayID,
|
||||||
|
pub name: Arc<str>,
|
||||||
|
pub category: OverlayCategory,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum OverlayEventData {
|
pub enum OverlayEventData {
|
||||||
ActiveSetChanged(Option<usize>),
|
ActiveSetChanged(Option<usize>),
|
||||||
NumSetsChanged(usize),
|
NumSetsChanged(usize),
|
||||||
EditModeChanged(bool),
|
EditModeChanged(bool),
|
||||||
///TODO: this only gets fired at startup
|
OverlaysChanged(Vec<OverlayMeta>),
|
||||||
ScreensChanged,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait OverlayBackend: Any {
|
pub trait OverlayBackend: Any {
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::{
|
windowing::{
|
||||||
backend::OverlayEventData, set::OverlayWindowSet, snap_upright, window::OverlayWindowData,
|
backend::{OverlayEventData, OverlayMeta},
|
||||||
|
set::OverlayWindowSet,
|
||||||
|
snap_upright,
|
||||||
|
window::{OverlayCategory, OverlayWindowData},
|
||||||
OverlayID, OverlaySelector,
|
OverlayID, OverlaySelector,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -101,7 +104,6 @@ where
|
|||||||
let mut watch = OverlayWindowData::from_config(create_watch(app)?);
|
let mut watch = OverlayWindowData::from_config(create_watch(app)?);
|
||||||
|
|
||||||
for ev in [
|
for ev in [
|
||||||
OverlayEventData::ScreensChanged,
|
|
||||||
OverlayEventData::NumSetsChanged(me.sets.len()),
|
OverlayEventData::NumSetsChanged(me.sets.len()),
|
||||||
OverlayEventData::EditModeChanged(false),
|
OverlayEventData::EditModeChanged(false),
|
||||||
] {
|
] {
|
||||||
@@ -109,6 +111,8 @@ where
|
|||||||
}
|
}
|
||||||
me.watch_id = me.add(watch, app);
|
me.watch_id = me.add(watch, app);
|
||||||
|
|
||||||
|
me.overlays_changed(app)?;
|
||||||
|
|
||||||
// overwrite default layout with saved layout, if exists
|
// overwrite default layout with saved layout, if exists
|
||||||
me.restore_layout(app);
|
me.restore_layout(app);
|
||||||
|
|
||||||
@@ -238,13 +242,28 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
pub fn remove_by_selector(
|
pub fn remove_by_selector(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &OverlaySelector,
|
selector: &OverlaySelector,
|
||||||
|
app: &mut AppState,
|
||||||
) -> Option<OverlayWindowData<T>> {
|
) -> Option<OverlayWindowData<T>> {
|
||||||
match selector {
|
let id = match selector {
|
||||||
OverlaySelector::Id(id) => self.overlays.remove(*id),
|
OverlaySelector::Id(id) => *id,
|
||||||
OverlaySelector::Name(name) => {
|
OverlaySelector::Name(name) => {
|
||||||
self.lookup(name).and_then(|id| self.overlays.remove(id))
|
let Some(id) = self.lookup(name) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
id
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret_val = self.overlays.remove(id);
|
||||||
|
let internal = ret_val
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|o| matches!(o.config.category, OverlayCategory::Internal));
|
||||||
|
|
||||||
|
if !internal && let Err(e) = self.overlays_changed(app) {
|
||||||
|
log::error!("Error while removing overlay: {e:?}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret_val
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_by_id(&mut self, id: OverlayID) -> Option<&OverlayWindowData<T>> {
|
pub fn get_by_id(&mut self, id: OverlayID) -> Option<&OverlayWindowData<T>> {
|
||||||
@@ -279,10 +298,24 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, mut overlay: OverlayWindowData<T>, app: &mut AppState) -> OverlayID {
|
pub fn add(&mut self, mut overlay: OverlayWindowData<T>, app: &mut AppState) -> OverlayID {
|
||||||
|
let internal = matches!(overlay.config.category, OverlayCategory::Internal);
|
||||||
|
|
||||||
|
while self.lookup(&overlay.config.name).is_some() {
|
||||||
|
log::error!(
|
||||||
|
"An overlay with name {} already exists. Deduplicating, but things may break!",
|
||||||
|
overlay.config.name
|
||||||
|
);
|
||||||
|
overlay.config.name = format!("{}_2", overlay.config.name).into();
|
||||||
|
}
|
||||||
|
|
||||||
if overlay.config.show_on_spawn {
|
if overlay.config.show_on_spawn {
|
||||||
overlay.config.activate(app);
|
overlay.config.activate(app);
|
||||||
}
|
}
|
||||||
self.overlays.insert(overlay)
|
let ret_val = self.overlays.insert(overlay);
|
||||||
|
if !internal && let Err(e) = self.overlays_changed(app) {
|
||||||
|
log::error!("Error while adding overlay: {e:?}");
|
||||||
|
}
|
||||||
|
ret_val
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_or_toggle_set(&mut self, app: &mut AppState, set: usize) {
|
pub fn switch_or_toggle_set(&mut self, app: &mut AppState, set: usize) {
|
||||||
@@ -419,4 +452,27 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlays_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||||
|
let mut meta = Vec::with_capacity(self.overlays.len());
|
||||||
|
for (id, data) in self.overlays.iter() {
|
||||||
|
if matches!(data.config.category, OverlayCategory::Internal) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
meta.push(OverlayMeta {
|
||||||
|
id: id.clone(),
|
||||||
|
name: data.config.name.clone(),
|
||||||
|
category: data.config.category,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(watch) = self.mut_by_id(self.watch_id) {
|
||||||
|
watch
|
||||||
|
.config
|
||||||
|
.backend
|
||||||
|
.notify(app, OverlayEventData::OverlaysChanged(meta))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,15 @@ impl<T> OverlayWindowData<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum OverlayCategory {
|
||||||
|
Internal,
|
||||||
|
PanelCustom,
|
||||||
|
Screen,
|
||||||
|
Mirror,
|
||||||
|
WayVR,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct OverlayWindowConfig {
|
pub struct OverlayWindowConfig {
|
||||||
pub name: Arc<str>,
|
pub name: Arc<str>,
|
||||||
pub backend: Box<dyn OverlayBackend>,
|
pub backend: Box<dyn OverlayBackend>,
|
||||||
@@ -60,6 +69,8 @@ pub struct OverlayWindowConfig {
|
|||||||
pub z_order: u32,
|
pub z_order: u32,
|
||||||
/// If set, hovering this overlay will cause the HID provider to switch focus.
|
/// If set, hovering this overlay will cause the HID provider to switch focus.
|
||||||
pub keyboard_focus: Option<KeyboardFocus>,
|
pub keyboard_focus: Option<KeyboardFocus>,
|
||||||
|
/// Category of the overlay, used by toolbox on the watch.
|
||||||
|
pub category: OverlayCategory,
|
||||||
/// Should the overlay be displayed on the next frame?
|
/// Should the overlay be displayed on the next frame?
|
||||||
pub show_on_spawn: bool,
|
pub show_on_spawn: bool,
|
||||||
/// Does not belong to any set; switching sets does not affect this overlay.
|
/// Does not belong to any set; switching sets does not affect this overlay.
|
||||||
@@ -82,6 +93,7 @@ impl OverlayWindowConfig {
|
|||||||
active_state: None,
|
active_state: None,
|
||||||
z_order: 0,
|
z_order: 0,
|
||||||
keyboard_focus: None,
|
keyboard_focus: None,
|
||||||
|
category: OverlayCategory::Internal,
|
||||||
show_on_spawn: false,
|
show_on_spawn: false,
|
||||||
global: false,
|
global: false,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user