NSStatusItem not keyboard navigable - macos

I've created an NSStatusItem for my app, but would like it to be navigable, as the system items are, when using Control+F8 (Control+fn+f8).
The status item is inexplicably skipped in the navigation sequence. Is there a secret handshake of accepting first responder or something that needs to be done for this?
This is basically all the setup code I have for the item:
statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(28)
statusItem.menu = menu
statusItem.button?.image = NSImage(named: "menuIcon")

I found a a similar question asked on quoara.com: http://www.quora.com/Why-cant-I-focus-third-party-icons-in-the-status-menu-area-on-OS-X-with-a-keyboard-shortcut-like-Ctrl-F8-SOLVED.
Quoting Colin Barrett:
The third party items are implemented with a different API (NSStatusItem) than the built-in ones (NSMenuExtra). Note that you can drag to re-arrange the menu extras but not the status items (which always appear to the left of menu extras).
Unfortunately NSMenuExtra is private API and with the Mac App Store you're likely to see less and less apps using it.
Just as an example of third party apps which do support this, you can probably F8 to the MenuMeters icon / graph.
So if you really want to make your status menu items available via keyboard you'll have to dig within Apple's private frameworks, however that's an unstable territory, as they're subject to change at any time, without any notification.

As of at least macOS 10.12, it is possible to navigate to an NSStatusItem using the keyboard with Control+F8. This change presumably occurred between OS X 10.10 and macOS 10.12 (I haven’t tested this with any versions earlier than 10.12).
In order to enable keyboard navigation, the NSStatusItem’s menu property must be defined.

Related

Mac OS X 10.10 Find window by title, find button by label and press it

I use Mac OS X 10.10 and I would like to write a program that looks continuously for a window analyzing all the names of the opened windows. When the windows appear, I would like that the program will look for a button with a specific label and once found it, the app should send it a "pressed message".
I would be able to do it under windows, but I am not so familiar with Mac.
I have found a question related to mine (How do I get a list of the window titles on the Mac OSX?), but I think the most difficult part is finding the button and sending it a "pressed message".
Thank you in advance!
What you are looking for is the Accessibilty APIs. These are mostly Core Foundation style C APIs and typically prefixed with AX.
You might also want to consider additional identifiers beyond window title as window titles are not necessarily unique.
Using the AX APIs is not easy and is extremely verbose. You can use them to explore the UI and find things and interact with them but you might have more limited success observing user interaction. That might require a more fragile combination with event monitoring using NSEvent globalMonitor or CGEventTap depending on the UI widgets involved.
Also note that using the AX APIs to control anything outside your app is not sandbox capable.

SDL 2.0 on Mac - how to activate/respond to greyed-out Toggle Full Screen menu item?

Working on porting a Windows game to Mac using SDL2, and I noticed on the Mac that the menu bar for the game includes a View menu with an item Toggle Full Screen. I'm assuming SDL put it there automatically, as I didn't request it anywhere in my code. However, since the game can run in a window or full screen (and the window is resizable), I figure I should make this menu item actually work somehow.
I know practically nothing about Mac OSX coding (I just followed a tutorial to get my SDL app to build on the Mac side in XCode), and I can't find anything in the SDL documentation about how to respond to, activate, or remove this menu item (or other ones that are greyed out, like the Minimize option in the Window menu). Best I could find online is a reference to it being added in a commit to the SDL code base, but nothing in the commit message about how one is supposed to hook it up to their application code.
So, how do I activate and respond to this menu item (and any other menu items that only show up in OSX builds for that matter, like Minimize and About)? And is it something I can hook up in a cross-platform way via SDL itself, or did SDL add something that I have to deal with using platform-specific OSX code?

64-bit replacement for [NSMenuItemCell menuView] and [NSMenuItemCell setMenuView]

What are the 64-bit replacements for the two methods -[NSMenuItemCell menuView] and -[NSMenuItemCell setMenuView:] of the NSMenuItemCell class?
How can I obtain the same results?
The NSMenuItemCell and NSMenuView have never been used to draw menus in any release version of Mac OS X. The following is an excerpt from the Mac OS X Developer Release Notes:
Notes specific to MacOS X Developer Preview 3
Menu
The implementation of menus has changed drastically; NSMenuView and
NSMenuItemCell are no longer used, -[NSMenu menuRepresentation] now
returns nil, and tear off menus are no longer available. For Developer
Preview 3, there is no support for menu item images. If there is no
text in the menu item, a placeholder text consisting of "< image >" or
"< image name >" will be inserted instead. Menu item state images are
not supported either and in their place the standard checkbox or dash
for on and mixed states are used.
(I'm not sure of the exact timeline of pre-Public beta Mac OS X, but for "Developer Preview 3", I'd guess we're talking around the late 1990s here).
For more info on how menus are currently implemented, see Application Menu and Pop-up List Programming Topics: How Menus Work
As 一二三 alluded to, you use NSMenu along with NSMenuItem to implement menus. In OS X 10.5 and greater, you can use custom NSView instances in NSMenuItems using the -setView: method.
To customise menu item drawing, you need to supply a custom view to NSMenuItem.

How to drag NSStatusItems

You all know the menu bar (or better said NSStatusBar) in Mac OS X.
There are some items which I can move and other which not.
I would like to be able to drag the NSStatusItem of my app.
Any idea how to implement this?
Although NSStatusItems appear near Apple's internal "menu extras", they are distinct and behave differently. It would be nice if Apple unified the items that can appear in the right-hand area of the menu bar, but for now the section is split into distinct "apple internal" (on the right), and "app-provided (NSStatusItem)" on the left.
You can visualize the distinction by putting your computer into screen capture mode (cmd-shift-4), and pressing the space bar to switch to "capture whole window". When you hover over Apple's menu icons, you'll see that they all live in a single window. This explains their ability to be easily managed and dragged about. Hovering over the other items reveals that each NSStatusItem is in fact living in a single window of its own (which happens to be owned by the application that installed it).
It's best to stick with NSStatusItem even though you can't drag them. It's a shortcoming from Apple which most users will understand, even if it's annoying. Emphasizing the positive tradeoffs of offering a more stable application for the long term will usually soften the opinions of your customers (or managers?) who are pushing for the draggability.
You'll have to use NSMenuExtra, not NSStatusItem, and make the menu item a bundle running inside the SystemUIServer process, not your own app. You'll also need code like that supplied by MenuCracker to get this to work.
NSMenuExtra is undocumented and unsupported, and therefore considered a "hack".
My guess for there being two APIs in the first place: a menu extra crashing (or memory leaking) means the entire SystemUIServer process crashing or memory leaking — including other third party modules as well as system-supplied ones. With a status item, on the other hand, such a problem would only affect your own code.
As of macOS Sierra 10.12 http://www.macworld.co.uk/how-to/mac-software/7-sierra-menu-bar-tips-how-use-mac-menu-bar-in-macos-sierra-3649163/
Third-party apps sometimes install as menu extras, have controls that exist in the menu bar, or can be relaunched as faceless apps despite not initially being so. As of macOS Sierra, these menu extras can be rearranged just like native ones. (This wasn't the case through to OS X El Capitan.)

How do I get keyboard events in an NSStatusWindowLevel window while my application is not frontmost?

After creating a translucent window (based on example code by Matt Gemmell) I want to get keyboard events in this window. It seems that there are only keyboard events when my application is the active application while I want keyboard events even when my application isn't active but the window is visible.
Basically I want behavior like that provided by the Quicksilver application (by blacktree).
Does anybody have any hints on how to do this?
There are two options:
Use GetEventMonitorTarget() with a tacked-on Carbon run loop to grab keyboard events. Sample code is available on this page at CocoaDev.
Register an event trap with CGEventTapCreate. Sample code can be found in this thread from the Apple developer mailing list.
Edit: Note that these methods only work if you check off “Enable access for assistive devices” in the Universal Access preference pane.
A simpler route that may work better for you is to make your app background-only. The discussion on CocoaDev of the LSUIElement plist key explains how to set it up. Basically, your application will not appear in the dock or the app switcher, and will not replace the current application's menu bar when activated. From a user perspective it's never the 'active' application, but any windows you open can get activated and respond to events normally. The only caveat is that you'll never get to show your menu bar, so you'll probably have to set up an NSStatusItem (one of those icon menus that show up on the right side of the menu bar) to control (i.e. quit, bring up prefs, etc.) your application.
Edit: I completely forgot about the Non-Activating Panel checkbox in Interface Builder. You need to use an NSPanel instead of an NSWindow to get this choice. This setting lets your panel accept clicks and keyboard input without activating your application. I'm betting that some mix of this setting and the Carbon Hot Keys API is what QuickSilver is using for their UI.
Update:
Apple actually seems to have changed everything again starting with 10.5 BTW (I recently upgraded and my sample code did not work as before).
Now you can indeed only capture keydown events setting up an event tap if you are either root or assistive devices are enabled, regardless on which level you plan to capture and regardless if you selected to capture (which allows you to modify and even discard events) or to be listen only. You can still get information when flags have changed (actually even change these) and other events, but keydown under no other circumstances.
However, using the carbon event handler and the method RegisterEventHotKey() allows you to register a hotkey and you'll get notified when it is pressed, you neither need to be root for that nor do you need anything like assistive devices enabled. I think Quicksilver is probably doing it that way.

Resources