Cocoa validateUserInterfaceItem and menu item hiding - cocoa

Using validateUserInterfaceItem it is possible to disable/enable a menu item but I need a similar behaviour to hide menu items, have you some hints?

- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
Some details in the docs.
Update:
NSMenus do not hide NSMenuItems if validateMenuItem: returns NO. The according NSMenuItem gets disabled (Which tells the user that the functionality is there, but currently not applicable).
This behavior is also suggested in Apple's HIG. (See the second paragraph in the "Naming Menu Items" section)
Two other notes about NSMenuItem's enabled state:
setEnabled only works if autoenablesItems: of the hosting NSMenu is set to YES
The default implementation of validateMenuItem: seems to traverse the responder chain to check if the target/action of a NSMenuItem is available.

You could use -(BOOL) validateMenuItem: to remove a menu item when it was unneeded and insert it when required.
See the Menu documentation

Related

How do you set menuFormRepresentation for an NSToolbarItem in IB?

How do you set the menuFormRepresentation for an NSToolbarItem in Interface Builder?
I was trying to transition from programmatically constructed window toolbars to ones created entirely in IB (with an ultimate goal of easier localization).
I'm also transitioning from image-based items to view-based item to support NSButton and NSSegmentedControl items.
My first step was to just modify the existing code to create view-based toolbar items instead of image items. That works just fine.
My next step was to rip out all of the code and use just IB (Xcode 9.2) to create the toolbar and toolbar items. Most of it works, but the NSButton items don't get enabled or disabled properly.
When I wrote the code, I had to create NSMenuItem (or NSMenu) objects for each toolbar item and use that to set the item's menuFormRepresentation property. According to the documentation "Setting a Toolbar Item’s Representation", if you don't do this a view-based item (a) won't get validated automatically and (b) will not appear in the text-only version of the toolbar or the overflow menu.
I've looked everywhere in IB I can think of, but there's no outlet, property, or attribute where you can set the item's menuFormRepresentation property. I've also tried dragging and dropping a menu item on the toolbar item; nothing happens.
The documentation for "Creating a Toolbar in Interface Builder" doesn't make any mention of what to do about menuFormRepresentation, yet without it the item is functionally crippled.
Is this a huge hole in IB or am I missing something?

Cannot seem to setEnabled:NO on NSMenuItem

I have subclassed NSMenu and connected a bunch of NSMenuItem's via Interface Builder. I have tested via the debugger to see that they really get initialized.
The menu is set to not auto enable items. Still when I set any of my NSMenuItem's to [myMenuItem setEnabled:NO] they continue to be enabled. Even if I call [self update] from within the NSMenu subclass.
What am I missing?
Had the same issue, so I thought I'd post my solution. NSMenu auto enables NSMenuButtons, so we have to override that.
In IB:
Or programmatically:
// Disable auto enable
[myMenu setAutoenablesItems:NO];
// Test it
[myMenuButton setEnabled:NO];
[myMenuButton setEnabled:YES];
I solved it with the help of a colleague, so I post it here for others that experience the same issue.
You should set your NSMenu-sublass to auto-enable items (default behaviour) and then implement this method in the NSMenu-sublass.
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
return [menuItem isEnabled];
}
You should uncheck Auto Enables Items on the closest parent NSMenu
You can solve this problem without subclassing.
If only you need is standard menu + some custom NSMenuItems that can be disabled on your control, than you can just:
Add one more menu node - just drag and drop Submenu Menu Item from Object library to your menu.
Add all NSMenuItems you want to manage to this new Menu node.
Open Attributes inspector for your New Menu node, and switch off Auto Enables Items option:
configure any other options of your menu & items.
Now you can write a code like:
#property (weak) IBOutlet NSMenuItem *hidePlateMenuItem;
...
[self.hidePlateMenuItem setEnabled:NO];
and it will works well.
Adding to the response of itsdavyh, if the menu item is located inside one or more submenus you only have to uncheck the 'Auto enables items'-property on the submenu of the menu item and not on any other parentmenus.
I tried all these solution but finally i found the real problem that also make more sense to me.
It also is the simplest way to handle disabled nsmenuitem that no needs to subclass or do code.
The nsmenuitem before to be child of menu itself is child of the main item for example "Save as..." is child of "File". Just select the parent item (in this example is File) and set off "auto enable menu items" in the menu ispector panel, and here you go!
Swift 3 answer:
I have a submenu item under the standard "View" menu called "Enable System Setup On Launch". I use the function below to enable or disable the menu item. Note: the view menu does need the "Auto Enable Items" in IB to be turned off.
func enableSystemSetupMenuItem(enabled:Bool) {
//set view menu item to enabled: value
//requires "Auto Enable Items" of "View" menu item to be turned off in IB
//because "View" menu is now turned off for "Auto Enable" we have to handle all
//of the "View" menu items ourselves
//just to avoid operating on menu separators I set all other menu items to TAG = -1
let main = NSApplication.shared().menu?.item(withTitle: "View")
let subMenuItems = main?.submenu?.items
for item in subMenuItems! {
if item.title == "Enable System Setup On Launch" {
item.isEnabled = enabled
} else if item.tag == -1 {
item.isEnabled = true
}
}
}
Try calling [myMenuItem setEnabled:NO] from a different place and making sure that it happens after the menu-containing nib is loaded. Maybe do it right in the subclassed NSMenu's awakeFromNib.

Facing issue with internationalize MainMenu.xib for Cocoa based Mac OS X Application

I am working in a Cocoa based Mac OS X project and facing one issue with internationalize MainMenu.xib.
In the menu items, all titles are need to be internationalized programmatically. All the menu items like “cut”, ”copy”, ”paste” can be internationalized using setTitle except the undo and redo menu item title. Adding to this, after typing anything in the text fields of the project forms, the undo menu item title dynamically changed to “Undo Typing”. The same happens for “Redo” also.
I can set the titles of other menu and menuitems' title using,
[[[[NSApp mainMenu] itemAtIndex:1] submenu]setTitle:#"Edit_Test"]
for MainMenu.xib "Edit" menu and similarly,
[[[[[NSApp mainMenu] itemAtIndex:1] submenu]itemAtIndex:4]setTitle:#"Copy_Test"]
for NSMenuItem "Copy" which is in under "Edit" menu.
But If I use the same piece of code,
[[[[[NSApp mainMenu] itemAtIndex:1] submenu]itemAtIndex:0]setTitle:#"Undo_Test"]
the menuItem title still remain as "Undo"
NSUndoManager provides the methods undoMenuItemTitle and redoMenuItemTitle, but NSUndoManager does not send the -setTitle: messages to the "Undo" and "Redo" menu items.
So how can I track that dynamic change in title and make that "Undo Typing" internationalized also?
Is it possible to manually get the First responder of the MainMenu.xib and from that get the undomanager object? So that i can unbind the undo action that is currently present in the first responder with the undo menu item and perform undo operation manually or is it possible to just change the title programmatically without doing all these.
Please let me know if any one had come across this problem and resolved the issue.
Make a subclass of NSUndoManager and override the undoMenuTitleForUndoActionName: method and the redoMenuTitleForUndoActionName: method. Create instances of this subclass for each document (or managed object context, or other thing) that needs an undo manager.

How to programmatically add new NSToolbarItem to existing toolbar?

I'm looking for a method called addNewItem:(NSToolbarItem *)item or something like this that lets me add a programmatically created item to my toolbar, but I haven't found any. I would like to add an item that shows a popover when the user clicks on it, like in Safari when the user downloads something.
You need to have a class that conforms to the NSToolbarDelegate protocol and have an instance of that class be the delegate of your toolbar. This delegate would, for example, implement -toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:, which returns an NSToolbarItem instance for a given identifier, potentially creating that item on demand. By doing this, you’re preparing your delegate to return a toolbar item when the toolbar asks it for the item corresponding to an identifier.
Having done that, you can programatically add a new toolbar item to the toolbar by sending -[NSToolbar insertItemWithItemIdentifier:atIndex] to the toolbar instance. The identifier string argument should match the one used in the paragraph above. If you need to remove an item, send -[NSToolbar removeItemAtIndex:] to the toolbar.
This is described with examples in the Adding and Removing Toolbar Items section of the Toolbar Programming Topics for Cocoa document.

Binding an NSMenuItem's title breaks enabled/disabled validation

I have a menu where some of the menu items use bindings to get their title. These items are always enabled, and don't neither automatically enable/disable like they should NOR do they cause a call to validateUserInterfaceItem:. If you remove the binding on title, then that starts working again. The menu items have the target set to nil (First Responder). If you click on one, it does execute the selector (action).
Bug? What to do?
For some reason when you set a menu item's title with bindings, the menu item becomes enabled
even if the target/action are nil.
If you want to permanently disable the menu item you can workaround this by binding the menu item's enabled status to a constant NO:
NSNumber *alwaysNo = [NSNumber numberWithBool:NO];
[menuItem bind:#"enabled" toObject:alwaysNo withKeyPath:#"boolValue" options:nil];
Note that this isn't the most elegant workaround, but in my case it was still cleaner than not using bindings for the title.

Resources