opacity & additive & curvature works
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user