@@ -29,6 +29,12 @@ use super::{
29
29
/// Name of the field used to store the `WindowState` pointer.
30
30
pub ( super ) const BASEVIEW_STATE_IVAR : & str = "baseview_state" ;
31
31
32
+ #[ link( name = "AppKit" , kind = "framework" ) ]
33
+ extern "C" {
34
+ static NSWindowDidBecomeKeyNotification : id ;
35
+ static NSWindowDidResignKeyNotification : id ;
36
+ }
37
+
32
38
macro_rules! add_simple_mouse_class_method {
33
39
( $class: ident, $sel: ident, $event: expr) => {
34
40
#[ allow( non_snake_case) ]
@@ -94,6 +100,18 @@ macro_rules! add_simple_keyboard_class_method {
94
100
} ;
95
101
}
96
102
103
+ unsafe fn register_notification ( observer : id , notification_name : id , object : id ) {
104
+ let notification_center: id = msg_send ! [ class!( NSNotificationCenter ) , defaultCenter] ;
105
+
106
+ let _: ( ) = msg_send ! [
107
+ notification_center,
108
+ addObserver: observer
109
+ selector: sel!( handleNotification: )
110
+ name: notification_name
111
+ object: object
112
+ ] ;
113
+ }
114
+
97
115
pub ( super ) unsafe fn create_view ( window_options : & WindowOpenOptions ) -> id {
98
116
let class = create_view_class ( ) ;
99
117
@@ -103,6 +121,9 @@ pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id {
103
121
104
122
view. initWithFrame_ ( NSRect :: new ( NSPoint :: new ( 0. , 0. ) , NSSize :: new ( size. width , size. height ) ) ) ;
105
123
124
+ register_notification ( view, NSWindowDidBecomeKeyNotification , nil) ;
125
+ register_notification ( view, NSWindowDidResignKeyNotification , nil) ;
126
+
106
127
let _: id = msg_send ! [
107
128
view,
108
129
registerForDraggedTypes: NSArray :: arrayWithObjects( nil, & [ NSFilenamesPboardType ] )
@@ -124,6 +145,14 @@ unsafe fn create_view_class() -> &'static Class {
124
145
sel ! ( acceptsFirstResponder) ,
125
146
property_yes as extern "C" fn ( & Object , Sel ) -> BOOL ,
126
147
) ;
148
+ class. add_method (
149
+ sel ! ( becomeFirstResponder) ,
150
+ become_first_responder as extern "C" fn ( & Object , Sel ) -> BOOL ,
151
+ ) ;
152
+ class. add_method (
153
+ sel ! ( resignFirstResponder) ,
154
+ resign_first_responder as extern "C" fn ( & Object , Sel ) -> BOOL ,
155
+ ) ;
127
156
class. add_method ( sel ! ( isFlipped) , property_yes as extern "C" fn ( & Object , Sel ) -> BOOL ) ;
128
157
class. add_method (
129
158
sel ! ( preservesContentInLiveResize) ,
@@ -177,6 +206,10 @@ unsafe fn create_view_class() -> &'static Class {
177
206
dragging_updated as extern "C" fn ( & Object , Sel , id ) -> NSUInteger ,
178
207
) ;
179
208
class. add_method ( sel ! ( draggingExited: ) , dragging_exited as extern "C" fn ( & Object , Sel , id ) ) ;
209
+ class. add_method (
210
+ sel ! ( handleNotification: ) ,
211
+ handle_notification as extern "C" fn ( & Object , Sel , id ) ,
212
+ ) ;
180
213
181
214
add_mouse_button_class_method ! ( class, mouseDown, ButtonPressed , MouseButton :: Left ) ;
182
215
add_mouse_button_class_method ! ( class, mouseUp, ButtonReleased , MouseButton :: Left ) ;
@@ -208,6 +241,25 @@ extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL
208
241
YES
209
242
}
210
243
244
+ extern "C" fn become_first_responder ( this : & Object , _sel : Sel ) -> BOOL {
245
+ let state = unsafe { WindowState :: from_view ( this) } ;
246
+ let is_key_window = unsafe {
247
+ let window: id = msg_send ! [ this, window] ;
248
+ let is_key_window: BOOL = msg_send ! [ window, isKeyWindow] ;
249
+ is_key_window == YES
250
+ } ;
251
+ if is_key_window {
252
+ state. trigger_event ( Event :: Window ( WindowEvent :: Focused ) ) ;
253
+ }
254
+ YES
255
+ }
256
+
257
+ extern "C" fn resign_first_responder ( this : & Object , _sel : Sel ) -> BOOL {
258
+ let state = unsafe { WindowState :: from_view ( this) } ;
259
+ state. trigger_event ( Event :: Window ( WindowEvent :: Unfocused ) ) ;
260
+ YES
261
+ }
262
+
211
263
extern "C" fn window_should_close ( this : & Object , _: Sel , _sender : id ) -> BOOL {
212
264
let state = unsafe { WindowState :: from_view ( this) } ;
213
265
@@ -473,3 +525,30 @@ extern "C" fn dragging_exited(this: &Object, _sel: Sel, _sender: id) {
473
525
474
526
on_event ( & state, MouseEvent :: DragLeft ) ;
475
527
}
528
+
529
+ extern "C" fn handle_notification ( this : & Object , _cmd : Sel , notification : id ) {
530
+ unsafe {
531
+ let state = WindowState :: from_view ( this) ;
532
+
533
+ // The subject of the notication, in this case an NSWindow object.
534
+ let notification_object: id = msg_send ! [ notification, object] ;
535
+
536
+ // The NSWindow object associated with our NSView.
537
+ let window: id = msg_send ! [ this, window] ;
538
+
539
+ let first_responder: id = msg_send ! [ window, firstResponder] ;
540
+
541
+ // Only trigger focus events if the NSWindow that's being notified about is our window,
542
+ // and if the window's first responder is our NSView.
543
+ // If the first responder isn't our NSView, the focus events will instead be triggered
544
+ // by the becomeFirstResponder and resignFirstResponder methods on the NSView itself.
545
+ if notification_object == window && first_responder == this as * const Object as id {
546
+ let is_key_window: BOOL = msg_send ! [ window, isKeyWindow] ;
547
+ state. trigger_event ( Event :: Window ( if is_key_window == YES {
548
+ WindowEvent :: Focused
549
+ } else {
550
+ WindowEvent :: Unfocused
551
+ } ) ) ;
552
+ }
553
+ }
554
+ }
0 commit comments