edit overlay progress
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
components::{self, Component, ComponentBase, ComponentTrait, InitData, tooltip::ComponentTooltip},
|
||||
components::{self, tooltip::ComponentTooltip, Component, ComponentBase, ComponentTrait, InitData},
|
||||
drawing::{self, Boundary, Color},
|
||||
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
|
||||
i18n::Translation,
|
||||
@@ -10,15 +10,15 @@ use crate::{
|
||||
util::centered_matrix,
|
||||
},
|
||||
widget::{
|
||||
ConstructEssentials, EventResult, WidgetData,
|
||||
label::{WidgetLabel, WidgetLabelParams},
|
||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
util::WLength,
|
||||
ConstructEssentials, EventResult, WidgetData,
|
||||
},
|
||||
};
|
||||
use glam::{Mat4, Vec3};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use taffy::{AlignItems, JustifyContent, prelude::length};
|
||||
use taffy::{prelude::length, AlignItems, JustifyContent};
|
||||
|
||||
pub struct Params {
|
||||
pub text: Option<Translation>, // if unset, label will not be populated
|
||||
@@ -31,6 +31,10 @@ pub struct Params {
|
||||
pub style: taffy::Style,
|
||||
pub text_style: TextStyle,
|
||||
pub tooltip: Option<components::tooltip::TooltipInfo>,
|
||||
/// make this a toggle-style button that stays depressed
|
||||
/// until "un-clicked". this is visual only.
|
||||
/// set the initial state using `set_sticky_state`
|
||||
pub sticky: bool,
|
||||
}
|
||||
|
||||
impl Default for Params {
|
||||
@@ -46,6 +50,7 @@ impl Default for Params {
|
||||
style: Default::default(),
|
||||
text_style: TextStyle::default(),
|
||||
tooltip: None,
|
||||
sticky: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +61,7 @@ pub type ButtonClickCallback = Box<dyn Fn(&mut CallbackDataCommon, ButtonClickEv
|
||||
struct State {
|
||||
hovered: bool,
|
||||
down: bool,
|
||||
sticky_down: bool,
|
||||
on_click: Option<ButtonClickCallback>,
|
||||
active_tooltip: Option<Rc<ComponentTooltip>>,
|
||||
}
|
||||
@@ -67,6 +73,7 @@ struct Data {
|
||||
initial_hover_border_color: drawing::Color,
|
||||
id_label: WidgetID, // Label
|
||||
id_rect: WidgetID, // Rectangle
|
||||
sticky: bool,
|
||||
}
|
||||
|
||||
pub struct ComponentButton {
|
||||
@@ -95,6 +102,45 @@ impl ComponentButton {
|
||||
pub fn on_click(&self, func: ButtonClickCallback) {
|
||||
self.state.borrow_mut().on_click = Some(func);
|
||||
}
|
||||
|
||||
/// Sets the sticky state of the button.
|
||||
///
|
||||
/// On buttons where sticky is false, sticky state won't automatically clear.
|
||||
pub fn set_sticky_state(&self, common: &mut CallbackDataCommon, sticky_down: bool) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
// only play anim if we're not changing the border highlight
|
||||
let dirty = !state.hovered && !state.down && state.sticky_down != sticky_down;
|
||||
|
||||
state.sticky_down = sticky_down;
|
||||
|
||||
if !dirty {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = self.data.clone();
|
||||
let anim = Animation::new(
|
||||
self.data.id_rect,
|
||||
if sticky_down { 5 } else { 10 },
|
||||
AnimationEasing::OutCubic,
|
||||
Box::new(move |common, anim_data| {
|
||||
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
let mult = if sticky_down {
|
||||
anim_data.pos
|
||||
} else {
|
||||
1.0 - anim_data.pos
|
||||
};
|
||||
|
||||
let bgcolor = data.initial_color.lerp(&data.initial_hover_color, mult * 0.5);
|
||||
rect.params.color = bgcolor;
|
||||
rect.params.color2 = get_color2(&bgcolor);
|
||||
rect.params.border_color = data.initial_border_color.lerp(&data.initial_hover_border_color, mult);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
);
|
||||
|
||||
common.alterables.animations.push(anim);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_color2(color: &drawing::Color) -> drawing::Color {
|
||||
@@ -108,9 +154,20 @@ fn anim_hover(
|
||||
widget_boundary: Boundary,
|
||||
pos: f32,
|
||||
pressed: bool,
|
||||
sticky_down: bool,
|
||||
) {
|
||||
let mult = pos * if pressed { 1.5 } else { 1.0 };
|
||||
let bgcolor = data.initial_color.lerp(&data.initial_hover_color, mult);
|
||||
|
||||
let (init_border_color, init_color) = if sticky_down {
|
||||
(
|
||||
data.initial_hover_border_color,
|
||||
data.initial_color.lerp(&data.initial_hover_color, 0.5),
|
||||
)
|
||||
} else {
|
||||
(data.initial_border_color, data.initial_color)
|
||||
};
|
||||
|
||||
let bgcolor = init_color.lerp(&data.initial_hover_color, mult);
|
||||
|
||||
//let t = Mat4::from_scale(Vec3::splat(1.0 + pos * 0.5)) * Mat4::from_rotation_z(pos * 1.0);
|
||||
|
||||
@@ -119,7 +176,8 @@ fn anim_hover(
|
||||
|
||||
rect.params.color = bgcolor;
|
||||
rect.params.color2 = get_color2(&bgcolor);
|
||||
rect.params.border_color = data.initial_border_color.lerp(&data.initial_hover_border_color, mult);
|
||||
|
||||
rect.params.border_color = init_border_color.lerp(&data.initial_hover_border_color, mult);
|
||||
}
|
||||
|
||||
fn anim_hover_create(data: Rc<Data>, state: Rc<RefCell<State>>, widget_id: WidgetID, fade_in: bool) -> Animation {
|
||||
@@ -129,13 +187,15 @@ fn anim_hover_create(data: Rc<Data>, state: Rc<RefCell<State>>, widget_id: Widge
|
||||
AnimationEasing::OutCubic,
|
||||
Box::new(move |common, anim_data| {
|
||||
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
let state = state.borrow();
|
||||
anim_hover(
|
||||
rect,
|
||||
anim_data.data,
|
||||
&data,
|
||||
anim_data.widget_boundary,
|
||||
if fade_in { anim_data.pos } else { 1.0 - anim_data.pos },
|
||||
state.borrow().down,
|
||||
state.down,
|
||||
state.sticky_down,
|
||||
);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
@@ -219,6 +279,7 @@ fn register_event_mouse_press(
|
||||
common.state.get_node_boundary(event_data.node_id),
|
||||
1.0,
|
||||
true,
|
||||
state.sticky_down,
|
||||
);
|
||||
|
||||
common.alterables.trigger_haptics();
|
||||
@@ -243,6 +304,12 @@ fn register_event_mouse_release(
|
||||
EventListenerKind::MouseRelease,
|
||||
Box::new(move |common, event_data, (), ()| {
|
||||
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
let mut state = state.borrow_mut();
|
||||
|
||||
if data.sticky {
|
||||
state.sticky_down = !state.sticky_down;
|
||||
}
|
||||
|
||||
anim_hover(
|
||||
rect,
|
||||
event_data.widget_data,
|
||||
@@ -250,12 +317,12 @@ fn register_event_mouse_release(
|
||||
common.state.get_node_boundary(event_data.node_id),
|
||||
1.0,
|
||||
false,
|
||||
state.sticky_down,
|
||||
);
|
||||
|
||||
common.alterables.trigger_haptics();
|
||||
common.alterables.mark_redraw();
|
||||
|
||||
let mut state = state.borrow_mut();
|
||||
if state.down {
|
||||
state.down = false;
|
||||
|
||||
@@ -357,6 +424,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
initial_border_color: border_color,
|
||||
initial_hover_color: hover_color,
|
||||
initial_hover_border_color: hover_border_color,
|
||||
sticky: params.sticky,
|
||||
});
|
||||
|
||||
let state = Rc::new(RefCell::new(State {
|
||||
@@ -364,6 +432,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
hovered: false,
|
||||
on_click: None,
|
||||
active_tooltip: None,
|
||||
sticky_down: false,
|
||||
}));
|
||||
|
||||
let base = ComponentBase {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::{
|
||||
components::{Component, button, tooltip},
|
||||
components::{button, tooltip, Component},
|
||||
drawing::Color,
|
||||
i18n::Translation,
|
||||
layout::WidgetID,
|
||||
parser::{
|
||||
AttribPair, ParserContext, ParserFile, parse_check_f32, parse_children, print_invalid_attrib, process_component,
|
||||
parse_check_f32, parse_check_i32, parse_children, print_invalid_attrib, process_component,
|
||||
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
|
||||
AttribPair, ParserContext, ParserFile,
|
||||
},
|
||||
widget::util::WLength,
|
||||
};
|
||||
@@ -25,6 +26,7 @@ pub fn parse_component_button<'a>(
|
||||
let mut round = WLength::Units(4.0);
|
||||
let mut tooltip: Option<String> = None;
|
||||
let mut tooltip_side: Option<tooltip::TooltipSide> = None;
|
||||
let mut sticky: bool = false;
|
||||
|
||||
let mut translation: Option<Translation> = None;
|
||||
|
||||
@@ -71,6 +73,10 @@ pub fn parse_component_button<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
"sticky" => {
|
||||
let mut sticky_i32 = 0;
|
||||
sticky = parse_check_i32(value, &mut sticky_i32) && sticky_i32 == 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -91,6 +97,7 @@ pub fn parse_component_button<'a>(
|
||||
side: tooltip_side.map_or(tooltip::TooltipSide::Bottom, |f| f),
|
||||
text: Translation::from_translation_key(&t),
|
||||
}),
|
||||
sticky,
|
||||
},
|
||||
)?;
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ use crate::{
|
||||
globals::Globals,
|
||||
layout::WidgetID,
|
||||
renderer_vk::text::{
|
||||
DEFAULT_METRICS,
|
||||
custom_glyph::{CustomGlyph, CustomGlyphData},
|
||||
DEFAULT_METRICS,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ pub struct WidgetSpriteParams {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct WidgetSprite {
|
||||
params: WidgetSpriteParams,
|
||||
pub params: WidgetSpriteParams,
|
||||
id: WidgetID,
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B |
|
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 609 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
|
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 347 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 331 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
1
wlx-overlay-s/src/assets/edit/float.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 Tabler Icons by Paweł Kuna - https://github.com/tabler/tabler-icons/blob/master/LICENSE --><g fill="white"><path d="M12 1a7 7 0 0 1 7 7c0 5.457-3.028 10-7 10c-3.9 0-6.89-4.379-6.997-9.703L5 8l.004-.24A7 7 0 0 1 12 1m0 4a1 1 0 0 0 0 2l.117.007A1 1 0 0 1 13 8l.007.117A1 1 0 0 0 15 8a3 3 0 0 0-3-3"/><path d="M12 16a1 1 0 0 1 .993.883L13 17v1a3 3 0 0 1-2.824 2.995L10 21H7a1 1 0 0 0-.993.883L6 22a1 1 0 0 1-2 0a3 3 0 0 1 2.824-2.995L7 19h3a1 1 0 0 0 .993-.883L11 18v-1a1 1 0 0 1 1-1"/></g></svg>
|
||||
|
After Width: | Height: | Size: 593 B |
13
wlx-overlay-s/src/assets/edit/grid.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="white" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 13 L6 5 H18 L22 13 Z"></path>
|
||||
|
||||
<path d="M7 13 L9 5"></path>
|
||||
<path d="M12 13 L12 5"></path>
|
||||
<path d="M17 13 L15 5"></path>
|
||||
|
||||
<path d="M3 11 H21"></path>
|
||||
<path d="M4 9 H20"></path>
|
||||
<path d="M5 7 H19"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 440 B |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 346 B |
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 511 B |
|
Before Width: | Height: | Size: 504 B After Width: | Height: | Size: 504 B |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 620 B After Width: | Height: | Size: 620 B |
|
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 475 B |
|
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 325 B |
1
wlx-overlay-s/src/assets/edit/pin.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="m22.313 10.175l-1.415 1.414l-.707-.707l-4.242 4.243l-.707 3.536l-1.415 1.414l-4.242-4.243l-4.95 4.95l-1.414-1.414l4.95-4.95l-4.243-4.243l1.414-1.414l3.536-.707l4.242-4.243l-.707-.707l1.414-1.414z"/></svg>
|
||||
|
After Width: | Height: | Size: 419 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
|
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 357 B |
@@ -1,29 +0,0 @@
|
||||
<layout>
|
||||
<include src="wlx_theme.xml" />
|
||||
|
||||
<template name="TopButton">
|
||||
<rectangle id="~id" color="~color_bg" padding="8" round="50%">
|
||||
<sprite width="48" height="48" src="${src}" />
|
||||
</rectangle>
|
||||
</template>
|
||||
|
||||
<elements>
|
||||
<div box_sizing="content_box" flex_direction="column" justify_content="center">
|
||||
<rectangle padding="10" gap="8" round="100%" color="~color_bg" justify_content="center">
|
||||
<TopButton id="lock" src="bar/lock_open.svg" />
|
||||
<TopButton id="anchor" src="bar/anchor.svg" />
|
||||
<TopButton id="mouse" src="bar/mouse.svg" />
|
||||
<TopButton id="fade" src="bar/fade.svg" />
|
||||
<TopButton id="move" src="bar/move-all.svg" />
|
||||
<TopButton id="resize" src="bar/resize.svg" />
|
||||
<TopButton id="inout" src="bar/inout.svg" />
|
||||
<TopButton id="delete" src="bar/delete.svg" />
|
||||
</rectangle>
|
||||
<rectangle padding="8" gap="8" round="100%" color="~color_bg_active" justify_content="center" align_items="center">
|
||||
<label size="18" translation="BAR.OPACITY" color="~text_color" />
|
||||
<Slider width="150" height="24" min_value="0" max_value="100" value="100" />
|
||||
<CheckBox translation="BAR.ADDITIVE" color="~text_color" />
|
||||
</rectangle>
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
@@ -6,49 +6,84 @@
|
||||
border="2" round="50%" padding="8" gradient="vertical" tooltip_side="bottom" />
|
||||
|
||||
<template name="TopButton">
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}" border_color="#0044CC" color="#000A1C" color2="#000002">
|
||||
<sprite width="48" height="48" src="${src}" _release="${release}" />
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<template name="TopButtonDanger">
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}" border_color="#CC0000" color="#110000" color2="#020000">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<template name="TopButtonFaded">
|
||||
<Button id="${id}" macro="button_style" tooltip="${tooltip}" _press="${press}" border_color="#707070" color="#202020" color2="#010101">
|
||||
<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>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<elements>
|
||||
<div width="100%" height="100%">
|
||||
<rectangle id="shadow" width="100%" height="100%" padding="4" gap="4" align_items="center" justify_content="center" color="#000000DD" flex_direction="row">
|
||||
<div flex_direction="column">
|
||||
<rectangle padding="16" gap="8" round="32" color="~color_bg" border="2" border_color="~color_accent" justify_content="center">
|
||||
<div flex_direction="column" gap="8">
|
||||
<label align="center" translation="EDIT_MODE.HINT_POINT_WINDOW" />
|
||||
<div flex_direction="row" gap="4">
|
||||
<TopButton id="lock" src="bar/lock_open.svg" tooltip="EDIT_MODE.LOCK_INTERACTION" kind="btn_primary" press="::EditModeToggleLock" />
|
||||
<TopButton id="anchor" src="bar/anchor.svg" tooltip="EDIT_MODE.POSITIONING" kind="btn_primary" press="::EditModeToggleAnchor" />
|
||||
<TopButton id="alpha" src="bar/fade.svg" tooltip="EDIT_MODE.OPACITY" kind="btn_primary" press="::EditModeToggleFade" />
|
||||
<TopButton id="curve" src="bar/curve.svg" tooltip="EDIT_MODE.ADJUST_CURVATURE" kind="btn_primary" press="::EditModeToggleCurve" />
|
||||
<TopButton id="move" src="bar/move-all.svg" tooltip="EDIT_MODE.MOVE_PRESS_AND_DRAG" kind="btn_primary" />
|
||||
<TopButton id="resize" src="bar/resize.svg" tooltip="EDIT_MODE.RESIZE_PRESS_AND_DRAG" kind="btn_primary" />
|
||||
<TopButtonDanger id="delete" src="bar/delete.svg" tooltip="EDIT_MODE.DELETE" kind="btn_danger" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
|
||||
<TopButton sticky="1" id="top_lock" src="edit/lock_open.svg" tooltip="EDIT_MODE.LOCK_INTERACTION" press="::EditModeToggleLock" />
|
||||
<TopButton sticky="0" id="top_pos" src="edit/anchor.svg" tooltip="EDIT_MODE.POSITIONING" press="::EditModeTab pos" />
|
||||
<TopButton sticky="0" id="top_alpha" src="edit/fade.svg" tooltip="EDIT_MODE.OPACITY" press="::EditModeTab alpha" />
|
||||
<TopButton sticky="0" id="top_curve" src="edit/curve.svg" tooltip="EDIT_MODE.ADJUST_CURVATURE" press="::EditModeTab curve" />
|
||||
<TopButton sticky="0" id="top_move" src="edit/move-all.svg" tooltip="EDIT_MODE.MOVE_PRESS_AND_DRAG" />
|
||||
<TopButton sticky="0" id="top_resize" src="edit/resize.svg" tooltip="EDIT_MODE.RESIZE_PRESS_AND_DRAG" />
|
||||
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
|
||||
<div width="8" height="100%" />
|
||||
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" kind="btn_faded" press="::EditToggle" />
|
||||
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" press="::EditToggle" />
|
||||
</div>
|
||||
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.OPACITY" />
|
||||
<Slider id="alpha_slider" width="200" height="16" min_value="0.1" max_value="1" value="1" step="0.05" />
|
||||
<CheckBox id="additive_box" translation="EDIT_MODE.BLENDING_ADDITIVE" tooltip="EDIT_MODE.ALPHA_BLEND_MODE" tooltip_side="bottom" />
|
||||
<div id="tab_none" display="none">
|
||||
<div height="100">
|
||||
<label align="center" translation="EDIT_MODE.HINT_POINT_WINDOW" />
|
||||
</div>
|
||||
</div>
|
||||
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.CURVATURE" />
|
||||
<Slider id="curve_slider" width="250" height="16" min_value="0" max_value="0.5" value="0.15" step="0.01" />
|
||||
<div id="tab_pos" display="none">
|
||||
<div height="100" flex_direction="column">
|
||||
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<PosButton id="pos_static" src="edit/pin.svg" tooltip="EDIT_MODE.POS_STATIC" press="::EditModeSetPos static" />
|
||||
<PosButton id="pos_anchored" src="edit/anchor.svg" tooltip="EDIT_MODE.POS_ANCHORED" press="::EditModeSetPos anchored" />
|
||||
<PosButton id="pos_floating" src="edit/float.svg" tooltip="EDIT_MODE.POS_FLOATING" press="::EditModeSetPos floating" />
|
||||
<PosButton id="pos_hmd" src="watch/hmd.svg" tooltip="EDIT_MODE.POS_HMD" press="::EditModeSetPos hmd" />
|
||||
<PosButton id="pos_hand_l" src="watch/controller_l.svg" tooltip="EDIT_MODE.POS_HAND_L" press="::EditModeSetPos hand_l" />
|
||||
<PosButton id="pos_hand_r" src="watch/controller_r.svg" tooltip="EDIT_MODE.POS_HAND_R" press="::EditModeSetPos hand_r" />
|
||||
</div>
|
||||
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.INTERPOLATION" />
|
||||
<Slider id="lerp_slider" width="250" height="16" min_value="0.05" max_value="1" value="1" step="0.05" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab_alpha" display="none">
|
||||
<div height="100" padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.OPACITY" />
|
||||
<Slider id="alpha_slider" width="200" height="16" min_value="0.1" max_value="1" value="1" step="0.05" />
|
||||
<CheckBox id="additive_box" translation="EDIT_MODE.BLENDING_ADDITIVE" tooltip="EDIT_MODE.ALPHA_BLEND_MODE" tooltip_side="bottom" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab_curve" display="none">
|
||||
<div height="100" flex_direction="column">
|
||||
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.CURVATURE_EXPLANATION" />
|
||||
</div>
|
||||
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.CURVATURE" />
|
||||
<Slider id="curve_slider" width="250" height="16" min_value="0" max_value="0.5" value="0.15" step="0.01" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</rectangle>
|
||||
|
||||
@@ -12,5 +12,11 @@
|
||||
"OPACITY": "Undurchsichtigkeit",
|
||||
"POSITIONING": "Positionierung",
|
||||
"RESIZE_PRESS_AND_DRAG": "Größe ändern (gedrückt halten & ziehen)"
|
||||
"POS_STATIC": "Statisch: Bleibt an Ort und Stelle und wird niemals neu zentriert.",
|
||||
"POS_ANCHORED": "Verankert: Bewegt sich zusammen mit dem Rest des Sets. Standard.",
|
||||
"POS_FLOATING": "Schwebend: Bleibt an Ort und Stelle, wird beim Anzeigen neu zentriert.",
|
||||
"POS_HMD": "Folge dem HMD.",
|
||||
"POS_HAND_L": "Folge der linken Hand.",
|
||||
"POS_HAND_R": "Folge der rechten Hand."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,20 @@
|
||||
"ALPHA_BLEND_MODE": "Alpha blend mode",
|
||||
"BLENDING_ADDITIVE": "Additive",
|
||||
"CURVATURE": "Curvature",
|
||||
"DELETE": "Delete",
|
||||
"DELETE": "Long press to remove from current set",
|
||||
"HINT_POINT_WINDOW": "Point at a window to change its parameters.\nOnce done, leave edit mode using the button on the right.",
|
||||
"LEAVE": "Leave edit mode",
|
||||
"LOCK_INTERACTION": "Lock interaction",
|
||||
"MOVE_PRESS_AND_DRAG": "Move (press & drag)",
|
||||
"OPACITY": "Opacity",
|
||||
"POSITIONING": "Positioning",
|
||||
"RESIZE_PRESS_AND_DRAG": "Resize (press & drag)"
|
||||
"RESIZE_PRESS_AND_DRAG": "Resize (press & drag)",
|
||||
"POS_STATIC": "Static: Stay in place and never recenter.",
|
||||
"POS_ANCHORED": "Anchored: Moves together with the rest of the set. Default.",
|
||||
"POS_FLOATING": "Floating: Stay in place, recenter when shown.",
|
||||
"POS_HMD": "Follow the HMD.",
|
||||
"POS_HAND_L": "Follow the left hand.",
|
||||
"POS_HAND_R": "Follow the right hand.",
|
||||
"INTERPOLATION": "Interpolation"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,11 @@
|
||||
"OPACITY": "Opacidad",
|
||||
"POSITIONING": "Posicionamiento",
|
||||
"RESIZE_PRESS_AND_DRAG": "Redimensionar (presionar y arrastrar)"
|
||||
"POS_STATIC": "Estático: Permanece en su lugar y nunca se recentra.",
|
||||
"POS_ANCHORED": "Anclado: Se mueve junto con el resto del conjunto. Predeterminado.",
|
||||
"POS_FLOATING": "Flotante: Permanece en su lugar, se recentra cuando se muestra.",
|
||||
"POS_HMD": "Seguir el HMD.",
|
||||
"POS_HAND_L": "Seguir la mano izquierda.",
|
||||
"POS_HAND_R": "Seguir la mano derecha."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,11 @@
|
||||
"OPACITY": "不透明度",
|
||||
"POSITIONING": "位置調整",
|
||||
"RESIZE_PRESS_AND_DRAG": "サイズ変更(押してドラッグ)"
|
||||
"POS_STATIC": "固定:その場に留まり、再センタリングされません。",
|
||||
"POS_ANCHORED": "アンカー:他のセット要素と一緒に移動します。デフォルト。",
|
||||
"POS_FLOATING": "フローティング:その場に留まり、表示されるときに再センタリングされます。",
|
||||
"POS_HMD": "HMD に追従する。",
|
||||
"POS_HAND_L": "左手に追従する。",
|
||||
"POS_HAND_R": "右手に追従する。"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,11 @@
|
||||
"OPACITY": "Przezroczystość",
|
||||
"POSITIONING": "Pozycjonowanie",
|
||||
"RESIZE_PRESS_AND_DRAG": "Zmień rozmiar (naciśnij i przeciągnij)"
|
||||
"POS_STATIC": "Statyczne: Pozostaje w miejscu i nigdy nie jest ponownie wyśrodkowywane.",
|
||||
"POS_ANCHORED": "Zakotwiczone: Porusza się razem z resztą zestawu. Domyślne.",
|
||||
"POS_FLOATING": "Pływające: Pozostaje w miejscu, ponownie się wyśrodkowuje po pokazaniu.",
|
||||
"POS_HMD": "Podążaj za HMD.",
|
||||
"POS_HAND_L": "Podążaj za lewą dłonią.",
|
||||
"POS_HAND_R": "Podążaj za prawą dłonią."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,9 +657,8 @@ impl Pointer {
|
||||
Positioning::FollowHeadPaused { lerp } => Positioning::FollowHead { lerp },
|
||||
x => x,
|
||||
};
|
||||
|
||||
pointer.interaction.grabbed = None;
|
||||
overlay.config.save_transform(app);
|
||||
overlay_state.save_transform(app);
|
||||
|
||||
// Hide anchor
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
|
||||
@@ -83,7 +83,7 @@ impl TaskContainer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transfer_from(&mut self, other: &mut TaskContainer) {
|
||||
pub fn transfer_from(&mut self, other: &mut Self) {
|
||||
self.tasks.append(&mut other.tasks);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use button::setup_custom_button;
|
||||
use glam::{Affine2, Vec2, vec2};
|
||||
use glam::{vec2, Affine2, Vec2};
|
||||
use label::setup_custom_label;
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::ComponentTrait,
|
||||
drawing,
|
||||
event::{
|
||||
CallbackDataCommon, Event as WguiEvent, EventAlterables, EventCallback, EventListenerID,
|
||||
EventListenerKind, InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent,
|
||||
MouseLeaveEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent,
|
||||
Event as WguiEvent, EventCallback, EventListenerID, EventListenerKind,
|
||||
InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent,
|
||||
MouseMotionEvent, MouseUpEvent, MouseWheelEvent,
|
||||
},
|
||||
gfx::cmd::WGfxClearMode,
|
||||
layout::{Layout, LayoutParams, WidgetID},
|
||||
parser::{CustomAttribsInfoOwned, ParserState},
|
||||
renderer_vk::context::Context as WguiContext,
|
||||
widget::{EventResult, label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle, EventResult},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::input::{Haptics, HoverResult, PointerHit, PointerMode},
|
||||
state::AppState,
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::backend::{FrameMeta, OverlayBackend, RenderResources, ShouldRender, ui_transform},
|
||||
windowing::backend::{ui_transform, FrameMeta, OverlayBackend, RenderResources, ShouldRender},
|
||||
};
|
||||
|
||||
use super::{timer::GuiTimer, timestep::Timestep};
|
||||
@@ -211,22 +210,6 @@ impl<S: 'static> GuiPanel<S> {
|
||||
) -> Option<EventListenerID> {
|
||||
self.layout.add_event_listener(widget_id, kind, callback)
|
||||
}
|
||||
|
||||
pub fn component_make_call<C: ComponentTrait>(
|
||||
&mut self,
|
||||
component: Rc<C>,
|
||||
run: Box<dyn Fn(Rc<C>, &mut CallbackDataCommon)>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut alterables = EventAlterables::default();
|
||||
let mut common = CallbackDataCommon {
|
||||
state: &self.layout.state,
|
||||
alterables: &mut alterables,
|
||||
};
|
||||
|
||||
run(component, &mut common);
|
||||
|
||||
self.layout.process_alterables(alterables)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
use glam::{Affine3A, Vec3};
|
||||
|
||||
use crate::{
|
||||
gui::panel::GuiPanel,
|
||||
state::AppState,
|
||||
windowing::window::{OverlayWindowConfig, OverlayWindowState},
|
||||
};
|
||||
|
||||
pub const BAR_NAME: &str = "bar";
|
||||
|
||||
struct BarState {}
|
||||
|
||||
#[allow(clippy::significant_drop_tightening)]
|
||||
#[allow(clippy::for_kv_map)] // TODO: remove later
|
||||
#[allow(clippy::match_same_arms)] // TODO: remove later
|
||||
pub fn create_bar(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
let state = BarState {};
|
||||
let mut panel = GuiPanel::new_from_template(app, "gui/bar.xml", state, Default::default())?;
|
||||
|
||||
for (id, _widget_id) in &panel.parser_state.data.ids {
|
||||
match id.as_ref() {
|
||||
"lock" => {}
|
||||
"anchor" => {}
|
||||
"mouse" => {}
|
||||
"fade" => {}
|
||||
"move" => {}
|
||||
"resize" => {}
|
||||
"inout" => {}
|
||||
"delete" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
panel.update_layout()?;
|
||||
|
||||
Ok(OverlayWindowConfig {
|
||||
name: BAR_NAME.into(),
|
||||
default_state: OverlayWindowState {
|
||||
interactable: true,
|
||||
transform: Affine3A::from_scale(Vec3::ONE * 0.15),
|
||||
..OverlayWindowState::default()
|
||||
},
|
||||
global: true,
|
||||
..OverlayWindowConfig::from_backend(Box::new(panel))
|
||||
})
|
||||
}
|
||||
111
wlx-overlay-s/src/overlays/edit/lock.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
use glam::FloatExt;
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
event::CallbackDataCommon,
|
||||
layout::WidgetID,
|
||||
parser::Fetchable,
|
||||
widget::rectangle::WidgetRectangle,
|
||||
};
|
||||
|
||||
use crate::{backend::task::OverlayTask, overlays::edit::EditModeWrapPanel, state::AppState};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct InteractLockHandler {
|
||||
id: WidgetID,
|
||||
color: wgui::drawing::Color,
|
||||
interactable: bool,
|
||||
}
|
||||
|
||||
impl InteractLockHandler {
|
||||
pub fn new(panel: &mut EditModeWrapPanel) -> anyhow::Result<Self> {
|
||||
let id = panel.parser_state.get_widget_id("shadow")?;
|
||||
let shadow_rect = panel
|
||||
.layout
|
||||
.state
|
||||
.widgets
|
||||
.get_as::<WidgetRectangle>(id)
|
||||
.ok_or_else(|| anyhow::anyhow!("Element with id=\"shadow\" must be a <rectangle>"))?;
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
color: shadow_rect.params.color,
|
||||
interactable: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, common: &mut CallbackDataCommon, interactable: bool) {
|
||||
self.interactable = interactable;
|
||||
let mut rect = common
|
||||
.state
|
||||
.widgets
|
||||
.get_as::<WidgetRectangle>(self.id)
|
||||
.unwrap(); // can only fail if set_up_rect has issues
|
||||
|
||||
let globals = common.state.globals.get();
|
||||
if interactable {
|
||||
set_anim_color(&mut rect, 0.0, self.color, globals.defaults.danger_color);
|
||||
} else {
|
||||
set_anim_color(&mut rect, 0.2, self.color, globals.defaults.danger_color);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle(
|
||||
&mut self,
|
||||
common: &mut CallbackDataCommon,
|
||||
app: &mut AppState,
|
||||
) -> Box<OverlayTask> {
|
||||
let defaults = app.wgui_globals.get().defaults.clone();
|
||||
let rect_color = self.color;
|
||||
|
||||
self.interactable = !self.interactable;
|
||||
|
||||
let anim = if self.interactable {
|
||||
Animation::new(
|
||||
self.id,
|
||||
10,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(
|
||||
rect,
|
||||
0.2 - (data.pos * 0.2),
|
||||
rect_color,
|
||||
defaults.danger_color,
|
||||
);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
Animation::new(
|
||||
self.id,
|
||||
10,
|
||||
AnimationEasing::OutBack,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(rect, data.pos * 0.2, rect_color, defaults.danger_color);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
)
|
||||
};
|
||||
|
||||
common.alterables.animate(anim);
|
||||
|
||||
let interactable = self.interactable;
|
||||
Box::new(move |_app, owc| {
|
||||
let state = owc.active_state.as_mut().unwrap(); //want panic
|
||||
state.interactable = interactable;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn set_anim_color(
|
||||
rect: &mut WidgetRectangle,
|
||||
pos: f32,
|
||||
rect_color: wgui::drawing::Color,
|
||||
target_color: wgui::drawing::Color,
|
||||
) {
|
||||
// rect to target_color
|
||||
rect.params.color.r = rect_color.r.lerp(target_color.r, pos);
|
||||
rect.params.color.g = rect_color.g.lerp(target_color.g, pos);
|
||||
rect.params.color.b = rect_color.b.lerp(target_color.b, pos);
|
||||
}
|
||||
@@ -6,15 +6,14 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use glam::{vec2, FloatExt, UVec2};
|
||||
use glam::vec2;
|
||||
use slotmap::Key;
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
components::{checkbox::ComponentCheckbox, slider::ComponentSlider},
|
||||
event::EventCallback,
|
||||
layout::{Layout, WidgetID},
|
||||
event::{CallbackDataCommon, EventAlterables, EventCallback},
|
||||
layout::Layout,
|
||||
parser::{CustomAttribsInfoOwned, Fetchable},
|
||||
widget::{rectangle::WidgetRectangle, EventResult},
|
||||
widget::EventResult,
|
||||
};
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
@@ -22,15 +21,22 @@ use crate::{backend::task::TaskType, windowing::OverlaySelector};
|
||||
use crate::{
|
||||
backend::{input::HoverResult, task::TaskContainer},
|
||||
gui::panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams},
|
||||
overlays::edit::{
|
||||
lock::InteractLockHandler, pos::PositioningHandler, tab::ButtonPaneTabSwitcher,
|
||||
},
|
||||
state::AppState,
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::{
|
||||
backend::{DummyBackend, OverlayBackend, RenderResources, ShouldRender},
|
||||
window::{OverlayWindowConfig, Positioning},
|
||||
window::OverlayWindowConfig,
|
||||
OverlayID,
|
||||
},
|
||||
};
|
||||
|
||||
mod lock;
|
||||
mod pos;
|
||||
mod tab;
|
||||
|
||||
struct LongPressButtonState {
|
||||
pressed: Instant,
|
||||
}
|
||||
@@ -38,12 +44,10 @@ struct LongPressButtonState {
|
||||
struct EditModeState {
|
||||
tasks: Rc<RefCell<TaskContainer>>,
|
||||
id: Rc<RefCell<OverlayID>>,
|
||||
interact_lock: bool,
|
||||
positioning: Positioning,
|
||||
delete: LongPressButtonState,
|
||||
rect_id: WidgetID,
|
||||
rect_color: wgui::drawing::Color,
|
||||
border_color: wgui::drawing::Color,
|
||||
tabs: ButtonPaneTabSwitcher,
|
||||
lock: InteractLockHandler,
|
||||
pos: PositioningHandler,
|
||||
}
|
||||
|
||||
type EditModeWrapPanel = GuiPanel<EditModeState>;
|
||||
@@ -65,22 +69,14 @@ impl EditWrapperManager {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(meta) = owc.backend.frame_meta() else {
|
||||
log::error!("META NULL");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
log::debug!("EditMode wrap on {}", owc.name);
|
||||
let inner = mem::replace(&mut owc.backend, Box::new(DummyBackend {}));
|
||||
let mut panel = self.panel_pool.pop();
|
||||
if panel.is_none() {
|
||||
panel = Some(make_edit_panel(
|
||||
app,
|
||||
UVec2::new(meta.extent[0], meta.extent[1]),
|
||||
)?);
|
||||
panel = Some(make_edit_panel(app)?);
|
||||
}
|
||||
let mut panel = panel.unwrap();
|
||||
panel_new_assignment(&mut panel, id, owc, app)?;
|
||||
reset_panel(&mut panel, id, owc)?;
|
||||
|
||||
owc.backend = Box::new(EditModeBackendWrapper {
|
||||
inner: ManuallyDrop::new(inner),
|
||||
@@ -214,27 +210,16 @@ impl OverlayBackend for EditModeBackendWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_edit_panel(
|
||||
app: &mut AppState,
|
||||
overlay_resolution: UVec2,
|
||||
) -> anyhow::Result<EditModeWrapPanel> {
|
||||
log::error!(
|
||||
"overlay res {} {}",
|
||||
overlay_resolution.x,
|
||||
overlay_resolution.y
|
||||
);
|
||||
|
||||
fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
let state = EditModeState {
|
||||
id: Rc::new(RefCell::new(OverlayID::null())),
|
||||
interact_lock: false,
|
||||
positioning: Positioning::Static,
|
||||
tasks: Rc::new(RefCell::new(TaskContainer::new())),
|
||||
delete: LongPressButtonState {
|
||||
pressed: Instant::now(),
|
||||
},
|
||||
rect_id: WidgetID::null(),
|
||||
rect_color: wgui::drawing::Color::default(),
|
||||
border_color: wgui::drawing::Color::default(),
|
||||
tabs: ButtonPaneTabSwitcher::default(),
|
||||
lock: InteractLockHandler::default(),
|
||||
pos: PositioningHandler::default(),
|
||||
};
|
||||
|
||||
let on_custom_attrib: Box<dyn Fn(&mut Layout, &CustomAttribsInfoOwned, &AppState)> =
|
||||
@@ -251,58 +236,27 @@ fn make_edit_panel(
|
||||
|
||||
let callback: EventCallback<AppState, EditModeState> = match command {
|
||||
"::EditModeToggleLock" => Box::new(move |common, _data, app, state| {
|
||||
state.interact_lock = !state.interact_lock;
|
||||
|
||||
let defaults = app.wgui_globals.get().defaults.clone();
|
||||
let rect_color = state.rect_color.clone();
|
||||
let border_color = state.border_color.clone();
|
||||
|
||||
if state.interact_lock {
|
||||
common.alterables.animate(Animation::new(
|
||||
state.rect_id,
|
||||
10,
|
||||
AnimationEasing::OutBack,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(
|
||||
rect,
|
||||
data.pos * 0.2,
|
||||
rect_color,
|
||||
border_color,
|
||||
defaults.danger_color,
|
||||
);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
common.alterables.animate(Animation::new(
|
||||
state.rect_id,
|
||||
10,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(
|
||||
rect,
|
||||
0.2 - (data.pos * 0.2),
|
||||
rect_color,
|
||||
border_color,
|
||||
defaults.danger_color,
|
||||
);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
));
|
||||
};
|
||||
|
||||
let interactable = !state.interact_lock;
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(state.id.borrow().clone()),
|
||||
Box::new(move |_app, owc| {
|
||||
let state = owc.active_state.as_mut().unwrap(); //want panic
|
||||
state.interactable = interactable;
|
||||
}),
|
||||
));
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.lock.toggle(common, app);
|
||||
app.tasks.enqueue(TaskType::Overlay(sel, task));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeTab" => {
|
||||
let tab_name = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, _app, state| {
|
||||
state.tabs.tab_button_clicked(common, &tab_name);
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditModeSetPos" => {
|
||||
let pos_key = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, app, state| {
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.pos.pos_button_clicked(common, &pos_key);
|
||||
app.tasks.enqueue(TaskType::Overlay(sel, task));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditModeDeletePress" => Box::new(move |_common, _data, _app, state| {
|
||||
state.delete.pressed = Instant::now();
|
||||
// TODO: animate to light up button after 2s
|
||||
@@ -312,9 +266,12 @@ fn make_edit_panel(
|
||||
if state.delete.pressed.elapsed() > Duration::from_secs(2) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
app.tasks.enqueue(TaskType::DropOverlay(OverlaySelector::Id(
|
||||
state.id.borrow().clone(),
|
||||
)));
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(*state.id.borrow()),
|
||||
Box::new(move |_app, owc| {
|
||||
owc.active_state = None;
|
||||
}),
|
||||
));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
_ => return,
|
||||
@@ -336,7 +293,10 @@ fn make_edit_panel(
|
||||
},
|
||||
)?;
|
||||
|
||||
set_up_shadow(&mut panel)?;
|
||||
panel.state.pos = PositioningHandler::new(&mut panel)?;
|
||||
panel.state.lock = InteractLockHandler::new(&mut panel)?;
|
||||
panel.state.tabs = ButtonPaneTabSwitcher::new(&mut panel)?;
|
||||
|
||||
set_up_checkbox(&mut panel, "additive_box", cb_assign_additive)?;
|
||||
set_up_slider(&mut panel, "alpha_slider", cb_assign_alpha)?;
|
||||
set_up_slider(&mut panel, "curve_slider", cb_assign_curve)?;
|
||||
@@ -344,7 +304,45 @@ fn make_edit_panel(
|
||||
Ok(panel)
|
||||
}
|
||||
|
||||
fn cb_assign_alpha(_app: &mut AppState, owc: &mut OverlayWindowConfig, alpha: f32) {
|
||||
fn reset_panel(
|
||||
panel: &mut EditModeWrapPanel,
|
||||
id: OverlayID,
|
||||
owc: &mut OverlayWindowConfig,
|
||||
) -> anyhow::Result<()> {
|
||||
*panel.state.id.borrow_mut() = id;
|
||||
let state = owc.active_state.as_mut().unwrap();
|
||||
|
||||
let mut alterables = EventAlterables::default();
|
||||
let mut common = CallbackDataCommon {
|
||||
alterables: &mut alterables,
|
||||
state: &panel.layout.state,
|
||||
};
|
||||
|
||||
let c = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentSlider>("alpha_slider")?;
|
||||
c.set_value(&mut common, state.alpha);
|
||||
|
||||
let c = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentSlider>("curve_slider")?;
|
||||
c.set_value(&mut common, state.curvature.unwrap_or(0.0));
|
||||
|
||||
let c = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentCheckbox>("additive_box")?;
|
||||
c.set_checked(&mut common, state.additive);
|
||||
|
||||
panel.state.pos.reset(&mut common, state.positioning);
|
||||
panel.state.lock.reset(&mut common, state.interactable);
|
||||
panel.state.tabs.reset(&mut common);
|
||||
|
||||
panel.layout.process_alterables(alterables)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const fn cb_assign_alpha(_app: &mut AppState, owc: &mut OverlayWindowConfig, alpha: f32) {
|
||||
owc.dirty = true;
|
||||
owc.active_state.as_mut().unwrap().alpha = alpha;
|
||||
}
|
||||
@@ -358,7 +356,7 @@ fn cb_assign_curve(_app: &mut AppState, owc: &mut OverlayWindowConfig, curvature
|
||||
};
|
||||
}
|
||||
|
||||
fn cb_assign_additive(_app: &mut AppState, owc: &mut OverlayWindowConfig, additive: bool) {
|
||||
const fn cb_assign_additive(_app: &mut AppState, owc: &mut OverlayWindowConfig, additive: bool) {
|
||||
owc.dirty = true;
|
||||
owc.active_state.as_mut().unwrap().additive = additive;
|
||||
}
|
||||
@@ -378,7 +376,7 @@ fn set_up_slider(
|
||||
let e_value = e.value;
|
||||
|
||||
tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id.borrow().clone()),
|
||||
OverlaySelector::Id(*overlay_id.borrow()),
|
||||
Box::new(move |app, owc| callback(app, owc, e_value)),
|
||||
));
|
||||
Ok(())
|
||||
@@ -402,7 +400,7 @@ fn set_up_checkbox(
|
||||
let e_checked = e.checked;
|
||||
|
||||
tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id.borrow().clone()),
|
||||
OverlaySelector::Id(*overlay_id.borrow()),
|
||||
Box::new(move |app, owc| callback(app, owc, e_checked)),
|
||||
));
|
||||
Ok(())
|
||||
@@ -410,92 +408,3 @@ fn set_up_checkbox(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_up_shadow(panel: &mut EditModeWrapPanel) -> anyhow::Result<()> {
|
||||
panel.state.rect_id = panel.parser_state.get_widget_id("shadow")?;
|
||||
let shadow_rect = panel
|
||||
.layout
|
||||
.state
|
||||
.widgets
|
||||
.get_as::<WidgetRectangle>(panel.state.rect_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("Element with id=\"shadow\" must be a <rectangle>"))?;
|
||||
panel.state.rect_color = shadow_rect.params.color;
|
||||
panel.state.border_color = shadow_rect.params.border_color;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn panel_new_assignment(
|
||||
panel: &mut EditModeWrapPanel,
|
||||
id: OverlayID,
|
||||
owc: &mut OverlayWindowConfig,
|
||||
app: &mut AppState,
|
||||
) -> anyhow::Result<()> {
|
||||
*panel.state.id.borrow_mut() = id;
|
||||
let active_state = owc.active_state.as_mut().unwrap();
|
||||
panel.state.interact_lock = !active_state.interactable;
|
||||
panel.state.positioning = active_state.positioning;
|
||||
|
||||
let alpha = active_state.alpha;
|
||||
let c = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentSlider>("alpha_slider")?;
|
||||
panel.component_make_call(c, Box::new(move |c, cdc| c.set_value(cdc, alpha)))?;
|
||||
|
||||
let curve = active_state.curvature.unwrap_or(0.0);
|
||||
let c = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentSlider>("curve_slider")?;
|
||||
panel.component_make_call(c, Box::new(move |c, cdc| c.set_value(cdc, curve)))?;
|
||||
|
||||
let additive = active_state.additive;
|
||||
let c = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentCheckbox>("additive_box")?;
|
||||
panel.component_make_call(c, Box::new(move |c, cdc| c.set_checked(cdc, additive)))?;
|
||||
|
||||
let mut rect = panel
|
||||
.layout
|
||||
.state
|
||||
.widgets
|
||||
.get_as::<WidgetRectangle>(panel.state.rect_id)
|
||||
.unwrap(); // can only fail if set_up_rect has issues
|
||||
|
||||
if active_state.interactable {
|
||||
set_anim_color(
|
||||
&mut rect,
|
||||
0.0,
|
||||
panel.state.rect_color,
|
||||
panel.state.border_color,
|
||||
app.wgui_globals.get().defaults.danger_color,
|
||||
);
|
||||
} else {
|
||||
set_anim_color(
|
||||
&mut rect,
|
||||
0.2,
|
||||
panel.state.rect_color,
|
||||
panel.state.border_color,
|
||||
app.wgui_globals.get().defaults.danger_color,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_anim_color(
|
||||
rect: &mut WidgetRectangle,
|
||||
pos: f32,
|
||||
rect_color: wgui::drawing::Color,
|
||||
border_color: wgui::drawing::Color,
|
||||
target_color: wgui::drawing::Color,
|
||||
) {
|
||||
// rect to target_color
|
||||
rect.params.color.r = rect_color.r.lerp(target_color.r, pos);
|
||||
rect.params.color.g = rect_color.g.lerp(target_color.g, pos);
|
||||
rect.params.color.b = rect_color.b.lerp(target_color.b, pos);
|
||||
|
||||
// border to white
|
||||
rect.params.border_color.r = border_color.r.lerp(1.0, pos);
|
||||
rect.params.border_color.g = border_color.g.lerp(1.0, pos);
|
||||
rect.params.border_color.b = border_color.b.lerp(1.0, pos);
|
||||
rect.params.border_color.a = border_color.a.lerp(1.0, pos);
|
||||
}
|
||||
154
wlx-overlay-s/src/overlays/edit/pos.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use wgui::{
|
||||
components::button::ComponentButton, event::CallbackDataCommon, layout::WidgetID,
|
||||
parser::Fetchable, renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||
widget::sprite::WidgetSprite,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::task::OverlayTask, overlays::edit::EditModeWrapPanel, state::LeftRight,
|
||||
windowing::window::Positioning,
|
||||
};
|
||||
|
||||
static POS_NAMES: [&str; 6] = ["static", "anchored", "floating", "hmd", "hand_l", "hand_r"];
|
||||
|
||||
struct PosButtonState {
|
||||
name: &'static str,
|
||||
sprite: CustomGlyphData,
|
||||
component: Rc<ComponentButton>,
|
||||
positioning: Positioning,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct PositioningHandler {
|
||||
top_sprite_id: WidgetID,
|
||||
buttons: HashMap<&'static str, Rc<PosButtonState>>,
|
||||
active_button: Option<Rc<PosButtonState>>,
|
||||
}
|
||||
|
||||
impl PositioningHandler {
|
||||
pub fn new(panel: &mut EditModeWrapPanel) -> anyhow::Result<Self> {
|
||||
let mut buttons = HashMap::new();
|
||||
|
||||
for name in &POS_NAMES {
|
||||
let button_id = format!("pos_{name}");
|
||||
let component = panel.parser_state.fetch_component_as(&button_id)?;
|
||||
|
||||
let sprite_id = format!("{button_id}_sprite");
|
||||
let id = panel.parser_state.get_widget_id(&sprite_id)?;
|
||||
let sprite_w = panel
|
||||
.layout
|
||||
.state
|
||||
.widgets
|
||||
.get_as::<WidgetSprite>(id)
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("Element with id=\"{sprite_id}\" must be a <sprite>")
|
||||
})?;
|
||||
|
||||
let sprite = sprite_w.params.glyph_data.clone().ok_or_else(|| {
|
||||
anyhow::anyhow!("Element with id=\"{sprite_id}\" must have a valid src!")
|
||||
})?;
|
||||
|
||||
buttons.insert(
|
||||
*name,
|
||||
Rc::new(PosButtonState {
|
||||
component,
|
||||
name,
|
||||
sprite,
|
||||
positioning: key_to_pos(name),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let top_sprite_id = panel.parser_state.get_widget_id("top_pos_sprite")?;
|
||||
Ok(Self {
|
||||
buttons,
|
||||
active_button: None,
|
||||
top_sprite_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn change_highlight(&mut self, common: &mut CallbackDataCommon, key: &str) {
|
||||
if let Some(old) = self.active_button.take() {
|
||||
old.component.set_sticky_state(common, false);
|
||||
}
|
||||
let new = self.buttons.get_mut(key).unwrap();
|
||||
new.component.set_sticky_state(common, true);
|
||||
self.active_button = Some(new.clone());
|
||||
|
||||
// change top sprite
|
||||
if let Some(mut sprite) = common
|
||||
.state
|
||||
.widgets
|
||||
.get_as::<WidgetSprite>(self.top_sprite_id)
|
||||
{
|
||||
sprite.params.glyph_data = Some(new.sprite.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pos_button_clicked(
|
||||
&mut self,
|
||||
common: &mut CallbackDataCommon,
|
||||
key: &str,
|
||||
) -> Box<OverlayTask> {
|
||||
self.change_highlight(common, key);
|
||||
|
||||
let pos = key_to_pos(key);
|
||||
Box::new(move |app, owc| {
|
||||
let state = owc.active_state.as_mut().unwrap(); //want panic
|
||||
state.positioning = pos;
|
||||
state.save_transform(app);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, common: &mut CallbackDataCommon, pos: Positioning) {
|
||||
let key = pos_to_key(pos);
|
||||
self.change_highlight(common, key);
|
||||
}
|
||||
}
|
||||
|
||||
fn key_to_pos(key: &str) -> Positioning {
|
||||
match key {
|
||||
"static" => Positioning::Static,
|
||||
"anchored" => Positioning::Anchored,
|
||||
"floating" => Positioning::Floating,
|
||||
"hmd" => Positioning::FollowHead { lerp: 1.0 },
|
||||
"hand_l" => Positioning::FollowHand {
|
||||
hand: LeftRight::Left,
|
||||
lerp: 1.0,
|
||||
},
|
||||
"hand_r" => Positioning::FollowHand {
|
||||
hand: LeftRight::Right,
|
||||
lerp: 1.0,
|
||||
},
|
||||
_ => {
|
||||
panic!("cannot translate to positioning: {key}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn pos_to_key(pos: Positioning) -> &'static str {
|
||||
match pos {
|
||||
Positioning::Static => "static",
|
||||
Positioning::Anchored => "anchored",
|
||||
Positioning::Floating => "floating",
|
||||
Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => "hmd",
|
||||
Positioning::FollowHand {
|
||||
hand: LeftRight::Left,
|
||||
..
|
||||
}
|
||||
| Positioning::FollowHandPaused {
|
||||
hand: LeftRight::Left,
|
||||
..
|
||||
} => "hand_l",
|
||||
Positioning::FollowHand {
|
||||
hand: LeftRight::Right,
|
||||
..
|
||||
}
|
||||
| Positioning::FollowHandPaused {
|
||||
hand: LeftRight::Right,
|
||||
..
|
||||
} => "hand_r",
|
||||
}
|
||||
}
|
||||
96
wlx-overlay-s/src/overlays/edit/tab.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
event::CallbackDataCommon,
|
||||
layout::WidgetID,
|
||||
parser::Fetchable,
|
||||
taffy::{Display, Style},
|
||||
};
|
||||
|
||||
use crate::overlays::edit::EditModeWrapPanel;
|
||||
|
||||
static TABS: [&str; 4] = ["none", "pos", "alpha", "curve"];
|
||||
static BUTTON_PREFIX: &str = "top_";
|
||||
static PANE_PREFIX: &str = "tab_";
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TabData {
|
||||
button: Option<Rc<ComponentButton>>,
|
||||
pane: WidgetID,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct ButtonPaneTabSwitcher {
|
||||
tabs: HashMap<&'static str, Rc<TabData>>,
|
||||
active_tab: Option<Rc<TabData>>,
|
||||
}
|
||||
|
||||
impl ButtonPaneTabSwitcher {
|
||||
pub fn new(panel: &mut EditModeWrapPanel) -> anyhow::Result<Self> {
|
||||
let mut tabs = HashMap::new();
|
||||
|
||||
for tab_name in &TABS {
|
||||
let name = format!("{BUTTON_PREFIX}{tab_name}");
|
||||
let button = panel.parser_state.fetch_component_as(&name).ok();
|
||||
|
||||
let name = format!("{PANE_PREFIX}{tab_name}");
|
||||
let pane = panel.parser_state.get_widget_id(&name)?;
|
||||
|
||||
tabs.insert(
|
||||
*tab_name,
|
||||
Rc::new(TabData {
|
||||
button: button.clone(),
|
||||
pane,
|
||||
name: tab_name,
|
||||
}),
|
||||
);
|
||||
}
|
||||
Ok(Self {
|
||||
tabs,
|
||||
active_tab: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tab_button_clicked(&mut self, common: &mut CallbackDataCommon, mut tab: &str) {
|
||||
// deactivate active tab
|
||||
if let Some(old_tab) = self.active_tab.take() {
|
||||
set_tab_active(common, &old_tab, false);
|
||||
|
||||
if old_tab.name == tab {
|
||||
// close current tab
|
||||
tab = "none";
|
||||
}
|
||||
}
|
||||
let data = self.tabs[tab].clone();
|
||||
set_tab_active(common, &data, true);
|
||||
self.active_tab = Some(data);
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, common: &mut CallbackDataCommon) {
|
||||
if let Some(data) = self.active_tab.take() {
|
||||
set_tab_active(common, &data, false);
|
||||
}
|
||||
|
||||
let data = self.tabs["none"].clone();
|
||||
set_tab_active(common, &data, true);
|
||||
self.active_tab = Some(data);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_tab_active(common: &mut CallbackDataCommon, data: &TabData, active: bool) {
|
||||
let pane_node = common.state.nodes[data.pane];
|
||||
let style = Style {
|
||||
display: if active {
|
||||
Display::Block
|
||||
} else {
|
||||
Display::None
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
common.alterables.set_style(pane_node, style);
|
||||
if let Some(button) = data.button.as_ref() {
|
||||
button.set_sticky_state(common, active);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod anchor;
|
||||
pub mod bar;
|
||||
pub mod custom;
|
||||
pub mod edit;
|
||||
pub mod keyboard;
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::{
|
||||
},
|
||||
state::AppState,
|
||||
windowing::{
|
||||
Z_ORDER_WATCH,
|
||||
window::{OverlayWindowConfig, OverlayWindowData, OverlayWindowState, Positioning},
|
||||
Z_ORDER_WATCH,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ use crate::{
|
||||
},
|
||||
state::AppState,
|
||||
windowing::{
|
||||
OverlayID, OverlaySelector,
|
||||
set::{OverlayWindowSet, SerializedWindowSet},
|
||||
snap_upright,
|
||||
window::OverlayWindowData,
|
||||
OverlayID, OverlaySelector,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -288,7 +288,7 @@ impl<T> OverlayWindowManager<T> {
|
||||
ws.overlays.clear();
|
||||
for (id, data) in self.overlays.iter_mut().filter(|(_, d)| !d.config.global) {
|
||||
if let Some(mut state) = data.config.active_state.take() {
|
||||
if let Some(transform) = data.config.saved_transform.take() {
|
||||
if let Some(transform) = state.saved_transform {
|
||||
state.transform = transform;
|
||||
} else {
|
||||
state.transform = Affine3A::ZERO;
|
||||
@@ -309,7 +309,7 @@ impl<T> OverlayWindowManager<T> {
|
||||
for (id, data) in self.overlays.iter_mut().filter(|(_, d)| !d.config.global) {
|
||||
if let Some(mut state) = ws.overlays.remove(id) {
|
||||
if state.transform.x_axis.length_squared() > f32::EPSILON {
|
||||
data.config.saved_transform = Some(state.transform);
|
||||
state.saved_transform = Some(state.transform);
|
||||
}
|
||||
state.transform = Affine3A::IDENTITY;
|
||||
log::debug!("{}: ws{} → active_state", data.config.name, new_set);
|
||||
|
||||
@@ -93,7 +93,6 @@ pub struct OverlayWindowConfig {
|
||||
pub dirty: bool,
|
||||
/// True if the window is showing the edit overlay
|
||||
pub editing: bool,
|
||||
pub saved_transform: Option<Affine3A>,
|
||||
}
|
||||
|
||||
impl OverlayWindowConfig {
|
||||
@@ -106,7 +105,6 @@ impl OverlayWindowConfig {
|
||||
..OverlayWindowState::default()
|
||||
},
|
||||
active_state: None,
|
||||
saved_transform: None,
|
||||
z_order: 0,
|
||||
keyboard_focus: None,
|
||||
show_on_spawn: false,
|
||||
@@ -149,7 +147,9 @@ impl OverlayWindowConfig {
|
||||
return;
|
||||
};
|
||||
|
||||
let cur_transform = self.saved_transform.unwrap_or(self.default_state.transform);
|
||||
let cur_transform = state
|
||||
.saved_transform
|
||||
.unwrap_or(self.default_state.transform);
|
||||
|
||||
let (target_transform, lerp) = match state.positioning {
|
||||
Positioning::FollowHead { lerp } => (app.input_state.hmd * cur_transform, lerp),
|
||||
@@ -185,34 +185,14 @@ impl OverlayWindowConfig {
|
||||
}
|
||||
|
||||
/// Returns true if changes were saved.
|
||||
pub fn save_transform(&mut self, app: &mut AppState) -> bool {
|
||||
let Some(state) = self.active_state.as_mut() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let parent_transform = match state.positioning {
|
||||
Positioning::Floating => snap_upright(app.input_state.hmd, Vec3A::Y),
|
||||
Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => {
|
||||
app.input_state.hmd
|
||||
}
|
||||
Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => {
|
||||
app.input_state.pointers[hand as usize].pose
|
||||
}
|
||||
Positioning::Anchored => snap_upright(app.anchor, Vec3A::Y),
|
||||
Positioning::Static => return false,
|
||||
};
|
||||
|
||||
self.saved_transform = Some(parent_transform.inverse() * state.transform);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, app: &mut AppState, hard_reset: bool) {
|
||||
let Some(state) = self.active_state.as_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let cur_transform = self.saved_transform.unwrap_or(self.default_state.transform);
|
||||
let cur_transform = state
|
||||
.saved_transform
|
||||
.unwrap_or(self.default_state.transform);
|
||||
|
||||
let parent_transform = match state.positioning {
|
||||
Positioning::Floating
|
||||
@@ -226,7 +206,7 @@ impl OverlayWindowConfig {
|
||||
};
|
||||
|
||||
if hard_reset {
|
||||
self.saved_transform = None;
|
||||
state.saved_transform = None;
|
||||
}
|
||||
|
||||
state.transform = parent_transform * cur_transform;
|
||||
@@ -294,6 +274,7 @@ pub struct OverlayWindowState {
|
||||
pub positioning: Positioning,
|
||||
pub curvature: Option<f32>,
|
||||
pub additive: bool,
|
||||
pub saved_transform: Option<Affine3A>,
|
||||
}
|
||||
|
||||
impl Default for OverlayWindowState {
|
||||
@@ -306,6 +287,27 @@ impl Default for OverlayWindowState {
|
||||
curvature: None,
|
||||
transform: Affine3A::IDENTITY,
|
||||
additive: false,
|
||||
saved_transform: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlayWindowState {
|
||||
pub fn save_transform(&mut self, app: &mut AppState) -> bool {
|
||||
let parent_transform = match self.positioning {
|
||||
Positioning::Floating => snap_upright(app.input_state.hmd, Vec3A::Y),
|
||||
Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => {
|
||||
app.input_state.hmd
|
||||
}
|
||||
Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => {
|
||||
app.input_state.pointers[hand as usize].pose
|
||||
}
|
||||
Positioning::Anchored => snap_upright(app.anchor, Vec3A::Y),
|
||||
Positioning::Static => return false,
|
||||
};
|
||||
|
||||
self.saved_transform = Some(parent_transform.inverse() * self.transform);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||