22use std:: cell:: { Cell , RefCell } ;
33use std:: collections:: { HashMap , VecDeque } ;
44use std:: ptr;
5- use std:: rc:: Rc ;
5+ use std:: rc:: { Rc , Weak } ;
66
77use dpi:: { LogicalPosition , LogicalSize } ;
88use objc2:: rc:: Retained ;
@@ -17,6 +17,7 @@ use objc2_foundation::{
1717 NSArray , NSAttributedString , NSAttributedStringKey , NSCopying , NSMutableAttributedString ,
1818 NSNotFound , NSObject , NSPoint , NSRange , NSRect , NSSize , NSString , NSUInteger ,
1919} ;
20+ use smol_str:: SmolStr ;
2021use winit_core:: event:: {
2122 DeviceEvent , ElementState , Ime , KeyEvent , Modifiers , MouseButton , MouseScrollDelta ,
2223 PointerKind , PointerSource , TouchPhase , WindowEvent ,
@@ -45,6 +46,24 @@ impl Default for CursorState {
4546 }
4647}
4748
49+ #[ derive( Debug ) ]
50+ struct EventFilterToken {
51+ deliver : Cell < bool > ,
52+ }
53+
54+ impl EventFilterToken {
55+ fn new ( ) -> Self {
56+ Self { deliver : Cell :: new ( true ) }
57+ }
58+ }
59+
60+ #[ derive( Debug ) ]
61+ struct PendingRawCharacter {
62+ serial : u64 ,
63+ text : SmolStr ,
64+ token : Weak < EventFilterToken > ,
65+ }
66+
4867#[ derive( Debug , Eq , PartialEq , Clone , Copy , Default ) ]
4968enum ImeState {
5069 #[ default]
@@ -135,6 +154,10 @@ pub struct ViewState {
135154 marked_text : RefCell < Retained < NSMutableAttributedString > > ,
136155 accepts_first_mouse : bool ,
137156
157+ current_event_serial : Cell < u64 > ,
158+ last_handled_event_serial : Cell < u64 > ,
159+ pending_raw_characters : RefCell < Vec < PendingRawCharacter > > ,
160+
138161 /// The state of the `Option` as `Alt`.
139162 option_as_alt : Cell < OptionAsAlt > ,
140163}
@@ -395,6 +418,7 @@ define_class!(
395418
396419 // Commit only if we have marked text.
397420 if self . hasMarkedText( ) && self . is_ime_enabled( ) && !is_control {
421+ self . drop_conflicting_raw_characters( & string) ;
398422 self . queue_event( WindowEvent :: Ime ( Ime :: Preedit ( String :: new( ) , None ) ) ) ;
399423 self . queue_event( WindowEvent :: Ime ( Ime :: Commit ( string) ) ) ;
400424 self . ivars( ) . ime_state. set( ImeState :: Committed ) ;
@@ -445,6 +469,8 @@ define_class!(
445469 #[ unsafe ( method( keyDown: ) ) ]
446470 fn key_down( & self , event: & NSEvent ) {
447471 trace_scope!( "keyDown:" ) ;
472+ self . begin_key_event( ) ;
473+ let mut ime_consumed_event = false ;
448474 {
449475 let mut prev_input_source = self . ivars( ) . input_source. borrow_mut( ) ;
450476 let current_input_source = self . current_input_source( ) ;
@@ -468,8 +494,11 @@ define_class!(
468494 // `doCommandBySelector`. (doCommandBySelector means that the keyboard input
469495 // is not handled by IME and should be handled by the application)
470496 if self . ivars( ) . ime_capabilities. get( ) . is_some( ) {
471- let events_for_nsview = NSArray :: from_slice( & [ & * event] ) ;
472- self . interpretKeyEvents( & events_for_nsview) ;
497+ ime_consumed_event = self . handle_text_input_event( & event) ;
498+ if !ime_consumed_event {
499+ let events_for_nsview = NSArray :: from_slice( & [ & * event] ) ;
500+ self . interpretKeyEvents( & events_for_nsview) ;
501+ }
473502
474503 // If the text was committed we must treat the next keyboard event as IME related.
475504 if self . ivars( ) . ime_state. get( ) == ImeState :: Committed {
@@ -491,30 +520,31 @@ define_class!(
491520 _ => old_ime_state != self . ivars( ) . ime_state. get( ) ,
492521 } ;
493522
494- if !had_ime_input || self . ivars( ) . forward_key_to_app. get( ) {
523+ if self . ivars( ) . forward_key_to_app. get( ) || ( !had_ime_input && !ime_consumed_event ) {
495524 let key_event = create_key_event( & event, true , event. isARepeat( ) ) ;
496- self . queue_event( WindowEvent :: KeyboardInput {
497- device_id: None ,
498- event: key_event,
499- is_synthetic: false ,
500- } ) ;
525+ self . queue_keyboard_input_event( key_event, false ) ;
501526 }
502527 }
503528
504529 #[ unsafe ( method( keyUp: ) ) ]
505530 fn key_up( & self , event: & NSEvent ) {
506531 trace_scope!( "keyUp:" ) ;
532+ self . begin_key_event( ) ;
533+ let mut ime_consumed_event = false ;
507534
508535 let event = replace_event( event, self . option_as_alt( ) ) ;
509536 self . update_modifiers( & event, false ) ;
510537
538+ if self . ivars( ) . ime_capabilities. get( ) . is_some( ) {
539+ ime_consumed_event = self . handle_text_input_event( & event) ;
540+ }
541+
511542 // We want to send keyboard input when we are currently in the ground state.
512- if matches!( self . ivars( ) . ime_state. get( ) , ImeState :: Ground | ImeState :: Disabled ) {
513- self . queue_event( WindowEvent :: KeyboardInput {
514- device_id: None ,
515- event: create_key_event( & event, false , false ) ,
516- is_synthetic: false ,
517- } ) ;
543+ if matches!( self . ivars( ) . ime_state. get( ) , ImeState :: Ground | ImeState :: Disabled )
544+ && !ime_consumed_event
545+ {
546+ let key_event = create_key_event( & event, false , false ) ;
547+ self . queue_keyboard_input_event( key_event, false ) ;
518548 }
519549 }
520550
@@ -561,11 +591,7 @@ define_class!(
561591 self . update_modifiers( & event, false ) ;
562592 let event = create_key_event( & event, true , event. isARepeat( ) ) ;
563593
564- self . queue_event( WindowEvent :: KeyboardInput {
565- device_id: None ,
566- event,
567- is_synthetic: false ,
568- } ) ;
594+ self . queue_keyboard_input_event( event, false ) ;
569595 }
570596
571597 // In the past (?), `mouseMoved:` events were not generated when the
@@ -812,6 +838,9 @@ impl WinitView {
812838 forward_key_to_app : Default :: default ( ) ,
813839 marked_text : Default :: default ( ) ,
814840 accepts_first_mouse,
841+ current_event_serial : Cell :: new ( 0 ) ,
842+ last_handled_event_serial : Cell :: new ( 0 ) ,
843+ pending_raw_characters : RefCell :: new ( Vec :: new ( ) ) ,
815844 option_as_alt : Cell :: new ( option_as_alt) ,
816845 } ) ;
817846 let this: Retained < Self > = unsafe { msg_send ! [ super ( this) , init] } ;
@@ -832,6 +861,98 @@ impl WinitView {
832861 } ) ;
833862 }
834863
864+ fn queue_keyboard_input_event ( & self , key_event : KeyEvent , is_synthetic : bool ) {
865+ self . cleanup_pending_raw_characters ( ) ;
866+
867+ let serial = self . ivars ( ) . current_event_serial . get ( ) ;
868+ let token = key_event. text . as_ref ( ) . map ( |text| {
869+ let token = Rc :: new ( EventFilterToken :: new ( ) ) ;
870+ self . ivars ( ) . pending_raw_characters . borrow_mut ( ) . push ( PendingRawCharacter {
871+ serial,
872+ text : text. clone ( ) ,
873+ token : Rc :: downgrade ( & token) ,
874+ } ) ;
875+ token
876+ } ) ;
877+
878+ let window_event =
879+ WindowEvent :: KeyboardInput { device_id : None , event : key_event, is_synthetic } ;
880+ let window_id = window_id ( & self . window ( ) ) ;
881+
882+ if let Some ( token) = token {
883+ let event_to_dispatch = window_event. clone ( ) ;
884+ self . ivars ( ) . app_state . maybe_queue_with_handler ( move |app, event_loop| {
885+ if !token. deliver . get ( ) {
886+ return ;
887+ }
888+ app. window_event ( event_loop, window_id, event_to_dispatch. clone ( ) ) ;
889+ } ) ;
890+ } else {
891+ let event_to_dispatch = window_event;
892+ self . ivars ( ) . app_state . maybe_queue_with_handler ( move |app, event_loop| {
893+ app. window_event ( event_loop, window_id, event_to_dispatch. clone ( ) ) ;
894+ } ) ;
895+ }
896+ }
897+
898+ fn begin_key_event ( & self ) {
899+ let next = self . ivars ( ) . current_event_serial . get ( ) . wrapping_add ( 1 ) ;
900+ self . ivars ( ) . current_event_serial . set ( next) ;
901+ }
902+
903+ fn handle_text_input_event ( & self , event : & NSEvent ) -> bool {
904+ let Some ( input_context) = self . inputContext ( ) else {
905+ return false ;
906+ } ;
907+
908+ let serial = self . ivars ( ) . current_event_serial . get ( ) ;
909+ self . ivars ( ) . last_handled_event_serial . set ( serial) ;
910+
911+ input_context. handleEvent ( event)
912+ }
913+
914+ fn drop_conflicting_raw_characters ( & self , commit : & str ) {
915+ let serial = self . ivars ( ) . last_handled_event_serial . get ( ) ;
916+ let mut pending = self . ivars ( ) . pending_raw_characters . borrow_mut ( ) ;
917+
918+ let mut target: Option < ( Weak < EventFilterToken > , SmolStr ) > = None ;
919+
920+ for entry in pending. iter ( ) . rev ( ) {
921+ if entry. token . upgrade ( ) . is_none ( ) {
922+ continue ;
923+ }
924+
925+ if entry. serial == serial {
926+ target = Some ( ( entry. token . clone ( ) , entry. text . clone ( ) ) ) ;
927+ break ;
928+ }
929+ }
930+
931+ if target. is_none ( ) {
932+ if let Some ( entry) = pending. iter ( ) . rev ( ) . find ( |entry| entry. token . upgrade ( ) . is_some ( ) )
933+ {
934+ target = Some ( ( entry. token . clone ( ) , entry. text . clone ( ) ) ) ;
935+ }
936+ }
937+
938+ if let Some ( ( token, text) ) = target {
939+ if text. as_str ( ) != commit {
940+ if let Some ( token) = token. upgrade ( ) {
941+ token. deliver . set ( false ) ;
942+ }
943+ }
944+ }
945+
946+ pending. retain ( |entry| entry. token . upgrade ( ) . is_some ( ) ) ;
947+ }
948+
949+ fn cleanup_pending_raw_characters ( & self ) {
950+ self . ivars ( )
951+ . pending_raw_characters
952+ . borrow_mut ( )
953+ . retain ( |entry| entry. token . upgrade ( ) . is_some ( ) ) ;
954+ }
955+
835956 fn scale_factor ( & self ) -> f64 {
836957 self . window ( ) . backingScaleFactor ( ) as f64
837958 }
@@ -1030,7 +1151,12 @@ impl WinitView {
10301151 drop ( phys_mod_state) ;
10311152
10321153 for event in events {
1033- self . queue_event ( event) ;
1154+ match event {
1155+ WindowEvent :: KeyboardInput { event : key_event, is_synthetic, .. } => {
1156+ self . queue_keyboard_input_event ( key_event, is_synthetic) ;
1157+ } ,
1158+ other => self . queue_event ( other) ,
1159+ }
10341160 }
10351161 }
10361162 }
0 commit comments