Modify NSMenu structure on each open? - objective-c

I need to be able to dynamically modify an NSMenu hierarchy each time it is shown (add/remove items etc). For example:
user starts a tracking session on a main menu and selects a submenu
detect submenu is about to open and run code to modify it
keep tracking , user tracks over the same submenu again: goto 2
So to do this I have an object implementing the NSMenuDelegate protocol. The method menuNeedsUpdate works the first time (2), but does not work for 2nd time the submenu is opened. (Only called once per tracking session)
The method menuWillOpen is called each time, but has docs have the following warning which seems to disqualify using this approach:
Do not modify the structure of the
menu or the menu items during this
method.
Is there any way to accomplish this ?

You could subclass NSMenu and override submenuAction:.
Or you could just subscribe to the NSMenuWillSendActionNotification.
And while doesn't sound like it will work for you, just for reference, NSMenuValidation is a good place to update menu items on an item by item basis.

menuWillOpen will only be called once, the first time you track over the submenu. At that point, you populate the menu.
After that, menuWillOpen will not be called again. However, any changes to the menu will happen live. So while the main parent menu is open, whenever the source data changes (or periodically if you can't detect changes), update the menu using the normal NSMenu API.
Make sure whatever method you use to update the menu will run while the system is tracking your menu.

Related

Dont hide a menu/menuitem on click

I am using vb.net and don't want to use WPF.
I have a menustrip having many menus.
These menus have many menuItems.
When I click on any menuItem I show a new form to the user.
When user finish his work and return to the main form he should be able to see the menu as it was while he left the main form.
I mean when I click on a menuItem the menu closes. But I don't want it to close.
It will likely be more confusing for your users having a menu that never closes because it defies expected behaviour. You are better off using one of the other controls suggested.
Every Operating System (Windows, Mac etc) define a standard set of controls for a reason - to enforce consistency and predictability across multiple applications. It's why you know to look to the menu to find a command. Microsoft, Apple etc all produce "design standards" documents that specify a baseline for the way that your user interacts with your app.
If a user selects something from the menu and the menu never closes, the user could well think that your application is broken.

Form sent to back on interaction with menu item

I've got two forms, one being frmMain and the other being frmDatasets. The idea is that frmDatasets is used to manage the application's datasets (it's a program for comparing sorting algorithms).
I have a MenuStrip on frmDatasets with a few items. I just decided to change the Add Dataset item to a drop-down menu with options for loading one from file, or generating one according to certain parameters. All well and good; just add the items, swap some icons around, and change the event handler's signature to handle the right Click event.
Except when I run the program, clicking on the Add Datasets top-level item for the first time sends frmDatasets to the back, displaying an inactive frmMain. Clicking the item again shows the dropdown menu as usual.
If I instead click another top-level menu item, that works just fine. But then simply hovering my mouse over the Add Dataset item causes the 'send to back' thing to happen.
I say "send to back", but it's really just putting it behind frmMain. It remains in front of other windows like Visual Studio and Firefox.
Anyone know what on Earth is going on with this form?
EDIT: If I show frmDatasets as a modal dialog, it stops the whole "sending to back" thing, but I still have to click the menu item twice before it shows the list.
EDIT2: Overriding the onClick event to call mnuAddDatasetDropDown.ShowDropDown removes the double-click issue, but it's only a solution if I keep the form as a modal dialog. Still gets sent to back. Overriding mouseEnter doesn't do anything to solve it.
Setting the TopMost property of frmDatasets from its Design view to 'true' fixed all of the problems I was having. No insight as to what was going on, unfortunately, but if anyone else is having this problem then hopefully it'll work for them too.

How can I make a button not fire its action on click-through?

The Apple Human Interface Guidelines state that:
An item that provides click-through is one that a user can activate on an inactive window with one click, instead of clicking first to make the window active and then clicking the item. Click-through provides greater efficiency in performing such tasks as closing or resizing inactive windows, and copying or moving files. In many cases, however, click-through could confuse a user who clicks an item unintentionally.
and
Don’t provide click-through for an item or action that:
Is potentially harmful and does not allow the user to cancel it (for example, the Delete button in Mail)
Is difficult or impossible to cancel (such as the Send button in Mail)
Dismisses a dialog without telling the user what action was taken (for example, the Save button in a Save dialog that overwrites an existing file and automatically dismisses the dialog)
Removes the user from the current context (for example, selecting a new item in a Finder column can change the target of the Finder window)
What I want to do is that if the user clicks a specific button it will not send its message unless the window is active (for example, the delete message button in Mail). How can I achieve this? If I need to subclass NSButton that's fine.
Look at the NSView Documentation:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/Reference/NSView.html#//apple_ref/occ/instm/NSView/acceptsFirstMouse:
You need to override acceptsFirstMouse to return TRUE to enable click through.
The default behaviour is not click-through:
The default implementation ignores theEvent and returns NO.
It's possible you have already overridden this method in your code, or in code you have based your code on. Try removing the implementation of acceptsFirstMouse in your code.

Connecting two DialogBoxes in GWT

In my GWT project, I'm trying to get it so two DialogBoxes can pass information between each other. One of them holds a MapWidget, and when a button is pressed in the other DialogBox, the position information is received from that other DialogBox's MapWidget. Does anyone have any tips for how I should coordinate between having two different DialogBoxes show up? Should I wrap the code for the two in a Composite? Furthermore, is there an example anywhere of dealing with two DialogBoxes at once in GWT? For example, if I click outside of the two boxes, both should be dismissed. I'm wondering if there's a way to keep both of them in focus at once, so I can switch between the two without causing either to disappear.
Sharing Data Between Dialog Boxes
In my opinion, the "correct" way to do this would be to implement some sort of MVP structure in the application so that a presenter manages the view (DialogBoxes, among other things) and knows how to pass simple data to the view for it to display (the presenter would handle the MapWidget data, the view would take care of displaying it on the DOM).
However, if you're looking for a quicker/more simpler approach, you have a couple of options (which you choose really depends on the application structure):
Create a Composite, as you mentioned, that knows how to pass the necessary data back and forth. By having the Composite manage the data object and tell the two DialogBoxes how to display it, you are actually approaching an MVP architecture within your Composite.
Subclass DialogBox into a class that contains a HandlerManager (sometimes used as an "Event Bus") that fires events when the button is pressed. You can create events that are designed to pass data back and forth between the two DialogBoxes (even make them type-safe with type parameters). See this StackOverflow question for details on using a HandlerManager. The MVP article, linked above, also has some good information on using an event bus.
Model-View-Presenter is a tried-and-true method of structuring an application that results in more testable code, better project structure, and can help guide you when making decisions like this. I strongly recommend checking it out if you haven't already.
Sharing Auto-Hide Functionality
GWT's PopupPanel (on which DialogBox is based) offers a method addAutoHidePartner(Element) which is describe thusly:
Mouse events that occur within an autoHide partner will not hide a panel set to autoHide.
So, you can create two auto-hiding DialogBoxes that only close when you click outside both of them (e.g., they do not close when you click within either of the boxes) with the following code:
// Create the dialog boxes
DialogBox dbox1 = new DialogBox(true, false);
DialogBox dbox2 = new DialogBox(true, false);
// Set some visual options
dbox1.setPopupPosition(10, 10);
dbox2.setPopupPosition(200, 10);
dbox1.setAnimationEnabled(true);
dbox2.setAnimationEnabled(false);
// Set the dialog boxes' caption and content
dbox1.setHTML("Dialog Box 1");
dbox2.setHTML("Dialog Box 2");
dbox1.setWidget(new HTML("This is the first dialog box."));
dbox2.setWidget(new HTML("This is the second dialog box."));
// Making dobx2 a partner of dbox1 means clicking
// in dbox2 won't cause dbox1 to close
dbox1.addAutoHidePartner(dbox2.getElement());
// Similarly, setting dbox1 as a partner of dbox2 means
// clicking in dbox1 won't cause dbox2 to close
dbox2.addAutoHidePartner(dbox1.getElement());
// Show the dialog boxes
dbox1.show();
dbox2.show();
You can interact with either of the dialog boxes without the other closing. Omit the appropriate call to setAutoHidePartner if you only want a one-way partnership.

NSWindow does not respond to keystroke command-s

It may be very simple, but I cannot find it:
I have three windows in three separate NIBs in my application. One is opened when a new document is opened, the other two can be opened from the program's window menu.
The problem is: two windows (in them the one that is opened at the beginning) accepts the normal keystroke as for example command-s for save, and the other one does not and gives a warning sound instead. I cannot figure out the difference between the two windows or their controllers. I know it will have to do with the responder chain, but I am left clueless.
Any ideas?
Check to make sure that the window's delegate is set to the window controller, and that the window controller implements -saveDocument: (or whatever action the Save item is connected to).
Windows don't respond to key combinations. Menu items do. In response to being pressed (whether using the mouse, using a key combination, or using Accessibility), the menu item sends its action message down the responder chain.
You get a beep when nothing in the responder chain responds to the action message.
Assuming that this is an NSDocument-based application and you've started Apple's doc-based-app template, the menu item's action is saveDocument:, and the NSDocument object is the object that responds to that message. When your document windows are active, their documents are in the responder chain, so the menu item that sends that action message is enabled. When your third window is active, the document is not in the responder chain; nothing else responds to that message, so the menu item is disabled.
This problem isn't specific to Save—it affects all action messages that should go through to the document object. One important other example is Print: The user will probably mean to print the document, not the third window.
You've probably made this third window a kind of window that exists as a peer to the other windows. Besides this responder-chain problem you're having, the user will also probably not realize that they have left the document; they expect to still be able to do document things. Consider making it a utility panel instead.
If you really do have a good reason to make this window whatever kind of window it is, you'll need to keep the last-active document object in the responder chain when this third window becomes main, while at the same time handling the case where the window becomes main because a document window (possibly the last one) has closed.
Well, it turns out that I implemented the third window in a way where I created it with its controller using initWithNibFile, ran a procedure in the controller and then sent it a [window close] command because I did not want it to appear on the screen yet. That somehow took it out of the document-associated window, no idea why. No I migrated that specific called procedure into the document controller itself, treat the window like the second window and voila, it works again.