Menus provide the user with a list of actions and/or
submenus. Submenus themselves are full fledged
menus and so a heirarchical structure of appears.
Every application has one special menu, the so called
Application menu. This menu is always visible
on the screen when the application is active. This menu
normally contains items like, info,
services, print, hide and
quit.
After the info item normally some submenus
follow containing the application specific actions.
On GNUstep the content of the menu is stacked vertically
as oppossed to the Windows and Mac world, where they are
stacked horizontally. Also because the menus are
not part of any normal window they can be dragged
around opened and closed independend of the
application windows.
This can lead to a few menus being open
simultanuously. The collection of open
menus is remembered, when the program is started
again, all the torn off menus aka detached menus,
are displayed at their last known position.
The menu behaviour is richer than in most other
environments and bear some explanation. This
explanation is aimed at users of Menus but more
so at the developer of custom menus.
Application menu
There alwasy at least one menu present and visible
while the application is active. This is the
application menu. This window can never be
closed.
Attached menu
Normally when you click in a menu on a submenu
item, the submenu is shown directly next to the
menu you click in. The submenu is now called an
attached menu. It is attached to the menu
that was clicked in.
Detached menu
A menu is detached when it is not attached to its
parent menu. A menu can become detached when the
user drags a submenu away from its parents. A
detached window contains in its title a close
button.
Transient menu
A transient menu is a menu that dissappears as soon as
the user stops interacting with the menus. Typically
a transient menu is created when a right mouse click
appears in an application window. The right mouse
click will bring up the Application menu at the
place the user clicks. While keeping the mouse
button down the user can select items by moving
around. When releasing the button, all transient
menus will be removed from the screen and the
action will be executed.
It is important to note that it is impossible to
click in transient menus.
Attached transient menu
This is a menu that is attached and transient at the
same time.
A single NSMenu instance can be displayed zero or one times
when the user is not interaction with the menus. When
the user is interaction with the menus it can occur that
the same NSMenu is displayed in two locations at the same
time. This is only possible when one of the displayed
instances is a transient menu. To understand
how the diffent kind of menus are created lets look at
some user actions:
The user clicks on an item which is not a submenu.
The item is highlighted until the action
corresponding with the item is completed.
More precisely, the application highlights the menu
item, performs the action, and unhighlights the
item.
The user clicks on a submenu item which is not
highlighted already If the submenu is
not a detached menu, the submenu will become an
attached menu to the menu that is clicked in.
The item that is clicked in will become highlighted
and stays highlighted.
If the submenu is a detached menu, the transient
version of the submenu will be shown
The user clicks on a submenu item which is
highlighted This means that the
submenu is an attached submenu for this menu.
After clicking the submenu item will no be no
longer highlighted and the submenu will be removed
from the screen.
The user drags over a menu item The item will
be highlighted, if the item is a submenu item, the
submenu will be shown as an attached submenu.
This can be transient, or non transient.
Customizing the look of Menus There are basically three ways of customizing
the look of NSMenu
Using custom NSMenuItemCell's. This you should do
when you want to influence the look of the items
displayed in the menu.
Using custom NSMenuView. This is the class to
modify if you want to change the way the menu is
layout on the screen. So if you want to stack the
menu items horizontally, you should change this
class. This should be rarely needed.
Reimplement NSMenu. This you should not do.
But, if you implement everything yourself you can
achieve anything.
Information for implementing custom NSMenuView
class
When implementing a custom NSMenuView class it
is important to keep the following information in mind.
The menus (or the menu items) form a tree. Navigating
through this tree is done with the methods
[NSMenu -supermenu]
, which returns the parent menu of the receiver, and
with
[NSMenu -itemAtIndex:]
which returns a NSMenuItem on which we can call
[<NSMenuItem>-submenu]
for a child menu.
The menus as displayed on the screen do NOT form a
tree. This because detached and transient menus
lead to duplicate menus on the screen.
The displayed menus on the screen have the following
structure:
The ordered graph of displayed menus (note, NOT the
set of NSMenus) form a collection of line graphs.
The attached menus are precisely the non root
vertices in this graph.
An attached menu of a transient menu is itself a
transient menu.
The collection of transient menus form connect
subgraph of the menu graph.
Returns the position where submenu will be
displayed when it will be displayed as an
attached menu of this menu. The result is
undefined when aSubmenu is not
actually a submenu of this menu.
Return the NSView that is used for drawing the menu.
It is the view set with
[NSMenu -setMenuRepresentation:]
and therefore it should be safe to assume it is an NSView implementing the NSMenuView protocol.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
Warning the underscore at the start of the
name of this instance variable indicates that, even
though it is not technically private, it is
intended for internal use within the package, and
you should not use the variable in other code.
This interface exist contains methods that are meant for
the NSMenuView. If you write your own implementation of
the NSMenuView interface you can use these methods to
popup other menus or close them.
Warning the underscore at the start of the
name of this method indicates that it is private, for
internal use only, and you should not use the
method in your code.
Warning the underscore at the start of the
name of this method indicates that it is private, for
internal use only, and you should not use the
method in your code.
Warning the underscore at the start of the
name of this method indicates that it is private, for
internal use only, and you should not use the
method in your code.
Flag this menu to be the main menu of the application,
when isMain is YES. Flag it as
no longer being the main menu when NO is
handed in.
This method also checks the user defaults to
determine how the menu is to be displayed (eg
vertical or horizontal) and can therefore be
used to change window geometry.
When the flag is YES this
method will detach the receiver from its parent and
update the menurepresentation so it will display a
close button if appropriate.
If the flag is NO this method
will update the menurepresentation so it will be
able to remove the close button if needed. Note that
it will not reattach to its parent menu.
Returns the window in which this menu is displayed.
If there is a transient version it will return the
window in which the transient version is displayed.
If the Menu is not displayed at all the result is
meaningless.
Specifies the protocol to which an object must
confirm if it is to be used to validate menu items
(in order to implement automatic enabling and disabling
of menu items).
The receiver should return YES if the
menuItem is valid... and should be
enabled in the menu, NO if it is
invalid and the user should not be able to select
it.
This method is invoked automatically to determine
whether menu items should be enabled or disabled
automatically whenever
[NSMenu -update]
is invoked (usually by the applications event loop).
This should ensure that if there is an attached
submenu this submenu will be detached. Detaching
means that this particular menu representation should
be removed from the screen. It should implement a deep
detach, that is, all attached submenus of this menu
should also be detached.
Set the currently highlighted item. This is used by the
NSMenu class to restore the selected item when it is
temporary set to another item. This happens when
both the regular version and the transient version are
on the screen. A value of -1 means that no item will be
highlighted.
Set the menu that this view object will be
drawing. This method will NOT retain the
menu. In normal usage an instance of
NSMenu will use this method to supply the NSMenuView
with reference to itself. The NSMenu will retain the
NSMenuView.
This is method is responsible for handling all
events while the user is interacting with this
menu. It should pass on this call to another
menurepresentation when the user moves
the mouse cursor over either a submenu or over the
supermenu.
The method returns when the interaction from the user
with the menu system is over.
The method returns NO when the user
releases the mouse button above a submenu item
and YES in all other cases.
This return value can be used to determine if
submenus should be removed from the screen or
that they are supposed to stay.
The implementation should roughly follow the
following logic:
{while (have not released mouse button) {if (mouse
hovers over submenu, or supermenu) {if
([(menurepresentation under mouse)
trackWithEvent: the event]) {
[self detachSubmenu]; return YES;}
return NO;} //highlight item under
mouse if (highlighting submenu item) {[self
attachSubmenuAtIndex:..];} else
{[self detachSubmenu];} get next event.}
execute the menu action if applicable; return
YES | NO depending on
the situation;}
Note that actual implementations tend to be more
complicated because because of all kind of
useability issues. Useabilities issues to look
out for are:
Menus that are only partly on the screen. Those
need to be moved while navigation the menu.
Submenus that are hard to reach. If the
natural route to the content of a submenu
travels through other menu items you do not
want to remove the submenu immediately.
Transient menus require slightly different
behaviour from the normal menus. For example,
when selecting a action from a transient menu that
brings up a modal panel you would expect the
transient menu to dissappear. However in the
normal menu system you want it to stay, so you
still have feedback on which menu action
triggered the modal panel.
This will relayout the NSMenuView. It should be called
when the menu changes. Changes include becoming
detached, adding or removing submenu items
etcetera. However, normally it does not need to
be called directly because Because the NSMenuView is
supposed to listen to the NSMenu notifications for
the item added, removed and change notifications. It
should be called explicitly when other changes
occur, such as becoming detached or changing the
title.