I’m currently working on an application that runs in a dual headless/GUI model.
The main interaction point is a MAAttachedWindow (hattip to Matt Gemmell) attached to a NSStatusItem that lives in the Menu Bar. The user can invoke this manually or it becomes active when they use the Mac OS X Services contextual menu on some text.
They can however open the application itself for extra functionality, using the dock icon/application switcher. (I do offer the ability to disable this and use it purely through the NSStatusItem, but that’s not relevant to this post). This makes running purely headless, using LSUIElement not an option.
So, my MAAttachedWindow contains a couple of NSTextFields and NSDatePickers, used to present values to the user for copying/editing. The problem I was having was that when the application is invokved the first text field would focus but as the application itself wasn’t in the foreground it wouldn’t accept user input. The user would have to click on the item to focus and this caused the application come to the foreground.
I find this confusing, in this mode of operation I want the fact that I’m running as an application rather than a built in OSX utility to be transparent, the application title shouldn’t change, the menus shouldn’t change, everything should remain as is apart from this dropdown.
The behaviour I want is that the MAAttachedWindow becomes frontmost, accepts keyboard input, but doesn’t switch application context unless the user specifically opens it from the doc, application switcher or the menu option in the status bar.
A screenshot would be useful at this point, right? The application is totally not finished, the UI is a mess but here’s one anyway.
Yes, it’s a small utility that allows you to easily convert unix timestamps to human readable times and vice versa. It integrates with the Services menu so that if you have a timestamp selected you can invoke it with a keypress and see the results. The whole thing should be very focused around identifying a timestamp in a document you’re working on, selecting it, seeing the translation and dismissing it. Alternatively you can use the date pickers to select a new time and get the timestamp for that. This should all be possible with key presses without leaving the current application, thus allowing you to easily paste the results back in.
So, the solution is to make MAAttachedWindow an NSPanel and tell it to act as a Non Activating Panel. Non activating panels can become key without activating the application that owns them.
Matt’s code already takes care of the utility of the two style masks I usually use when creating floating panels, (NSUtilityWindowMask
to create as a floating window and NSHUDWindowMask to creat as a transparent panel, sometimes called a “heads-up display”).
So the trick is simply to use the style mask of
No effect on appearance, but owning application is not necessarily active when this window is the key window.
We also need to ensure it always displays on top of other applications, we do this by setting its display level sufficiently high.
To make the changes;
1) In MAAttachedWindow.h change the parent type from NSWindow to NSPanel.
2) In MAAttachedWindow.m
- (MAAttachedWindow *)initWithView:(NSView *)view
and in the
if ((self = [super initWithContentRect:contentRect
call set NSBorderlessWindowMask to be NSNonactivatingPanelMask
3) Beneath that set
That’s it. You now have a NSPanel that floats above all Windows and when invoked gives focus to the first responder but doesn’t bring the applicatoin itself to the foreground. Perfect for helper services that live in the menubar.
I’ll be releasing the app itself as open source in the next couple of weeks so watch this space.