In trying to learn the very fundamentals of menu handling. My test app's menubar has 3 menus -- namely "TestApp", "File" and "Help". I find I can remove these menus entirely, simply by calling say:
NSMenu* rootMenu = [NSApp mainMenu];
[rootMenu removeItemAtIndex:2];
However, I'd only ever want to temporarily disable them (gray them out). Is there an equally simple way to do this, please?
I may be misunderstanding your question, but it seems like you want to be able to gray-out the actual titles of menus that appear with the system's menu bar (Such as graying-out the "File" menu). I'm not sure if it's even possible, but it certainly goes against the Apple Human Interface Guidelines:
A menu’s title is displayed undimmed
even if all of the menu’s commands are
unavailable (dimmed) at the same time.
Users should always be able to view a
menu’s contents, whether or not they
are currently available.
So, the real solution to the problem is to be able to gray-out all of the menu items within a certain menu when your application is in a certain state. To do this, implement the NSUserInterfaceValidations protocol. It only requires implementing the - (BOOL)validateUserInterfaceItem: method. Typically, when implementing this method, you simply check the selector of the user interface item being validated, and return YES if it should be enabled, or NO if it should not (which will gray-out the menu item).
Related
I have an application that manages different types of NSDocument subclasses (along with matching NSWindow subclasses).
For instance, it's possible that the app has one window of type A open, and two windows of type B.
Now, if a window of type B is active, and the user chooses "Close All" or hits cmd+option+W, all my app's windows are sent the close message.
But I only want all of the active window type's windows closed instead, i.e. only the two type B, not the type A window. How do I accomplish this?
I currently have no explicit menu entry for "Close All". Instead, macOS provides that automagically. If there perhaps a way to intercept a "closeAll" message? Can't find one, though.
AppKit will add the Close All menu item if there isn't one. Add an alternate menu item item with key equivalent cmd+option+W below the Close menu and connect it to your own action method.
You might succeed with overriding your document's canClose(withDelegate:,shouldClose:,contextInfo:) to return whether a document should be closed.
If this doesn't behave the way you want, you can create a subclass of NSDocumentController (if you don't have one already). Details on how to do that vary, but usually you have main (menu) XIB or main Storyboard, which has a "Document Controller" object: set its class to your custom class.
Then override closeAllDocuments(withDelegate:,didCloseAllSelector:,contextInfo:) and implement your custom logic.
Note that you should detect whether your app is about to quit and then really do close all you documents (unless you really want to prevent the app quit, e.g. because a document is dirty).
After some digging I figured out where the auto-generated "Close All" menu item sends its action to: To the closeAll: selector of the application target:
Thus my solution is to subclass NSApplication and implement the handler there, which then simply closes all windows that are of the same type (this assumes that I use specific subclasses for my different types of windows):
- (IBAction)closeAll:(id)sender {
Class windowClass = self.keyWindow.class;
for (NSWindow *w in self.windows) {
if (windowClass == w.class) {
[w performClose:sender];
}
}
}
Caution: If you adopt this pattern be aware that:
The closeAll: selector is not documented nor mentioned in the header files, meaning that Apple might feel free to change in a future SDK, though I find that unlikely. It will probably not break anything if that happens, but instead your custom handler won't be called any more.
The code simply tells all windows to close, ignoring the fact that one might reject to be closed, e.g. by user interaction. In that case you may want to stop the loop instead of continuing to close more windows (though I know of no easy way to accomplish that).
I have an NSMenuItem for which I would like to use the key equivalent Command-Option-C. However, when I set the key equivalent in IB, it does not get associated with the menu item when the app is actually run. The entry has no visible key equivalent, and that command does not invoke the item. Other key equivalents, like Shift-Control-C, do indeed work. The one I am trying to use does not conflict with any other key equivalent in the app.
What could be causing this seemingly random problem?
Command-Option-C works just fine here. Could it be that you have a custom keyboard shortcut set up in the keyboard system preferences that uses the same key combination? That would override the application's own shortcuts.
Is it possible the menu item in question is a "special" menu item which may be getting substituted at launch-time by the system? If so, it would be helpful to know whether you are able to set the same keyboard shortcut on a different, perhaps less interesting menu item.
I don't really have an authoritative understanding of which menu items may get this kind of treatment, but have a suspicion for example that maybe the "Help" menu, "Application" menu, or others that are common across many apps get tweaked or even regenerated dynamically, altering what you specified in the nib.
The easiest workaround I would shoot for first is to call setKeyEquvialent: directly on the menu time from code, after the nib has loaded. I couldn't tell from your Twitter summary if you had already tried this, and it also failed.
Check the tag on your menu item. If set to certain values it might Cocoa to override stuff
Check your system Prefs aren't overriding key bindings
Check the key binding doesn't already exist elsewhere in the menu hierarchy, especially in the edit menu
I've got some code that grabs the TaskBar buttons and their text from the windows TaskBar using User32.SendMessage with the TB_GETBUTTON message to retrieve a TBBUTTON structure (Win32 API via C# P/Invokes). But I'm trying to figure out how to then, once I have the handle to the button, grab the associated context menu text. There is some status information on there for a specific application that I would like to retrieve. The button text gets me some of it, but I need to the context menu text to complete it.
Any ideas?
This is not completely clear... Context menus don't have text, as such - they have menu items, each one of which will have text. By "context menu text", do you mean the text of the menu items in the taskbar button's popup/context menu? For example, "Restore", "Minimize" etc in the screenshot below?
If so, I suspect you're going about this the wrong way:
This menu doesn't belong to the button, but is the system menu of the window represented by the taskbar button. If the button has a context menu, this is probably for a grouped collection of windows, not one specific window (or even windows for one process.)
Making judgements based on the context menu of a window sounds like a dodgy approach to me, especially based on text since that will change depending on where in the world your user is located. Applications can also change the contents of this menu so there's no guarantee it will contain something you expect to be there. It would be better to check the window style, if it's minimized, etc, to find out the information that also affects the contents of the menu.
I'm going to answer this based on what your needs seem to be from the question, not what you've directly asked, since (a) it's not possible as asked and (b) I think you're trying to do something else. (As a general guideline, in a question it's good to state why you're trying to do something - and even maybe ask about that, ie 'how do I achieve X' - in case there's a better method than the one you're using. Here, X is probably 'find out information about this window' not 'get the text of the context menu', because that's probably only one possible method to get to X.) Also I think extracting data from the internals of a third-party application like Explorer (the taskbar is an Explorer window) is fragile and prone to break in future versions of Windows.
The system menu or window information (whichever one) belongs to application windows. Unless taskbar buttons are grouped (and then it's the subitems) one taskbar button corresponds to one specific window in the system. So what you want to do is find these windows. You do this by:
Using the EnumWindows function
Then for each window that is passed to the callback, checking the extended window style using GetWindowLong with GWL_EXSTYLE to see if the WS_EX_APPWINDOW bit is set
In addition, sometimes other windows are shown: these heuristics should help.
Each one of these windows is a window that should appear on the taskbar, Alt-Tab dialog, etc.
You say you're getting the text of the taskbar button - this is probably the window caption of the window, and GetWindowText is the canonical (read: probably a lot more reliable) way to get the caption of a window belonging to another process.
If you really want the popup menu, then:
Use GetSystemMenu to retrieve the handle for the system menu for the window. Applications can customise this, so if your app is doing this (and that's why you want the popup menu) ensure you pass false to the bRevert parameter
You can then get the number of menu items using GetMenuItemCount and for each one call GetMenuItemInfo to get info about each menu item. Pass true to the fByPosition parameter to indicate you're accessing the menus by position (since you know the count, you're getting item 0, 1, 2... count-1).
This fills a MENUITEMINFO structure, which (I think, I haven't ever had to code this so I haven't tested) will tell you the text associated with an item via the dwTypeData field "if the MIIM_STRING flag is set in the fMask member".
If you really want information about the window status, you can get this information using methods like IsIconic to see if it's minimized, GetWindowLong again to get other information, etc. I'd suggest you ask another SO question about how to get whatever specific information about a window for details.
Hope that helps!
I'm relatively new to Cocoa and I would like to implement the ability to add or delete items from a pop-up menu in the same way that the OS X System Preferences/Network Location pop-up works. Selecting the 'Edit Locations...' option rolls down a window that provides the ability to add to, or delete from the existing Location list. My interest in doing things this way is as much about conforming to the relevant Human Interface Guidelines as having a way to dynamically change the menu content. (I have no real problem with the 'background' coding side of things, it's the user interface that's my primary issue at this stage.)
Is this a standard IB View?
On the surface, I can't see anything appropriate, but maybe that's just my inexperience. I'm assuming that, because this is not an uncommon sort of requirement, the task should be pretty straightforward and that Apple, or someone, would even have a relevant code sample to show how to define such a window.
Can anyone point me in the right direction?
Sorry for the late answer. I found this tutorial: http://cocoadevcentral.com/articles/000014.php
Doing some restructuring on the my applications menu bar. When looking at other applications, there seems to be two different ways of structure.
Either the "old school" most common way, the verb/command followed by the subject. I.e. what do you want to do and what do you want to do that on. Like so:
File
New
Foo
Bar
Open
Foo
Bar
Quit
Or the one that new applications sometimes try, probably since Microsoft introduced its ribbon structure. I.e. what do you want to work with and what operation do you want to execute on that. Like so:
File
Quit
Foo
New
Open
Bar
New
Open
Are these two paradigms established? Do they have a name? Would help me in referring to them and their differences.
Yes, menus at the top level may be organized by the class of object they act on (e.g., Foos or Bars), or the type of action they carry out (e.g., filing actions). As a general rule, the menu bar or Ribbon at the top of the window should be organized by action type in order to provide the user with an alternative way to find a command to the context (right-click) menus that are necessarily organized by object class.
That said, many menu hierarchies, including the “old school” one, would benefit from being “flattened” –from being made broader at each level and less deep. Deep hierarchies mean cascade menus, which are slow and awkward to use. Few options at the top level mean general vague labels that provide very little information scent (what does File really mean anyway?).
There are several ways to fix this while still organizing the menu bar by action type. First, there’s the simple flattening of the old school File menu, much like Firefox does:
File
New Foo
New Bar
Open Foo
Open Bar
Quit
The problem is that the traditional File menu was intended for “document” applications that operate on only one principal object class. For example word processors operate on papers, spreadsheet programs operate on worksheets, image editors work on pictures, and so on. File becomes unwieldy when there are multiple principal classes. Two classes isn’t a problem, but three or more is.
In some cases, it’s best to take the “suite” approach and make it look like you’ve a separate program for each object class. Take object class selection out of your menu bar and put it in the Start menu, where you’ve installed shortcuts corresponding to each object class that open a primary window for that class. Each of these “applications” only has New and Open acting on only its class:
File
New
Open
Quit
In a sense, you’ve made broader the menu above your menu bar in the hierarchy. This is entirely consistent with other desktop apps. It’s an attractive option if users tend to work with only one class for a session. Frequent trips to the Start menu gets old.
If you need to keep everything in your menu bar, you can spread the File menu out along the menu bar.
File
Save
Print
Quit
New
Foo
Bar
Open
Foo
Bar
Many apps with multiple principal classes are database apps where each window shows multiple objects (database records). What the user is “opening” is not a single file but a query result. Typically, the user almost never has use for a blank window. Even for data entry, it’s often helpful and rarely hurtful to show the results of a default query in order to provide some context (e.g., records entered last time). If the user wants to add a new record to those already shown, it’s an action under Edit, not File. So we can eliminate New.
Program
Foos
Bars
Quit
File
Query
Close
I suggest you take a cue from Mac OSX and have a Program menu for Quit (in OSX the name of the application is the menu caption). The Program menu has menu items labeled by their object class, but they’re actions –they open the Foo and Bar windows respectively. You either fill these windows with a default query result (which could be empty), or automatically show the query dialog for the user to select one. The Query menu item under File pops up this dialog to allow the user to change the query for the window at any time. This dialog may include an Empty option for edge cases where users need an empty window.
I think it's more personal preference. What do you think would go best? If you're not implementing the ribbon GUI into your app then there's no need to copy their structure.
Personally I'd go with the first option - "old school", I find it more straightforward and the most pragmatic approach toward the problem.