0
votes

The application I'm developing needs to be able to veto menubar clicks. There are a number of ways to be notified of when a menu is clicked (NSMenu notifications and NSMenu delegation, mostly), but as far as I can see, none of them will let you refuse the action.

The solution I've been thinking of is to use a borderless and transparent window, and overlay it on the menu bar. By setting the window level to a high enough value (NSStatusWindowLevel in this case), it is effectively positioned and drawn above the menu bar, catching mouse events that would otherwise go to it. That way, I have what I'm looking for.

The problem with that solution is that it also masks events for the status bar (the right-aligned, global menu items), and I don't need, or want, to veto those. To fix that issue, I would need to be able to tell either the span of the status bar, or the span of the menu bar, but I found no obvious way for either. NSApp.mainMenu.size returns the size of the main menu as if it was laid out in a popup menu, which is obviously not what I need.

Is there any better way to mask events to the menu bar, or is there any way to find its width excluding the status bar?

This probably all sounds very dirty, and it is. I'm doing it for an application-level emulator for an old platform where the application was responsible for showing menus when the user clicks in the menu bar, and therefore could do pretty much anything it wants when that happens.

1

1 Answers

1
votes

The width of the menu bar is easy: It's the width of the menu bar screen. And that includes all of the application's menus:

Screenshot of the menu bar, all the way across the screen, with the menus included.

That's from Layers, so I don't think you'll be able to do better in your app.

So, you're going to have to subtract the status bar.

This is easy enough with Quartz Window Services.

First, create a borderless, non-opaque, clear-background, empty window on the main menu window level and then order it back—that is, behind the real main menu.

Then, get your window's window number, and pass that to the CGWindowListCreate function, specifying that you want all of the windows above your other window.

For extra robustness (just to make sure the cursor or something doesn't get included), do the same thing in the opposite direction: Create a second window on the same window level, order it front (in front of the menu and status bars), and create a list of the windows that are behind that window.

The windows that are in both arrays are the menu bar and all of the status items. The one whose width is that of the main menu screen ([NSScreen screens][0]) is the menu bar; union all of the status items' rectangles together, and cut the menu bar's rectangle to end at the origin of the status items' rectangle.

Note that all of this assumes that the menu bar and status items are organized a certain way, which is subject to change in any OS update.