@@ -29,6 +29,12 @@ use super::{
2929/// Name of the field used to store the `WindowState` pointer.
3030pub ( super ) const BASEVIEW_STATE_IVAR : & str = "baseview_state" ;
3131
32+ #[ link( name = "AppKit" , kind = "framework" ) ]
33+ extern "C" {
34+ static NSWindowDidBecomeKeyNotification : id ;
35+ static NSWindowDidResignKeyNotification : id ;
36+ }
37+
3238macro_rules! add_simple_mouse_class_method {
3339 ( $class: ident, $sel: ident, $event: expr) => {
3440 #[ allow( non_snake_case) ]
@@ -94,6 +100,18 @@ macro_rules! add_simple_keyboard_class_method {
94100 } ;
95101}
96102
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+
97115pub ( super ) unsafe fn create_view ( window_options : & WindowOpenOptions ) -> id {
98116 let class = create_view_class ( ) ;
99117
@@ -103,6 +121,9 @@ pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id {
103121
104122 view. initWithFrame_ ( NSRect :: new ( NSPoint :: new ( 0. , 0. ) , NSSize :: new ( size. width , size. height ) ) ) ;
105123
124+ register_notification ( view, NSWindowDidBecomeKeyNotification , nil) ;
125+ register_notification ( view, NSWindowDidResignKeyNotification , nil) ;
126+
106127 let _: id = msg_send ! [
107128 view,
108129 registerForDraggedTypes: NSArray :: arrayWithObjects( nil, & [ NSFilenamesPboardType ] )
@@ -124,6 +145,14 @@ unsafe fn create_view_class() -> &'static Class {
124145 sel ! ( acceptsFirstResponder) ,
125146 property_yes as extern "C" fn ( & Object , Sel ) -> BOOL ,
126147 ) ;
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+ ) ;
127156 class. add_method ( sel ! ( isFlipped) , property_yes as extern "C" fn ( & Object , Sel ) -> BOOL ) ;
128157 class. add_method (
129158 sel ! ( preservesContentInLiveResize) ,
@@ -177,6 +206,10 @@ unsafe fn create_view_class() -> &'static Class {
177206 dragging_updated as extern "C" fn ( & Object , Sel , id ) -> NSUInteger ,
178207 ) ;
179208 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+ ) ;
180213
181214 add_mouse_button_class_method ! ( class, mouseDown, ButtonPressed , MouseButton :: Left ) ;
182215 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
208241 YES
209242}
210243
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+
211263extern "C" fn window_should_close ( this : & Object , _: Sel , _sender : id ) -> BOOL {
212264 let state = unsafe { WindowState :: from_view ( this) } ;
213265
@@ -473,3 +525,30 @@ extern "C" fn dragging_exited(this: &Object, _sel: Sel, _sender: id) {
473525
474526 on_event ( & state, MouseEvent :: DragLeft ) ;
475527}
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