watch controls for edit mode

This commit is contained in:
galister
2025-11-25 23:13:14 +09:00
parent 8bd1097886
commit 3f907180f8
17 changed files with 424 additions and 130 deletions

View File

@@ -34,10 +34,17 @@ use crate::{
mod lock;
mod pos;
mod tab;
pub(crate) mod tab;
struct LongPressButtonState {
pressed: Instant,
pub(super) struct LongPressButtonState {
pub(super) pressed: Instant,
}
impl Default for LongPressButtonState {
fn default() -> Self {
Self {
pressed: Instant::now(),
}
}
}
struct EditModeState {
@@ -264,7 +271,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
Ok(EventResult::Consumed)
}),
"::EditModeDeleteRelease" => Box::new(move |_common, _data, app, state| {
if state.delete.pressed.elapsed() > Duration::from_secs(2) {
if state.delete.pressed.elapsed() < Duration::from_secs(2) {
return Ok(EventResult::Pass);
}
app.tasks.enqueue(TaskType::Overlay(
@@ -296,7 +303,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
panel.state.pos = PositioningHandler::new(&mut panel)?;
panel.state.lock = InteractLockHandler::new(&mut panel)?;
panel.state.tabs = ButtonPaneTabSwitcher::new(&mut panel)?;
panel.state.tabs = ButtonPaneTabSwitcher::new(&mut panel, &["none", "pos", "alpha", "curve"])?;
set_up_checkbox(&mut panel, "additive_box", cb_assign_additive)?;
set_up_slider(&mut panel, "alpha_slider", cb_assign_alpha)?;

View File

@@ -5,9 +5,8 @@ use wgui::{
parser::Fetchable, taffy,
};
use crate::overlays::edit::EditModeWrapPanel;
use crate::gui::panel::GuiPanel;
static TABS: [&str; 4] = ["none", "pos", "alpha", "curve"];
static BUTTON_PREFIX: &str = "top_";
static PANE_PREFIX: &str = "tab_";
@@ -19,16 +18,16 @@ struct TabData {
}
#[derive(Default)]
pub(super) struct ButtonPaneTabSwitcher {
pub struct ButtonPaneTabSwitcher {
tabs: HashMap<&'static str, Rc<TabData>>,
active_tab: Option<Rc<TabData>>,
}
impl ButtonPaneTabSwitcher {
pub fn new(panel: &mut EditModeWrapPanel) -> anyhow::Result<Self> {
pub fn new<S>(panel: &mut GuiPanel<S>, tab_names: &[&'static str]) -> anyhow::Result<Self> {
let mut tabs = HashMap::new();
for tab_name in &TABS {
for tab_name in tab_names {
let name = format!("{BUTTON_PREFIX}{tab_name}");
let button = panel.parser_state.fetch_component_as(&name).ok();

View File

@@ -1,23 +1,34 @@
use std::{collections::HashMap, rc::Rc, time::Duration};
use std::{
collections::HashMap,
rc::Rc,
time::{Duration, Instant},
};
use glam::{Affine3A, Vec3, Vec3A};
use wgui::{
components::button::ComponentButton,
event::{CallbackDataCommon, EventAlterables},
event::{CallbackDataCommon, EventAlterables, EventCallback},
i18n::Translation,
layout::WidgetID,
parser::Fetchable,
taffy,
widget::EventResult,
};
use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::{
backend::task::{ManagerTask, TaskType},
gui::{
panel::{GuiPanel, NewGuiPanelParams},
panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc},
timer::GuiTimer,
},
overlays::edit::LongPressButtonState,
state::AppState,
windowing::{
Z_ORDER_WATCH,
backend::OverlayEventData,
manager::MAX_OVERLAY_SETS,
window::{OverlayWindowConfig, OverlayWindowData},
OverlaySelector, Z_ORDER_WATCH,
},
};
@@ -27,11 +38,78 @@ pub const WATCH_NAME: &str = "watch";
struct WatchState {
current_set: Option<usize>,
set_buttons: Vec<Rc<ComponentButton>>,
screen_buttons: Vec<Rc<ComponentButton>>,
edit_mode_widgets: Vec<(WidgetID, bool)>,
edit_add_widget: WidgetID,
num_sets: usize,
delete: LongPressButtonState,
}
#[allow(clippy::significant_drop_tightening)]
pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<OverlayWindowConfig> {
pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
let state = WatchState::default();
let on_custom_attrib: OnCustomAttribFunc = 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, WatchState> = match command {
"::EditModeDeleteDown" => Box::new(move |_common, _data, _app, state| {
state.delete.pressed = Instant::now();
Ok(EventResult::Consumed)
}),
"::EditModeDeleteUp" => Box::new(move |_common, _data, app, state| {
if state.delete.pressed.elapsed() < Duration::from_secs(2) {
return Ok(EventResult::Consumed);
}
app.tasks
.enqueue(TaskType::Manager(ManagerTask::DeleteActiveSet));
Ok(EventResult::Consumed)
}),
"::EditModeAddSet" => Box::new(move |_common, _data, app, _state| {
app.tasks.enqueue(TaskType::Manager(ManagerTask::AddSet));
Ok(EventResult::Consumed)
}),
"::EditModeScreenToggle" => {
let arg = args.next().unwrap_or_default();
let Ok(idx) = arg.parse::<usize>() else {
log::error!("{command} has invalid argument: \"{arg}\"");
return;
};
Box::new(move |_common, _data, app, _state| {
let Some(screen) = app.screens.get(idx) else {
log::error!("No screen at index {idx}.");
return Ok(EventResult::Consumed);
};
app.tasks.enqueue(TaskType::Overlay(
OverlaySelector::Name(screen.name.clone()),
Box::new(move |app, owc| {
if owc.active_state.is_none() {
owc.activate(app);
} else {
owc.deactivate();
}
}),
));
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/watch.xml",
@@ -39,37 +117,56 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
NewGuiPanelParams {
on_custom_id: Some(Box::new(
move |id, widget, doc_params, layout, parser_state, state| {
if &*id != "sets" {
return Ok(());
}
if id.starts_with("norm_") {
state.edit_mode_widgets.push((widget, false));
} else if &*id == "edit_add" {
state.edit_add_widget = widget;
} else if id.starts_with("edit_") {
state.edit_mode_widgets.push((widget, true));
} else if &*id == "sets" {
for idx in 0..MAX_OVERLAY_SETS {
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert("display".into(), (idx + 1).to_string().into());
params.insert("handle".into(), idx.to_string().into());
parser_state
.instantiate_template(doc_params, "Set", layout, widget, params)?;
for idx in 0..num_sets {
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert("display".into(), (idx + 1).to_string().into());
params.insert("handle".into(), idx.to_string().into());
parser_state
.instantiate_template(doc_params, "Set", layout, widget, params)?;
let button_id = format!("set_{idx}");
let component =
parser_state.fetch_component_as::<ComponentButton>(&button_id)?;
state.set_buttons.push(component);
}
} else if &*id == "toolbox" {
for idx in 0..9 {
let screen_id = format!("screen_{idx}");
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert("idx".into(), idx.to_string().into());
parser_state.instantiate_template(
doc_params, "Screen", layout, widget, params,
)?;
let button_id = format!("set_{idx}");
let component =
parser_state.fetch_component_as::<ComponentButton>(&button_id)?;
state.set_buttons.push(component);
let component =
parser_state.fetch_component_as::<ComponentButton>(&screen_id)?;
state.screen_buttons.push(component);
}
}
Ok(())
},
)),
on_custom_attrib: Some(on_custom_attrib),
..Default::default()
},
)?;
panel.on_notify = Some(Box::new(|panel, _app, event_data| {
panel.on_notify = Some(Box::new(|panel, app, event_data| {
let mut alterables = EventAlterables::default();
let mut common = CallbackDataCommon {
alterables: &mut alterables,
state: &panel.layout.state,
};
match event_data {
OverlayEventData::SetChanged(current_set) => {
let mut alterables = EventAlterables::default();
let mut common = CallbackDataCommon {
alterables: &mut alterables,
state: &panel.layout.state,
};
OverlayEventData::ActiveSetChanged(current_set) => {
if let Some(old_set) = panel.state.current_set.take() {
panel.state.set_buttons[old_set].set_sticky_state(&mut common, false);
}
@@ -77,9 +174,64 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
panel.state.set_buttons[new_set].set_sticky_state(&mut common, true);
}
panel.state.current_set = current_set;
panel.layout.process_alterables(alterables)?;
}
OverlayEventData::NumSetsChanged(num_sets) => {
panel.state.num_sets = num_sets;
for i in 0..MAX_OVERLAY_SETS {
let comp = panel.state.set_buttons[i].clone();
let rect_id = comp.get_rect();
let display = if i < num_sets {
taffy::Display::Flex
} else {
taffy::Display::None
};
panel.widget_set_display(rect_id, display, &mut common.alterables);
}
let display = if num_sets < 7 {
taffy::Display::Flex
} else {
taffy::Display::None
};
panel.widget_set_display(
panel.state.edit_add_widget,
display,
&mut common.alterables,
);
}
OverlayEventData::EditModeChanged(edit_mode) => {
for (w, e) in panel.state.edit_mode_widgets.iter() {
let display = if *e == edit_mode {
taffy::Display::Flex
} else {
taffy::Display::None
};
panel.widget_set_display(*w, display, &mut common.alterables);
}
let display = if edit_mode && panel.state.num_sets < 7 {
taffy::Display::Flex
} else {
taffy::Display::None
};
panel.widget_set_display(
panel.state.edit_add_widget,
display,
&mut common.alterables,
);
}
OverlayEventData::ScreensChanged => {
for (idx, btn) in panel.state.screen_buttons.iter().enumerate() {
let display = if let Some(screen) = app.screens.get(idx) {
btn.set_text(&mut common, Translation::from_raw_text(&screen.name));
taffy::Display::Flex
} else {
taffy::Display::None
};
panel.widget_set_display(btn.get_rect(), display, &mut common.alterables);
}
}
}
panel.layout.process_alterables(alterables)?;
Ok(())
}));