opacity & additive & curvature works
This commit is contained in:
@@ -15,11 +15,11 @@ use crate::{
|
||||
util,
|
||||
},
|
||||
widget::{
|
||||
ConstructEssentials, EventResult,
|
||||
div::WidgetDiv,
|
||||
label::{WidgetLabel, WidgetLabelParams},
|
||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
util::WLength,
|
||||
ConstructEssentials, EventResult,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -113,7 +113,8 @@ impl ComponentSlider {
|
||||
pub fn get_value(&self) -> f32 {
|
||||
self.state.borrow().values.value
|
||||
}
|
||||
pub fn set_value(&mut self, common: &mut CallbackDataCommon, value: f32) {
|
||||
|
||||
pub fn set_value(&self, common: &mut CallbackDataCommon, value: f32) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.set_value(common, &self.data, value);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
event::EventAlterables,
|
||||
globals::Globals,
|
||||
layout::Widget,
|
||||
renderer_vk::text::{TextShadow, custom_glyph::CustomGlyph},
|
||||
renderer_vk::text::{custom_glyph::CustomGlyph, TextShadow},
|
||||
stack::{self, ScissorBoundary, ScissorStack, TransformStack},
|
||||
widget::{self, ScrollbarInfo, WidgetState},
|
||||
};
|
||||
@@ -264,7 +264,7 @@ fn draw_widget(
|
||||
state.primitives.push(primitive_debug_rect(
|
||||
&boundary,
|
||||
&state.transform_stack.get().transform,
|
||||
Color::new(0.0, 1.0, 1.0, 0.5),
|
||||
Color::new(0.0, 1.0, 1.0, 0.5 * params.alpha),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ fn draw_widget(
|
||||
state.primitives.push(primitive_debug_rect(
|
||||
&boundary_relative,
|
||||
&state.transform_stack.get().transform,
|
||||
Color::new(1.0, 0.0, 1.0, 1.0),
|
||||
Color::new(1.0, 0.0, 1.0, params.alpha),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,13 @@ use crate::{
|
||||
i18n::I18n,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Defaults {
|
||||
pub dark_mode: bool,
|
||||
pub text_color: drawing::Color,
|
||||
pub button_color: drawing::Color,
|
||||
pub accent_color: drawing::Color,
|
||||
pub danger_color: drawing::Color,
|
||||
}
|
||||
|
||||
impl Default for Defaults {
|
||||
@@ -25,6 +27,7 @@ impl Default for Defaults {
|
||||
text_color: drawing::Color::new(1.0, 1.0, 1.0, 1.0),
|
||||
button_color: drawing::Color::new(1.0, 1.0, 1.0, 0.05),
|
||||
accent_color: drawing::Color::new(0.0, 0.54, 1.0, 1.0),
|
||||
danger_color: drawing::Color::new(0.8, 0.0, 0.0, 1.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,21 @@ use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
use cosmic_text::Buffer;
|
||||
use glam::{Mat4, Vec2, Vec3};
|
||||
use slotmap::{SlotMap, new_key_type};
|
||||
use slotmap::{new_key_type, SlotMap};
|
||||
use vulkano::pipeline::graphics::viewport;
|
||||
|
||||
use crate::{
|
||||
drawing::{self},
|
||||
font_config,
|
||||
gfx::{WGfx, cmd::GfxCommandBuffer},
|
||||
gfx::{cmd::GfxCommandBuffer, WGfx},
|
||||
};
|
||||
|
||||
use super::{
|
||||
rect::{RectPipeline, RectRenderer},
|
||||
text::{
|
||||
DEFAULT_METRICS, SWASH_CACHE, TextArea, TextBounds,
|
||||
text_atlas::{TextAtlas, TextPipeline},
|
||||
text_renderer::TextRenderer,
|
||||
TextArea, TextBounds, DEFAULT_METRICS, SWASH_CACHE,
|
||||
},
|
||||
viewport::Viewport,
|
||||
};
|
||||
|
||||
@@ -7,48 +7,48 @@
|
||||
|
||||
<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}" />
|
||||
<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">
|
||||
<sprite width="48" height="48" src="${src}" />
|
||||
<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">
|
||||
<sprite width="48" height="48" src="${src}" />
|
||||
<sprite width="48" height="48" src="${src}" _release="${release}" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<elements>
|
||||
<div width="100%" height="100%">
|
||||
<rectangle width="100%" height="100%" padding="4" gap="4" align_items="center" justify_content="center" color="#000000DD" flex_direction="row">
|
||||
<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" />
|
||||
<TopButton id="anchor" src="bar/anchor.svg" tooltip="EDIT_MODE.POSITIONING" kind="btn_primary" />
|
||||
<TopButton id="fade" src="bar/fade.svg" tooltip="EDIT_MODE.OPACITY" kind="btn_primary" />
|
||||
<TopButton id="curve" src="bar/curve.svg" tooltip="EDIT_MODE.ADJUST_CURVATURE" kind="btn_primary" />
|
||||
<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" />
|
||||
<TopButtonDanger id="delete" src="bar/delete.svg" tooltip="EDIT_MODE.DELETE" kind="btn_danger" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
|
||||
<div width="8" height="100%" />
|
||||
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" press="::EditToggle" kind="btn_faded" />
|
||||
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" kind="btn_faded" press="::EditToggle" />
|
||||
</div>
|
||||
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.OPACITY" />
|
||||
<Slider width="200" height="16" min_value="5" max_value="100" value="100" />
|
||||
<CheckBox id="additive" translation="EDIT_MODE.BLENDING_ADDITIVE" tooltip="EDIT_MODE.ALPHA_BLEND_MODE" tooltip_side="bottom" />
|
||||
<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 padding="8" gap="8" justify_content="center" align_items="center">
|
||||
<label translation="EDIT_MODE.CURVATURE" />
|
||||
<Slider width="250" height="16" min_value="0" max_value="0.5" value="0.15" step="0.01" />
|
||||
<Slider id="curve_slider" width="250" height="16" min_value="0" max_value="0.5" value="0.15" step="0.01" />
|
||||
</div>
|
||||
</div>
|
||||
</rectangle>
|
||||
@@ -56,4 +56,4 @@
|
||||
</rectangle>
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
</layout>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"BLENDING_ADDITIVE": "Additive",
|
||||
"CURVATURE": "Curvature",
|
||||
"DELETE": "Delete",
|
||||
"HINT_POINT_WINDOW": "Point on a window to change its parameters.\nOnce done, leave edit mode using the button on the right.",
|
||||
"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)",
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::{f32::consts::PI, sync::Arc};
|
||||
use vulkano::image::view::ImageView;
|
||||
use xr::EyeVisibility;
|
||||
|
||||
use super::{CompositionLayer, XrState, helpers, swapchain::WlxSwapchain};
|
||||
use super::{helpers, swapchain::WlxSwapchain, CompositionLayer, XrState};
|
||||
use crate::{
|
||||
backend::openxr::swapchain::{SwapchainOpts, create_swapchain},
|
||||
backend::openxr::swapchain::{create_swapchain, SwapchainOpts},
|
||||
state::AppState,
|
||||
windowing::window::OverlayWindowData,
|
||||
};
|
||||
@@ -74,6 +74,13 @@ impl OverlayWindowData<OpenXrOverlayData> {
|
||||
(major / aspect_ratio, major)
|
||||
};
|
||||
|
||||
let flags = if state.additive {
|
||||
CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA
|
||||
} else {
|
||||
CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA
|
||||
| CompositionLayerFlags::UNPREMULTIPLIED_ALPHA
|
||||
};
|
||||
|
||||
if let Some(curvature) = state.curvature {
|
||||
let radius = scale_x / (2.0 * PI * curvature);
|
||||
let quat = helpers::transform_to_norm_quat(&transform);
|
||||
@@ -83,7 +90,7 @@ impl OverlayWindowData<OpenXrOverlayData> {
|
||||
let angle = 2.0 * (scale_x / (2.0 * radius));
|
||||
|
||||
let cylinder = xr::CompositionLayerCylinderKHR::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.layer_flags(flags)
|
||||
.pose(posef)
|
||||
.sub_image(sub_image)
|
||||
.eye_visibility(EyeVisibility::BOTH)
|
||||
@@ -95,7 +102,7 @@ impl OverlayWindowData<OpenXrOverlayData> {
|
||||
} else {
|
||||
let posef = helpers::transform_to_posef(&transform);
|
||||
let quad = xr::CompositionLayerQuad::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.layer_flags(flags)
|
||||
.pose(posef)
|
||||
.sub_image(sub_image)
|
||||
.eye_visibility(EyeVisibility::BOTH)
|
||||
|
||||
@@ -83,6 +83,10 @@ impl TaskContainer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transfer_from(&mut self, other: &mut TaskContainer) {
|
||||
self.tasks.append(&mut other.tasks);
|
||||
}
|
||||
|
||||
pub fn enqueue(&mut self, task: TaskType) {
|
||||
self.tasks.push(AppTask {
|
||||
not_before: Instant::now(),
|
||||
|
||||
@@ -18,23 +18,22 @@ use crate::backend::{task::TaskType, wayvr::WayVRAction};
|
||||
|
||||
use super::helper::read_label_from_pipe;
|
||||
|
||||
pub const BUTTON_EVENTS: [(&str, EventListenerKind); 2] = [
|
||||
("_press", EventListenerKind::MousePress),
|
||||
("_release", EventListenerKind::MouseRelease),
|
||||
];
|
||||
|
||||
pub(super) fn setup_custom_button<S: 'static>(
|
||||
layout: &mut Layout,
|
||||
attribs: &CustomAttribsInfoOwned,
|
||||
_app: &AppState,
|
||||
) {
|
||||
const EVENTS: [(&str, EventListenerKind); 2] = [
|
||||
("_press", EventListenerKind::MousePress),
|
||||
("_release", EventListenerKind::MouseRelease),
|
||||
];
|
||||
|
||||
for (name, kind) in &EVENTS {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut args = action.split_whitespace();
|
||||
|
||||
let Some(command) = args.next() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
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::{
|
||||
Event as WguiEvent, EventCallback, EventListenerID, EventListenerKind,
|
||||
InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent,
|
||||
MouseMotionEvent, MouseUpEvent, MouseWheelEvent,
|
||||
CallbackDataCommon, Event as WguiEvent, EventAlterables, EventCallback, EventListenerID,
|
||||
EventListenerKind, InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent,
|
||||
MouseLeaveEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent,
|
||||
},
|
||||
gfx::cmd::WGfxClearMode,
|
||||
layout::{Layout, LayoutParams, WidgetID},
|
||||
parser::ParserState,
|
||||
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};
|
||||
|
||||
mod button;
|
||||
pub mod button;
|
||||
mod helper;
|
||||
mod label;
|
||||
|
||||
@@ -57,8 +58,11 @@ pub type OnCustomIdFunc = Box<
|
||||
) -> anyhow::Result<()>,
|
||||
>;
|
||||
|
||||
pub type OnCustomAttribFunc = Box<dyn Fn(&mut Layout, &CustomAttribsInfoOwned, &AppState)>;
|
||||
|
||||
pub struct NewGuiPanelParams {
|
||||
pub on_custom_id: Option<OnCustomIdFunc>, // used only in `new_from_template`
|
||||
pub on_custom_attrib: Option<OnCustomAttribFunc>, // used only in `new_from_template`
|
||||
pub resize_to_parent: bool,
|
||||
pub gui_scale: f32,
|
||||
}
|
||||
@@ -67,6 +71,7 @@ impl Default for NewGuiPanelParams {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
on_custom_id: None,
|
||||
on_custom_attrib: None,
|
||||
resize_to_parent: false,
|
||||
gui_scale: 1.0,
|
||||
}
|
||||
@@ -133,6 +138,10 @@ impl<S: 'static> GuiPanel<S> {
|
||||
{
|
||||
setup_custom_button::<S>(&mut layout, elem, app);
|
||||
}
|
||||
|
||||
if let Some(on_custom_attrib) = ¶ms.on_custom_attrib {
|
||||
on_custom_attrib(&mut layout, elem, app);
|
||||
}
|
||||
}
|
||||
|
||||
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
|
||||
@@ -202,6 +211,22 @@ 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> {
|
||||
@@ -264,7 +289,7 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
||||
globals: &mut globals,
|
||||
layout: &mut self.layout,
|
||||
debug_draw: false,
|
||||
alpha: rdr.alpha,
|
||||
alpha: self.timestep.alpha,
|
||||
})?;
|
||||
self.context.draw(
|
||||
&globals.font_system,
|
||||
|
||||
@@ -1,23 +1,52 @@
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
mem::{self, ManuallyDrop},
|
||||
sync::Arc,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use glam::{UVec2, vec2};
|
||||
use glam::{vec2, FloatExt, UVec2};
|
||||
use slotmap::Key;
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
components::{checkbox::ComponentCheckbox, slider::ComponentSlider},
|
||||
event::EventCallback,
|
||||
layout::{Layout, WidgetID},
|
||||
parser::{CustomAttribsInfoOwned, Fetchable},
|
||||
widget::{rectangle::WidgetRectangle, EventResult},
|
||||
};
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
use crate::{backend::task::TaskType, windowing::OverlaySelector};
|
||||
use crate::{
|
||||
backend::input::HoverResult,
|
||||
gui::panel::{GuiPanel, NewGuiPanelParams},
|
||||
backend::{input::HoverResult, task::TaskContainer},
|
||||
gui::panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams},
|
||||
state::AppState,
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::{
|
||||
backend::{DummyBackend, OverlayBackend, RenderResources, ShouldRender},
|
||||
window::OverlayWindowConfig,
|
||||
window::{OverlayWindowConfig, Positioning},
|
||||
OverlayID,
|
||||
},
|
||||
};
|
||||
|
||||
type EditModeWrapPanel = GuiPanel<Arc<str>>;
|
||||
struct LongPressButtonState {
|
||||
pressed: Instant,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
type EditModeWrapPanel = GuiPanel<EditModeState>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EditWrapperManager {
|
||||
@@ -28,6 +57,7 @@ pub struct EditWrapperManager {
|
||||
impl EditWrapperManager {
|
||||
pub fn wrap_edit_mode(
|
||||
&mut self,
|
||||
id: OverlayID,
|
||||
owc: &mut OverlayWindowConfig,
|
||||
app: &mut AppState,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -50,7 +80,8 @@ impl EditWrapperManager {
|
||||
)?);
|
||||
}
|
||||
let mut panel = panel.unwrap();
|
||||
panel.state = owc.name.clone();
|
||||
panel_new_assignment(&mut panel, id, owc, app)?;
|
||||
|
||||
owc.backend = Box::new(EditModeBackendWrapper {
|
||||
inner: ManuallyDrop::new(inner),
|
||||
panel: ManuallyDrop::new(panel),
|
||||
@@ -102,6 +133,11 @@ impl OverlayBackend for EditModeBackendWrapper {
|
||||
self.panel.resume(app)
|
||||
}
|
||||
fn should_render(&mut self, app: &mut crate::state::AppState) -> anyhow::Result<ShouldRender> {
|
||||
{
|
||||
let mut local_tasks = self.panel.state.tasks.borrow_mut();
|
||||
app.tasks.transfer_from(&mut local_tasks);
|
||||
}
|
||||
|
||||
let i = self.inner.should_render(app)?;
|
||||
|
||||
if !matches!(i, ShouldRender::Unable)
|
||||
@@ -188,15 +224,278 @@ fn make_edit_panel(
|
||||
overlay_resolution.y
|
||||
);
|
||||
|
||||
let panel = GuiPanel::new_from_template(
|
||||
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(),
|
||||
};
|
||||
|
||||
let on_custom_attrib: Box<dyn Fn(&mut Layout, &CustomAttribsInfoOwned, &AppState)> =
|
||||
Box::new(move |layout, attribs, _app| {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut args = action.split_whitespace();
|
||||
let Some(command) = args.next() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let callback: EventCallback<AppState, 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;
|
||||
}),
|
||||
));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeletePress" => Box::new(move |_common, _data, _app, state| {
|
||||
state.delete.pressed = Instant::now();
|
||||
// TODO: animate to light up button after 2s
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeleteRelease" => Box::new(move |_common, _data, app, state| {
|
||||
if state.delete.pressed.elapsed() > Duration::from_secs(2) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
app.tasks.enqueue(TaskType::DropOverlay(OverlaySelector::Id(
|
||||
state.id.borrow().clone(),
|
||||
)));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let id = layout.add_event_listener(attribs.widget_id, *kind, callback);
|
||||
log::debug!("Registered {action} on {:?} as {id:?}", attribs.widget_id);
|
||||
}
|
||||
});
|
||||
|
||||
let mut panel = GuiPanel::new_from_template(
|
||||
app,
|
||||
"gui/edit.xml",
|
||||
"".into(),
|
||||
state,
|
||||
NewGuiPanelParams {
|
||||
on_custom_attrib: Some(on_custom_attrib),
|
||||
resize_to_parent: true,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
set_up_shadow(&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)?;
|
||||
|
||||
Ok(panel)
|
||||
}
|
||||
|
||||
fn cb_assign_alpha(_app: &mut AppState, owc: &mut OverlayWindowConfig, alpha: f32) {
|
||||
owc.dirty = true;
|
||||
owc.active_state.as_mut().unwrap().alpha = alpha;
|
||||
}
|
||||
|
||||
fn cb_assign_curve(_app: &mut AppState, owc: &mut OverlayWindowConfig, curvature: f32) {
|
||||
owc.dirty = true;
|
||||
owc.active_state.as_mut().unwrap().curvature = if curvature < 0.005 {
|
||||
None
|
||||
} else {
|
||||
Some(curvature)
|
||||
};
|
||||
}
|
||||
|
||||
fn cb_assign_additive(_app: &mut AppState, owc: &mut OverlayWindowConfig, additive: bool) {
|
||||
owc.dirty = true;
|
||||
owc.active_state.as_mut().unwrap().additive = additive;
|
||||
}
|
||||
|
||||
fn set_up_slider(
|
||||
panel: &mut EditModeWrapPanel,
|
||||
id: &str,
|
||||
callback: fn(&mut AppState, &mut OverlayWindowConfig, f32),
|
||||
) -> anyhow::Result<()> {
|
||||
let slider = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentSlider>(id)?;
|
||||
let tasks = panel.state.tasks.clone();
|
||||
let overlay_id = panel.state.id.clone();
|
||||
slider.on_value_changed(Box::new(move |_common, e| {
|
||||
let mut tasks = tasks.borrow_mut();
|
||||
let e_value = e.value;
|
||||
|
||||
tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id.borrow().clone()),
|
||||
Box::new(move |app, owc| callback(app, owc, e_value)),
|
||||
));
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_up_checkbox(
|
||||
panel: &mut EditModeWrapPanel,
|
||||
id: &str,
|
||||
callback: fn(&mut AppState, &mut OverlayWindowConfig, bool),
|
||||
) -> anyhow::Result<()> {
|
||||
let checkbox = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentCheckbox>(id)?;
|
||||
let tasks = panel.state.tasks.clone();
|
||||
let overlay_id = panel.state.id.clone();
|
||||
checkbox.on_toggle(Box::new(move |_common, e| {
|
||||
let mut tasks = tasks.borrow_mut();
|
||||
let e_checked = e.checked;
|
||||
|
||||
tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id.borrow().clone()),
|
||||
Box::new(move |app, owc| callback(app, owc, e_checked)),
|
||||
));
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ use crate::{
|
||||
edit::EditWrapperManager,
|
||||
keyboard::builder::create_keyboard,
|
||||
screen::create_screens,
|
||||
watch::{WATCH_NAME, create_watch},
|
||||
watch::{create_watch, WATCH_NAME},
|
||||
},
|
||||
state::AppState,
|
||||
windowing::{
|
||||
OverlayID, OverlaySelector,
|
||||
set::{OverlayWindowSet, SerializedWindowSet},
|
||||
snap_upright,
|
||||
window::OverlayWindowData,
|
||||
OverlayID, OverlaySelector,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -197,13 +197,13 @@ impl<T> OverlayWindowManager<T> {
|
||||
return;
|
||||
};
|
||||
|
||||
if &*overlay.config.name == WATCH_NAME {
|
||||
if !overlay.config.default_state.grabbable {
|
||||
return; // FIXME: not a proper solution
|
||||
}
|
||||
|
||||
if enabled {
|
||||
self.wrappers
|
||||
.wrap_edit_mode(&mut overlay.config, app)
|
||||
.wrap_edit_mode(id, &mut overlay.config, app)
|
||||
.unwrap(); // FIXME: unwrap
|
||||
} else {
|
||||
self.wrappers.unwrap_edit_mode(&mut overlay.config);
|
||||
|
||||
@@ -285,6 +285,7 @@ impl OverlayWindowConfig {
|
||||
|
||||
// Contains the window state for a given set
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct OverlayWindowState {
|
||||
pub transform: Affine3A,
|
||||
pub alpha: f32,
|
||||
@@ -292,6 +293,7 @@ pub struct OverlayWindowState {
|
||||
pub interactable: bool,
|
||||
pub positioning: Positioning,
|
||||
pub curvature: Option<f32>,
|
||||
pub additive: bool,
|
||||
}
|
||||
|
||||
impl Default for OverlayWindowState {
|
||||
@@ -303,6 +305,7 @@ impl Default for OverlayWindowState {
|
||||
positioning: Positioning::Floating,
|
||||
curvature: None,
|
||||
transform: Affine3A::IDENTITY,
|
||||
additive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user