9

I have a custom NSView called SurfaceView. It is the contentView of a NSWindow and it handles basic events like mouse click and drawing. But don't matters what I do, it does not handle the keyDown function. I've already override the acceptsFirstResponder but nothing happens.

If it matters, I run the application using a custom NSEvent loop, shown below:

NSDictionary* info = [[NSBundle mainBundle] infoDictionary];
NSString* mainNibName = [info objectForKey:@"NSMainNibFile"];

NSApplication* app = [NSApplication sharedApplication];
NSNib* mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:[NSBundle mainBundle]];
[mainNib instantiateNibWithOwner:app topLevelObjects:nil];

[app finishLaunching];

while(true)
{   
    NSEvent* event = [app nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate date] inMode:NSDefaultRunLoopMode dequeue:YES];
    [app sendEvent:event];

    // Some code is execute here every frame to do some tasks...

    usleep(5000);
}

Here's the SurfaceView code:

@interface SurfaceView : NSView
{
    Panel* panel;
}

@property (nonatomic) Panel* panel;

- (void)drawRect:(NSRect)dirtyRect;
- (BOOL)isFlipped;
- (void)mouseDown:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
- (void)keyDown:(NSEvent *)theEvent;
- (BOOL)acceptsFirstResponder;
- (BOOL)becomeFirstResponder;

@end

--

@implementation SurfaceView

@synthesize panel;

- (BOOL)acceptsFirstResponder
{
    return YES;
};

- (void)keyDown:(NSEvent *)theEvent
{
    // this function is never called
};

...

@end

Here's how I create the view:

NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(left, top, wide, tall) styleMask:NSBorderlessWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:NO];

...

[window makeKeyAndOrderFront:nil];

SurfaceView* mainView = [SurfaceView alloc];
[mainView initWithFrame:NSMakeRect(0, 0, wide, tall)];
mainView.panel = panel;
[window setContentView:mainView];
[window setInitialFirstResponder:mainView];
[window setNextResponder:mainView];
[window makeFirstResponder:mainView];
user1467310
  • 542
  • 4
  • 9

3 Answers3

29

I found out what was preventing the keyDown event from being called. It was the NSBorderlessWindowMask mask, it prevents the window from become the key and main window. So I have created a subclass of NSWindow called BorderlessWindow:

@interface BorderlessWindow : NSWindow
{
}

@end

@implementation BorderlessWindow

- (BOOL)canBecomeKeyWindow
{
    return YES;
}

- (BOOL)canBecomeMainWindow
{
    return YES;
}

@end
user1467310
  • 542
  • 4
  • 9
  • Holly s*! You save my day! – skywinder Oct 21 '14 at 13:57
  • Thanks so much for this. You saved me a lot of time. For those using swift with NSViewController you can simply make an extension of NSWindow and return true on these methods. – Epic Byte Apr 03 '15 at 22:31
  • 1
    @Epic Byte - This is not correct. According to the [documentation](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html) : "You cannot use extensions to override existing methods or properties on Objective-C types" – Teo Sartori Jul 04 '15 at 19:47
  • @TeoSartori That comment was made when overriding was possible with extensions. – Epic Byte Jul 04 '15 at 20:11
  • 1
    Then it is worth correcting. I wasted some time not getting it working before doubting the comment and checking the docs. – Teo Sartori Jul 04 '15 at 20:39
  • After two hours of search, this was the solution – Swift1 Jan 24 '17 at 00:47
2

In addition to answer: Check in your IB checkbox for NSWindow.

Title Bar should be checked. It the similar to NSBorderlessWindowMask

enter image description here

skywinder
  • 21,291
  • 15
  • 93
  • 123
0

In my case I had to override the keyDown method on both NSView and NSWindow (created a customization of both). In the NSWindow in the keyDown override I had to call

- (void) keyDown:(NSEvent *) event {

    if (self.firstResponder != self) {
    
        [self.firstResponder keyDown:event];
    }
}

to let the event reach the view. Of course I had also to call makeFirstResponder first on the NSWindow and pass the contentView to it :

[window makeFirstResponder: [window contentView] ];
Bemipefe
  • 1,397
  • 4
  • 17
  • 30