301 lines
7.0 KiB
Rust
301 lines
7.0 KiB
Rust
use std::{
|
|
any::{Any, TypeId},
|
|
cell::RefMut,
|
|
collections::HashSet,
|
|
};
|
|
|
|
use glam::Vec2;
|
|
use slotmap::{DenseSlotMap, new_key_type};
|
|
|
|
use crate::{
|
|
animation::{self, Animation},
|
|
i18n::I18n,
|
|
layout::{LayoutState, LayoutTask, WidgetID},
|
|
stack::{ScissorStack, Transform, TransformStack},
|
|
widget::{EventResult, 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,
|
|
pub device: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct MousePosition {
|
|
pub pos: Vec2,
|
|
pub device: usize,
|
|
}
|
|
|
|
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, /* mouse position */
|
|
pub delta: Vec2, /* wheel delta */
|
|
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.abs_pos.x
|
|
&& pos.x < transform.abs_pos.x + transform.visual_dim.x
|
|
&& pos.y >= transform.abs_pos.y
|
|
&& pos.y < transform.abs_pos.y + transform.visual_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,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum StyleSetRequest {
|
|
Display(taffy::Display),
|
|
Margin(taffy::Rect<taffy::LengthPercentageAuto>),
|
|
Width(taffy::Dimension),
|
|
Height(taffy::Dimension),
|
|
}
|
|
|
|
// alterables which will be dispatched in the next loop iteration phase
|
|
#[derive(Default)]
|
|
pub struct EventAlterables {
|
|
pub dirty_nodes: Vec<taffy::NodeId>,
|
|
pub style_set_requests: Vec<(WidgetID, StyleSetRequest)>,
|
|
pub animations: Vec<animation::Animation>,
|
|
pub widgets_to_tick: HashSet<WidgetID>, // widgets which needs to be ticked in the next `Layout::update()` fn
|
|
pub transform_stack: TransformStack,
|
|
pub scissor_stack: ScissorStack,
|
|
pub tasks: Vec<LayoutTask>,
|
|
pub needs_redraw: bool,
|
|
pub trigger_haptics: bool,
|
|
}
|
|
|
|
// helper functions
|
|
impl EventAlterables {
|
|
pub const fn mark_redraw(&mut self) {
|
|
self.needs_redraw = true;
|
|
}
|
|
|
|
pub fn set_style(&mut self, widget_id: WidgetID, request: StyleSetRequest) {
|
|
self.style_set_requests.push((widget_id, request));
|
|
}
|
|
|
|
pub fn mark_dirty(&mut self, node_id: taffy::NodeId) {
|
|
self.dirty_nodes.push(node_id);
|
|
}
|
|
|
|
pub fn mark_tick(&mut self, widget_id: WidgetID) {
|
|
self.widgets_to_tick.insert(widget_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()
|
|
}
|
|
|
|
// helper function
|
|
pub fn mark_widget_dirty(&mut self, id: WidgetID) {
|
|
if let Some(node_id) = self.state.nodes.get(id) {
|
|
self.alterables.mark_dirty(*node_id);
|
|
}
|
|
self.alterables.mark_redraw();
|
|
}
|
|
}
|
|
|
|
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().abs_pos)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum EventListenerKind {
|
|
MousePress,
|
|
MouseRelease,
|
|
MouseEnter,
|
|
MouseMotion,
|
|
MouseLeave,
|
|
InternalStateChange,
|
|
}
|
|
|
|
pub type EventCallbackInternal = Box<
|
|
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>,
|
|
>;
|
|
|
|
pub type EventCallback<U1, U2> = Box<
|
|
dyn for<'a, 'b, 'c, 'd> Fn(
|
|
&'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: Option<TypeId>,
|
|
tid2: Option<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> {
|
|
let a1: &mut (dyn Any + 'static) = if self.tid1.is_none() { &mut () } else { user_data.0 };
|
|
let a2: &mut (dyn Any + 'static) = if self.tid2.is_none() { &mut () } else { user_data.1 };
|
|
(self.callback)(common, data, a1, a2)
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct EventListenerCollection {
|
|
inner: DenseSlotMap<EventListenerID, EventListener>,
|
|
}
|
|
|
|
impl EventListenerCollection {
|
|
/// Iterates over event handlers with a matching U type
|
|
pub fn iter_filtered<U1: 'static, U2: 'static>(
|
|
&self,
|
|
kind: EventListenerKind,
|
|
) -> impl Iterator<Item = &EventListener> {
|
|
let tid1 = TypeId::of::<U1>();
|
|
let tid2 = TypeId::of::<U2>();
|
|
self
|
|
.inner
|
|
.values()
|
|
.filter(move |p| p.tid1.is_none_or(|a| a == tid1) && p.tid2.is_none_or(|a| a == tid2) && p.kind == kind)
|
|
}
|
|
|
|
pub fn register<U1: 'static, U2: 'static>(
|
|
&mut self,
|
|
kind: EventListenerKind,
|
|
callback: EventCallback<U1, U2>,
|
|
) -> EventListenerID {
|
|
let tid_unit = TypeId::of::<()>();
|
|
|
|
let tid1 = TypeId::of::<U1>();
|
|
let tid2 = TypeId::of::<U2>();
|
|
|
|
let callback_inner: EventCallbackInternal = Box::new(move |common, data, u1_any, u2_any| {
|
|
if let Some(u1) = u1_any.downcast_mut::<U1>()
|
|
&& let Some(u2) = u2_any.downcast_mut::<U2>()
|
|
{
|
|
callback(common, data, u1, u2)
|
|
} else {
|
|
Ok(EventResult::Pass)
|
|
}
|
|
});
|
|
|
|
let new_item = EventListener {
|
|
kind,
|
|
callback: callback_inner,
|
|
tid1: (tid1 != tid_unit).then_some(tid1),
|
|
tid2: (tid2 != tid_unit).then_some(tid2),
|
|
};
|
|
|
|
self.inner.insert(new_item)
|
|
}
|
|
|
|
pub fn remove(&mut self, event_listener_id: EventListenerID) -> Option<EventListener> {
|
|
self.inner.remove(event_listener_id)
|
|
}
|
|
}
|