custom panels

This commit is contained in:
galister
2025-12-17 15:54:43 +09:00
parent 9dbd35e8f7
commit ea90853e56
18 changed files with 181 additions and 61 deletions

View File

@@ -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},
}; };

View File

@@ -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},
}; };

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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")]

View File

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 329 B

View File

Before

Width:  |  Height:  |  Size: 354 B

After

Width:  |  Height:  |  Size: 354 B

View File

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 371 B

View File

@@ -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>

View File

@@ -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,

View File

@@ -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;

View File

@@ -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(())
} }

View File

@@ -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),
), ),

View File

@@ -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
} }
} }

View File

@@ -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()));
} }
} }

View File

@@ -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 {

View File

@@ -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
} }
} }

View File

@@ -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,