2022-02-05 22:28:19 +00:00
use crossterm ::event ::{
Event as TermEvent , KeyCode as TermKeyCode , KeyModifiers , MouseButton , MouseEventKind ,
} ;
2023-03-13 22:49:37 +00:00
use dioxus_native_core ::prelude ::* ;
2023-01-28 20:51:05 +00:00
use dioxus_native_core ::real_dom ::NodeImmutable ;
2022-10-13 01:27:26 +00:00
use rustc_hash ::{ 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-12 07:54:38 +00:00
use dioxus_html ::geometry ::{
ClientPoint , Coordinates , ElementPoint , PagePoint , ScreenPoint , WheelDelta ,
} ;
2022-05-12 11:10:25 +00:00
use dioxus_html ::input_data ::keyboard_types ::{ Code , Key , Location , Modifiers } ;
2022-05-11 10:47:58 +00:00
use dioxus_html ::input_data ::MouseButtonSet as DioxusMouseButtons ;
use dioxus_html ::input_data ::{ MouseButton as DioxusMouseButton , MouseButtonSet } ;
2023-02-07 03:28:48 +00:00
use dioxus_html ::{ event_bubbles , EventData , FocusData , KeyboardData , MouseData , WheelData } ;
2022-01-12 14:40:36 +00:00
use std ::{
2022-05-03 22:19:16 +00:00
cell ::{ RefCell , RefMut } ,
2022-01-12 14:40:36 +00:00
rc ::Rc ,
2022-02-05 22:28:19 +00:00
time ::{ Duration , Instant } ,
2022-01-12 14:40:36 +00:00
} ;
2022-06-10 22:41:51 +00:00
use taffy ::geometry ::{ Point , Size } ;
2022-06-10 22:49:04 +00:00
use taffy ::{ prelude ::Layout , Taffy } ;
2022-02-07 11:57:57 +00:00
2023-01-28 20:51:05 +00:00
use crate ::focus ::{ Focus , Focused } ;
use crate ::layout ::TaffyLayout ;
2022-12-06 23:38:04 +00:00
use crate ::{ layout_to_screen_space , FocusState } ;
pub ( crate ) struct Event {
2023-02-07 03:28:48 +00:00
pub id : NodeId ,
2022-12-06 23:38:04 +00:00
pub name : & 'static str ,
2023-02-07 03:28:48 +00:00
pub data : Rc < EventData > ,
2022-12-06 23:38:04 +00:00
pub bubbles : bool ,
}
2022-01-12 14:40:36 +00:00
2022-02-05 22:28:19 +00:00
type EventCore = ( & 'static str , EventData ) ;
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 ) > ,
2022-05-03 22:19:16 +00:00
pub ( crate ) focus_state : FocusState ,
2022-02-05 22:28:19 +00:00
// subscribers: Vec<Rc<dyn Fn() + 'static>>,
}
2022-02-04 20:52:01 +00:00
2022-02-05 22:28:19 +00:00
impl InnerInputState {
2023-02-07 03:28:48 +00:00
fn create ( rdom : & mut RealDom ) -> 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 ,
// subscribers: Vec::new(),
2023-02-07 03:28:48 +00:00
focus_state : FocusState ::create ( rdom ) ,
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-02-05 22:28:19 +00:00
EventData ::Keyboard ( ref mut k ) = > {
2022-05-12 11:36:52 +00:00
let is_repeating = self
2022-02-05 22:28:19 +00:00
. last_key_pressed
. as_ref ( )
2022-05-12 11:36:52 +00:00
// heuristic for guessing which presses are auto-repeating. not necessarily accurate
2022-06-29 04:44:19 +00:00
. filter ( | ( last_data , last_instant ) | {
last_data . key ( ) = = k . key ( ) & & last_instant . elapsed ( ) < MAX_REPEAT_TIME
} )
2022-02-05 22:28:19 +00:00
. is_some ( ) ;
2022-05-12 11:36:52 +00:00
2022-06-29 04:44:19 +00:00
if is_repeating {
2022-05-12 11:36:52 +00:00
* k = KeyboardData ::new ( k . key ( ) , k . code ( ) , k . location ( ) , true , k . modifiers ( ) ) ;
}
2022-06-29 04:44:19 +00:00
self . last_key_pressed = Some ( ( k . clone ( ) , Instant ::now ( ) ) ) ;
2022-02-05 22:28:19 +00:00
}
2023-02-07 03:28:48 +00:00
_ = > { }
2022-02-05 22:28:19 +00:00
}
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-05-03 21:44:53 +00:00
evts : & mut Vec < EventCore > ,
2022-12-06 23:38:04 +00:00
resolved_events : & mut Vec < Event > ,
2022-06-10 22:23:30 +00:00
layout : & Taffy ,
2023-01-28 20:51:05 +00:00
dom : & mut RealDom ,
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 ;
2022-05-05 12:17:33 +00:00
let old_focus = self . focus_state . last_focused_id ;
2022-05-03 21:44:53 +00:00
evts . retain ( | e | match & e . 1 {
2022-06-28 20:10:44 +00:00
EventData ::Keyboard ( k ) = > match k . code ( ) {
Code ::Tab = > ! self
. focus_state
. progress ( dom , ! k . modifiers ( ) . contains ( Modifiers ::SHIFT ) ) ,
2022-05-03 21:44:53 +00:00
_ = > true ,
} ,
_ = > true ,
} ) ;
2022-03-23 19:18:17 +00:00
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
2022-05-05 12:17:33 +00:00
if old_focus ! = self . focus_state . last_focused_id {
2022-10-18 21:42:45 +00:00
// elements with listeners will always have a element id
2022-12-06 23:38:04 +00:00
if let Some ( id ) = self . focus_state . last_focused_id {
2023-02-07 03:28:48 +00:00
resolved_events . push ( Event {
name : " focus " ,
id ,
data : Rc ::new ( EventData ::Focus ( FocusData { } ) ) ,
bubbles : event_bubbles ( " focus " ) ,
} ) ;
resolved_events . push ( Event {
name : " focusin " ,
id ,
data : Rc ::new ( EventData ::Focus ( FocusData { } ) ) ,
bubbles : event_bubbles ( " focusin " ) ,
} ) ;
2022-05-07 13:39:55 +00:00
}
2022-12-06 23:38:04 +00:00
if let Some ( id ) = old_focus {
2023-02-07 03:28:48 +00:00
resolved_events . push ( Event {
name : " focusout " ,
id ,
data : Rc ::new ( EventData ::Focus ( FocusData { } ) ) ,
bubbles : event_bubbles ( " focusout " ) ,
} ) ;
2022-05-05 12:17:33 +00:00
}
}
2022-03-23 19:18:17 +00:00
// for s in &self.subscribers {
// s();
// }
}
fn resolve_mouse_events (
2022-05-13 01:52:06 +00:00
& mut self ,
2022-05-07 13:11:48 +00:00
previous_mouse : Option < MouseData > ,
2022-12-06 23:38:04 +00:00
resolved_events : & mut Vec < Event > ,
2022-06-10 22:23:30 +00:00
layout : & Taffy ,
2023-01-28 20:51:05 +00:00
dom : & mut RealDom ,
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 ;
2022-12-06 23:38:04 +00:00
let ( x , y ) = (
layout_to_screen_space ( x ) . round ( ) ,
layout_to_screen_space ( y ) . round ( ) ,
) ;
2022-05-07 13:27:34 +00:00
let Size { width , height } = layout . size ;
2022-12-06 23:38:04 +00:00
let ( width , height ) = (
layout_to_screen_space ( width ) . round ( ) ,
layout_to_screen_space ( height ) . round ( ) ,
) ;
2022-05-07 13:27:34 +00:00
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 ,
2023-02-07 03:28:48 +00:00
data : Rc < EventData > ,
2022-12-06 23:38:04 +00:00
will_bubble : & mut FxHashSet < NodeId > ,
resolved_events : & mut Vec < Event > ,
2023-01-28 20:51:05 +00:00
node : NodeRef ,
dom : & RealDom ,
2022-03-23 19:18:17 +00:00
) {
// only trigger event if the event was not triggered already by a child
2023-02-07 03:28:48 +00:00
let id = node . id ( ) ;
2022-09-30 19:03:06 +00:00
if will_bubble . insert ( id ) {
2023-01-28 20:51:05 +00:00
let mut parent = Some ( node ) ;
2022-12-06 23:38:04 +00:00
while let Some ( current_parent ) = parent {
2023-02-07 03:28:48 +00:00
let parent_id = current_parent . id ( ) ;
2022-03-23 19:18:17 +00:00
will_bubble . insert ( parent_id ) ;
2023-01-28 20:51:05 +00:00
parent = current_parent . parent_id ( ) . and_then ( | id | dom . get ( id ) ) ;
2022-12-06 23:38:04 +00:00
}
2023-02-07 03:28:48 +00:00
resolved_events . push ( Event {
name ,
id ,
data ,
bubbles : event_bubbles ( name ) ,
} )
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 ;
2022-12-08 12:40:00 +00:00
let node_origin = ClientPoint ::new (
layout_to_screen_space ( x ) . into ( ) ,
layout_to_screen_space ( y ) . into ( ) ,
) ;
2022-05-07 12:32:19 +00:00
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-05-12 07:54:38 +00:00
let was_scrolled = self
. wheel
. as_ref ( )
. map_or ( false , | data | ! data . delta ( ) . is_zero ( ) ) ;
2022-02-07 11:57:57 +00:00
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 " ) {
2022-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
2022-05-06 22:01:50 +00:00
let previously_contained = old_pos
2022-06-14 00:42:03 +00:00
. filter ( | pos | layout_contains_point ( & node_layout , * pos ) )
2022-05-06 22:01:50 +00:00
. is_some ( ) ;
2022-06-14 00:42:03 +00:00
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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( prepare_mouse_data (
mouse_data ,
& node_layout ,
) ) ) ,
2022-05-06 22:01:50 +00:00
& 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-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-06-14 00:42:03 +00:00
. filter ( | pos | layout_contains_point ( & node_layout , * pos ) )
2022-03-23 19:18:17 +00:00
. is_some ( ) ;
2022-06-14 00:42:03 +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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( dioxus_html ::EventData ::Mouse ( 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-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-06-14 00:42:03 +00:00
. filter ( | pos | layout_contains_point ( & node_layout , * pos ) )
2022-03-23 19:18:17 +00:00
. is_some ( ) ;
2022-06-14 00:42:03 +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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( 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-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( 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 " ) {
2022-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( prepare_mouse_data (
mouse_data ,
& node_layout ,
) ) ) ,
2022-05-07 14:10:17 +00:00
& 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 " ) {
2022-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( prepare_mouse_data (
mouse_data ,
& node_layout ,
) ) ) ,
2022-05-07 14:10:17 +00:00
& 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 " ) {
2022-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( 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 {
2022-05-12 07:54:38 +00:00
if was_scrolled {
2022-05-07 14:10:17 +00:00
let mut will_bubble = FxHashSet ::default ( ) ;
for node in dom . get_listening_sorted ( " wheel " ) {
2022-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
let currently_contains = layout_contains_point ( & node_layout , new_pos ) ;
2022-05-07 14:10:17 +00:00
if currently_contains {
try_create_event (
" wheel " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Wheel ( w . clone ( ) ) ) ,
2022-05-07 14:10:17 +00:00
& 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-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-06-14 00:42:03 +00:00
. filter ( | pos | layout_contains_point ( & node_layout , * pos ) )
2022-03-23 19:18:17 +00:00
. is_some ( ) ;
2022-06-14 00:42:03 +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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( 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-06-14 00:42:03 +00:00
let node_layout = get_abs_layout ( node , dom , layout ) ;
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-06-14 00:42:03 +00:00
. filter ( | pos | layout_contains_point ( & node_layout , * pos ) )
2022-03-23 19:18:17 +00:00
. is_some ( ) ;
2022-06-14 00:42:03 +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 " ,
2023-02-07 03:28:48 +00:00
Rc ::new ( EventData ::Mouse ( 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-05-03 22:19:16 +00:00
// update focus
2022-05-13 01:52:06 +00:00
if was_released {
2022-05-03 22:19:16 +00:00
let mut focus_id = None ;
dom . traverse_depth_first ( | node | {
2023-01-28 20:51:05 +00:00
let node_layout = layout
. layout ( node . get ::< TaffyLayout > ( ) . unwrap ( ) . node . unwrap ( ) )
. unwrap ( ) ;
2022-05-05 17:46:25 +00:00
let currently_contains = layout_contains_point ( node_layout , new_pos ) ;
2022-05-03 22:19:16 +00:00
2023-01-28 20:51:05 +00:00
if currently_contains & & node . get ::< Focus > ( ) . unwrap ( ) . level . focusable ( ) {
2023-02-07 03:28:48 +00:00
focus_id = Some ( node . id ( ) ) ;
2022-05-03 22:19:16 +00:00
}
} ) ;
if let Some ( id ) = focus_id {
self . focus_state . set_focus ( dom , id ) ;
}
}
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
2023-01-28 20:51:05 +00:00
fn get_abs_layout ( node : NodeRef , dom : & RealDom , taffy : & Taffy ) -> Layout {
let mut node_layout = * taffy
. layout ( node . get ::< TaffyLayout > ( ) . unwrap ( ) . node . unwrap ( ) )
. unwrap ( ) ;
2022-06-14 00:42:03 +00:00
let mut current = node ;
2022-06-20 06:24:39 +00:00
2023-01-28 20:51:05 +00:00
while let Some ( parent ) = current . parent_id ( ) {
let parent = dom . get ( parent ) . unwrap ( ) ;
2022-06-14 00:42:03 +00:00
current = parent ;
2023-01-28 20:51:05 +00:00
let parent_layout = taffy
. layout ( parent . get ::< TaffyLayout > ( ) . unwrap ( ) . node . unwrap ( ) )
. unwrap ( ) ;
2022-06-14 00:42:03 +00:00
node_layout . location . x + = parent_layout . location . x ;
node_layout . location . y + = parent_layout . location . y ;
}
node_layout
}
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
2023-02-07 20:51:30 +00:00
pub fn create ( rdom : & mut RealDom ) -> ( Self , 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
2023-02-07 03:28:48 +00:00
let state = Rc ::new ( RefCell ::new ( InnerInputState ::create ( rdom ) ) ) ;
2022-02-05 22:28:19 +00:00
(
Self {
2023-02-07 20:51:30 +00:00
state ,
2022-02-05 22:28:19 +00:00
queued_events ,
} ,
2022-04-02 21:46:46 +00:00
regester_event ,
2022-02-05 22:28:19 +00:00
)
}
2023-01-28 20:51:05 +00:00
pub ( crate ) fn get_events ( & self , layout : & Taffy , dom : & mut RealDom ) -> Vec < Event > {
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 )
} )
2023-02-07 03:28:48 +00:00
. map ( | evt | ( evt . 0 , evt . 1 ) ) ;
2022-02-05 22:28:19 +00:00
2023-02-07 03:28:48 +00:00
let mut hm : FxHashMap < & 'static str , Vec < Rc < EventData > > > = FxHashMap ::default ( ) ;
2022-03-23 19:18:17 +00:00
for ( event , data ) in events {
if let Some ( v ) = hm . get_mut ( event ) {
2023-02-07 03:28:48 +00:00
v . push ( Rc ::new ( data ) ) ;
2022-03-23 19:18:17 +00:00
} else {
2023-02-07 03:28:48 +00:00
hm . insert ( event , vec! [ Rc ::new ( data ) ] ) ;
2022-03-23 19:18:17 +00:00
}
}
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 {
2023-02-01 20:36:56 +00:00
let focused = node . get ::< Focused > ( ) ;
if focused . is_some ( ) & & focused . unwrap ( ) . 0 {
2023-02-07 03:28:48 +00:00
resolved_events . push ( Event {
name : event ,
id : node . id ( ) ,
data : data . clone ( ) ,
bubbles : event_bubbles ( event ) ,
} ) ;
2022-05-03 16:02:35 +00:00
}
2022-03-23 19:18:17 +00:00
}
}
}
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-05-03 22:19:16 +00:00
pub ( crate ) fn state ( & self ) -> RefMut < InnerInputState > {
self . state . borrow_mut ( )
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 | {
2022-05-12 07:54:38 +00:00
let y = if up { - 1.0 } else { 1.0 } ;
EventData ::Wheel ( WheelData ::new ( WheelDelta ::lines ( 0. , y , 0. ) ) )
2022-02-05 22:28:19 +00:00
} ;
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
}
}
2023-02-07 03:28:48 +00:00
_ = > return None ,
2022-02-05 22:28:19 +00:00
} ;
Some ( ( name , data ) )
}
2022-03-10 03:06:45 +00:00
fn translate_key_event ( event : crossterm ::event ::KeyEvent ) -> Option < EventData > {
2022-05-12 11:10:25 +00:00
let key = key_from_crossterm_key_code ( event . code ) ;
// crossterm does not provide code. we make a guess as to which key might have been pressed
// this is probably garbage if the user has a custom keyboard layout
let code = guess_code_from_crossterm_key_code ( event . code ) ? ;
let modifiers = modifiers_from_crossterm_modifiers ( event . modifiers ) ;
Some ( EventData ::Keyboard ( KeyboardData ::new (
key ,
code ,
Location ::Standard ,
false ,
modifiers ,
) ) )
}
/// The crossterm key_code nicely represents the meaning of the key and we can mostly convert it without any issues
///
/// Exceptions:
2022-06-28 18:17:46 +00:00
/// BackTab is converted to Key::Tab, and Null is converted to Key::Unidentified
2022-05-12 11:10:25 +00:00
fn key_from_crossterm_key_code ( key_code : TermKeyCode ) -> Key {
match key_code {
TermKeyCode ::Backspace = > Key ::Backspace ,
TermKeyCode ::Enter = > Key ::Enter ,
TermKeyCode ::Left = > Key ::ArrowLeft ,
TermKeyCode ::Right = > Key ::ArrowRight ,
TermKeyCode ::Up = > Key ::ArrowUp ,
TermKeyCode ::Down = > Key ::ArrowDown ,
TermKeyCode ::Home = > Key ::Home ,
TermKeyCode ::End = > Key ::End ,
TermKeyCode ::PageUp = > Key ::PageUp ,
TermKeyCode ::PageDown = > Key ::PageDown ,
TermKeyCode ::Tab = > Key ::Tab ,
// ? no corresponding Key
2022-06-28 18:17:46 +00:00
TermKeyCode ::BackTab = > Key ::Tab ,
2022-05-12 11:10:25 +00:00
TermKeyCode ::Delete = > Key ::Delete ,
TermKeyCode ::Insert = > Key ::Insert ,
TermKeyCode ::F ( 1 ) = > Key ::F1 ,
TermKeyCode ::F ( 2 ) = > Key ::F2 ,
TermKeyCode ::F ( 3 ) = > Key ::F3 ,
TermKeyCode ::F ( 4 ) = > Key ::F4 ,
TermKeyCode ::F ( 5 ) = > Key ::F5 ,
TermKeyCode ::F ( 6 ) = > Key ::F6 ,
TermKeyCode ::F ( 7 ) = > Key ::F7 ,
TermKeyCode ::F ( 8 ) = > Key ::F8 ,
TermKeyCode ::F ( 9 ) = > Key ::F9 ,
TermKeyCode ::F ( 10 ) = > Key ::F10 ,
TermKeyCode ::F ( 11 ) = > Key ::F11 ,
TermKeyCode ::F ( 12 ) = > Key ::F12 ,
TermKeyCode ::F ( 13 ) = > Key ::F13 ,
TermKeyCode ::F ( 14 ) = > Key ::F14 ,
TermKeyCode ::F ( 15 ) = > Key ::F15 ,
TermKeyCode ::F ( 16 ) = > Key ::F16 ,
TermKeyCode ::F ( 17 ) = > Key ::F17 ,
TermKeyCode ::F ( 18 ) = > Key ::F18 ,
TermKeyCode ::F ( 19 ) = > Key ::F19 ,
TermKeyCode ::F ( 20 ) = > Key ::F20 ,
TermKeyCode ::F ( 21 ) = > Key ::F21 ,
TermKeyCode ::F ( 22 ) = > Key ::F22 ,
TermKeyCode ::F ( 23 ) = > Key ::F23 ,
TermKeyCode ::F ( 24 ) = > Key ::F24 ,
TermKeyCode ::F ( other ) = > {
panic! ( " Unexpected function key: {other:?} " )
}
TermKeyCode ::Char ( c ) = > Key ::Character ( c . to_string ( ) ) ,
TermKeyCode ::Null = > Key ::Unidentified ,
TermKeyCode ::Esc = > Key ::Escape ,
}
}
// Crossterm does not provide a way to get the `code` (physical key on keyboard)
// So we make a guess based on their `key_code`, but this is probably going to break on anything other than a very standard european keyboard
2022-06-28 18:17:46 +00:00
// It may look fine, but it's a horrible hack. But there's nothing better we can do.
2022-05-12 11:10:25 +00:00
fn guess_code_from_crossterm_key_code ( key_code : TermKeyCode ) -> Option < Code > {
let code = match key_code {
TermKeyCode ::Backspace = > Code ::Backspace ,
TermKeyCode ::Enter = > Code ::Enter ,
TermKeyCode ::Left = > Code ::ArrowLeft ,
TermKeyCode ::Right = > Code ::ArrowRight ,
TermKeyCode ::Up = > Code ::ArrowUp ,
TermKeyCode ::Down = > Code ::ArrowDown ,
TermKeyCode ::Home = > Code ::Home ,
TermKeyCode ::End = > Code ::End ,
TermKeyCode ::PageUp = > Code ::PageUp ,
TermKeyCode ::PageDown = > Code ::PageDown ,
TermKeyCode ::Tab = > Code ::Tab ,
2022-06-28 18:17:46 +00:00
// ? Apparently you get BackTab by pressing Tab
2022-05-12 11:10:25 +00:00
TermKeyCode ::BackTab = > Code ::Tab ,
TermKeyCode ::Delete = > Code ::Delete ,
TermKeyCode ::Insert = > Code ::Insert ,
TermKeyCode ::F ( 1 ) = > Code ::F1 ,
TermKeyCode ::F ( 2 ) = > Code ::F2 ,
TermKeyCode ::F ( 3 ) = > Code ::F3 ,
TermKeyCode ::F ( 4 ) = > Code ::F4 ,
TermKeyCode ::F ( 5 ) = > Code ::F5 ,
TermKeyCode ::F ( 6 ) = > Code ::F6 ,
TermKeyCode ::F ( 7 ) = > Code ::F7 ,
TermKeyCode ::F ( 8 ) = > Code ::F8 ,
TermKeyCode ::F ( 9 ) = > Code ::F9 ,
TermKeyCode ::F ( 10 ) = > Code ::F10 ,
TermKeyCode ::F ( 11 ) = > Code ::F11 ,
TermKeyCode ::F ( 12 ) = > Code ::F12 ,
TermKeyCode ::F ( 13 ) = > Code ::F13 ,
TermKeyCode ::F ( 14 ) = > Code ::F14 ,
TermKeyCode ::F ( 15 ) = > Code ::F15 ,
TermKeyCode ::F ( 16 ) = > Code ::F16 ,
TermKeyCode ::F ( 17 ) = > Code ::F17 ,
TermKeyCode ::F ( 18 ) = > Code ::F18 ,
TermKeyCode ::F ( 19 ) = > Code ::F19 ,
TermKeyCode ::F ( 20 ) = > Code ::F20 ,
TermKeyCode ::F ( 21 ) = > Code ::F21 ,
TermKeyCode ::F ( 22 ) = > Code ::F22 ,
TermKeyCode ::F ( 23 ) = > Code ::F23 ,
TermKeyCode ::F ( 24 ) = > Code ::F24 ,
TermKeyCode ::F ( other ) = > {
panic! ( " Unexpected function key: {other:?} " )
}
// this is a horrible way for crossterm to represent keys but we have to deal with it
TermKeyCode ::Char ( c ) = > match c {
2022-03-10 03:06:45 +00:00
'A' ..= 'Z' | 'a' ..= 'z' = > match c . to_ascii_uppercase ( ) {
2022-05-12 11:10:25 +00:00
'A' = > Code ::KeyA ,
'B' = > Code ::KeyB ,
'C' = > Code ::KeyC ,
'D' = > Code ::KeyD ,
'E' = > Code ::KeyE ,
'F' = > Code ::KeyF ,
'G' = > Code ::KeyG ,
'H' = > Code ::KeyH ,
'I' = > Code ::KeyI ,
'J' = > Code ::KeyJ ,
'K' = > Code ::KeyK ,
'L' = > Code ::KeyL ,
'M' = > Code ::KeyM ,
'N' = > Code ::KeyN ,
'O' = > Code ::KeyO ,
'P' = > Code ::KeyP ,
'Q' = > Code ::KeyQ ,
'R' = > Code ::KeyR ,
'S' = > Code ::KeyS ,
'T' = > Code ::KeyT ,
'U' = > Code ::KeyU ,
'V' = > Code ::KeyV ,
'W' = > Code ::KeyW ,
'X' = > Code ::KeyX ,
'Y' = > Code ::KeyY ,
'Z' = > Code ::KeyZ ,
2022-06-28 18:17:46 +00:00
_ = > unreachable! ( " Exhaustively checked all characters in range A..Z " ) ,
2022-03-10 03:06:45 +00:00
} ,
2022-05-12 11:10:25 +00:00
' ' = > Code ::Space ,
'[' | '{' = > Code ::BracketLeft ,
']' | '}' = > Code ::BracketRight ,
';' = > Code ::Semicolon ,
':' = > Code ::Semicolon ,
',' = > Code ::Comma ,
'<' = > Code ::Comma ,
'.' = > Code ::Period ,
'>' = > Code ::Period ,
'1' = > Code ::Digit1 ,
'2' = > Code ::Digit2 ,
'3' = > Code ::Digit3 ,
'4' = > Code ::Digit4 ,
'5' = > Code ::Digit5 ,
'6' = > Code ::Digit6 ,
'7' = > Code ::Digit7 ,
'8' = > Code ::Digit8 ,
'9' = > Code ::Digit9 ,
'0' = > Code ::Digit0 ,
'!' = > Code ::Digit1 ,
'@' = > Code ::Digit2 ,
'#' = > Code ::Digit3 ,
'$' = > Code ::Digit4 ,
'%' = > Code ::Digit5 ,
'^' = > Code ::Digit6 ,
'&' = > Code ::Digit7 ,
'*' = > Code ::Digit8 ,
'(' = > Code ::Digit9 ,
')' = > Code ::Digit0 ,
2022-06-28 18:17:46 +00:00
// numpad characters are ambiguous; we don't know which key was really pressed
// it could be also:
2022-05-12 11:10:25 +00:00
// '*' => Code::Multiply,
// '/' => Code::Divide,
// '-' => Code::Subtract,
// '+' => Code::Add,
'+' = > Code ::Equal ,
'-' | '_' = > Code ::Minus ,
'\'' = > Code ::Quote ,
'"' = > Code ::Quote ,
'\\' = > Code ::Backslash ,
'|' = > Code ::Backslash ,
'/' = > Code ::Slash ,
'?' = > Code ::Slash ,
'=' = > Code ::Equal ,
'`' = > Code ::Backquote ,
'~' = > Code ::Backquote ,
2022-03-10 03:06:45 +00:00
_ = > return None ,
2022-05-12 11:10:25 +00:00
} ,
TermKeyCode ::Null = > return None ,
TermKeyCode ::Esc = > Code ::Escape ,
2022-03-10 03:06:45 +00:00
} ;
2022-05-12 11:10:25 +00:00
Some ( code )
}
fn modifiers_from_crossterm_modifiers ( src : KeyModifiers ) -> Modifiers {
let mut modifiers = Modifiers ::empty ( ) ;
if src . contains ( KeyModifiers ::SHIFT ) {
modifiers . insert ( Modifiers ::SHIFT ) ;
}
if src . contains ( KeyModifiers ::ALT ) {
modifiers . insert ( Modifiers ::ALT ) ;
}
if src . contains ( KeyModifiers ::CONTROL ) {
modifiers . insert ( Modifiers ::CONTROL ) ;
}
modifiers
2022-02-05 22:28:19 +00:00
}