custom panels
This commit is contained in:
@@ -2,9 +2,9 @@ use crate::{
|
|||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
AttribPair, ParserContext, ParserFile, parse_check_i32, parse_children, parse_i32, parse_widget_universal,
|
parse_children, parse_i32, parse_widget_universal, print_invalid_attrib,
|
||||||
print_invalid_attrib,
|
|
||||||
style::{parse_style, parse_text_style},
|
style::{parse_style, parse_text_style},
|
||||||
|
AttribPair, ParserContext, ParserFile,
|
||||||
},
|
},
|
||||||
widget::label::{WidgetLabel, WidgetLabelParams},
|
widget::label::{WidgetLabel, WidgetLabelParams},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style},
|
parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile},
|
||||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
||||||
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::{
|
use std::{
|
||||||
f32,
|
f32,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ use crate::{assets::AssetPath, globals::WguiGlobals};
|
|||||||
|
|
||||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
/// The raw content of a glyph.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum CustomGlyphContent {
|
pub enum CustomGlyphContent {
|
||||||
Svg(Box<Tree>),
|
Svg(Box<Tree>),
|
||||||
@@ -43,6 +44,8 @@ impl CustomGlyphContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The cached contents of a glyph.
|
||||||
|
/// Clone and reuse this to avoid atlasing the same content twice.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CustomGlyphData {
|
pub struct CustomGlyphData {
|
||||||
pub(super) id: usize,
|
pub(super) id: usize,
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use crate::{
|
|||||||
globals::Globals,
|
globals::Globals,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
renderer_vk::text::{
|
renderer_vk::text::{
|
||||||
DEFAULT_METRICS,
|
|
||||||
custom_glyph::{CustomGlyph, CustomGlyphData},
|
custom_glyph::{CustomGlyph, CustomGlyphData},
|
||||||
|
DEFAULT_METRICS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ pub struct WidgetSpriteParams {
|
|||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct WidgetSprite {
|
pub struct WidgetSprite {
|
||||||
pub params: WidgetSpriteParams,
|
params: WidgetSpriteParams,
|
||||||
id: WidgetID,
|
id: WidgetID,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +34,22 @@ impl WidgetSprite {
|
|||||||
id: WidgetID::null(),
|
id: WidgetID::null(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_color(&mut self, color: drawing::Color) {
|
||||||
|
self.params.color = Some(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_color(&self) -> Option<drawing::Color> {
|
||||||
|
self.params.color
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_content(&mut self, content: Option<CustomGlyphData>) {
|
||||||
|
self.params.glyph_data = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_content(&self) -> Option<CustomGlyphData> {
|
||||||
|
self.params.glyph_data.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetObj for WidgetSprite {
|
impl WidgetObj for WidgetSprite {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ fn def_timezones() -> Vec<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn def_screens() -> AStrSet {
|
const fn def_astrset_empty() -> AStrSet {
|
||||||
AStrSet::new()
|
AStrSet::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +187,10 @@ pub struct GeneralConfig {
|
|||||||
#[serde(default = "def_false")]
|
#[serde(default = "def_false")]
|
||||||
pub double_cursor_fix: bool,
|
pub double_cursor_fix: bool,
|
||||||
|
|
||||||
#[serde(default = "def_screens")]
|
#[serde(default = "def_astrset_empty")]
|
||||||
|
pub custom_panels: AStrSet,
|
||||||
|
|
||||||
|
#[serde(default = "def_astrset_empty")]
|
||||||
pub show_screens: AStrSet,
|
pub show_screens: AStrSet,
|
||||||
|
|
||||||
#[serde(default = "def_curve_values")]
|
#[serde(default = "def_curve_values")]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 354 B |
|
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 371 B |
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<template name="Device">
|
<template name="Device">
|
||||||
<rectangle id="dev_${idx}" macro="decorative_rect" padding_top="4" padding_bottom="4" display="none" align_items="center" gap="8">
|
<rectangle id="dev_${idx}" macro="decorative_rect" padding_top="4" padding_bottom="4" display="none" align_items="center" gap="8">
|
||||||
<sprite id="dev_${idx}_sprite" width="32" height="32" src="${src}" />
|
<sprite id="dev_${idx}_sprite" width="32" height="32" src_builtin="${src}" />
|
||||||
<label _source="battery" _device="${idx}" size="24" weight="bold" />
|
<label _source="battery" _device="${idx}" size="24" weight="bold" />
|
||||||
</rectangle>
|
</rectangle>
|
||||||
</template>
|
</template>
|
||||||
@@ -34,14 +34,14 @@
|
|||||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::EditModeOverlayToggle ${idx}"
|
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::EditModeOverlayToggle ${idx}"
|
||||||
align_items="center"
|
align_items="center"
|
||||||
height="40">
|
height="40">
|
||||||
<sprite src_builtin="watch/display.svg" width="32" height="32" />
|
<sprite id="overlay_${idx}_sprite" src_builtin="${src}" width="32" height="32" />
|
||||||
<label text="WLX-${idx}" size="18" />
|
<label id="overlay_${idx}_label" text="WLX-${idx}" size="18" />
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="Set">
|
<template name="Set">
|
||||||
<Button macro="button_style" id="set_${idx}" _press="::SetToggle ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="top">
|
<Button macro="button_style" id="set_${idx}" _press="::SetToggle ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="top">
|
||||||
<sprite width="40" height="40" color="~set_color" src="watch/set2.svg" />
|
<sprite width="40" height="40" color="~set_color" src_builtin="watch/set2.svg" />
|
||||||
<div position="absolute" margin_top="9">
|
<div position="absolute" margin_top="9">
|
||||||
<label text="${display}" size="24" color="#00050F" weight="bold" />
|
<label text="${display}" size="24" color="#00050F" weight="bold" />
|
||||||
</div>
|
</div>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
flex_direction="column" gap="8">
|
flex_direction="column" gap="8">
|
||||||
<!-- Top elements (device battery levels) -->
|
<!-- Top elements (device battery levels) -->
|
||||||
<div flex_direction="row" id="devices" interactable="0" gap="8">
|
<div flex_direction="row" id="devices" interactable="0" gap="8">
|
||||||
<!-- Src here may be changed, but maintain order: HMD, Left, Right, Tracker -->
|
<!-- Src here may be changed, but maintain `TrackedDeviceRole` order: HMD, Left, Right, Tracker -->
|
||||||
<Device src="watch/hmd.svg" idx="0" />
|
<Device src="watch/hmd.svg" idx="0" />
|
||||||
<Device src="watch/controller_l.svg" idx="1" />
|
<Device src="watch/controller_l.svg" idx="1" />
|
||||||
<Device src="watch/controller_r.svg" idx="2" />
|
<Device src="watch/controller_r.svg" idx="2" />
|
||||||
@@ -119,11 +119,16 @@
|
|||||||
<label translation="WATCH.EDIT_MODE_EXPLANATION" align="center" />
|
<label translation="WATCH.EDIT_MODE_EXPLANATION" align="center" />
|
||||||
</div>
|
</div>
|
||||||
<div flex_direction="column" align_items="center" justify_content="center">
|
<div flex_direction="column" align_items="center" justify_content="center">
|
||||||
<div id="toolbox" gap="8" width="100%" flex_wrap="wrap">
|
<div id="toolbox" gap="8" width="100%" flex_direction="column" flex_wrap="wrap">
|
||||||
<Button height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd">
|
<Button height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd">
|
||||||
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
|
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
|
||||||
<label translation="EDIT_MODE.KEYBOARD" size="18" />
|
<label translation="EDIT_MODE.KEYBOARD" size="18" />
|
||||||
</Button>
|
</Button>
|
||||||
|
<!-- Src here may be changed, but maintain `OverlayCategor` order: Panel, Screen, Mirror, WayVR -->
|
||||||
|
<Overlay src="edit/panel.svg" idx="0" />
|
||||||
|
<Overlay src="edit/screen.svg" idx="1" />
|
||||||
|
<Overlay src="edit/mirror.svg" idx="2" />
|
||||||
|
<Overlay src="edit/wayvr.svg" idx="3" />
|
||||||
<!-- Will populate additional <Overlay> tags at runtime -->
|
<!-- Will populate additional <Overlay> tags at runtime -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -155,4 +160,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</elements>
|
</elements>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ use crate::{
|
|||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::notifications::NotificationManager,
|
subsystem::notifications::NotificationManager,
|
||||||
windowing::{
|
windowing::{
|
||||||
backend::{RenderResources, RenderTarget, ShouldRender, StereoMode},
|
backend::{RenderResources, RenderTarget, ShouldRender},
|
||||||
manager::OverlayWindowManager,
|
manager::OverlayWindowManager,
|
||||||
},
|
},
|
||||||
RUNNING,
|
RUNNING,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use vulkano::{
|
|||||||
command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract},
|
command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract},
|
||||||
image::view::ImageView,
|
image::view::ImageView,
|
||||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
||||||
sync::{now, GpuFuture},
|
sync::GpuFuture,
|
||||||
};
|
};
|
||||||
use wgui::gfx::WGfx;
|
use wgui::gfx::WGfx;
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ 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`
|
||||||
pub on_custom_attrib: Option<OnCustomAttribFunc>, // used only in `new_from_template`
|
pub on_custom_attrib: Option<OnCustomAttribFunc>, // used only in `new_from_template`
|
||||||
pub resize_to_parent: bool,
|
pub resize_to_parent: bool,
|
||||||
|
pub external_xml: bool,
|
||||||
pub gui_scale: f32,
|
pub gui_scale: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +84,7 @@ impl<S> Default for NewGuiPanelParams<S> {
|
|||||||
on_custom_id: None,
|
on_custom_id: None,
|
||||||
on_custom_attrib: None,
|
on_custom_attrib: None,
|
||||||
resize_to_parent: false,
|
resize_to_parent: false,
|
||||||
|
external_xml: false,
|
||||||
gui_scale: 1.0,
|
gui_scale: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +101,10 @@ impl<S: 'static> GuiPanel<S> {
|
|||||||
|
|
||||||
let doc_params = wgui::parser::ParseDocumentParams {
|
let doc_params = wgui::parser::ParseDocumentParams {
|
||||||
globals: app.wgui_globals.clone(),
|
globals: app.wgui_globals.clone(),
|
||||||
path: AssetPath::FileOrBuiltIn(path),
|
path: params
|
||||||
|
.external_xml
|
||||||
|
.then_some(AssetPath::File(path))
|
||||||
|
.unwrap_or(AssetPath::FileOrBuiltIn(path)),
|
||||||
extra: wgui::parser::ParseDocumentExtra {
|
extra: wgui::parser::ParseDocumentExtra {
|
||||||
on_custom_attribs: Some(Box::new({
|
on_custom_attribs: Some(Box::new({
|
||||||
let custom_elems = custom_elems.clone();
|
let custom_elems = custom_elems.clone();
|
||||||
@@ -244,6 +249,7 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||||
|
self.layout.needs_redraw = true;
|
||||||
self.timestep.reset();
|
self.timestep.reset();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,39 @@
|
|||||||
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::{
|
use crate::{
|
||||||
gui::panel::GuiPanel,
|
gui::panel::{GuiPanel, NewGuiPanelParams},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::window::{OverlayCategory, OverlayWindowConfig},
|
windowing::window::{OverlayCategory, OverlayWindowConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
const SETTINGS_NAME: &str = "settings";
|
|
||||||
|
|
||||||
#[allow(unreachable_code)]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindowConfig> {
|
pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindowConfig> {
|
||||||
return None;
|
let params = NewGuiPanelParams {
|
||||||
|
external_xml: true,
|
||||||
|
..NewGuiPanelParams::default()
|
||||||
|
};
|
||||||
|
|
||||||
unreachable!();
|
let mut panel = GuiPanel::new_from_template(app, &format!("gui/{name}.xml"), (), params)
|
||||||
|
.inspect_err(|e| log::warn!("Error creating '{name}': {e:?}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
let panel = GuiPanel::new_blank(app, (), Default::default()).ok()?;
|
panel
|
||||||
panel.update_layout().ok()?;
|
.update_layout()
|
||||||
|
.inspect_err(|e| log::warn!("Error layouting '{name}': {e:?}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
let scale = panel.layout.content_size.x / 40.0 * 0.05;
|
||||||
|
|
||||||
Some(OverlayWindowConfig {
|
Some(OverlayWindowConfig {
|
||||||
name,
|
name,
|
||||||
category: OverlayCategory::PanelCustom,
|
category: OverlayCategory::Panel,
|
||||||
default_state: OverlayWindowState {
|
default_state: OverlayWindowState {
|
||||||
interactable: true,
|
interactable: true,
|
||||||
grabbable: true,
|
grabbable: true,
|
||||||
transform: Affine3A::from_scale_rotation_translation(
|
transform: Affine3A::from_scale_rotation_translation(
|
||||||
Vec3::ONE * 0.1, // TODO scale
|
Vec3::ONE * scale,
|
||||||
Quat::IDENTITY,
|
Quat::IDENTITY,
|
||||||
vec3(0.0, 0.0, -0.5),
|
vec3(0.0, 0.0, -0.5),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use std::{
|
|||||||
|
|
||||||
use glam::vec2;
|
use glam::vec2;
|
||||||
use slotmap::Key;
|
use slotmap::Key;
|
||||||
use smallvec::smallvec;
|
|
||||||
use wgui::{
|
use wgui::{
|
||||||
components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
|
components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
|
||||||
event::{CallbackDataCommon, EventAlterables, EventCallback},
|
event::{CallbackDataCommon, EventAlterables, EventCallback},
|
||||||
@@ -107,9 +106,13 @@ impl EditWrapperManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_edit_mode(&mut self, owc: &mut OverlayWindowConfig) {
|
pub fn unwrap_edit_mode(
|
||||||
|
&mut self,
|
||||||
|
owc: &mut OverlayWindowConfig,
|
||||||
|
app: &mut AppState,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
if !owc.editing {
|
if !owc.editing {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("EditMode unwrap on {}", owc.name);
|
log::debug!("EditMode unwrap on {}", owc.name);
|
||||||
@@ -126,6 +129,8 @@ impl EditWrapperManager {
|
|||||||
owc.backend = inner;
|
owc.backend = inner;
|
||||||
owc.editing = false;
|
owc.editing = false;
|
||||||
|
|
||||||
|
owc.backend.resume(app)
|
||||||
|
|
||||||
// wrapper is destroyed with nothing left inside
|
// wrapper is destroyed with nothing left inside
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ where
|
|||||||
anyhow::anyhow!("Element with id=\"{sprite_id}\" must be a <sprite>")
|
anyhow::anyhow!("Element with id=\"{sprite_id}\" must be a <sprite>")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let sprite = sprite_w.params.glyph_data.clone().ok_or_else(|| {
|
let sprite = sprite_w.get_content().ok_or_else(|| {
|
||||||
anyhow::anyhow!("Element with id=\"{sprite_id}\" must have a valid src!")
|
anyhow::anyhow!("Element with id=\"{sprite_id}\" must have a valid src!")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ where
|
|||||||
.widgets
|
.widgets
|
||||||
.get_as::<WidgetSprite>(self.top_sprite_id)
|
.get_as::<WidgetSprite>(self.top_sprite_id)
|
||||||
{
|
{
|
||||||
sprite.params.glyph_data = Some(new.sprite.clone());
|
sprite.set_content(Some(new.sprite.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use glam::{Affine3A, Quat, Vec3, Vec3A, vec3};
|
use glam::{vec3, Affine3A, Quat, Vec3, Vec3A};
|
||||||
use idmap::DirectIdMap;
|
use idmap::DirectIdMap;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
components::button::ComponentButton,
|
components::button::ComponentButton,
|
||||||
@@ -14,7 +14,7 @@ use wgui::{
|
|||||||
parser::Fetchable,
|
parser::Fetchable,
|
||||||
renderer_vk::text::custom_glyph::CustomGlyphData,
|
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
taffy,
|
taffy,
|
||||||
widget::{EventResult, sprite::WidgetSprite},
|
widget::{label::WidgetLabel, sprite::WidgetSprite, EventResult},
|
||||||
};
|
};
|
||||||
use wlx_common::{
|
use wlx_common::{
|
||||||
common::LeftRight,
|
common::LeftRight,
|
||||||
@@ -27,16 +27,16 @@ use crate::{
|
|||||||
task::{OverlayTask, TaskType},
|
task::{OverlayTask, TaskType},
|
||||||
},
|
},
|
||||||
gui::{
|
gui::{
|
||||||
panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS},
|
panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc},
|
||||||
timer::GuiTimer,
|
timer::GuiTimer,
|
||||||
},
|
},
|
||||||
overlays::edit::LongPressButtonState,
|
overlays::edit::LongPressButtonState,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::{
|
windowing::{
|
||||||
OverlaySelector, Z_ORDER_WATCH,
|
|
||||||
backend::{OverlayEventData, OverlayMeta},
|
backend::{OverlayEventData, OverlayMeta},
|
||||||
manager::MAX_OVERLAY_SETS,
|
manager::MAX_OVERLAY_SETS,
|
||||||
window::{OverlayWindowConfig, OverlayWindowData},
|
window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData},
|
||||||
|
OverlaySelector, Z_ORDER_WATCH,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,15 +47,22 @@ const MAX_DEVICES: usize = 9;
|
|||||||
pub const WATCH_POS: Vec3 = vec3(-0.03, -0.01, 0.125);
|
pub const WATCH_POS: Vec3 = vec3(-0.03, -0.01, 0.125);
|
||||||
pub const WATCH_ROT: Quat = Quat::from_xyzw(-0.707_106_6, 0.000_796_361_8, 0.707_106_6, 0.0);
|
pub const WATCH_ROT: Quat = Quat::from_xyzw(-0.707_106_6, 0.000_796_361_8, 0.707_106_6, 0.0);
|
||||||
|
|
||||||
|
struct OverlayButton {
|
||||||
|
button: Rc<ComponentButton>,
|
||||||
|
label: WidgetID,
|
||||||
|
sprite: WidgetID,
|
||||||
|
}
|
||||||
|
|
||||||
#[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>>,
|
||||||
overlay_buttons: Vec<Rc<ComponentButton>>,
|
overlay_buttons: Vec<OverlayButton>,
|
||||||
overlay_metas: Vec<OverlayMeta>,
|
overlay_metas: Vec<OverlayMeta>,
|
||||||
edit_mode_widgets: Vec<(WidgetID, bool)>,
|
edit_mode_widgets: Vec<(WidgetID, bool)>,
|
||||||
edit_add_widget: WidgetID,
|
edit_add_widget: WidgetID,
|
||||||
device_role_icons: DirectIdMap<TrackedDeviceRole, CustomGlyphData>,
|
device_role_icons: DirectIdMap<TrackedDeviceRole, CustomGlyphData>,
|
||||||
|
overlay_cat_icons: DirectIdMap<OverlayCategory, CustomGlyphData>,
|
||||||
devices: Vec<(WidgetID, WidgetID)>,
|
devices: Vec<(WidgetID, WidgetID)>,
|
||||||
num_sets: usize,
|
num_sets: usize,
|
||||||
delete: LongPressButtonState,
|
delete: LongPressButtonState,
|
||||||
@@ -159,22 +166,63 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
state.set_buttons.push(comp);
|
state.set_buttons.push(comp);
|
||||||
}
|
}
|
||||||
} else if &*id == "toolbox" {
|
} else if &*id == "toolbox" {
|
||||||
let node = layout.state.nodes[widget];
|
|
||||||
let num_children = layout.state.tree.children(node).iter().len() - 1; // -1 for keyboard
|
|
||||||
|
|
||||||
for idx in 0..MAX_TOOLBOX_BUTTONS {
|
for idx in 0..MAX_TOOLBOX_BUTTONS {
|
||||||
if idx >= num_children {
|
let id_str = format!("overlay_{idx}");
|
||||||
|
|
||||||
|
let button = if let Some(button) = parser_state
|
||||||
|
.fetch_component_as::<ComponentButton>(&id_str)
|
||||||
|
.ok()
|
||||||
|
{
|
||||||
|
button
|
||||||
|
} else {
|
||||||
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, "Overlay", layout, widget, params,
|
doc_params, "Overlay", layout, widget, params,
|
||||||
)?;
|
)?;
|
||||||
}
|
parser_state.fetch_component_as::<ComponentButton>(&id_str)?
|
||||||
|
};
|
||||||
|
|
||||||
let comp = parser_state
|
state.overlay_buttons.push(OverlayButton {
|
||||||
.fetch_component_as::<ComponentButton>(&format!("overlay_{idx}"))?;
|
button,
|
||||||
state.overlay_buttons.push(comp);
|
label: parser_state
|
||||||
|
.get_widget_id(&format!("overlay_{idx}_label"))
|
||||||
|
.inspect_err(|e| log::warn!("{e:?}"))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
sprite: parser_state
|
||||||
|
.get_widget_id(&format!("overlay_{idx}_sprite"))
|
||||||
|
.inspect_err(|e| log::warn!("{e:?}"))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} else if id.starts_with("overlay_") && id.ends_with("_sprite") {
|
||||||
|
// store device icons from xml
|
||||||
|
let id_n = id
|
||||||
|
.replace("overlay_", "")
|
||||||
|
.replace("_sprite", "")
|
||||||
|
.parse::<u64>()?;
|
||||||
|
|
||||||
|
let category = match id_n {
|
||||||
|
0 => OverlayCategory::Panel,
|
||||||
|
1 => OverlayCategory::Screen,
|
||||||
|
2 => OverlayCategory::Mirror,
|
||||||
|
3 => OverlayCategory::WayVR,
|
||||||
|
_ => return Ok(()), // not parsing the first 4 elems
|
||||||
|
};
|
||||||
|
|
||||||
|
let sprite = layout
|
||||||
|
.state
|
||||||
|
.widgets
|
||||||
|
.get_as::<WidgetSprite>(widget)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("{id} is expected to be a sprite, but it isn't.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let src = sprite.get_content().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("{id} is expected to have a src, but it doesn't.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
state.overlay_cat_icons.insert(category, src);
|
||||||
} else if id.starts_with("dev_") && id.ends_with("_sprite") {
|
} else if id.starts_with("dev_") && id.ends_with("_sprite") {
|
||||||
// store device icons from xml
|
// store device icons from xml
|
||||||
let id_n = id
|
let id_n = id
|
||||||
@@ -198,7 +246,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
anyhow::anyhow!("{id} is expected to be a sprite, but it isn't.")
|
anyhow::anyhow!("{id} is expected to be a sprite, but it isn't.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let src = sprite.params.glyph_data.clone().ok_or_else(|| {
|
let src = sprite.get_content().ok_or_else(|| {
|
||||||
anyhow::anyhow!("{id} is expected to have a src, but it doesn't.")
|
anyhow::anyhow!("{id} is expected to have a src, but it doesn't.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -293,14 +341,31 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
panel.state.overlay_metas = metas;
|
panel.state.overlay_metas = metas;
|
||||||
for (idx, btn) in panel.state.overlay_buttons.iter().enumerate() {
|
for (idx, btn) in panel.state.overlay_buttons.iter().enumerate() {
|
||||||
let display = if let Some(meta) = panel.state.overlay_metas.get(idx) {
|
let display = if let Some(meta) = panel.state.overlay_metas.get(idx) {
|
||||||
btn.set_text(&mut com, Translation::from_raw_text(&meta.name));
|
if let Some(mut label) =
|
||||||
//TODO: add category icons
|
panel.layout.state.widgets.get_as::<WidgetLabel>(btn.label)
|
||||||
|
{
|
||||||
|
label.set_text(&mut com, Translation::from_raw_text(&meta.name));
|
||||||
|
} else {
|
||||||
|
btn.button
|
||||||
|
.set_text(&mut com, Translation::from_raw_text(&meta.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut sprite) = panel
|
||||||
|
.layout
|
||||||
|
.state
|
||||||
|
.widgets
|
||||||
|
.get_as::<WidgetSprite>(btn.sprite)
|
||||||
|
&& let Some(glyph) = panel.state.overlay_cat_icons.get(meta.category)
|
||||||
|
{
|
||||||
|
sprite.set_content(Some(glyph.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
taffy::Display::Flex
|
taffy::Display::Flex
|
||||||
} else {
|
} else {
|
||||||
taffy::Display::None
|
taffy::Display::None
|
||||||
};
|
};
|
||||||
com.alterables
|
com.alterables
|
||||||
.set_style(btn.get_rect(), StyleSetRequest::Display(display));
|
.set_style(btn.button.get_rect(), StyleSetRequest::Display(display));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OverlayEventData::DevicesChanged => {
|
OverlayEventData::DevicesChanged => {
|
||||||
@@ -309,7 +374,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
&& let Some(glyph) = panel.state.device_role_icons.get(dev.role)
|
&& let Some(glyph) = panel.state.device_role_icons.get(dev.role)
|
||||||
&& let Some(mut s) = panel.layout.state.widgets.get_as::<WidgetSprite>(*s)
|
&& let Some(mut s) = panel.layout.state.widgets.get_as::<WidgetSprite>(*s)
|
||||||
{
|
{
|
||||||
s.params.glyph_data = Some(glyph.clone());
|
s.set_content(Some(glyph.clone()));
|
||||||
com.alterables
|
com.alterables
|
||||||
.set_style(*div, StyleSetRequest::Display(taffy::Display::Flex));
|
.set_style(*div, StyleSetRequest::Display(taffy::Display::Flex));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ use wlx_common::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
backend::task::OverlayTask,
|
backend::task::OverlayTask,
|
||||||
overlays::{
|
overlays::{
|
||||||
anchor::create_anchor, edit::EditWrapperManager, keyboard::create_keyboard,
|
anchor::create_anchor, custom::create_custom, edit::EditWrapperManager,
|
||||||
screen::create_screens, toast::Toast, watch::create_watch,
|
keyboard::create_keyboard, screen::create_screens, toast::Toast, watch::create_watch,
|
||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::{
|
windowing::{
|
||||||
@@ -114,6 +114,15 @@ where
|
|||||||
let watch = OverlayWindowData::from_config(create_watch(app)?);
|
let watch = OverlayWindowData::from_config(create_watch(app)?);
|
||||||
me.watch_id = me.add(watch, app);
|
me.watch_id = me.add(watch, app);
|
||||||
|
|
||||||
|
let custom_panels = app.session.config.custom_panels.clone();
|
||||||
|
for name in custom_panels.into_iter() {
|
||||||
|
let Some(panel) = create_custom(app, name) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
log::info!("Loaded custom panel '{}'", panel.name);
|
||||||
|
me.add(OverlayWindowData::from_config(panel), 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);
|
||||||
me.overlays_changed(app)?;
|
me.overlays_changed(app)?;
|
||||||
@@ -375,7 +384,7 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
self.edit_mode = enabled;
|
self.edit_mode = enabled;
|
||||||
if !enabled {
|
if !enabled {
|
||||||
for o in self.overlays.values_mut() {
|
for o in self.overlays.values_mut() {
|
||||||
self.wrappers.unwrap_edit_mode(&mut o.config);
|
self.wrappers.unwrap_edit_mode(&mut o.config, app)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if changed && let Some(watch) = self.mut_by_id(self.watch_id) {
|
if changed && let Some(watch) = self.mut_by_id(self.watch_id) {
|
||||||
@@ -412,7 +421,10 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
.inspect_err(|e| log::error!("{e:?}"))
|
.inspect_err(|e| log::error!("{e:?}"))
|
||||||
.unwrap(); // FIXME: unwrap
|
.unwrap(); // FIXME: unwrap
|
||||||
} else {
|
} else {
|
||||||
self.wrappers.unwrap_edit_mode(&mut overlay.config);
|
self.wrappers
|
||||||
|
.unwrap_edit_mode(&mut overlay.config, app)
|
||||||
|
.inspect_err(|e| log::error!("{e:?}"))
|
||||||
|
.unwrap(); // FIXME: unwrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use glam::{Affine3A, Mat3A, Quat, Vec3, Vec3A};
|
use glam::{Affine3A, Mat3A, Quat, Vec3, Vec3A};
|
||||||
|
use idmap_derive::IntegerId;
|
||||||
use std::{f32::consts::PI, sync::Arc};
|
use std::{f32::consts::PI, sync::Arc};
|
||||||
use wlx_common::windowing::{OverlayWindowState, Positioning};
|
use wlx_common::windowing::{OverlayWindowState, Positioning};
|
||||||
|
|
||||||
@@ -49,10 +50,10 @@ impl<T> OverlayWindowData<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy, IntegerId, PartialEq)]
|
||||||
pub enum OverlayCategory {
|
pub enum OverlayCategory {
|
||||||
Internal,
|
Internal,
|
||||||
PanelCustom,
|
Panel,
|
||||||
Screen,
|
Screen,
|
||||||
Mirror,
|
Mirror,
|
||||||
WayVR,
|
WayVR,
|
||||||
|
|||||||
Reference in New Issue
Block a user