2022-02-05 22:28:19 +00:00
use crossterm ::event ::{
Event as TermEvent , KeyCode as TermKeyCode , KeyModifiers , MouseButton , MouseEventKind ,
} ;
2022-03-09 18:30:44 +00:00
use dioxus_core ::* ;
2022-03-27 01:10:15 +00:00
use fxhash ::{ FxHashMap , FxHashSet } ;
2022-02-05 22:28:19 +00:00
use dioxus_html ::{ on ::* , KeyCode } ;
2022-01-12 14:40:36 +00:00
use std ::{
2022-02-05 22:28:19 +00:00
any ::Any ,
cell ::RefCell ,
2022-01-12 14:40:36 +00:00
rc ::Rc ,
2022-02-05 22:28:19 +00:00
sync ::Arc ,
time ::{ Duration , Instant } ,
2022-01-12 14:40:36 +00:00
} ;
2022-06-10 22:23:30 +00:00
use taffy ::{ prelude ::Layout , Taffy } ;
2022-02-07 11:57:57 +00:00
2022-04-12 23:46:16 +00:00
use crate ::{ Dom , Node } ;
2022-01-12 14:40:36 +00:00
2022-02-05 22:28:19 +00:00
// a wrapper around the input state for easier access
// todo: fix loop
// pub struct InputState(Rc<Rc<RefCell<InnerInputState>>>);
// impl InputState {
// pub fn get(cx: &ScopeState) -> InputState {
// let inner = cx
// .consume_context::<Rc<RefCell<InnerInputState>>>()
// .expect("Rink InputState can only be used in Rink apps!");
// (**inner).borrow_mut().subscribe(cx.schedule_update());
// InputState(inner)
// }
// pub fn mouse(&self) -> Option<MouseData> {
// let data = (**self.0).borrow();
2022-04-17 12:29:35 +00:00
// data.mouse.as_ref().map(|m| m.clone())
2022-02-05 22:28:19 +00:00
// }
// pub fn wheel(&self) -> Option<WheelData> {
// let data = (**self.0).borrow();
2022-04-17 12:29:35 +00:00
// data.wheel.as_ref().map(|w| w.clone())
2022-02-05 22:28:19 +00:00
// }
// pub fn screen(&self) -> Option<(u16, u16)> {
// let data = (**self.0).borrow();
// data.screen.as_ref().map(|m| m.clone())
// }
// pub fn last_key_pressed(&self) -> Option<KeyboardData> {
// let data = (**self.0).borrow();
// data.last_key_pressed
// .as_ref()
2022-04-17 12:29:35 +00:00
// .map(|k| &k.0.clone())
2022-02-05 22:28:19 +00:00
// }
// }
type EventCore = ( & 'static str , EventData ) ;
#[ derive(Debug) ]
enum EventData {
Mouse ( MouseData ) ,
Wheel ( WheelData ) ,
Screen ( ( u16 , u16 ) ) ,
Keyboard ( KeyboardData ) ,
}
impl EventData {
fn into_any ( self ) -> Arc < dyn Any + Send + Sync > {
match self {
Self ::Mouse ( m ) = > Arc ::new ( m ) ,
Self ::Wheel ( w ) = > Arc ::new ( w ) ,
Self ::Screen ( s ) = > Arc ::new ( s ) ,
Self ::Keyboard ( k ) = > Arc ::new ( k ) ,
}
}
2022-01-12 14:40:36 +00:00
}
2022-02-05 22:28:19 +00:00
const MAX_REPEAT_TIME : Duration = Duration ::from_millis ( 100 ) ;
2022-02-04 20:52:01 +00:00
2022-02-05 22:28:19 +00:00
pub struct InnerInputState {
2022-02-06 13:08:15 +00:00
mouse : Option < ( MouseData , Vec < u16 > ) > ,
2022-02-05 22:28:19 +00:00
wheel : Option < WheelData > ,
last_key_pressed : Option < ( KeyboardData , Instant ) > ,
screen : Option < ( u16 , u16 ) > ,
// subscribers: Vec<Rc<dyn Fn() + 'static>>,
}
2022-02-04 20:52:01 +00:00
2022-02-05 22:28:19 +00:00
impl InnerInputState {
fn new ( ) -> Self {
2022-01-12 14:40:36 +00:00
Self {
2022-02-05 22:28:19 +00:00
mouse : None ,
wheel : None ,
last_key_pressed : None ,
screen : None ,
// subscribers: Vec::new(),
2022-01-12 14:40:36 +00:00
}
}
2022-02-04 20:52:01 +00:00
2022-02-05 22:28:19 +00:00
// stores current input state and transforms events based on that state
fn apply_event ( & mut self , evt : & mut EventCore ) {
match evt . 1 {
2022-02-06 13:08:15 +00:00
// limitations: only two buttons may be held at once
2022-02-05 22:28:19 +00:00
EventData ::Mouse ( ref mut m ) = > match & mut self . mouse {
Some ( state ) = > {
2022-02-06 13:08:15 +00:00
let mut buttons = state . 0. buttons ;
2022-04-17 12:29:35 +00:00
state . 0 = m . clone ( ) ;
2022-02-06 13:08:15 +00:00
match evt . 0 {
// this code only runs when there are no buttons down
" mouseup " = > {
buttons = 0 ;
state . 1 = Vec ::new ( ) ;
}
" mousedown " = > {
if state . 1. contains ( & m . buttons ) {
// if we already pressed a button and there is another button released the button crossterm sends is the button remaining
if state . 1. len ( ) > 1 {
2022-02-07 11:57:57 +00:00
evt . 0 = " mouseup " ;
2022-02-06 13:08:15 +00:00
state . 1 = vec! [ m . buttons ] ;
}
// otherwise some other button was pressed. In testing it was consistantly this mapping
else {
match m . buttons {
0x01 = > state . 1. push ( 0x02 ) ,
0x02 = > state . 1. push ( 0x01 ) ,
0x04 = > state . 1. push ( 0x01 ) ,
_ = > ( ) ,
}
}
} else {
state . 1. push ( m . buttons ) ;
}
buttons = state . 1. iter ( ) . copied ( ) . reduce ( | a , b | a | b ) . unwrap ( ) ;
}
_ = > ( ) ,
}
state . 0. buttons = buttons ;
m . buttons = buttons ;
2022-02-05 22:28:19 +00:00
}
None = > {
2022-02-06 13:08:15 +00:00
self . mouse = Some ( (
2022-04-17 12:29:35 +00:00
m . clone ( ) ,
2022-02-06 13:08:15 +00:00
if m . buttons = = 0 {
Vec ::new ( )
} else {
vec! [ m . buttons ]
} ,
) ) ;
2022-02-05 22:28:19 +00:00
}
} ,
2022-04-17 12:29:35 +00:00
EventData ::Wheel ( ref w ) = > self . wheel = Some ( w . clone ( ) ) ,
2022-03-09 18:30:44 +00:00
EventData ::Screen ( ref s ) = > self . screen = Some ( * s ) ,
2022-02-05 22:28:19 +00:00
EventData ::Keyboard ( ref mut k ) = > {
let repeat = self
. last_key_pressed
. as_ref ( )
. filter ( | k2 | k2 . 0. key = = k . key & & k2 . 1. elapsed ( ) < MAX_REPEAT_TIME )
. is_some ( ) ;
k . repeat = repeat ;
2022-04-17 12:29:35 +00:00
let new = k . clone ( ) ;
2022-02-05 22:28:19 +00:00
self . last_key_pressed = Some ( ( new , Instant ::now ( ) ) ) ;
}
}
2022-02-04 20:52:01 +00:00
}
2022-04-02 21:46:46 +00:00
fn update (
2022-02-07 11:57:57 +00:00
& mut self ,
2022-04-17 13:43:15 +00:00
evts : & mut [ EventCore ] ,
2022-02-07 11:57:57 +00:00
resolved_events : & mut Vec < UserEvent > ,
2022-06-10 22:23:30 +00:00
layout : & Taffy ,
2022-04-12 23:46:16 +00:00
dom : & mut Dom ,
2022-03-23 19:18:17 +00:00
) {
2022-04-17 12:29:35 +00:00
let previous_mouse = self . mouse . as_ref ( ) . map ( | m | ( m . 0. clone ( ) , m . 1. clone ( ) ) ) ;
2022-03-23 19:18:17 +00:00
self . wheel = None ;
for e in evts . iter_mut ( ) {
self . apply_event ( e ) ;
}
2022-04-04 18:37:04 +00:00
self . resolve_mouse_events ( previous_mouse , resolved_events , layout , dom ) ;
2022-03-23 19:18:17 +00:00
// for s in &self.subscribers {
// s();
// }
}
fn resolve_mouse_events (
& self ,
previous_mouse : Option < ( MouseData , Vec < u16 > ) > ,
resolved_events : & mut Vec < UserEvent > ,
2022-06-10 22:23:30 +00:00
layout : & Taffy ,
2022-04-12 23:46:16 +00:00
dom : & mut Dom ,
2022-02-07 11:57:57 +00:00
) {
fn layout_contains_point ( layout : & Layout , point : ( i32 , i32 ) ) -> bool {
layout . location . x as i32 < = point . 0
& & layout . location . x as i32 + layout . size . width as i32 > = point . 0
& & layout . location . y as i32 < = point . 1
& & layout . location . y as i32 + layout . size . height as i32 > = point . 1
2022-02-04 20:52:01 +00:00
}
2022-02-07 11:57:57 +00:00
2022-03-23 19:18:17 +00:00
fn try_create_event (
name : & 'static str ,
data : Arc < dyn Any + Send + Sync > ,
2022-03-27 01:10:15 +00:00
will_bubble : & mut FxHashSet < ElementId > ,
2022-02-07 11:57:57 +00:00
resolved_events : & mut Vec < UserEvent > ,
2022-04-12 23:46:16 +00:00
node : & Node ,
dom : & Dom ,
2022-03-23 19:18:17 +00:00
) {
// only trigger event if the event was not triggered already by a child
if will_bubble . insert ( node . id ) {
let mut parent = node . parent ;
while let Some ( parent_id ) = parent {
will_bubble . insert ( parent_id ) ;
2022-04-04 18:37:04 +00:00
parent = dom [ parent_id . 0 ] . parent ;
2022-02-07 11:57:57 +00:00
}
2022-03-23 19:18:17 +00:00
resolved_events . push ( UserEvent {
scope_id : None ,
priority : EventPriority ::Medium ,
name ,
element : Some ( node . id ) ,
2022-04-02 21:46:46 +00:00
data ,
2022-03-23 19:18:17 +00:00
} )
2022-02-07 11:57:57 +00:00
}
}
2022-05-04 18:58:48 +00:00
fn prepare_mouse_data ( mouse_data : & MouseData , layout : & Layout ) -> MouseData {
let mut data = mouse_data . clone ( ) ;
data . offset_x = data . client_x - layout . location . x as i32 ;
data . offset_y = data . client_y - layout . location . y as i32 ;
data
}
2022-02-07 11:57:57 +00:00
if let Some ( mouse ) = & self . mouse {
let new_pos = ( mouse . 0. screen_x , mouse . 0. screen_y ) ;
let old_pos = previous_mouse
. as_ref ( )
. map ( | m | ( m . 0. screen_x , m . 0. screen_y ) ) ;
2022-05-07 00:35:55 +00:00
// the a mouse button is pressed if a button was not down and is now down
let pressed =
( mouse . 0. buttons & ! previous_mouse . as_ref ( ) . map ( | m | m . 0. buttons ) . unwrap_or ( 0 ) ) > 0 ;
// the a mouse button is pressed if a button was down and is now not down
2022-02-07 11:57:57 +00:00
let released =
2022-05-07 00:35:55 +00:00
( ! mouse . 0. buttons & previous_mouse . map ( | m | m . 0. buttons ) . unwrap_or ( 0 ) ) > 0 ;
2022-02-07 11:57:57 +00:00
let wheel_delta = self . wheel . as_ref ( ) . map_or ( 0.0 , | w | w . delta_y ) ;
let mouse_data = & mouse . 0 ;
let wheel_data = & self . wheel ;
2022-03-23 19:18:17 +00:00
{
// mousemove
2022-05-06 22:01:50 +00:00
if old_pos ! = Some ( new_pos ) {
let mut will_bubble = FxHashSet ::default ( ) ;
for node in dom . get_listening_sorted ( " mousemove " ) {
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
let previously_contained = old_pos
. filter ( | pos | layout_contains_point ( node_layout , * pos ) )
. is_some ( ) ;
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
2022-05-06 22:01:50 +00:00
if currently_contains & & previously_contained {
try_create_event (
" mousemove " ,
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
& mut will_bubble ,
resolved_events ,
node ,
dom ,
) ;
}
2022-03-23 19:18:17 +00:00
}
}
}
{
// mouseenter
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
2022-04-04 18:37:04 +00:00
for node in dom . get_listening_sorted ( " mouseenter " ) {
2022-04-12 23:46:16 +00:00
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
. filter ( | pos | layout_contains_point ( node_layout , * pos ) )
. is_some ( ) ;
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
2022-04-02 21:46:46 +00:00
if currently_contains & & ! previously_contained {
try_create_event (
" mouseenter " ,
2022-05-04 18:58:48 +00:00
Arc ::new ( mouse_data . clone ( ) ) ,
2022-04-02 21:46:46 +00:00
& mut will_bubble ,
resolved_events ,
node ,
2022-04-04 18:37:04 +00:00
dom ,
2022-04-02 21:46:46 +00:00
) ;
2022-03-23 19:18:17 +00:00
}
}
}
{
// mouseover
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
2022-04-04 18:37:04 +00:00
for node in dom . get_listening_sorted ( " mouseover " ) {
2022-04-12 23:46:16 +00:00
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
. filter ( | pos | layout_contains_point ( node_layout , * pos ) )
. is_some ( ) ;
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
2022-04-02 21:46:46 +00:00
if currently_contains & & ! previously_contained {
try_create_event (
" mouseover " ,
2022-05-04 18:58:48 +00:00
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
2022-04-02 21:46:46 +00:00
& mut will_bubble ,
resolved_events ,
node ,
2022-04-04 18:37:04 +00:00
dom ,
2022-04-02 21:46:46 +00:00
) ;
2022-03-23 19:18:17 +00:00
}
}
}
2022-05-07 00:35:55 +00:00
// mousedown
if pressed {
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
2022-04-04 18:37:04 +00:00
for node in dom . get_listening_sorted ( " mousedown " ) {
2022-04-12 23:46:16 +00:00
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
2022-05-07 00:48:57 +00:00
if currently_contains {
2022-04-02 21:46:46 +00:00
try_create_event (
" mousedown " ,
2022-05-04 18:58:48 +00:00
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
2022-04-02 21:46:46 +00:00
& mut will_bubble ,
resolved_events ,
node ,
2022-04-04 18:37:04 +00:00
dom ,
2022-04-02 21:46:46 +00:00
) ;
2022-03-23 19:18:17 +00:00
}
}
}
{
// mouseup
2022-05-07 14:10:17 +00:00
if released {
let mut will_bubble = FxHashSet ::default ( ) ;
for node in dom . get_listening_sorted ( " mouseup " ) {
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
2022-05-07 14:10:17 +00:00
if currently_contains {
try_create_event (
" mouseup " ,
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
& mut will_bubble ,
resolved_events ,
node ,
dom ,
) ;
}
2022-03-23 19:18:17 +00:00
}
}
}
{
// click
2022-05-07 14:16:21 +00:00
if mouse_data . button = = 0 & & released {
2022-05-07 14:10:17 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
for node in dom . get_listening_sorted ( " click " ) {
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
2022-05-07 14:16:21 +00:00
if currently_contains {
2022-05-07 14:10:17 +00:00
try_create_event (
" click " ,
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
& mut will_bubble ,
resolved_events ,
node ,
dom ,
) ;
}
2022-03-23 19:18:17 +00:00
}
}
}
{
// contextmenu
2022-05-07 14:16:21 +00:00
if mouse_data . button = = 2 & & released {
2022-05-07 14:10:17 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
for node in dom . get_listening_sorted ( " contextmenu " ) {
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
2022-05-07 14:16:21 +00:00
if currently_contains {
2022-04-02 21:46:46 +00:00
try_create_event (
2022-05-07 14:10:17 +00:00
" contextmenu " ,
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
2022-04-02 21:46:46 +00:00
& mut will_bubble ,
resolved_events ,
node ,
2022-04-04 18:37:04 +00:00
dom ,
2022-04-02 21:46:46 +00:00
) ;
2022-03-23 19:18:17 +00:00
}
}
}
}
2022-05-07 14:10:17 +00:00
{
// wheel
if let Some ( w ) = wheel_data {
if wheel_delta ! = 0.0 {
let mut will_bubble = FxHashSet ::default ( ) ;
for node in dom . get_listening_sorted ( " wheel " ) {
let node_layout =
layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
if currently_contains {
try_create_event (
" wheel " ,
Arc ::new ( w . clone ( ) ) ,
& mut will_bubble ,
resolved_events ,
node ,
dom ,
) ;
}
}
}
}
}
2022-03-23 19:18:17 +00:00
{
// mouseleave
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
2022-04-04 18:37:04 +00:00
for node in dom . get_listening_sorted ( " mouseleave " ) {
2022-04-12 23:46:16 +00:00
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
. filter ( | pos | layout_contains_point ( node_layout , * pos ) )
. is_some ( ) ;
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
if ! currently_contains & & previously_contained {
try_create_event (
" mouseleave " ,
2022-05-04 18:58:48 +00:00
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
2022-03-23 19:18:17 +00:00
& mut will_bubble ,
resolved_events ,
node ,
2022-04-04 18:37:04 +00:00
dom ,
2022-03-23 19:18:17 +00:00
) ;
}
}
}
{
// mouseout
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
2022-04-04 18:37:04 +00:00
for node in dom . get_listening_sorted ( " mouseout " ) {
2022-04-12 23:46:16 +00:00
let node_layout = layout . layout ( node . state . layout . node . unwrap ( ) ) . unwrap ( ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
. filter ( | pos | layout_contains_point ( node_layout , * pos ) )
. is_some ( ) ;
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-03-23 19:18:17 +00:00
if ! currently_contains & & previously_contained {
try_create_event (
" mouseout " ,
2022-05-04 18:58:48 +00:00
Arc ::new ( prepare_mouse_data ( mouse_data , node_layout ) ) ,
2022-03-23 19:18:17 +00:00
& mut will_bubble ,
resolved_events ,
node ,
2022-04-04 18:37:04 +00:00
dom ,
2022-03-23 19:18:17 +00:00
) ;
}
}
}
}
2022-01-12 14:40:36 +00:00
}
2022-02-05 22:28:19 +00:00
// fn subscribe(&mut self, f: Rc<dyn Fn() + 'static>) {
// self.subscribers.push(f)
// }
}
2022-01-12 14:40:36 +00:00
2022-02-05 22:28:19 +00:00
pub struct RinkInputHandler {
state : Rc < RefCell < InnerInputState > > ,
queued_events : Rc < RefCell < Vec < EventCore > > > ,
2022-01-12 14:40:36 +00:00
}
2022-02-05 22:28:19 +00:00
impl RinkInputHandler {
/// global context that handles events
2022-02-06 13:08:15 +00:00
/// limitations: GUI key modifier is never detected, key up events are not detected, and only two mouse buttons may be pressed at once
2022-04-02 21:46:46 +00:00
pub fn new ( ) -> (
Self ,
Rc < RefCell < InnerInputState > > ,
impl FnMut ( crossterm ::event ::Event ) ,
) {
2022-02-05 22:28:19 +00:00
let queued_events = Rc ::new ( RefCell ::new ( Vec ::new ( ) ) ) ;
2022-03-23 19:18:17 +00:00
let queued_events2 = Rc ::downgrade ( & queued_events ) ;
2022-02-05 22:28:19 +00:00
2022-04-02 21:46:46 +00:00
let regester_event = move | evt : crossterm ::event ::Event | {
if let Some ( evt ) = get_event ( evt ) {
if let Some ( v ) = queued_events2 . upgrade ( ) {
( * v ) . borrow_mut ( ) . push ( evt ) ;
2022-02-04 20:52:01 +00:00
}
2022-02-05 22:28:19 +00:00
}
2022-04-02 21:46:46 +00:00
} ;
2022-02-05 22:28:19 +00:00
let state = Rc ::new ( RefCell ::new ( InnerInputState ::new ( ) ) ) ;
(
Self {
state : state . clone ( ) ,
queued_events ,
} ,
state ,
2022-04-02 21:46:46 +00:00
regester_event ,
2022-02-05 22:28:19 +00:00
)
}
2022-06-10 22:23:30 +00:00
pub ( crate ) fn get_events ( & self , layout : & Taffy , dom : & mut Dom ) -> Vec < UserEvent > {
2022-02-05 22:28:19 +00:00
let mut resolved_events = Vec ::new ( ) ;
2022-02-07 11:57:57 +00:00
( * self . state ) . borrow_mut ( ) . update (
& mut ( * self . queued_events ) . borrow_mut ( ) ,
& mut resolved_events ,
layout ,
2022-04-04 18:37:04 +00:00
dom ,
2022-02-07 11:57:57 +00:00
) ;
2022-02-05 22:28:19 +00:00
2022-03-23 19:18:17 +00:00
let events = self
2022-02-05 22:28:19 +00:00
. queued_events
. replace ( Vec ::new ( ) )
. into_iter ( )
2022-02-07 11:57:57 +00:00
// these events were added in the update stage
2022-03-23 19:18:17 +00:00
. filter ( | e | {
! [
" mouseenter " ,
" mouseover " ,
" mouseleave " ,
" mouseout " ,
" mousedown " ,
" mouseup " ,
" mousemove " ,
" drag " ,
" wheel " ,
" click " ,
" contextmenu " ,
]
. contains ( & e . 0 )
} )
. map ( | evt | ( evt . 0 , evt . 1. into_any ( ) ) ) ;
2022-02-05 22:28:19 +00:00
2022-03-23 19:18:17 +00:00
// todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
2022-03-27 01:10:15 +00:00
let mut hm : FxHashMap < & 'static str , Vec < Arc < dyn Any + Send + Sync > > > = FxHashMap ::default ( ) ;
2022-03-23 19:18:17 +00:00
for ( event , data ) in events {
if let Some ( v ) = hm . get_mut ( event ) {
v . push ( data ) ;
} else {
hm . insert ( event , vec! [ data ] ) ;
}
}
for ( event , datas ) in hm {
2022-04-04 18:37:04 +00:00
for node in dom . get_listening_sorted ( event ) {
2022-03-23 19:18:17 +00:00
for data in & datas {
resolved_events . push ( UserEvent {
scope_id : None ,
priority : EventPriority ::Medium ,
name : event ,
element : Some ( node . id ) ,
data : data . clone ( ) ,
} ) ;
}
}
}
2022-02-05 22:28:19 +00:00
2022-02-07 11:57:57 +00:00
resolved_events
2022-02-05 22:28:19 +00:00
}
}
2022-02-07 11:57:57 +00:00
// translate crossterm events into dioxus events
2022-02-05 22:28:19 +00:00
fn get_event ( evt : TermEvent ) -> Option < ( & 'static str , EventData ) > {
let ( name , data ) : ( & str , EventData ) = match evt {
2022-03-10 03:06:45 +00:00
TermEvent ::Key ( k ) = > ( " keydown " , translate_key_event ( k ) ? ) ,
2022-02-05 22:28:19 +00:00
TermEvent ::Mouse ( m ) = > {
let ( x , y ) = ( m . column . into ( ) , m . row . into ( ) ) ;
let alt = m . modifiers . contains ( KeyModifiers ::ALT ) ;
let shift = m . modifiers . contains ( KeyModifiers ::SHIFT ) ;
let ctrl = m . modifiers . contains ( KeyModifiers ::CONTROL ) ;
let meta = false ;
let get_mouse_data = | b | {
let buttons = match b {
None = > 0 ,
Some ( MouseButton ::Left ) = > 1 ,
Some ( MouseButton ::Right ) = > 2 ,
Some ( MouseButton ::Middle ) = > 4 ,
} ;
let button_state = match b {
None = > 0 ,
Some ( MouseButton ::Left ) = > 0 ,
Some ( MouseButton ::Middle ) = > 1 ,
Some ( MouseButton ::Right ) = > 2 ,
} ;
// from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
2022-05-04 13:42:14 +00:00
2022-05-04 18:58:48 +00:00
// The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively.
2022-05-04 13:42:14 +00:00
// todo?
// But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway.
2022-02-05 22:28:19 +00:00
EventData ::Mouse ( MouseData {
alt_key : alt ,
button : button_state ,
buttons ,
client_x : x ,
client_y : y ,
ctrl_key : ctrl ,
meta_key : meta ,
2022-05-04 18:58:48 +00:00
// offset x/y are set when the origin of the event is assigned to an element
offset_x : 0 ,
offset_y : 0 ,
2022-02-05 22:28:19 +00:00
page_x : x ,
page_y : y ,
screen_x : x ,
screen_y : y ,
shift_key : shift ,
} )
} ;
let get_wheel_data = | up | {
// from https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
EventData ::Wheel ( WheelData {
delta_mode : 0x01 ,
delta_x : 0.0 ,
delta_y : if up { - 1.0 } else { 1.0 } ,
delta_z : 0.0 ,
} )
} ;
match m . kind {
MouseEventKind ::Down ( b ) = > ( " mousedown " , get_mouse_data ( Some ( b ) ) ) ,
MouseEventKind ::Up ( b ) = > ( " mouseup " , get_mouse_data ( Some ( b ) ) ) ,
MouseEventKind ::Drag ( b ) = > ( " drag " , get_mouse_data ( Some ( b ) ) ) ,
MouseEventKind ::Moved = > ( " mousemove " , get_mouse_data ( None ) ) ,
2022-02-05 22:45:40 +00:00
MouseEventKind ::ScrollDown = > ( " wheel " , get_wheel_data ( false ) ) ,
MouseEventKind ::ScrollUp = > ( " wheel " , get_wheel_data ( true ) ) ,
2022-01-12 14:40:36 +00:00
}
}
2022-02-05 22:28:19 +00:00
TermEvent ::Resize ( x , y ) = > ( " resize " , EventData ::Screen ( ( x , y ) ) ) ,
} ;
Some ( ( name , data ) )
}
2022-03-10 03:06:45 +00:00
fn translate_key_event ( event : crossterm ::event ::KeyEvent ) -> Option < EventData > {
let ( code , key_str ) ;
if let TermKeyCode ::Char ( c ) = event . code {
code = match c {
'A' ..= 'Z' | 'a' ..= 'z' = > match c . to_ascii_uppercase ( ) {
'A' = > KeyCode ::A ,
'B' = > KeyCode ::B ,
'C' = > KeyCode ::C ,
'D' = > KeyCode ::D ,
'E' = > KeyCode ::E ,
'F' = > KeyCode ::F ,
'G' = > KeyCode ::G ,
'H' = > KeyCode ::H ,
'I' = > KeyCode ::I ,
'J' = > KeyCode ::J ,
'K' = > KeyCode ::K ,
'L' = > KeyCode ::L ,
'M' = > KeyCode ::M ,
'N' = > KeyCode ::N ,
'O' = > KeyCode ::O ,
'P' = > KeyCode ::P ,
'Q' = > KeyCode ::Q ,
'R' = > KeyCode ::R ,
'S' = > KeyCode ::S ,
'T' = > KeyCode ::T ,
'U' = > KeyCode ::U ,
'V' = > KeyCode ::V ,
'W' = > KeyCode ::W ,
'X' = > KeyCode ::X ,
'Y' = > KeyCode ::Y ,
'Z' = > KeyCode ::Z ,
_ = > return None ,
} ,
' ' = > KeyCode ::Space ,
'[' = > KeyCode ::OpenBracket ,
'{' = > KeyCode ::OpenBracket ,
']' = > KeyCode ::CloseBraket ,
'}' = > KeyCode ::CloseBraket ,
';' = > KeyCode ::Semicolon ,
':' = > KeyCode ::Semicolon ,
',' = > KeyCode ::Comma ,
'<' = > KeyCode ::Comma ,
'.' = > KeyCode ::Period ,
'>' = > KeyCode ::Period ,
'1' = > KeyCode ::Num1 ,
'2' = > KeyCode ::Num2 ,
'3' = > KeyCode ::Num3 ,
'4' = > KeyCode ::Num4 ,
'5' = > KeyCode ::Num5 ,
'6' = > KeyCode ::Num6 ,
'7' = > KeyCode ::Num7 ,
'8' = > KeyCode ::Num8 ,
'9' = > KeyCode ::Num9 ,
'0' = > KeyCode ::Num0 ,
'!' = > KeyCode ::Num1 ,
'@' = > KeyCode ::Num2 ,
'#' = > KeyCode ::Num3 ,
'$' = > KeyCode ::Num4 ,
'%' = > KeyCode ::Num5 ,
'^' = > KeyCode ::Num6 ,
'&' = > KeyCode ::Num7 ,
'*' = > KeyCode ::Num8 ,
'(' = > KeyCode ::Num9 ,
')' = > KeyCode ::Num0 ,
// numpad charicter are ambiguous to tui
// '*' => KeyCode::Multiply,
// '/' => KeyCode::Divide,
// '-' => KeyCode::Subtract,
// '+' => KeyCode::Add,
'+' = > KeyCode ::EqualSign ,
'-' = > KeyCode ::Dash ,
'_' = > KeyCode ::Dash ,
'\'' = > KeyCode ::SingleQuote ,
'"' = > KeyCode ::SingleQuote ,
'\\' = > KeyCode ::BackSlash ,
'|' = > KeyCode ::BackSlash ,
'/' = > KeyCode ::ForwardSlash ,
'?' = > KeyCode ::ForwardSlash ,
'=' = > KeyCode ::EqualSign ,
'`' = > KeyCode ::GraveAccent ,
'~' = > KeyCode ::GraveAccent ,
_ = > return None ,
} ;
key_str = c . to_string ( ) ;
} else {
code = match event . code {
TermKeyCode ::Esc = > KeyCode ::Escape ,
TermKeyCode ::Backspace = > KeyCode ::Backspace ,
TermKeyCode ::Enter = > KeyCode ::Enter ,
TermKeyCode ::Left = > KeyCode ::LeftArrow ,
TermKeyCode ::Right = > KeyCode ::RightArrow ,
TermKeyCode ::Up = > KeyCode ::UpArrow ,
TermKeyCode ::Down = > KeyCode ::DownArrow ,
TermKeyCode ::Home = > KeyCode ::Home ,
TermKeyCode ::End = > KeyCode ::End ,
TermKeyCode ::PageUp = > KeyCode ::PageUp ,
TermKeyCode ::PageDown = > KeyCode ::PageDown ,
TermKeyCode ::Tab = > KeyCode ::Tab ,
TermKeyCode ::Delete = > KeyCode ::Delete ,
TermKeyCode ::Insert = > KeyCode ::Insert ,
TermKeyCode ::F ( fn_num ) = > match fn_num {
1 = > KeyCode ::F1 ,
2 = > KeyCode ::F2 ,
3 = > KeyCode ::F3 ,
4 = > KeyCode ::F4 ,
5 = > KeyCode ::F5 ,
6 = > KeyCode ::F6 ,
7 = > KeyCode ::F7 ,
8 = > KeyCode ::F8 ,
9 = > KeyCode ::F9 ,
10 = > KeyCode ::F10 ,
11 = > KeyCode ::F11 ,
12 = > KeyCode ::F12 ,
_ = > return None ,
} ,
TermKeyCode ::BackTab = > return None ,
TermKeyCode ::Null = > return None ,
_ = > return None ,
} ;
key_str = if let KeyCode ::BackSlash = code {
" \\ " . to_string ( )
} else {
format! ( " {code:?} " )
}
} ;
// from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
Some ( EventData ::Keyboard ( KeyboardData {
char_code : code . raw_code ( ) ,
2022-04-02 21:46:46 +00:00
key : key_str ,
2022-03-10 03:06:45 +00:00
key_code : code ,
alt_key : event . modifiers . contains ( KeyModifiers ::ALT ) ,
ctrl_key : event . modifiers . contains ( KeyModifiers ::CONTROL ) ,
meta_key : false ,
shift_key : event . modifiers . contains ( KeyModifiers ::SHIFT ) ,
locale : Default ::default ( ) ,
location : 0x00 ,
repeat : Default ::default ( ) ,
which : Default ::default ( ) ,
} ) )
2022-02-05 22:28:19 +00:00
}