319 lines
6.6 KiB
Rust
319 lines
6.6 KiB
Rust
use std::{
|
|
cell::{RefCell, RefMut},
|
|
rc::Rc,
|
|
};
|
|
|
|
use glam::Vec2;
|
|
use slotmap::SecondaryMap;
|
|
|
|
use crate::{
|
|
animation::{self, Animation},
|
|
i18n::I18n,
|
|
layout::{LayoutState, WidgetID},
|
|
transform_stack::{Transform, TransformStack},
|
|
widget::{WidgetData, WidgetObj},
|
|
};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum MouseButtonIndex {
|
|
Left,
|
|
Right,
|
|
Middle,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct MouseButton {
|
|
pub index: MouseButtonIndex,
|
|
pub pos: Vec2,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct MousePosition {
|
|
pub pos: Vec2,
|
|
}
|
|
|
|
pub struct MouseDownEvent {
|
|
pub pos: Vec2,
|
|
pub index: MouseButtonIndex,
|
|
pub device: usize,
|
|
}
|
|
|
|
pub struct MouseLeaveEvent {
|
|
pub device: usize,
|
|
}
|
|
|
|
pub struct MouseMotionEvent {
|
|
pub pos: Vec2,
|
|
pub device: usize,
|
|
}
|
|
|
|
pub struct MouseUpEvent {
|
|
pub pos: Vec2,
|
|
pub index: MouseButtonIndex,
|
|
pub device: usize,
|
|
}
|
|
|
|
pub struct MouseWheelEvent {
|
|
pub pos: Vec2,
|
|
pub shift: Vec2,
|
|
pub device: usize,
|
|
}
|
|
|
|
pub struct InternalStateChangeEvent {
|
|
pub metadata: usize,
|
|
}
|
|
|
|
pub enum Event {
|
|
InternalStateChange(InternalStateChangeEvent),
|
|
MouseDown(MouseDownEvent),
|
|
MouseLeave(MouseLeaveEvent),
|
|
MouseMotion(MouseMotionEvent),
|
|
MouseUp(MouseUpEvent),
|
|
MouseWheel(MouseWheelEvent),
|
|
}
|
|
|
|
impl Event {
|
|
fn test_transform_pos(transform: &Transform, pos: Vec2) -> bool {
|
|
pos.x >= transform.pos.x
|
|
&& pos.x < transform.pos.x + transform.dim.x
|
|
&& pos.y >= transform.pos.y
|
|
&& pos.y < transform.pos.y + transform.dim.y
|
|
}
|
|
|
|
|
|
pub fn test_mouse_within_transform(&self, transform: &Transform) -> bool {
|
|
match self {
|
|
Self::MouseDown(evt) => Self::test_transform_pos(transform, evt.pos),
|
|
Self::MouseMotion(evt) => Self::test_transform_pos(transform, evt.pos),
|
|
Self::MouseUp(evt) => Self::test_transform_pos(transform, evt.pos),
|
|
Self::MouseWheel(evt) => Self::test_transform_pos(transform, evt.pos),
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct EventAlterables {
|
|
pub dirty_nodes: Vec<taffy::NodeId>,
|
|
pub style_set_requests: Vec<(taffy::NodeId, taffy::Style)>,
|
|
pub animations: Vec<animation::Animation>,
|
|
pub transform_stack: TransformStack,
|
|
pub needs_redraw: bool,
|
|
pub trigger_haptics: bool,
|
|
}
|
|
|
|
impl EventAlterables {
|
|
pub const fn mark_redraw(&mut self) {
|
|
self.needs_redraw = true;
|
|
}
|
|
|
|
pub fn set_style(&mut self, node_id: taffy::NodeId, style: taffy::Style) {
|
|
self.style_set_requests.push((node_id, style));
|
|
}
|
|
|
|
pub fn mark_dirty(&mut self, node_id: taffy::NodeId) {
|
|
self.dirty_nodes.push(node_id);
|
|
}
|
|
|
|
pub const fn trigger_haptics(&mut self) {
|
|
self.trigger_haptics = true;
|
|
}
|
|
|
|
pub fn animate(&mut self, animation: Animation) {
|
|
self.animations.push(animation);
|
|
}
|
|
}
|
|
|
|
pub struct CallbackDataCommon<'a> {
|
|
pub state: &'a LayoutState,
|
|
pub alterables: &'a mut EventAlterables,
|
|
}
|
|
|
|
impl CallbackDataCommon<'_> {
|
|
|
|
pub fn i18n(&self) -> RefMut<I18n> {
|
|
self.state.globals.i18n()
|
|
}
|
|
}
|
|
|
|
pub struct CallbackData<'a> {
|
|
pub obj: &'a mut dyn WidgetObj,
|
|
pub widget_data: &'a mut WidgetData,
|
|
pub widget_id: WidgetID,
|
|
pub node_id: taffy::NodeId,
|
|
pub metadata: CallbackMetadata,
|
|
}
|
|
|
|
pub enum CallbackMetadata {
|
|
None,
|
|
MouseButton(MouseButton),
|
|
MousePosition(MousePosition),
|
|
Custom(usize),
|
|
}
|
|
|
|
impl CallbackMetadata {
|
|
// helper function
|
|
|
|
pub const fn get_mouse_pos_absolute(&self) -> Option<Vec2> {
|
|
match *self {
|
|
Self::MouseButton(b) => Some(b.pos),
|
|
Self::MousePosition(b) => Some(b.pos),
|
|
Self::Custom(_) | Self::None => None,
|
|
}
|
|
}
|
|
|
|
|
|
pub fn get_mouse_pos_relative(&self, transform_stack: &TransformStack) -> Option<Vec2> {
|
|
let mouse_pos_abs = self.get_mouse_pos_absolute()?;
|
|
Some(mouse_pos_abs - transform_stack.get_pos())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
pub enum EventListenerKind {
|
|
MousePress,
|
|
MouseRelease,
|
|
MouseEnter,
|
|
MouseMotion,
|
|
MouseLeave,
|
|
InternalStateChange,
|
|
}
|
|
|
|
pub type EventCallback<U1, U2> =
|
|
Box<dyn Fn(&mut CallbackDataCommon, &mut CallbackData, &mut U1, &mut U2) -> anyhow::Result<()>>;
|
|
|
|
//for ref-counting
|
|
pub struct ListenerHandle {
|
|
needs_gc: Rc<RefCell<bool>>, // this will be set to true on destructor
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct ListenerHandleVec(Vec<Rc<ListenerHandle>>);
|
|
|
|
impl ListenerHandleVec {
|
|
pub fn push(&mut self, handle: Rc<ListenerHandle>) {
|
|
self.0.push(handle);
|
|
}
|
|
}
|
|
|
|
impl Drop for ListenerHandle {
|
|
fn drop(&mut self) {
|
|
*self.needs_gc.borrow_mut() = true;
|
|
}
|
|
}
|
|
|
|
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,
|
|
kind: EventListenerKind,
|
|
) -> Option<
|
|
&impl Fn(&mut CallbackDataCommon, &mut CallbackData, &mut U1, &mut U2) -> anyhow::Result<()>,
|
|
> {
|
|
if self.kind == kind {
|
|
Some(&self.callback)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
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> {
|
|
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,
|
|
listener_handles: &mut ListenerHandleVec,
|
|
widget_id: WidgetID,
|
|
kind: EventListenerKind,
|
|
callback: EventCallback<U1, U2>,
|
|
) {
|
|
let res = self.add_single(widget_id, kind, callback);
|
|
listener_handles.push(res);
|
|
}
|
|
|
|
pub fn add_single(
|
|
&mut self,
|
|
widget_id: WidgetID,
|
|
kind: EventListenerKind,
|
|
callback: EventCallback<U1, U2>,
|
|
) -> Rc<ListenerHandle> {
|
|
let handle = Rc::new(ListenerHandle {
|
|
needs_gc: self.needs_gc.clone(),
|
|
});
|
|
|
|
let new_item = EventListener {
|
|
kind,
|
|
callback,
|
|
handle: Rc::downgrade(&handle),
|
|
};
|
|
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
|
|
}
|
|
|
|
// clean-up expired events
|
|
pub fn gc(&mut self) {
|
|
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");
|
|
}
|
|
|
|
|
|
pub fn get(&self, widget_id: WidgetID) -> Option<&EventListenerVec<U1, U2>> {
|
|
self.map.get(widget_id)
|
|
}
|
|
}
|