NSWindow doesn't receive keyboard events - macos

I am creating an NSWindow from a bundle loaded at runtime so I don't have the source code for the NSWindow that is created at app launch. When I show my window, it receives mouse event but it does not receive any keyboard events. I have tried adding the window with the following methods:
[_myWindow makeKeyAndOrderFront:nil];
[[NSApplication sharedApplication].mainWindow addChildWindow:_myWindow ordered:NSWindowAbove];
[NSApp beginModalSessionForWindow:_myWindow];
In all 3 cases it appears the main window is eating all the keyboard events. The mouse events also leak through to the original window (I can see mouse over highlights and buttons respond to clicks through my window). I have also tried:
[_myWindow makeMainWindow];
[_myWindow orderWindow:NSWindowAbove relativeTo:[originalWindow windowNumber]];
Any tips to get keyboard events to land on my window would be greatly appreciated.

You can subclass NSApplication and override sendEvent: to print [[NSApplication sharedApplication] keyWindow] before sending the event to the keyWindow (don't forget to call the super to actually send the event).
If the key window does not receive the events, then you might have an event monitor somewhere that grabs the events before they are dispatched.

Related

How to catch Command+W key action in OS X?

I subclassed NSWindows and implemented the keyDown: and keyUp: method like this:
- (void)keyDown:(NSEvent*)event
{
NSLog(#"Down: '%#'", [event characters]);
[super keyDown:event];
}
- (void)keyUp:(NSEvent*)event
{
NSLog(#"Up: '%#'", [event characters]);
[super keyUp:event];
}
If I pressed "W" key, it prints both my "Down" and "Up" correctly. But If I pressed Command+W combination, It prints ONLY the "Down" message, and windows' close action was not triggered. How should I do?
Command-W is typically the key equivalent of a menu item. It is handled by the menu when it is told to -performKeyEquivalent:. The menu item sends its action method to its target. The action method is typically -performClose: and the target is usually the first responder.
So, the normal processing of Command-W does not use the window's -keyDown: or -keyUp: at all.
If you want to control whether the window closes, have the window delegate implement -windowShouldClose:. If you want to be notified when the window is about to close, implement -windowWillClose: in the window delegate or observe the NSWindowWillCloseNotification notification.
Regarding keyboard handling, a quirk of -[NSApplication sendEvent:] is that it just doesn't dispatch any key up event if the Command key was down. If for some reason you really need to see the key up event, you'll have to implement a custom subclass of NSApplication, configure your Info.plist to make sure it's used, and implement an override of -sendEvent:.

Intercepting option-close

Normally closing a window with option key down closes all the windows in the application. In my application, I'd like it to close only windows related to the window that the user was closing. How can I do that? I can implement windowShouldClose for all my windows, but how can I know which window the user clicked on?
You can see if the option key was held down in the event that is being processed like this:
([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)!=0
If this is in response to the user clicking the window's close button, then you can find the window that was clicked like this: [[NSApp currentEvent] window]
I suppose you should also check that [NSApp currentEvent] is a mouse event, etc., but it seems like this combination of tests should get you the info that you want.
If, on the other hand, this is the user choosing the "Close Window" command from the "File" menu with the option key held down, you can override the performClose: method from NSWindows default implementation to your own, where you would do the currentEvent test above before calling [super performClose: sender]

mouseMoved event stopping when mouse is down

I have implemented a custom NSView (marked as ) and am able to get mouseUp:, mouseDown: and mouseMoved: events. To this I have acceptsFirstResponder returning YES, and also call the following in awakeFromNib:
[[self window] makeFirstResponder:self];
[[self window] seAcceptsMouseMovedEvents:true];
However the extremely strange thing is that if I click and drag, the mouseMoved: events stop coming in until I let go of the mouse button and get a mouseUp:
I have seen other related posts, but they all say that if the view is first responder (and it is), then I should be receiving these events.
Why is the mousemoved: event stopping?
-(void)mouseDragged:(NSEvent *)event
is called instead of
-(void)mouseMoved:(NSEvent *)event
when the left mouse button is down. So if you have anything you need to do in both cases, call a common method to do it from both of these.

In Cocoa, how can my NSView receive an event when the mouse is held down (but not moved)?

I'm looking for the right way to handle "mouse held down in one spot" events in my NSView subclass.
I am familiar with Cocoa's mouseDragged: event, but it is only triggered when the mouse moves. If the mouse stays in the same position, no drag event is triggered. Similarly, mouseDown: is only fired when the button is first pressed. My view needs to perform an action as long as the mouse is held down in a particular region.
What is the proper way to do this kind of thing?
Can you start performing the action when you receive a mouseDown: event, and stop when you receive mouseUp: (or mouseDragged:, if you want to stop then, too)?
I'm not sure exactly what you're trying to accomplish, but if you want an action to be repeated at set time intervals after the mouseDown:, you could set a recurring NSTimer in the mouseDown: method that gets cancelled as soon as there is a mouseDragged: or mouseUp: event.

Getting a borderless window to receive mouseMoved events (Cocoa OSX)

I have a little popup window used for selecting images sorted by groups, and I would like to add a selection box around whatever image is being hovered over. I am trying to this by overriding the mouseMoved event for the window but it seems that a window that has a border-less style mask receives no mouseMoved events even if you have set setAcceptsMouseMoved events to YES. Is there anyway to make a borderless window receive this events?
You need to allow the window to become the key window. By default, borderless windows cannot become key. Subclass NSWindow and override -canBecomeKeyWindow:
- (BOOL)canBecomeKeyWindow
{
return YES;
}
Aternatively, you can use an NSTrackingArea to do your mouse tracking, which may be easier/better anyway.

Resources