events refactor

This commit is contained in:
galister
2025-10-12 17:30:30 +09:00
parent fbe1d5b09e
commit 90eed4558f
30 changed files with 420 additions and 616 deletions

View File

@@ -5,7 +5,7 @@ use glam::Vec2;
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
components::button::ComponentButton, components::button::ComponentButton,
event::{CallbackDataCommon, EventAlterables, EventListenerCollection}, event::{CallbackDataCommon, EventAlterables},
globals::WguiGlobals, globals::WguiGlobals,
i18n::Translation, i18n::Translation,
layout::{LayoutParams, RcLayout, WidgetID}, layout::{LayoutParams, RcLayout, WidgetID},
@@ -14,8 +14,8 @@ use wgui::{
}; };
use crate::tab::{ use crate::tab::{
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, settings::TabSettings,
settings::TabSettings, Tab, TabParams, TabType,
}; };
mod assets; mod assets;
@@ -45,16 +45,11 @@ pub enum FrontendTask {
SetTab(TabType), SetTab(TabType),
} }
pub struct FrontendParams<'a> {
pub listeners: &'a mut EventListenerCollection<(), ()>,
}
impl Frontend { impl Frontend {
pub fn new(params: FrontendParams) -> anyhow::Result<(RcFrontend, RcLayout)> { pub fn new() -> anyhow::Result<(RcFrontend, RcLayout)> {
let globals = WguiGlobals::new(Box::new(assets::Asset {}), wgui::globals::Defaults::default())?; let globals = WguiGlobals::new(Box::new(assets::Asset {}), wgui::globals::Defaults::default())?;
let (layout, state) = wgui::parser::new_layout_from_assets( let (layout, state) = wgui::parser::new_layout_from_assets(
params.listeners,
&ParseDocumentParams { &ParseDocumentParams {
globals: globals.clone(), globals: globals.clone(),
path: AssetPath::BuiltIn("gui/dashboard.xml"), path: AssetPath::BuiltIn("gui/dashboard.xml"),
@@ -85,16 +80,9 @@ impl Frontend {
Ok((res, rc_layout)) Ok((res, rc_layout))
} }
pub fn update( pub fn update(&mut self, rc_this: &RcFrontend, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> {
&mut self,
rc_this: &RcFrontend,
listeners: &mut EventListenerCollection<(), ()>,
width: f32,
height: f32,
timestep_alpha: f32,
) -> anyhow::Result<()> {
while let Some(task) = self.tasks.pop_front() { while let Some(task) = self.tasks.pop_front() {
self.process_task(rc_this, task, listeners)?; self.process_task(rc_this, task)?;
} }
self.tick(width, height, timestep_alpha)?; self.tick(width, height, timestep_alpha)?;
@@ -143,24 +131,14 @@ impl Frontend {
self.tasks.push_back(task); self.tasks.push_back(task);
} }
fn process_task( fn process_task(&mut self, rc_this: &RcFrontend, task: FrontendTask) -> anyhow::Result<()> {
&mut self,
rc_this: &RcFrontend,
task: FrontendTask,
listeners: &mut EventListenerCollection<(), ()>,
) -> anyhow::Result<()> {
match task { match task {
FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this, listeners)?, FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this)?,
} }
Ok(()) Ok(())
} }
fn set_tab( fn set_tab(&mut self, tab_type: TabType, rc_this: &RcFrontend) -> anyhow::Result<()> {
&mut self,
tab_type: TabType,
rc_this: &RcFrontend,
listeners: &mut EventListenerCollection<(), ()>,
) -> anyhow::Result<()> {
log::info!("Setting tab to {tab_type:?}"); log::info!("Setting tab to {tab_type:?}");
let mut layout = self.layout.borrow_mut(); let mut layout = self.layout.borrow_mut();
let widget_content = self.state.fetch_widget(&layout.state, "content")?; let widget_content = self.state.fetch_widget(&layout.state, "content")?;
@@ -170,7 +148,6 @@ impl Frontend {
globals: &self.globals, globals: &self.globals,
layout: &mut layout, layout: &mut layout,
parent_id: widget_content.id, parent_id: widget_content.id,
listeners,
frontend: rc_this, frontend: rc_this,
}; };

View File

@@ -41,12 +41,7 @@ impl TabApps {
extra: Default::default(), extra: Default::default(),
}; };
let mut state = wgui::parser::parse_from_assets( let mut state = wgui::parser::parse_from_assets(doc_params, tab_params.layout, tab_params.parent_id)?;
doc_params,
tab_params.layout,
tab_params.listeners,
tab_params.parent_id,
)?;
gtk::init()?; gtk::init()?;
@@ -97,14 +92,7 @@ impl AppList {
template_params.insert(Rc::from("name"), Rc::from(entry.app_name.as_str())); template_params.insert(Rc::from("name"), Rc::from(entry.app_name.as_str()));
let data = parser_state.parse_template( let data = parser_state.parse_template(doc_params, "AppEntry", params.layout, list_parent.id, template_params)?;
doc_params,
"AppEntry",
params.layout,
params.listeners,
list_parent.id,
template_params,
)?;
let button = data.fetch_component_as::<ComponentButton>("button")?; let button = data.fetch_component_as::<ComponentButton>("button")?;
button.on_click(Box::new(move |_common, _evt| { button.on_click(Box::new(move |_common, _evt| {

View File

@@ -25,7 +25,6 @@ impl TabGames {
extra: Default::default(), extra: Default::default(),
}, },
params.layout, params.layout,
params.listeners,
params.parent_id, params.parent_id,
)?; )?;

View File

@@ -43,7 +43,6 @@ impl TabHome {
extra: Default::default(), extra: Default::default(),
}, },
params.layout, params.layout,
params.listeners,
params.parent_id, params.parent_id,
)?; )?;

View File

@@ -2,7 +2,6 @@ use std::rc::Rc;
use wgui::{ use wgui::{
components::button::ComponentButton, components::button::ComponentButton,
event::EventListenerCollection,
globals::WguiGlobals, globals::WguiGlobals,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
}; };
@@ -31,7 +30,6 @@ pub struct TabParams<'a> {
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub parent_id: WidgetID, pub parent_id: WidgetID,
pub frontend: &'a RcFrontend, pub frontend: &'a RcFrontend,
pub listeners: &'a mut EventListenerCollection<(), ()>,
} }
pub trait Tab { pub trait Tab {

View File

@@ -25,7 +25,6 @@ impl TabMonado {
extra: Default::default(), extra: Default::default(),
}, },
params.layout, params.layout,
params.listeners,
params.parent_id, params.parent_id,
)?; )?;

View File

@@ -25,7 +25,6 @@ impl TabProcesses {
extra: Default::default(), extra: Default::default(),
}, },
params.layout, params.layout,
params.listeners,
params.parent_id, params.parent_id,
)?; )?;

View File

@@ -25,7 +25,6 @@ impl TabSettings {
extra: Default::default(), extra: Default::default(),
}, },
params.layout, params.layout,
params.listeners,
params.parent_id, params.parent_id,
)?; )?;

View File

@@ -1,19 +1,19 @@
use crate::{ use crate::{
animation::{Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
components::{self, Component, ComponentBase, ComponentTrait, InitData, tooltip::ComponentTooltip}, components::{tooltip::ComponentTooltip, Component, ComponentBase, ComponentTrait, InitData},
drawing::{self, Boundary, Color}, drawing::{self, Boundary, Color},
event::{CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
i18n::Translation, i18n::Translation,
layout::{LayoutTask, WidgetID, WidgetPair}, layout::{WidgetID, WidgetPair},
renderer_vk::{ renderer_vk::{
text::{FontWeight, TextStyle}, text::{FontWeight, TextStyle},
util::centered_matrix, util::centered_matrix,
}, },
widget::{ widget::{
ConstructEssentials, EventResult, WidgetData,
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
ConstructEssentials, EventResult, WidgetData,
}, },
}; };
use glam::{Mat4, Vec3}; use glam::{Mat4, Vec3};
@@ -141,17 +141,14 @@ fn anim_hover_create(data: Rc<Data>, state: Rc<RefCell<State>>, widget_id: Widge
) )
} }
fn register_event_mouse_enter<U1, U2>( fn register_event_mouse_enter(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.id_rect,
EventListenerKind::MouseEnter, EventListenerKind::MouseEnter,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
common.alterables.mark_redraw(); common.alterables.mark_redraw();
common.alterables.animate(anim_hover_create( common.alterables.animate(anim_hover_create(
@@ -182,20 +179,17 @@ fn register_event_mouse_enter<U1, U2>(
state.hovered = true; state.hovered = true;
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
fn register_event_mouse_leave<U1, U2>( fn register_event_mouse_leave(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.id_rect,
EventListenerKind::MouseLeave, EventListenerKind::MouseLeave,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
common.alterables.animate(anim_hover_create( common.alterables.animate(anim_hover_create(
data.clone(), data.clone(),
@@ -208,20 +202,17 @@ fn register_event_mouse_leave<U1, U2>(
state.hovered = false; state.hovered = false;
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
fn register_event_mouse_press<U1, U2>( fn register_event_mouse_press(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.id_rect,
EventListenerKind::MousePress, EventListenerKind::MousePress,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
@@ -244,20 +235,17 @@ fn register_event_mouse_press<U1, U2>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
}), }),
); )
} }
fn register_event_mouse_release<U1, U2>( fn register_event_mouse_release(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.id_rect,
EventListenerKind::MouseRelease, EventListenerKind::MouseRelease,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
anim_hover( anim_hover(
rect, rect,
@@ -285,13 +273,10 @@ fn register_event_mouse_release<U1, U2>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
}), }),
); )
} }
pub fn construct<U1, U2>( pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentButton>)> {
ess: &mut ConstructEssentials<U1, U2>,
params: Params,
) -> anyhow::Result<(WidgetPair, Rc<ComponentButton>)> {
let globals = ess.layout.state.globals.clone(); let globals = ess.layout.state.globals.clone();
let mut style = params.style; let mut style = params.style;
@@ -384,12 +369,17 @@ pub fn construct<U1, U2>(
tooltip: None, tooltip: None,
})); }));
let mut base = ComponentBase::default(); let base = ComponentBase {
lhandles: {
register_event_mouse_enter(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); let mut widget = ess.layout.state.widgets.get(id_rect).unwrap().state();
register_event_mouse_leave(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); vec![
register_event_mouse_press(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_enter(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_release(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_press(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_release(data.clone(), state.clone(), &mut widget.event_listeners),
]
},
};
let button = Rc::new(ComponentButton { base, data, state }); let button = Rc::new(ComponentButton { base, data, state });

View File

@@ -1,22 +1,22 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use taffy::{ use taffy::{
AlignItems, JustifyContent,
prelude::{length, percent}, prelude::{length, percent},
AlignItems, JustifyContent,
}; };
use crate::{ use crate::{
animation::{Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
components::{Component, ComponentBase, ComponentTrait, InitData}, components::{Component, ComponentBase, ComponentTrait, InitData},
drawing::Color, drawing::Color,
event::{CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerID, EventListenerKind},
i18n::Translation, i18n::Translation,
layout::{self, LayoutState, WidgetID, WidgetPair}, layout::{self, LayoutState, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle}, renderer_vk::text::{FontWeight, TextStyle},
widget::{ widget::{
ConstructEssentials, EventResult,
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
ConstructEssentials, EventResult,
}, },
}; };
@@ -139,17 +139,10 @@ fn anim_hover_out(state: Rc<RefCell<State>>, widget_id: WidgetID) -> Animation {
) )
} }
fn register_event_mouse_enter<U1, U2>( fn register_event_mouse_enter(state: Rc<RefCell<State>>, listeners: &mut EventListenerCollection) -> EventListenerID {
data: &Rc<Data>,
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>,
listener_handles: &mut ListenerHandleVec,
) {
listeners.register( listeners.register(
listener_handles,
data.id_container,
EventListenerKind::MouseEnter, EventListenerKind::MouseEnter,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
common common
.alterables .alterables
@@ -157,20 +150,13 @@ fn register_event_mouse_enter<U1, U2>(
state.borrow_mut().hovered = true; state.borrow_mut().hovered = true;
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
fn register_event_mouse_leave<U1, U2>( fn register_event_mouse_leave(state: Rc<RefCell<State>>, listeners: &mut EventListenerCollection) -> EventListenerID {
data: &Rc<Data>,
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>,
listener_handles: &mut ListenerHandleVec,
) {
listeners.register( listeners.register(
listener_handles,
data.id_container,
EventListenerKind::MouseLeave, EventListenerKind::MouseLeave,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
common common
.alterables .alterables
@@ -178,20 +164,13 @@ fn register_event_mouse_leave<U1, U2>(
state.borrow_mut().hovered = false; state.borrow_mut().hovered = false;
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
fn register_event_mouse_press<U1, U2>( fn register_event_mouse_press(state: Rc<RefCell<State>>, listeners: &mut EventListenerCollection) -> EventListenerID {
data: &Rc<Data>,
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>,
listener_handles: &mut ListenerHandleVec,
) {
listeners.register( listeners.register(
listener_handles,
data.id_container,
EventListenerKind::MousePress, EventListenerKind::MousePress,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
@@ -207,20 +186,17 @@ fn register_event_mouse_press<U1, U2>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
}), }),
); )
} }
fn register_event_mouse_release<U1, U2>( fn register_event_mouse_release(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.id_container,
EventListenerKind::MouseRelease, EventListenerKind::MouseRelease,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
anim_hover(rect, 1.0, false); anim_hover(rect, 1.0, false);
@@ -244,13 +220,10 @@ fn register_event_mouse_release<U1, U2>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
}), }),
); )
} }
pub fn construct<U1, U2>( pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentCheckbox>)> {
ess: &mut ConstructEssentials<U1, U2>,
params: Params,
) -> anyhow::Result<(WidgetPair, Rc<ComponentCheckbox>)> {
let mut style = params.style; let mut style = params.style;
// force-override style // force-override style
@@ -348,12 +321,17 @@ pub fn construct<U1, U2>(
on_toggle: None, on_toggle: None,
})); }));
let mut base = ComponentBase::default(); let base = ComponentBase {
lhandles: {
register_event_mouse_enter(&data, state.clone(), ess.listeners, &mut base.lhandles); let mut widget = ess.layout.state.widgets.get(id_container).unwrap().state();
register_event_mouse_leave(&data, state.clone(), ess.listeners, &mut base.lhandles); vec![
register_event_mouse_press(&data, state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_enter(state.clone(), &mut widget.event_listeners),
register_event_mouse_release(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_leave(state.clone(), &mut widget.event_listeners),
register_event_mouse_press(state.clone(), &mut widget.event_listeners),
register_event_mouse_release(data.clone(), state.clone(), &mut widget.event_listeners),
]
},
};
let checkbox = Rc::new(ComponentCheckbox { base, data, state }); let checkbox = Rc::new(ComponentCheckbox { base, data, state });

View File

@@ -2,7 +2,7 @@ use std::rc::Rc;
use crate::{ use crate::{
any::AnyTrait, any::AnyTrait,
event::{self, CallbackDataCommon}, event::{CallbackDataCommon, EventListenerID},
}; };
pub mod button; pub mod button;
@@ -17,7 +17,7 @@ pub struct InitData<'a> {
// common component data // common component data
#[derive(Default)] #[derive(Default)]
pub struct ComponentBase { pub struct ComponentBase {
lhandles: event::ListenerHandleVec, lhandles: Vec<EventListenerID>,
} }
pub trait ComponentTrait: AnyTrait { pub trait ComponentTrait: AnyTrait {

View File

@@ -7,7 +7,7 @@ use crate::{
animation::{Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
components::{Component, ComponentBase, ComponentTrait, InitData}, components::{Component, ComponentBase, ComponentTrait, InitData},
drawing::{self}, drawing::{self},
event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind},
i18n::Translation, i18n::Translation,
layout::{WidgetID, WidgetPair}, layout::{WidgetID, WidgetPair},
renderer_vk::{ renderer_vk::{
@@ -15,11 +15,11 @@ use crate::{
util, util,
}, },
widget::{ widget::{
ConstructEssentials, EventResult,
div::WidgetDiv, div::WidgetDiv,
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
ConstructEssentials, EventResult,
}, },
}; };
@@ -197,55 +197,46 @@ fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
)); ));
} }
fn register_event_mouse_enter<U1, U2>( fn register_event_mouse_enter(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> event::EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.body,
EventListenerKind::MouseEnter, EventListenerKind::MouseEnter,
Box::new(move |common, _data, _, _| { Box::new(move |common, _data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
state.borrow_mut().hovered = true; state.borrow_mut().hovered = true;
on_enter_anim(common, data.slider_handle_rect_id); on_enter_anim(common, data.slider_handle_rect_id);
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
fn register_event_mouse_leave<U1, U2>( fn register_event_mouse_leave(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> event::EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.body,
EventListenerKind::MouseLeave, EventListenerKind::MouseLeave,
Box::new(move |common, _data, _, _| { Box::new(move |common, _data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
state.borrow_mut().hovered = false; state.borrow_mut().hovered = false;
on_leave_anim(common, data.slider_handle_rect_id); on_leave_anim(common, data.slider_handle_rect_id);
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
fn register_event_mouse_motion<U1, U2>( fn register_event_mouse_motion(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> event::EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.body,
EventListenerKind::MouseMotion, EventListenerKind::MouseMotion,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
if state.dragging { if state.dragging {
@@ -255,20 +246,17 @@ fn register_event_mouse_motion<U1, U2>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
}), }),
); )
} }
fn register_event_mouse_press<U1, U2>( fn register_event_mouse_press(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> event::EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.body,
EventListenerKind::MousePress, EventListenerKind::MousePress,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
@@ -280,20 +268,16 @@ fn register_event_mouse_press<U1, U2>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
}), }),
); )
} }
fn register_event_mouse_release<U1, U2>( fn register_event_mouse_release(
data: &Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection,
listener_handles: &mut ListenerHandleVec, ) -> event::EventListenerID {
) {
listeners.register( listeners.register(
listener_handles,
data.body,
EventListenerKind::MouseRelease, EventListenerKind::MouseRelease,
Box::new(move |common, _data, _, _| { Box::new(move |common, _data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
@@ -304,13 +288,10 @@ fn register_event_mouse_release<U1, U2>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
}), }),
); )
} }
pub fn construct<U1, U2>( pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> {
ess: &mut ConstructEssentials<U1, U2>,
params: Params,
) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> {
let mut style = params.style; let mut style = params.style;
style.position = taffy::Position::Relative; style.position = taffy::Position::Relative;
style.min_size = style.size; style.min_size = style.size;
@@ -410,14 +391,19 @@ pub fn construct<U1, U2>(
let state = Rc::new(RefCell::new(state)); let state = Rc::new(RefCell::new(state));
let mut base = ComponentBase::default(); let base = ComponentBase {
lhandles: {
register_event_mouse_enter(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); let mut widget = ess.layout.state.widgets.get(body_id).unwrap().state();
register_event_mouse_leave(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); vec![
register_event_mouse_motion(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_enter(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_press(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_leave(data.clone(), state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_motion(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_release(&data, state.clone(), ess.listeners, &mut base.lhandles); register_event_mouse_press(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_release(state.clone(), &mut widget.event_listeners),
]
},
};
let slider = Rc::new(ComponentSlider { base, data, state }); let slider = Rc::new(ComponentSlider { base, data, state });

View File

@@ -4,15 +4,15 @@ use taffy::prelude::length;
use crate::{ use crate::{
components::{Component, ComponentBase, ComponentTrait, InitData}, components::{Component, ComponentBase, ComponentTrait, InitData},
drawing::Color, drawing::Color,
event::{EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{EventListenerCollection, EventListenerKind},
i18n::Translation, i18n::Translation,
layout::{LayoutTasks, WidgetID, WidgetPair}, layout::{LayoutTasks, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle}, renderer_vk::text::{FontWeight, TextStyle},
widget::{ widget::{
ConstructEssentials, EventResult,
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
ConstructEssentials, EventResult,
}, },
}; };
@@ -53,44 +53,27 @@ impl ComponentTrait for ComponentTooltip {
impl ComponentTooltip {} impl ComponentTooltip {}
fn register_event_mouse_enter<U1, U2>( fn register_event_mouse_enter(listeners: &mut EventListenerCollection) -> crate::event::EventListenerID {
data: &Rc<Data>,
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>,
listener_handles: &mut ListenerHandleVec,
) {
listeners.register( listeners.register(
listener_handles,
data.id_container,
EventListenerKind::MouseEnter, EventListenerKind::MouseEnter,
Box::new(move |common, event_data, _, _| { Box::new(move |common, _event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
fn register_event_mouse_leave<U1, U2>( fn register_event_mouse_leave(listeners: &mut EventListenerCollection) -> crate::event::EventListenerID {
data: &Rc<Data>,
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection<U1, U2>,
listener_handles: &mut ListenerHandleVec,
) {
listeners.register( listeners.register(
listener_handles,
data.id_container,
EventListenerKind::MouseEnter, EventListenerKind::MouseEnter,
Box::new(move |common, event_data, _, _| { Box::new(move |common, _event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
); )
} }
pub fn construct<U1, U2>( pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTooltip>)> {
ess: &mut ConstructEssentials<U1, U2>,
params: Params,
) -> anyhow::Result<(WidgetPair, Rc<ComponentTooltip>)> {
let style = taffy::Style { let style = taffy::Style {
align_items: Some(taffy::AlignItems::Center), align_items: Some(taffy::AlignItems::Center),
justify_content: Some(taffy::JustifyContent::Center), justify_content: Some(taffy::JustifyContent::Center),
@@ -141,10 +124,15 @@ pub fn construct<U1, U2>(
let state = Rc::new(RefCell::new(State {})); let state = Rc::new(RefCell::new(State {}));
let mut base = ComponentBase::default(); let base = ComponentBase {
lhandles: {
register_event_mouse_enter(&data, state.clone(), ess.listeners, &mut base.lhandles); let mut widget = ess.layout.state.widgets.get(id_container).unwrap().state();
register_event_mouse_leave(&data, state.clone(), ess.listeners, &mut base.lhandles); vec![
register_event_mouse_enter(&mut widget.event_listeners),
register_event_mouse_leave(&mut widget.event_listeners),
]
},
};
let tooltip = Rc::new(ComponentTooltip { let tooltip = Rc::new(ComponentTooltip {
base, base,

View File

@@ -1,11 +1,11 @@
use std::{ use std::{
cell::{Ref, RefCell, RefMut}, any::{Any, TypeId},
cell::RefMut,
collections::HashSet, collections::HashSet,
rc::Rc,
}; };
use glam::Vec2; use glam::Vec2;
use slotmap::SecondaryMap; use slotmap::{new_key_type, DenseSlotMap};
use crate::{ use crate::{
animation::{self, Animation}, animation::{self, Animation},
@@ -192,130 +192,94 @@ pub enum EventListenerKind {
InternalStateChange, InternalStateChange,
} }
pub type EventCallback<U1, U2> = pub type EventCallbackInternal = Box<
Box<dyn Fn(&mut CallbackDataCommon, &mut CallbackData, &mut U1, &mut U2) -> anyhow::Result<EventResult>>; dyn for<'a, 'b, 'c, 'd> Fn(
&'a mut CallbackDataCommon<'b>,
&'a mut CallbackData<'c>,
&'d mut dyn Any,
&'d mut dyn Any,
) -> anyhow::Result<EventResult>,
>;
//for ref-counting pub type EventCallback<U1, U2> = Box<
pub struct ListenerHandle { dyn for<'a, 'b, 'c, 'd> Fn(
needs_gc: Rc<RefCell<bool>>, // this will be set to true on destructor &'a mut CallbackDataCommon<'b>,
&'a mut CallbackData<'c>,
&'d mut U1,
&'d mut U2,
) -> anyhow::Result<EventResult>,
>;
new_key_type! {
pub struct EventListenerID;
}
pub struct EventListener {
kind: EventListenerKind,
callback: EventCallbackInternal,
tid1: TypeId,
tid2: TypeId,
}
impl EventListener {
pub fn call_with<U1: 'static, U2: 'static>(
&self,
common: &mut CallbackDataCommon,
data: &mut CallbackData,
user_data: &mut (&mut U1, &mut U2),
) -> anyhow::Result<EventResult> {
(self.callback)(common, data, user_data.0, user_data.1)
}
} }
#[derive(Default)] #[derive(Default)]
pub struct ListenerHandleVec(Vec<Rc<ListenerHandle>>); pub struct EventListenerCollection {
inner: DenseSlotMap<EventListenerID, EventListener>,
impl ListenerHandleVec {
pub fn push(&mut self, handle: Rc<ListenerHandle>) {
self.0.push(handle);
}
} }
impl Drop for ListenerHandle { impl EventListenerCollection {
fn drop(&mut self) { /// Iterates over event handlers with a matching U type
*self.needs_gc.borrow_mut() = true; pub fn iter_filtered<U1: 'static, U2: 'static>(
}
}
pub struct EventListener<U1, U2> {
pub kind: EventListenerKind,
pub callback: EventCallback<U1, U2>,
pub handle: std::rc::Weak<ListenerHandle>,
}
impl<U1, U2> EventListener<U1, U2> {
pub fn callback_for_kind(
&self, &self,
kind: EventListenerKind, kind: EventListenerKind,
) -> Option<&impl Fn(&mut CallbackDataCommon, &mut CallbackData, &mut U1, &mut U2) -> anyhow::Result<EventResult>> { ) -> impl Iterator<Item = &EventListener> {
if self.kind == kind { Some(&self.callback) } else { None } let tid1 = TypeId::of::<U1>();
let tid2 = TypeId::of::<U2>();
self
.inner
.values()
.filter(move |p| p.tid1 == tid1 && p.tid2 == tid2 && p.kind == kind)
} }
}
#[derive(Default)] pub fn register<U1: 'static, U2: 'static>(
pub struct EventListenerVec<U1, U2>(Vec<EventListener<U1, U2>>);
impl<U1, U2> EventListenerVec<U1, U2> {
pub fn iter(&self) -> impl Iterator<Item = &EventListener<U1, U2>> {
self.0.iter().filter(|p| p.handle.strong_count() > 0)
}
}
pub struct EventListenerCollection<U1, U2> {
pub map: SecondaryMap<WidgetID, EventListenerVec<U1, U2>>,
needs_gc: Rc<RefCell<bool>>,
}
// derive only works if generics also implement Default
impl<U1, U2> Default for EventListenerCollection<U1, U2> {
fn default() -> Self {
Self {
map: SecondaryMap::default(),
needs_gc: Rc::new(RefCell::new(false)),
}
}
}
impl<U1, U2> EventListenerCollection<U1, U2> {
pub fn register(
&mut self, &mut self,
listener_handles: &mut ListenerHandleVec,
widget_id: WidgetID,
kind: EventListenerKind, kind: EventListenerKind,
callback: EventCallback<U1, U2>, callback: EventCallback<U1, U2>,
) { ) -> EventListenerID {
let res = self.add_single(widget_id, kind, callback); let tid1 = TypeId::of::<U1>();
listener_handles.push(res); let tid2 = TypeId::of::<U2>();
}
pub fn add_single( let callback_inner: EventCallbackInternal = Box::new(move |common, data, u1_any, u2_any| {
&mut self, if let Some(u1) = u1_any.downcast_mut::<U1>()
widget_id: WidgetID, && let Some(u2) = u2_any.downcast_mut::<U2>()
kind: EventListenerKind, {
callback: EventCallback<U1, U2>, callback(common, data, u1, u2)
) -> Rc<ListenerHandle> { } else {
let handle = Rc::new(ListenerHandle { Ok(EventResult::Pass)
needs_gc: self.needs_gc.clone(), }
}); });
let new_item = EventListener { let new_item = EventListener {
kind, kind,
callback, callback: callback_inner,
handle: Rc::downgrade(&handle), tid1,
tid2,
}; };
if let Some(vec) = self.map.get_mut(widget_id) {
vec.0.push(new_item);
} else {
self.map.insert(widget_id, EventListenerVec(vec![new_item]));
}
handle self.inner.insert(new_item)
} }
// clean-up expired events pub fn remove(&mut self, event_listener_id: EventListenerID) -> Option<EventListener> {
pub fn gc(&mut self) { self.inner.remove(event_listener_id)
{
let mut needs_gc = self.needs_gc.borrow_mut();
if !*needs_gc {
return;
}
*needs_gc = false;
}
let mut count = 0;
for (_id, vec) in &mut self.map {
vec.0.retain(|listener| {
if listener.handle.strong_count() != 0 {
true
} else {
count += 1;
false
}
});
}
self.map.retain(|_k, v| !v.0.is_empty());
log::debug!("EventListenerCollection: cleaned-up {count} expired events");
} }
} }

View File

@@ -8,14 +8,14 @@ use std::{
use crate::{ use crate::{
animation::Animations, animation::Animations,
components::{Component, InitData}, components::{Component, InitData},
drawing::{self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, push_scissor_stack, push_transform_stack}, drawing::{self, push_scissor_stack, push_transform_stack, Boundary, ANSI_BOLD_CODE, ANSI_RESET_CODE},
event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection}, event::{self, CallbackDataCommon, EventAlterables},
globals::WguiGlobals, globals::WguiGlobals,
widget::{self, EventParams, EventResult, WidgetObj, WidgetState, div::WidgetDiv}, widget::{self, div::WidgetDiv, EventParams, EventResult, WidgetObj, WidgetState},
}; };
use glam::{Vec2, vec2}; use glam::{vec2, Vec2};
use slotmap::{HopSlotMap, SecondaryMap, new_key_type}; use slotmap::{new_key_type, HopSlotMap, SecondaryMap};
use taffy::{NodeId, TaffyTree, TraversePartialTree}; use taffy::{NodeId, TaffyTree, TraversePartialTree};
new_key_type! { new_key_type! {
@@ -113,7 +113,7 @@ pub struct LayoutState {
pub struct ModifyLayoutStateData<'a> { pub struct ModifyLayoutStateData<'a> {
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
// don't uncomment this, todo! // don't uncomment this, todo!
// pub listeners: &'a mut EventListenerCollection<U1, U2>, // pub listeners: &'a mut EventListenerCollection,
} }
pub enum LayoutTask { pub enum LayoutTask {
@@ -310,9 +310,26 @@ impl Layout {
self.components_to_init.push(component); self.components_to_init.push(component);
} }
fn push_event_children<U1, U2>( /// Convenience function to avoid repeated `WidgetID` → `WidgetState` lookups.
pub fn add_event_listener<U1: 'static, U2: 'static>(
&self,
widget_id: WidgetID,
kind: event::EventListenerKind,
callback: event::EventCallback<U1, U2>,
) -> Option<event::EventListenerID> {
Some(
self
.state
.widgets
.get(widget_id)?
.state()
.event_listeners
.register(kind, callback),
)
}
fn push_event_children<U1: 'static, U2: 'static>(
&self, &self,
listeners: &mut EventListenerCollection<U1, U2>,
parent_node_id: taffy::NodeId, parent_node_id: taffy::NodeId,
event: &event::Event, event: &event::Event,
alterables: &mut EventAlterables, alterables: &mut EventAlterables,
@@ -325,7 +342,7 @@ impl Layout {
let mut iter = |idx: usize| -> anyhow::Result<bool> { let mut iter = |idx: usize| -> anyhow::Result<bool> {
let child_id = self.state.tree.get_child_id(parent_node_id, idx); let child_id = self.state.tree.get_child_id(parent_node_id, idx);
let child_result = self.push_event_widget(listeners, child_id, event, alterables, user_data, false)?; let child_result = self.push_event_widget(child_id, event, alterables, user_data, false)?;
if child_result != EventResult::Pass { if child_result != EventResult::Pass {
event_result = child_result; event_result = child_result;
return Ok(true); return Ok(true);
@@ -350,9 +367,8 @@ impl Layout {
Ok(event_result) Ok(event_result)
} }
fn push_event_widget<U1, U2>( fn push_event_widget<U1: 'static, U2: 'static>(
&self, &self,
listeners: &mut EventListenerCollection<U1, U2>,
node_id: taffy::NodeId, node_id: taffy::NodeId,
event: &event::Event, event: &event::Event,
alterables: &mut EventAlterables, alterables: &mut EventAlterables,
@@ -392,7 +408,7 @@ impl Layout {
let reverse_iter = is_root_node; let reverse_iter = is_root_node;
// check children first // check children first
let mut evt_result = self.push_event_children(listeners, node_id, event, alterables, user_data, reverse_iter)?; let mut evt_result = self.push_event_children(node_id, event, alterables, user_data, reverse_iter)?;
if evt_result == EventResult::Pass { if evt_result == EventResult::Pass {
let mut params = EventParams { let mut params = EventParams {
@@ -403,8 +419,7 @@ impl Layout {
style, style,
}; };
let listeners_vec = listeners.map.get(widget_id); let this_evt_result = widget.process_event(widget_id, node_id, event, user_data, &mut params)?;
let this_evt_result = widget.process_event(widget_id, listeners_vec, node_id, event, user_data, &mut params)?;
if this_evt_result != EventResult::Pass { if this_evt_result != EventResult::Pass {
evt_result = this_evt_result; evt_result = this_evt_result;
} }
@@ -436,25 +451,16 @@ impl Layout {
} }
} }
pub fn push_event<U1, U2>( pub fn push_event<U1: 'static, U2: 'static>(
&mut self, &mut self,
listeners: &mut EventListenerCollection<U1, U2>,
event: &event::Event, event: &event::Event,
mut user_data: (&mut U1, &mut U2), user1: &mut U1,
user2: &mut U2,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut alterables = EventAlterables::default(); let mut alterables = EventAlterables::default();
let _event_result = self.push_event_widget( let _event_result =
listeners, self.push_event_widget(self.tree_root_node, event, &mut alterables, &mut (user1, user2), true)?;
self.tree_root_node,
event,
&mut alterables,
&mut user_data,
true,
)?;
self.process_alterables(alterables)?; self.process_alterables(alterables)?;
listeners.gc();
Ok(()) Ok(())
} }

View File

@@ -7,12 +7,12 @@ use crate::{
AttribPair, ParserContext, ParserFile, parse_check_f32, parse_children, process_component, AttribPair, ParserContext, ParserFile, parse_check_f32, parse_children, process_component,
style::{parse_color_opt, parse_round, parse_style, parse_text_style}, style::{parse_color_opt, parse_round, parse_style, parse_text_style},
}, },
widget::{ConstructEssentials, util::WLength}, widget::util::WLength,
}; };
pub fn parse_component_button<'a, U1, U2>( pub fn parse_component_button<'a>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],

View File

@@ -3,11 +3,10 @@ use crate::{
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
parser::{AttribPair, ParserContext, parse_check_f32, parse_check_i32, process_component, style::parse_style}, parser::{AttribPair, ParserContext, parse_check_f32, parse_check_i32, process_component, style::parse_style},
widget::ConstructEssentials,
}; };
pub fn parse_component_checkbox<U1, U2>( pub fn parse_component_checkbox(
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {

View File

@@ -5,8 +5,8 @@ use crate::{
widget::ConstructEssentials, widget::ConstructEssentials,
}; };
pub fn parse_component_slider<U1, U2>( pub fn parse_component_slider(
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
@@ -35,7 +35,6 @@ pub fn parse_component_slider<U1, U2>(
let (widget, component) = slider::construct( let (widget, component) = slider::construct(
&mut ConstructEssentials { &mut ConstructEssentials {
layout: ctx.layout, layout: ctx.layout,
listeners: ctx.listeners,
parent: parent_id, parent: parent_id,
}, },
slider::Params { slider::Params {

View File

@@ -11,7 +11,6 @@ use crate::{
assets::{AssetPath, AssetPathOwned, normalize_path}, assets::{AssetPath, AssetPathOwned, normalize_path},
components::{Component, ComponentWeak}, components::{Component, ComponentWeak},
drawing::{self}, drawing::{self},
event::EventListenerCollection,
globals::WguiGlobals, globals::WguiGlobals,
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair}, layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair},
parser::{ parser::{
@@ -204,12 +203,11 @@ impl ParserState {
/// This function is suitable in cases if you don't want to pollute main parser state with dynamic IDs /// This function is suitable in cases if you don't want to pollute main parser state with dynamic IDs
/// Use `instantiate_template` instead unless you want to handle `components` results yourself. /// Use `instantiate_template` instead unless you want to handle `components` results yourself.
/// Make sure not to drop them if you want to have your listener handles valid /// Make sure not to drop them if you want to have your listener handles valid
pub fn parse_template<U1, U2>( pub fn parse_template(
&mut self, &mut self,
doc_params: &ParseDocumentParams, doc_params: &ParseDocumentParams,
template_name: &str, template_name: &str,
layout: &mut Layout, layout: &mut Layout,
listeners: &mut EventListenerCollection<U1, U2>,
widget_id: WidgetID, widget_id: WidgetID,
template_parameters: HashMap<Rc<str>, Rc<str>>, template_parameters: HashMap<Rc<str>, Rc<str>>,
) -> anyhow::Result<ParserData> { ) -> anyhow::Result<ParserData> {
@@ -219,7 +217,6 @@ impl ParserState {
let mut ctx = ParserContext { let mut ctx = ParserContext {
layout, layout,
listeners,
data_global: &self.data, data_global: &self.data,
data_local: ParserData::default(), data_local: ParserData::default(),
doc_params, doc_params,
@@ -236,23 +233,15 @@ impl ParserState {
} }
/// Instantinate template by saving all the results into the main `ParserState` /// Instantinate template by saving all the results into the main `ParserState`
pub fn instantiate_template<U1, U2>( pub fn instantiate_template(
&mut self, &mut self,
doc_params: &ParseDocumentParams, doc_params: &ParseDocumentParams,
template_name: &str, template_name: &str,
layout: &mut Layout, layout: &mut Layout,
listeners: &mut EventListenerCollection<U1, U2>,
widget_id: WidgetID, widget_id: WidgetID,
template_parameters: HashMap<Rc<str>, Rc<str>>, template_parameters: HashMap<Rc<str>, Rc<str>>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut data_local = self.parse_template( let mut data_local = self.parse_template(doc_params, template_name, layout, widget_id, template_parameters)?;
doc_params,
template_name,
layout,
listeners,
widget_id,
template_parameters,
)?;
self.data.take_results_from(&mut data_local); self.data.take_results_from(&mut data_local);
Ok(()) Ok(())
@@ -295,19 +284,17 @@ struct MacroAttribs {
attribs: HashMap<Rc<str>, Rc<str>>, attribs: HashMap<Rc<str>, Rc<str>>,
} }
struct ParserContext<'a, U1, U2> { struct ParserContext<'a> {
doc_params: &'a ParseDocumentParams<'a>, doc_params: &'a ParseDocumentParams<'a>,
layout: &'a mut Layout, layout: &'a mut Layout,
listeners: &'a mut EventListenerCollection<U1, U2>,
data_global: &'a ParserData, // current parser state at a given moment data_global: &'a ParserData, // current parser state at a given moment
data_local: ParserData, // newly processed items in a given template data_local: ParserData, // newly processed items in a given template
} }
impl<U1, U2> ParserContext<'_, U1, U2> { impl ParserContext<'_> {
const fn get_construct_essentials(&mut self, parent: WidgetID) -> ConstructEssentials<'_, U1, U2> { const fn get_construct_essentials(&mut self, parent: WidgetID) -> ConstructEssentials<'_> {
ConstructEssentials { ConstructEssentials {
layout: self.layout, layout: self.layout,
listeners: self.listeners,
parent, parent,
} }
} }
@@ -509,11 +496,11 @@ where
} }
} }
fn parse_widget_other_internal<U1, U2>( fn parse_widget_other_internal(
template: &Rc<Template>, template: &Rc<Template>,
template_parameters: HashMap<Rc<str>, Rc<str>>, template_parameters: HashMap<Rc<str>, Rc<str>>,
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let template_file = ParserFile { let template_file = ParserFile {
@@ -534,10 +521,10 @@ fn parse_widget_other_internal<U1, U2>(
Ok(()) Ok(())
} }
fn parse_widget_other<U1, U2>( fn parse_widget_other(
xml_tag_name: &str, xml_tag_name: &str,
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -552,9 +539,9 @@ fn parse_widget_other<U1, U2>(
parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id) parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id)
} }
fn parse_tag_include<U1, U2>( fn parse_tag_include(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -591,7 +578,7 @@ fn parse_tag_include<U1, U2>(
Ok(()) Ok(())
} }
fn parse_tag_var<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'a, 'a>) { fn parse_tag_var<'a>(ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>) {
let mut out_key: Option<&str> = None; let mut out_key: Option<&str> = None;
let mut out_value: Option<&str> = None; let mut out_value: Option<&str> = None;
@@ -646,12 +633,7 @@ pub fn replace_vars(input: &str, vars: &HashMap<Rc<str>, Rc<str>>) -> Rc<str> {
} }
#[allow(clippy::manual_strip)] #[allow(clippy::manual_strip)]
fn process_attrib<'a, U1, U2>( fn process_attrib<'a>(file: &'a ParserFile, ctx: &'a ParserContext, key: &str, value: &str) -> AttribPair {
file: &'a ParserFile,
ctx: &'a ParserContext<U1, U2>,
key: &str,
value: &str,
) -> AttribPair {
if value.starts_with('~') { if value.starts_with('~') {
let name = &value[1..]; let name = &value[1..];
@@ -673,9 +655,9 @@ fn raw_attribs<'a>(node: &'a roxmltree::Node<'a, 'a>) -> Vec<AttribPair> {
res res
} }
fn process_attribs<'a, U1, U2>( fn process_attribs<'a>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &'a ParserContext<U1, U2>, ctx: &'a ParserContext,
node: &'a roxmltree::Node<'a, 'a>, node: &'a roxmltree::Node<'a, 'a>,
is_tag_macro: bool, is_tag_macro: bool,
) -> Vec<AttribPair> { ) -> Vec<AttribPair> {
@@ -704,7 +686,7 @@ fn process_attribs<'a, U1, U2>(
res res
} }
fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'a, 'a>) { fn parse_tag_theme<'a>(ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>) {
for child_node in node.children() { for child_node in node.children() {
let child_name = child_node.tag_name().name(); let child_name = child_node.tag_name().name();
match child_name { match child_name {
@@ -719,7 +701,7 @@ fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, node: roxmltree:
} }
} }
fn parse_tag_template<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) { fn parse_tag_template(file: &ParserFile, ctx: &mut ParserContext, node: roxmltree::Node<'_, '_>) {
let mut template_name: Option<Rc<str>> = None; let mut template_name: Option<Rc<str>> = None;
let attribs = process_attribs(file, ctx, &node, false); let attribs = process_attribs(file, ctx, &node, false);
@@ -749,7 +731,7 @@ fn parse_tag_template<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>
); );
} }
fn parse_tag_macro<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) { fn parse_tag_macro(file: &ParserFile, ctx: &mut ParserContext, node: roxmltree::Node<'_, '_>) {
let mut macro_name: Option<Rc<str>> = None; let mut macro_name: Option<Rc<str>> = None;
let attribs = process_attribs(file, ctx, &node, true); let attribs = process_attribs(file, ctx, &node, true);
@@ -776,12 +758,7 @@ fn parse_tag_macro<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, n
ctx.insert_macro_attrib(name, MacroAttribs { attribs: macro_attribs }); ctx.insert_macro_attrib(name, MacroAttribs { attribs: macro_attribs });
} }
fn process_component<U1, U2>( fn process_component(ctx: &mut ParserContext, component: Component, widget_id: WidgetID, attribs: &[AttribPair]) {
ctx: &mut ParserContext<U1, U2>,
component: Component,
widget_id: WidgetID,
attribs: &[AttribPair],
) {
let mut component_id: Option<Rc<str>> = None; let mut component_id: Option<Rc<str>> = None;
for pair in attribs { for pair in attribs {
@@ -797,7 +774,7 @@ fn process_component<U1, U2>(
ctx.insert_component(widget_id, component, component_id); ctx.insert_component(widget_id, component, component_id);
} }
fn parse_widget_universal<U1, U2>(ctx: &mut ParserContext<U1, U2>, widget_id: WidgetID, attribs: &[AttribPair]) { fn parse_widget_universal(ctx: &mut ParserContext, widget_id: WidgetID, attribs: &[AttribPair]) {
for pair in attribs { for pair in attribs {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match pair.attrib.as_ref() { match pair.attrib.as_ref() {
@@ -810,9 +787,9 @@ fn parse_widget_universal<U1, U2>(ctx: &mut ParserContext<U1, U2>, widget_id: Wi
} }
} }
fn parse_child<'a, U1, U2>( fn parse_child<'a>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_node: roxmltree::Node<'a, 'a>, parent_node: roxmltree::Node<'a, 'a>,
child_node: roxmltree::Node<'a, 'a>, child_node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
@@ -893,9 +870,9 @@ fn parse_child<'a, U1, U2>(
Ok(()) Ok(())
} }
fn parse_children<'a, U1, U2>( fn parse_children<'a>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_node: roxmltree::Node<'a, 'a>, parent_node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -906,16 +883,14 @@ fn parse_children<'a, U1, U2>(
Ok(()) Ok(())
} }
fn create_default_context<'a, U1, U2>( fn create_default_context<'a>(
doc_params: &'a ParseDocumentParams, doc_params: &'a ParseDocumentParams,
layout: &'a mut Layout, layout: &'a mut Layout,
listeners: &'a mut EventListenerCollection<U1, U2>,
data_global: &'a ParserData, data_global: &'a ParserData,
) -> ParserContext<'a, U1, U2> { ) -> ParserContext<'a> {
ParserContext { ParserContext {
doc_params, doc_params,
layout, layout,
listeners,
data_local: ParserData::default(), data_local: ParserData::default(),
data_global, data_global,
} }
@@ -1011,14 +986,13 @@ pub struct ParseDocumentParams<'a> {
pub extra: ParseDocumentExtra, // optional field, can be Default-ed pub extra: ParseDocumentExtra, // optional field, can be Default-ed
} }
pub fn parse_from_assets<U1, U2>( pub fn parse_from_assets(
doc_params: &ParseDocumentParams, doc_params: &ParseDocumentParams,
layout: &mut Layout, layout: &mut Layout,
listeners: &mut EventListenerCollection<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<ParserState> { ) -> anyhow::Result<ParserState> {
let parser_data = ParserData::default(); let parser_data = ParserData::default();
let mut ctx = create_default_context(doc_params, layout, listeners, &parser_data); let mut ctx = create_default_context(doc_params, layout, &parser_data);
let (file, node_layout) = get_doc_from_asset_path(&ctx, doc_params.path)?; let (file, node_layout) = get_doc_from_asset_path(&ctx, doc_params.path)?;
parse_document_root(&file, &mut ctx, parent_id, node_layout)?; parse_document_root(&file, &mut ctx, parent_id, node_layout)?;
@@ -1033,19 +1007,18 @@ pub fn parse_from_assets<U1, U2>(
Ok(result) Ok(result)
} }
pub fn new_layout_from_assets<U1, U2>( pub fn new_layout_from_assets(
listeners: &mut EventListenerCollection<U1, U2>,
doc_params: &ParseDocumentParams, doc_params: &ParseDocumentParams,
layout_params: &LayoutParams, layout_params: &LayoutParams,
) -> anyhow::Result<(Layout, ParserState)> { ) -> anyhow::Result<(Layout, ParserState)> {
let mut layout = Layout::new(doc_params.globals.clone(), layout_params)?; let mut layout = Layout::new(doc_params.globals.clone(), layout_params)?;
let widget = layout.content_root_widget; let widget = layout.content_root_widget;
let state = parse_from_assets(doc_params, &mut layout, listeners, widget)?; let state = parse_from_assets(doc_params, &mut layout, widget)?;
Ok((layout, state)) Ok((layout, state))
} }
fn get_doc_from_asset_path<U1, U2>( fn get_doc_from_asset_path(
ctx: &ParserContext<U1, U2>, ctx: &ParserContext,
asset_path: AssetPath, asset_path: AssetPath,
) -> anyhow::Result<(ParserFile, roxmltree::NodeId)> { ) -> anyhow::Result<(ParserFile, roxmltree::NodeId)> {
let data = ctx.layout.state.globals.get_asset(asset_path)?; let data = ctx.layout.state.globals.get_asset(asset_path)?;
@@ -1071,9 +1044,9 @@ fn get_doc_from_asset_path<U1, U2>(
Ok((file, tag_layout.id())) Ok((file, tag_layout.id()))
} }
fn parse_document_root<U1, U2>( fn parse_document_root(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
node_layout: roxmltree::NodeId, node_layout: roxmltree::NodeId,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {

View File

@@ -4,9 +4,9 @@ use crate::{
widget::div::WidgetDiv, widget::div::WidgetDiv,
}; };
pub fn parse_widget_div<'a, U1, U2>( pub fn parse_widget_div<'a>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],

View File

@@ -8,9 +8,9 @@ use crate::{
widget::label::{WidgetLabel, WidgetLabelParams}, widget::label::{WidgetLabel, WidgetLabelParams},
}; };
pub fn parse_widget_label<'a, U1, U2>( pub fn parse_widget_label<'a>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],

View File

@@ -8,9 +8,9 @@ use crate::{
widget::rectangle::{WidgetRectangle, WidgetRectangleParams}, widget::rectangle::{WidgetRectangle, WidgetRectangleParams},
}; };
pub fn parse_widget_rectangle<'a, U1, U2>( pub fn parse_widget_rectangle<'a>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],

View File

@@ -8,9 +8,9 @@ use crate::{
use super::{parse_color_hex, print_invalid_attrib}; use super::{parse_color_hex, print_invalid_attrib};
pub fn parse_widget_sprite<'a, U1, U2>( pub fn parse_widget_sprite<'a>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],

View File

@@ -7,7 +7,8 @@ use crate::{
drawing::{self, PrimitiveExtent}, drawing::{self, PrimitiveExtent},
event::{ event::{
self, CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables, EventListenerCollection, self, CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables, EventListenerCollection,
EventListenerKind, EventListenerVec, MouseWheelEvent, EventListenerKind::{InternalStateChange, MouseEnter, MouseLeave, MouseMotion, MousePress, MouseRelease},
MouseWheelEvent,
}, },
layout::{Layout, LayoutState, WidgetID}, layout::{Layout, LayoutState, WidgetID},
stack::{ScissorStack, TransformStack}, stack::{ScissorStack, TransformStack},
@@ -75,6 +76,7 @@ impl WidgetData {
pub struct WidgetState { pub struct WidgetState {
pub data: WidgetData, pub data: WidgetData,
pub obj: Box<dyn WidgetObj>, pub obj: Box<dyn WidgetObj>,
pub event_listeners: EventListenerCollection,
} }
impl WidgetState { impl WidgetState {
@@ -95,6 +97,7 @@ impl WidgetState {
transform: glam::Mat4::IDENTITY, transform: glam::Mat4::IDENTITY,
}, },
obj, obj,
event_listeners: EventListenerCollection::default(),
} }
} }
} }
@@ -208,26 +211,23 @@ impl dyn WidgetObj {
} }
macro_rules! call_event { macro_rules! call_event {
($self:ident, $listeners:ident, $widget_id:ident, $node_id:ident, $params:ident, $kind:ident, $user_data:expr, $metadata:expr) => { ($self:ident, $widget_id:ident, $node_id:ident, $params:ident, $kind:ident, $u1:ty, $u2:ty, $user_data:expr, $metadata:expr) => {
for listener in $listeners.iter() { for listener in $self.event_listeners.iter_filtered::<$u1, $u2>($kind) {
if let Some(callback) = listener.callback_for_kind(EventListenerKind::$kind) { let mut data = CallbackData {
let mut data = CallbackData { obj: $self.obj.as_mut(),
obj: $self.obj.as_mut(), widget_data: &mut $self.data,
widget_data: &mut $self.data, $widget_id,
$widget_id, $node_id,
$node_id, metadata: $metadata,
metadata: $metadata, };
};
let mut common = CallbackDataCommon { let mut common = CallbackDataCommon {
state: $params.state, state: $params.state,
alterables: $params.alterables, alterables: $params.alterables,
}; };
let result = listener.call_with(&mut common, &mut data, $user_data)?;
let result = callback(&mut common, &mut data, $user_data.0, $user_data.1)?; if result == EventResult::Consumed {
if result == EventResult::Consumed { return Ok(EventResult::Consumed);
return Ok(EventResult::Consumed);
}
} }
} }
}; };
@@ -384,10 +384,9 @@ impl WidgetState {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn process_event<'a, U1, U2>( pub fn process_event<'a, U1: 'static, U2: 'static>(
&mut self, &mut self,
widget_id: WidgetID, widget_id: WidgetID,
listeners: Option<&EventListenerVec<U1, U2>>,
node_id: taffy::NodeId, node_id: taffy::NodeId,
event: &Event, event: &Event,
user_data: &mut (&mut U1, &mut U2), user_data: &mut (&mut U1, &mut U2),
@@ -397,17 +396,15 @@ impl WidgetState {
match &event { match &event {
Event::MouseDown(e) => { Event::MouseDown(e) => {
if hovered if hovered && self.data.set_device_pressed(e.device, true) {
&& self.data.set_device_pressed(e.device, true)
&& let Some(listeners) = &listeners
{
call_event!( call_event!(
self, self,
listeners,
widget_id, widget_id,
node_id, node_id,
params, params,
MousePress, MousePress,
U1,
U2,
user_data, user_data,
CallbackMetadata::MouseButton(event::MouseButton { CallbackMetadata::MouseButton(event::MouseButton {
index: e.index, index: e.index,
@@ -417,16 +414,15 @@ impl WidgetState {
} }
} }
Event::MouseUp(e) => { Event::MouseUp(e) => {
if self.data.set_device_pressed(e.device, false) if self.data.set_device_pressed(e.device, false) {
&& let Some(listeners) = listeners
{
call_event!( call_event!(
self, self,
listeners,
widget_id, widget_id,
node_id, node_id,
params, params,
MouseRelease, MouseRelease,
U1,
U2,
user_data, user_data,
CallbackMetadata::MouseButton(event::MouseButton { CallbackMetadata::MouseButton(event::MouseButton {
index: e.index, index: e.index,
@@ -438,40 +434,41 @@ impl WidgetState {
Event::MouseMotion(e) => { Event::MouseMotion(e) => {
let hover_state_changed = self.data.set_device_hovered(e.device, hovered); let hover_state_changed = self.data.set_device_hovered(e.device, hovered);
if let Some(listeners) = &listeners { if hover_state_changed {
if hover_state_changed { if self.data.is_hovered() {
if self.data.is_hovered() { call_event!(
call_event!( self,
self, widget_id,
listeners, node_id,
widget_id, params,
node_id, MouseEnter,
params, U1,
MouseEnter, U2,
user_data, user_data,
CallbackMetadata::None CallbackMetadata::None
); );
} else { } else {
call_event!( call_event!(
self, self,
listeners, widget_id,
widget_id, node_id,
node_id, params,
params, MouseLeave,
MouseLeave, U1,
user_data, U2,
CallbackMetadata::None user_data,
); CallbackMetadata::None
} );
} }
call_event!( call_event!(
self, self,
listeners,
widget_id, widget_id,
node_id, node_id,
params, params,
MouseMotion, MouseMotion,
U1,
U2,
user_data, user_data,
CallbackMetadata::MousePosition(event::MousePosition { pos: e.pos }) CallbackMetadata::MousePosition(event::MousePosition { pos: e.pos })
); );
@@ -483,42 +480,39 @@ impl WidgetState {
} }
} }
Event::MouseLeave(e) => { Event::MouseLeave(e) => {
if self.data.set_device_hovered(e.device, false) if self.data.set_device_hovered(e.device, false) {
&& let Some(listeners) = &listeners
{
call_event!( call_event!(
self, self,
listeners,
widget_id, widget_id,
node_id, node_id,
params, params,
MouseLeave, MouseLeave,
U1,
U2,
user_data, user_data,
CallbackMetadata::None CallbackMetadata::None
); );
} }
} }
Event::InternalStateChange(e) => { Event::InternalStateChange(e) => {
if let Some(listeners) = &listeners { call_event!(
call_event!( self,
self, widget_id,
listeners, node_id,
widget_id, params,
node_id, InternalStateChange,
params, U1,
InternalStateChange, U2,
user_data, user_data,
CallbackMetadata::Custom(e.metadata) CallbackMetadata::Custom(e.metadata)
); );
}
} }
} }
Ok(EventResult::Pass) Ok(EventResult::Pass)
} }
} }
pub struct ConstructEssentials<'a, U1, U2> { pub struct ConstructEssentials<'a> {
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub listeners: &'a mut EventListenerCollection<U1, U2>,
pub parent: WidgetID, pub parent: WidgetID,
} }

View File

@@ -6,7 +6,6 @@ use taffy::prelude::length;
use crate::{ use crate::{
assets::AssetPath, assets::AssetPath,
components::button::ComponentButton, components::button::ComponentButton,
event::EventListenerCollection,
globals::WguiGlobals, globals::WguiGlobals,
layout::{Layout, LayoutTask, LayoutTasks, WidgetPair}, layout::{Layout, LayoutTask, LayoutTasks, WidgetPair},
parser::{self, Fetchable, ParserState}, parser::{self, Fetchable, ParserState},
@@ -43,7 +42,6 @@ pub struct WguiWindowParams<'a> {
pub position: Vec2, pub position: Vec2,
pub globals: WguiGlobals, pub globals: WguiGlobals,
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub listeners: &'a mut EventListenerCollection<(), ()>,
} }
impl Default for WguiWindow { impl Default for WguiWindow {
@@ -84,7 +82,6 @@ impl WguiWindow {
extra: Default::default(), extra: Default::default(),
}, },
params.layout, params.layout,
params.listeners,
widget.id, widget.id,
)?; )?;

View File

@@ -5,7 +5,8 @@ use std::{
}; };
use wgui::{ use wgui::{
event::{self, EventCallback, EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{self, EventCallback, EventListenerKind},
layout::Layout,
parser::CustomAttribsInfoOwned, parser::CustomAttribsInfoOwned,
widget::EventResult, widget::EventResult,
}; };
@@ -17,10 +18,9 @@ use crate::backend::{task::TaskType, wayvr::WayVRAction};
use super::helper::read_label_from_pipe; use super::helper::read_label_from_pipe;
pub(super) fn setup_custom_button<S>( pub(super) fn setup_custom_button<S: 'static>(
layout: &mut Layout,
attribs: &CustomAttribsInfoOwned, attribs: &CustomAttribsInfoOwned,
listeners: &mut EventListenerCollection<AppState, S>,
listener_handles: &mut ListenerHandleVec,
_app: &AppState, _app: &AppState,
) { ) {
const EVENTS: [(&str, EventListenerKind); 2] = [ const EVENTS: [(&str, EventListenerKind); 2] = [
@@ -69,7 +69,7 @@ pub(super) fn setup_custom_button<S>(
_ => return, _ => return,
}; };
listeners.register(listener_handles, attribs.widget_id, *kind, callback); layout.add_event_listener(attribs.widget_id, *kind, callback);
} }
} }
struct ShellButtonMutableState { struct ShellButtonMutableState {

View File

@@ -12,11 +12,11 @@ use chrono_tz::Tz;
use interprocess::os::unix::fifo_file::create_fifo; use interprocess::os::unix::fifo_file::create_fifo;
use wgui::{ use wgui::{
drawing, drawing,
event::{self, EventCallback, EventListenerCollection, ListenerHandleVec}, event::{self, EventCallback},
i18n::Translation, i18n::Translation,
layout::Layout, layout::Layout,
parser::{CustomAttribsInfoOwned, parse_color_hex}, parser::{parse_color_hex, CustomAttribsInfoOwned},
widget::{EventResult, label::WidgetLabel}, widget::{label::WidgetLabel, EventResult},
}; };
use crate::state::AppState; use crate::state::AppState;
@@ -24,11 +24,9 @@ use crate::state::AppState;
use super::helper::{expand_env_vars, read_label_from_pipe}; use super::helper::{expand_env_vars, read_label_from_pipe};
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub(super) fn setup_custom_label<S>( pub(super) fn setup_custom_label<S: 'static>(
layout: &mut Layout, layout: &mut Layout,
attribs: &CustomAttribsInfoOwned, attribs: &CustomAttribsInfoOwned,
listeners: &mut EventListenerCollection<AppState, S>,
listener_handles: &mut ListenerHandleVec,
app: &AppState, app: &AppState,
) { ) {
let Some(source) = attribs.get_value("_source") else { let Some(source) = attribs.get_value("_source") else {
@@ -51,7 +49,7 @@ pub(super) fn setup_custom_label<S>(
}), }),
carry_over: RefCell::new(None), carry_over: RefCell::new(None),
}; };
Box::new(move |common, data, _app, _| { Box::new(move |common, data, _, _| {
shell_on_tick(&state, common, data); shell_on_tick(&state, common, data);
Ok(EventResult::Pass) Ok(EventResult::Pass)
}) })
@@ -69,7 +67,7 @@ pub(super) fn setup_custom_label<S>(
next_try: Instant::now(), next_try: Instant::now(),
}), }),
}; };
Box::new(move |common, data, _app, _| { Box::new(move |common, data, _, _| {
pipe_on_tick(&state, common, data); pipe_on_tick(&state, common, data);
Ok(EventResult::Pass) Ok(EventResult::Pass)
}) })
@@ -165,7 +163,7 @@ pub(super) fn setup_custom_label<S>(
format: format.into(), format: format.into(),
}; };
Box::new(move |common, data, _app, _| { Box::new(move |common, data, _, _| {
clock_on_tick(&state, common, data); clock_on_tick(&state, common, data);
Ok(EventResult::Pass) Ok(EventResult::Pass)
}) })
@@ -180,8 +178,7 @@ pub(super) fn setup_custom_label<S>(
} }
}; };
listeners.register( layout.add_event_listener(
listener_handles,
attribs.widget_id, attribs.widget_id,
wgui::event::EventListenerKind::InternalStateChange, wgui::event::EventListenerKind::InternalStateChange,
callback, callback,

View File

@@ -1,16 +1,16 @@
use std::{cell::RefCell, rc::Rc, sync::Arc}; use std::{cell::RefCell, rc::Rc, sync::Arc};
use button::setup_custom_button; use button::setup_custom_button;
use glam::{Affine2, Vec2, vec2}; use glam::{vec2, Affine2, Vec2};
use label::setup_custom_label; use label::setup_custom_label;
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView}; use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
drawing, drawing,
event::{ event::{
Event as WguiEvent, EventListenerCollection, InternalStateChangeEvent, ListenerHandleVec, Event as WguiEvent, EventCallback, EventListenerID, EventListenerKind,
MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent, MouseUpEvent, InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent,
MouseWheelEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent,
}, },
layout::{Layout, LayoutParams, WidgetID}, layout::{Layout, LayoutParams, WidgetID},
parser::ParserState, parser::ParserState,
@@ -22,7 +22,7 @@ use crate::{
backend::input::{Haptics, PointerHit, PointerMode}, backend::input::{Haptics, PointerHit, PointerMode},
graphics::{CommandBuffers, ExtentExt}, graphics::{CommandBuffers, ExtentExt},
state::AppState, state::AppState,
windowing::backend::{FrameMeta, OverlayBackend, ShouldRender, ui_transform}, windowing::backend::{ui_transform, FrameMeta, OverlayBackend, ShouldRender},
}; };
use super::{timer::GuiTimer, timestep::Timestep}; use super::{timer::GuiTimer, timestep::Timestep};
@@ -40,35 +40,29 @@ pub struct GuiPanel<S> {
pub layout: Layout, pub layout: Layout,
pub state: S, pub state: S,
pub timers: Vec<GuiTimer>, pub timers: Vec<GuiTimer>,
pub listeners: EventListenerCollection<AppState, S>,
pub listener_handles: ListenerHandleVec,
pub parser_state: ParserState, pub parser_state: ParserState,
interaction_transform: Option<Affine2>, interaction_transform: Option<Affine2>,
context: WguiContext, context: WguiContext,
timestep: Timestep, timestep: Timestep,
} }
pub type OnCustomIdFunc<S> = Box< pub type OnCustomIdFunc = Box<
dyn Fn( dyn Fn(
Rc<str>, Rc<str>,
WidgetID, WidgetID,
&wgui::parser::ParseDocumentParams, &wgui::parser::ParseDocumentParams,
&mut Layout, &mut Layout,
&mut ParserState, &mut ParserState,
&mut EventListenerCollection<AppState, S>,
) -> anyhow::Result<()>, ) -> anyhow::Result<()>,
>; >;
impl<S> GuiPanel<S> { impl<S: 'static> GuiPanel<S> {
pub fn new_from_template( pub fn new_from_template(
app: &mut AppState, app: &mut AppState,
path: &str, path: &str,
state: S, state: S,
on_custom_id: Option<OnCustomIdFunc<S>>, on_custom_id: Option<OnCustomIdFunc>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
let mut listeners = EventListenerCollection::<AppState, S>::default();
let mut listener_handles = ListenerHandleVec::default();
let custom_elems = Rc::new(RefCell::new(vec![])); let custom_elems = Rc::new(RefCell::new(vec![]));
let doc_params = wgui::parser::ParseDocumentParams { let doc_params = wgui::parser::ParseDocumentParams {
@@ -85,11 +79,8 @@ impl<S> GuiPanel<S> {
}, },
}; };
let (mut layout, mut parser_state) = wgui::parser::new_layout_from_assets( let (mut layout, mut parser_state) =
&mut listeners, wgui::parser::new_layout_from_assets(&doc_params, &LayoutParams::default())?;
&doc_params,
&LayoutParams::default(),
)?;
if let Some(on_element_id) = on_custom_id { if let Some(on_element_id) = on_custom_id {
let ids = parser_state.data.ids.clone(); // FIXME: copying all ids? let ids = parser_state.data.ids.clone(); // FIXME: copying all ids?
@@ -101,7 +92,6 @@ impl<S> GuiPanel<S> {
&doc_params, &doc_params,
&mut layout, &mut layout,
&mut parser_state, &mut parser_state,
&mut listeners,
)?; )?;
} }
} }
@@ -113,20 +103,14 @@ impl<S> GuiPanel<S> {
.get_as::<WidgetLabel>(elem.widget_id) .get_as::<WidgetLabel>(elem.widget_id)
.is_some() .is_some()
{ {
setup_custom_label( setup_custom_label::<S>(&mut layout, elem, app);
&mut layout,
elem,
&mut listeners,
&mut listener_handles,
app,
);
} else if layout } else if layout
.state .state
.widgets .widgets
.get_as::<WidgetRectangle>(elem.widget_id) .get_as::<WidgetRectangle>(elem.widget_id)
.is_some() .is_some()
{ {
setup_custom_button(elem, &mut listeners, &mut listener_handles, app); setup_custom_button::<S>(&mut layout, elem, app);
} }
} }
@@ -139,10 +123,8 @@ impl<S> GuiPanel<S> {
context, context,
timestep, timestep,
state, state,
listener_handles,
parser_state, parser_state,
timers: vec![], timers: vec![],
listeners,
interaction_transform: None, interaction_transform: None,
}) })
} }
@@ -159,9 +141,7 @@ impl<S> GuiPanel<S> {
timestep, timestep,
state, state,
parser_state: ParserState::default(), parser_state: ParserState::default(),
listener_handles: ListenerHandleVec::default(),
timers: vec![], timers: vec![],
listeners: EventListenerCollection::default(),
interaction_transform: None, interaction_transform: None,
}) })
} }
@@ -171,16 +151,22 @@ impl<S> GuiPanel<S> {
} }
pub fn push_event(&mut self, app: &mut AppState, event: &WguiEvent) { pub fn push_event(&mut self, app: &mut AppState, event: &WguiEvent) {
if let Err(e) = self if let Err(e) = self.layout.push_event(event, app, &mut self.state) {
.layout
.push_event(&mut self.listeners, event, (app, &mut self.state))
{
log::error!("Failed to push event: {e:?}"); log::error!("Failed to push event: {e:?}");
} }
} }
pub fn add_event_listener(
&mut self,
widget_id: WidgetID,
kind: EventListenerKind,
callback: EventCallback<AppState, S>,
) -> Option<EventListenerID> {
self.layout.add_event_listener(widget_id, kind, callback)
}
} }
impl<S> OverlayBackend for GuiPanel<S> { impl<S: 'static> OverlayBackend for GuiPanel<S> {
fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
if self.layout.content_size.x * self.layout.content_size.y != 0.0 { if self.layout.content_size.x * self.layout.content_size.y != 0.0 {
self.update_layout()?; self.update_layout()?;
@@ -274,13 +260,13 @@ impl<S> OverlayBackend for GuiPanel<S> {
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) { fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) {
self.layout self.layout
.push_event( .push_event(
&mut self.listeners,
&WguiEvent::MouseWheel(MouseWheelEvent { &WguiEvent::MouseWheel(MouseWheelEvent {
shift: vec2(delta_x, delta_y), shift: vec2(delta_x, delta_y),
pos: hit.uv * self.layout.content_size, pos: hit.uv * self.layout.content_size,
device: hit.pointer, device: hit.pointer,
}), }),
(app, &mut self.state), app,
&mut self.state,
) )
.unwrap(); // want panic .unwrap(); // want panic
} }

View File

@@ -1,6 +1,6 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use glam::{Affine3A, Mat4, Quat, Vec2, Vec3, vec2, vec3}; use glam::{vec2, vec3, Affine3A, Mat4, Quat, Vec2, Vec3};
use wgui::{ use wgui::{
animation::{Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
assets::AssetPath, assets::AssetPath,
@@ -11,24 +11,24 @@ use wgui::{
renderer_vk::util, renderer_vk::util,
taffy::{self, prelude::length}, taffy::{self, prelude::length},
widget::{ widget::{
EventResult,
div::WidgetDiv, div::WidgetDiv,
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
EventResult,
}, },
}; };
use crate::{ use crate::{
gui::panel::GuiPanel, gui::panel::GuiPanel,
state::AppState, state::AppState,
subsystem::hid::{ALT, CTRL, META, SHIFT, SUPER, XkbKeymap}, subsystem::hid::{XkbKeymap, ALT, CTRL, META, SHIFT, SUPER},
windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning}, windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning},
}; };
use super::{ use super::{
KEYBOARD_NAME, KeyButtonData, KeyState, KeyboardBackend, KeyboardState, handle_press, handle_press, handle_release,
handle_release,
layout::{self, AltModifier, KeyCapType}, layout::{self, AltModifier, KeyCapType},
KeyButtonData, KeyState, KeyboardBackend, KeyboardState, KEYBOARD_NAME,
}; };
const BACKGROUND_PADDING: f32 = 4.; const BACKGROUND_PADDING: f32 = 4.;
@@ -81,11 +81,8 @@ pub fn create_keyboard(
extra: Default::default(), extra: Default::default(),
}; };
let (_, mut gui_state_key) = wgui::parser::new_layout_from_assets( let (_, mut gui_state_key) =
&mut panel.listeners, wgui::parser::new_layout_from_assets(&parse_doc_params, &LayoutParams::default())?;
&parse_doc_params,
&LayoutParams::default(),
)?;
for row in 0..layout.key_sizes.len() { for row in 0..layout.key_sizes.len() {
let (div, _) = panel.layout.add_child( let (div, _) = panel.layout.add_child(
@@ -167,7 +164,6 @@ pub fn create_keyboard(
&parse_doc_params, &parse_doc_params,
&template_key, &template_key,
&mut panel.layout, &mut panel.layout,
&mut panel.listeners,
div.id, div.id,
params, params,
)?; )?;
@@ -190,8 +186,7 @@ pub fn create_keyboard(
}) })
}; };
panel.listeners.register( panel.add_event_listener(
&mut panel.listener_handles,
widget_id, widget_id,
EventListenerKind::MouseEnter, EventListenerKind::MouseEnter,
Box::new({ Box::new({
@@ -203,8 +198,7 @@ pub fn create_keyboard(
} }
}), }),
); );
panel.listeners.register( panel.add_event_listener(
&mut panel.listener_handles,
widget_id, widget_id,
EventListenerKind::MouseLeave, EventListenerKind::MouseLeave,
Box::new({ Box::new({
@@ -216,8 +210,7 @@ pub fn create_keyboard(
} }
}), }),
); );
panel.listeners.register( panel.add_event_listener(
&mut panel.listener_handles,
widget_id, widget_id,
EventListenerKind::MousePress, EventListenerKind::MousePress,
Box::new({ Box::new({
@@ -233,8 +226,7 @@ pub fn create_keyboard(
} }
}), }),
); );
panel.listeners.register( panel.add_event_listener(
&mut panel.listener_handles,
widget_id, widget_id,
EventListenerKind::MouseRelease, EventListenerKind::MouseRelease,
Box::new({ Box::new({
@@ -249,8 +241,7 @@ pub fn create_keyboard(
); );
if let Some(modifier) = my_modifier { if let Some(modifier) = my_modifier {
panel.listeners.register( panel.add_event_listener(
&mut panel.listener_handles,
widget_id, widget_id,
EventListenerKind::InternalStateChange, EventListenerKind::InternalStateChange,
Box::new({ Box::new({

View File

@@ -23,7 +23,7 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
"gui/watch.xml", "gui/watch.xml",
state, state,
Some(Box::new( Some(Box::new(
move |id, widget, doc_params, layout, parser_state, listeners| { move |id, widget, doc_params, layout, parser_state| {
if &*id != "sets" { if &*id != "sets" {
return Ok(()); return Ok(());
} }
@@ -32,9 +32,7 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new(); let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert("display".into(), (idx + 1).to_string().into()); params.insert("display".into(), (idx + 1).to_string().into());
params.insert("handle".into(), idx.to_string().into()); params.insert("handle".into(), idx.to_string().into());
parser_state.instantiate_template( parser_state.instantiate_template(doc_params, "Set", layout, widget, params)?;
doc_params, "Set", layout, listeners, widget, params,
)?;
} }
Ok(()) Ok(())
}, },