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
2022-05-07 13:27:34 +00:00
use dioxus_html ::geometry ::euclid ::{ Point2D , Rect , Size2D } ;
2022-05-07 07:49:49 +00:00
use dioxus_html ::geometry ::{ ClientPoint , Coordinates , ElementPoint , PagePoint , ScreenPoint } ;
use dioxus_html ::input ::keyboard_types ::Modifiers ;
use dioxus_html ::input ::MouseButtonSet as DioxusMouseButtons ;
2022-05-07 13:11:48 +00:00
use dioxus_html ::input ::{ MouseButton as DioxusMouseButton , MouseButtonSet } ;
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-05-07 13:27:34 +00:00
use stretch2 ::geometry ::{ Point , Size } ;
2022-02-07 11:57:57 +00:00
use stretch2 ::{ prelude ::Layout , Stretch } ;
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-05-07 13:11:48 +00:00
mouse : Option < MouseData > ,
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-05-07 13:11:48 +00:00
EventData ::Mouse ( ref mut m ) = > {
let mut held_buttons = match & self . mouse {
Some ( previous_data ) = > previous_data . held_buttons ( ) ,
None = > MouseButtonSet ::empty ( ) ,
} ;
match evt . 0 {
" mousedown " = > {
held_buttons . insert (
m . trigger_button ( )
. expect ( " No trigger button for mousedown event " ) ,
) ;
2022-02-06 13:08:15 +00:00
}
2022-05-07 13:11:48 +00:00
" mouseup " = > {
held_buttons . remove (
m . trigger_button ( )
. expect ( " No trigger button for mouseup event " ) ,
) ;
}
_ = > { }
2022-02-05 22:28:19 +00:00
}
2022-05-07 13:11:48 +00:00
let new_mouse_data = MouseData ::new (
m . coordinates ( ) ,
m . trigger_button ( ) ,
held_buttons ,
m . modifiers ( ) ,
) ;
self . mouse = Some ( new_mouse_data . clone ( ) ) ;
* m = new_mouse_data ;
}
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 > ,
layout : & Stretch ,
2022-04-12 23:46:16 +00:00
dom : & mut Dom ,
2022-03-23 19:18:17 +00:00
) {
2022-05-07 13:11:48 +00:00
let previous_mouse = self . mouse . 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 ,
2022-05-07 13:11:48 +00:00
previous_mouse : Option < MouseData > ,
2022-03-23 19:18:17 +00:00
resolved_events : & mut Vec < UserEvent > ,
layout : & Stretch ,
2022-04-12 23:46:16 +00:00
dom : & mut Dom ,
2022-02-07 11:57:57 +00:00
) {
2022-05-07 13:27:34 +00:00
fn layout_contains_point ( layout : & Layout , point : ScreenPoint ) -> bool {
let Point { x , y } = layout . location ;
let Size { width , height } = layout . size ;
let layout_rect = Rect ::new ( Point2D ::new ( x , y ) , Size2D ::new ( width , height ) ) ;
layout_rect . contains ( point . cast ( ) )
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 {
2022-05-07 12:32:19 +00:00
let Point { x , y } = layout . location ;
let node_origin = ClientPoint ::new ( x . into ( ) , y . into ( ) ) ;
let new_client_coordinates = ( mouse_data . client_coordinates ( ) - node_origin )
. to_point ( )
. cast_unit ( ) ;
let coordinates = Coordinates ::new (
mouse_data . screen_coordinates ( ) ,
mouse_data . client_coordinates ( ) ,
new_client_coordinates ,
mouse_data . page_coordinates ( ) ,
) ;
MouseData ::new (
coordinates ,
mouse_data . trigger_button ( ) ,
mouse_data . held_buttons ( ) ,
mouse_data . modifiers ( ) ,
)
2022-05-04 18:58:48 +00:00
}
2022-05-07 13:11:48 +00:00
if let Some ( mouse_data ) = & self . mouse {
2022-05-07 13:27:34 +00:00
let new_pos = mouse_data . screen_coordinates ( ) ;
let old_pos = previous_mouse . as_ref ( ) . map ( | m | m . screen_coordinates ( ) ) ;
// a mouse button is pressed if a button was not down and is now down
let previous_buttons = previous_mouse
. map_or ( MouseButtonSet ::empty ( ) , | previous_data | {
previous_data . held_buttons ( )
} ) ;
let was_pressed = ! ( mouse_data . held_buttons ( ) - previous_buttons ) . is_empty ( ) ;
// a mouse button is released if a button was down and is now not down
let was_released = ! ( previous_buttons - mouse_data . held_buttons ( ) ) . is_empty ( ) ;
2022-02-07 11:57:57 +00:00
let wheel_delta = self . wheel . as_ref ( ) . map_or ( 0.0 , | w | w . delta_y ) ;
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
2022-05-07 13:27:34 +00:00
if was_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:56:03 +00:00
if was_released {
2022-05-07 14:10:17 +00:00
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:56:03 +00:00
if mouse_data . trigger_button ( ) = = Some ( DioxusMouseButton ::Primary ) & & was_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:56:03 +00:00
if mouse_data . trigger_button ( ) = = Some ( DioxusMouseButton ::Secondary ) & & was_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-04-12 23:46:16 +00:00
pub ( crate ) fn get_events ( & self , layout : & Stretch , 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 ;
2022-05-07 07:49:49 +00:00
let get_mouse_data = | crossterm_button : Option < MouseButton > | {
let button = crossterm_button . map ( | b | match b {
MouseButton ::Left = > DioxusMouseButton ::Primary ,
MouseButton ::Right = > DioxusMouseButton ::Secondary ,
MouseButton ::Middle = > DioxusMouseButton ::Auxiliary ,
} ) ;
2022-02-05 22:28:19 +00:00
// 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-05-07 07:49:49 +00:00
let coordinates = Coordinates ::new (
ScreenPoint ::new ( x , y ) ,
ClientPoint ::new ( x , y ) ,
2022-05-04 18:58:48 +00:00
// offset x/y are set when the origin of the event is assigned to an element
2022-05-07 07:50:52 +00:00
ElementPoint ::new ( 0. , 0. ) ,
2022-05-07 07:49:49 +00:00
PagePoint ::new ( x , y ) ,
) ;
let mut modifiers = Modifiers ::empty ( ) ;
if shift {
modifiers . insert ( Modifiers ::SHIFT ) ;
}
if ctrl {
modifiers . insert ( Modifiers ::CONTROL ) ;
}
if meta {
modifiers . insert ( Modifiers ::META ) ;
}
if alt {
modifiers . insert ( Modifiers ::ALT ) ;
}
2022-05-07 13:11:48 +00:00
// held mouse buttons get set later by maintaining state, as crossterm does not provide them
EventData ::Mouse ( MouseData ::new (
coordinates ,
button ,
DioxusMouseButtons ::empty ( ) ,
modifiers ,
) )
2022-02-05 22:28:19 +00:00
} ;
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
}