子类 NSApplication 这样你就可以捕获预处理 NSEvents,收集你需要的信息,然后你的 NSControl 子类可以检索这些信息。
在我的例子中,我使用这种方法来避免在我非常大的多屏幕 UI 中悬挂字段编辑器。
@interface NSApplicationEventCatcher : NSApplication
{
}
- (void)sendEventDirectly:(NSEvent *)event;
+(void)setExcludedResponder:(NSResponder *)iResponder;
@end
- (void)sendEvent:(NSEvent *)event
{
// do some checking here (see example code below)
[super sendEvent:event];
}
在 main() 中,首先实例化 NSApplicationEventCatcher,
[NSApplicationEventCatcher sharedApplication];
在调用 NSApplicationMain() 之前
NSApplicationMain(argc, (const char **) argv);
现在,这是我在 NSApplicationEventCatcher sendEvent override 中所做的一些检查。
然而,这只是该解决方案的一小部分。
if ( [event type] == NSLeftMouseDown )
{
gVAppCancelAction = kVAppCancelOtherWindow;
//NSLog( @"before mouse down window %@ first responder %@", [[event window] description], [[[event window] firstResponder] description] );
if ( [event window] )
{
gVAppCancelAction = kVAppCancelMouseDown;
NSTextView *theFirstResponder = (NSTextView *)[[event window] firstResponder];
if ( theFirstResponder && sExcludedResponder != theFirstResponder )
sExcludedResponder = nil; // reset
if ( [theFirstResponder isKindOfClass:[NSTextView class]] )
{
NSPoint clickLocation;
// convert the mouse-down location into the view coords
clickLocation = [theFirstResponder convertPoint:[event locationInWindow]
fromView:nil];
// did the mouse-down occur in the item?
BOOL itemHit = NSPointInRect(clickLocation, [theFirstResponder bounds]);
id delegate = [(NSTextView *)theFirstResponder delegate];
if ( [delegate isKindOfClass: [NSComboBox class]] )
{
itemHit |= NSPointInRect(clickLocation, [delegate bounds]);
}
if (itemHit)
{
VLog::Log( kLogDbgNoteType, @"clicked on first responder %@", [[[event window] firstResponder] description] );
excludeResponder = theFirstResponder;
}
else
{
NSView *theContentView = [[event window] contentView];
if ( [theContentView isKindOfClass:[NSView class]] )
{
NSView *theHitView = [theContentView hitTest:[event locationInWindow]];
if ( theHitView == nil || theHitView == theContentView )
{
gVAppCancelAction = kVAppCancelLayerView;
}
else
{
gVAppCancelAction = kVAppCancelMouseDown;
if ( sExcludedResponder == theFirstResponder )
excludeResponder = theFirstResponder;
/*
if ( [theHitView isKindOfClass:[LayerView class]] )
{
NSView *theSuperview = [theHitView superview];
if ( theSuperview && [theSuperview isKindOfClass:[LayerView class]] )
{
// ignore VNumericKeypad-like views which are like pop-up dialog views on
// top of a LayerView superview.
gVAppCancelAction = kVAppCancelMouseDown;
if ( sExcludedResponder == theFirstResponder )
excludeResponder = theFirstResponder;
}
else
gVAppCancelAction = kVAppCancelLayerView;
}
else
{
if ( sExcludedResponder == theFirstResponder )
excludeResponder = theFirstResponder;
}
*/
}
}
}
}
}
}
这是一个相关的部分:
for ( NSWindow *theWindow in [self windows] )
{
NSResponder *theResponder = [theWindow firstResponder];
if ( theResponder != theWindow && theResponder && theResponder != excludeResponder )
{
// tbd could also check for [theResponder isKindOfClass:[NSControl class]] and call abortEditing
if ( [theResponder isKindOfClass:[NSTextView class]] && [(NSTextView *)theResponder isFieldEditor] )
{
NSWindow *evwindow = [event window];
NSArray *childwindows = [theWindow childWindows];
if ( evwindow && [childwindows containsObject:evwindow] )
{
// pass through clicks on attached NSMenu or NSComboBox
VLog::Log( kLogDbgNoteType, @"clicked child event window %@, my window %@", evwindow, theWindow );
break;
}
VLog::Log( kLogDbgNoteType, @"NSApplicationEventCatcher before cancel first responder %@", [theResponder description] );
BOOL cancelSucceeded;
if ( evwindow != theWindow && gVAppCancelAction == kVAppCancelMouseDown )
{
gVAppCancelAction = kVAppCancelOtherWindow;
cancelSucceeded = [theWindow makeFirstResponder:theWindow];
gVAppCancelAction = kVAppCancelMouseDown;
}
else
cancelSucceeded =[theWindow makeFirstResponder:theWindow];
if ( !cancelSucceeded )
{
VLog::Log( kLogDbgNoteType, @"Application about to FORCE cancel field editor %@", [[theWindow firstResponder] description] );
[theWindow endEditingFor:nil];
}
VLog::Log( kLogDbgNoteType, @"NSApplicationEventCatcher after cancel first responder %@", [[theWindow firstResponder] description] );
}
}
}