-
Notifications
You must be signed in to change notification settings - Fork 17
WMenu
WMenu is a component for creating menus, navigators and toolbars.
- Why use WMenu
- Creating a WMenu
- Usability and accessibility
- HTML output
- Types
- Item selection
- Disabling
- Appearance
- Related Components
- Further information
A menu provides a different operation paradigm from a list of links. A menu is easy to skip over as it will contain only a single TAB
stop (see Access without a mouse for details). Menus may also be more compact than a set of links or buttons.
WMenu provides three visual types. These are all menu (or menubar as appropriate) widgets and differ only in their visual presentation.
WMenu has two constructors: the default constructor and one which accepts a WMenu.MenuType to indicate the type of menu required.
// Create a default menu
WMenu menu = new WMenu();
// Create a BAR menu
WMenu barMenu = new WMenu(WMenu.MenuType.BAR);
// Create a FLYOUT menu
WMenu flyoutMenu = new WMenu(WMenu.MenuType.FLYOUT);
// Create a COLUMN menu
WMenu columnMenu = new WMenu(WMenu.MenuType.COLUMN);
// Create a TREE menu
WMenu treeMenu = new WMenu(WMenu.MenuType.TREE);
It is possible to create a WCAG 2.0 level AA compliant menu using WMenu. This does require that the individual items are created in an accessible way: if every WMenuItem and WSubMenu's labelling component has text content then accessibility is pretty much guaranteed, otherwise the components used as the content of the WMenuItem or WSubMenu labelling component must be individually accessible.
All of the interactions of a menu are able to be driven from a keyboard or other device. The interactions are based on the WAI-ARIA authoring practices for a menu.
For keyboard users a WMenu provides a single focus point and focus within the menu is generally driven from the ARROW
keys. individual items may also be accessed by pressing the first letter of their text label. In all cases pressing TAB
will exit the menu.
A simple one level bar menu could easily be a row of WButton components. Equally a column menu could be a list of WButton components. Using a menu provides to following advantages:
- menus have a single focus point which makes it quicker to by-pass when using a keyboard;
- when in a menu navigation can be by arrow keys or by pressing the first letter of an item's text label and does not rely on access key;
- WMenuItem allows selection which provides 'stickiness' so the state of a menu item can be remembered between page loads;
- sets of similar functions may be placed into a WSubMenu reducing the visual clutter of a large set of buttons.
On the other hand you may prefer to use a row of WButton components if:
- your control set is only two or three commands and one of them is a submit/save command;
- the commands are to be placed in a non-contiguous block;
- your design requires the controls take on a simple link-like appearance;
- you really like buttons.
WMenu will output a HTML menu element. This precludes a WMenu from being placed in the content of WComponents which must only contain phrasing content.
WMenus must not be nested.
The appearance and interaction model of a WMenu is determined by its MenuType property. It is implemented using the WMenu.MenuType
enum. Available settings for this property are:
-
BAR
; -
FLYOUT
; -
TREE
; and -
COLUMN
.
A WMenu MenuType
is set in the constructor and may not be reset.
// MenuType.BAR
WMenu menuBar = new WMenu(WMenu.MenuType.BAR);
MenuType.BAR
outputs a horizontal menu bar which is intended to emulate a typical desktop application menu bar. A menu of MenuType.BAR
will appear to fill the horizontal space available to it. Each WMenuItem and/or WSubMenu added directly to the WMenu will appear as an interactive control and will occupy only the amount of space required to display its labeling element's content. There may be a section of inactive menu bar after the last WMenuItem or WSubMenu has been rendered. A WMenu of MenuType.BAR
will be the height of the largest label of any WMenuItem or WSubMenu added directly to it.
A WMenu of MenuType.BAR
may only have one WSubMenu open at any given level. No WSubMenu will be open on page load.
WMenu of MenuType.COLUMN
is rendered as a single column of menu options. The menu will fill the available horizontal space so should generally be placed into a layout component such as a WColumn or a WPanel with a ColumnLayout.
If a WMenu of MenuType.COLUMN
has sub menus then each WSubMenu will open to the side of the column positioned beside its opener button. This is similar to nested WSubMenus of types MenuType.BAR and MenuType.FLYOUT. Each sub menu is transient and will flow over other content. If a submenu overflows the right edge of the viewport then it will attempt to open to the left instead of to the right. A WMenu of MenuType.COLUMN
may only have one WSubMenu open at any given level. No WSubMenu will be open on page load.
MenuType.FLYOUT
outputs a horizontal menu bar which will fill only enough horizontal space to output the label components of the WMenuItem and WSubMenu components added directly to it. Each WMenuItem and/or WSubMenu added directly to the WMenu will appear as an interactive control and will occupy only the amount of space required to display its labeling element's content. It is suggested (though not enforced) that a WMenu of MenuType.FLYOUT
have exactly one child element: most likely a WSubMenu but possibly a single WMenuItem (though in this case it may be better to use a WButton)
A WMenu of MenuType.FLYOUT
will be the height of the largest label of any WMenuItem or WSubMenu added directly to it. The labeling element's content may wrap. If this is unwanted you must specify that any soft wrap point (such as white space) be adjusted to attempt to prevent wrapping. You should not that only single white space characters can be changed to a character entity which can be guaranteed to not cause a soft wrap point in all user agents.
A WMenu of MenuType.FLYOUT
may only have one WSubMenu open at any given level. No WSubMenu will be open on page load.
A menu of MenuType.TREE
displays as a vertical menu. Each WSubMenu added to the menu is indented relative to its parent. Each WMenuItem and WSubMenu opener control occupies the full width available to the menu.
A WMenu of MenuType.TREE
may have any number of WSubMenus open at any time though a nested WSubMenu may only be open if its parent WSubMenu is open. In this type of menu one or more WSubMenu may be open on page load.
It should be noted that WMenu of MenuType.TREE
is still a menu and is exposed as such. It is not a tree widget. If an application requires a tree widget (which is a complex selection tool) then WTree is the appropriate component.
Menus allow selection of WMenuItems using the method setSelectionMode(SelectionMode)
. This property, if specified, indicates the menu item selection model for WMenuItems which are added directly to the WMenu. WMenuItems added to WSubMenus take their selection model from the WSubMenu selectMode property if it is set, otherwise from this property.
// with WMenu menu
// set single select mode.
menu.setSelectionMode(SelectionMode.SINGLE);
// set multiple select mode.
menu.setSelectionMode(SelectionMode.MULTIPLE);
// remove the selection mode
menu.setSelectionMode(SelectionMode.NONE);
If a WMenu has a SelectionMode
set then one (if the mode is SINGLE) or more (if the mode is MULTIPLE) descendant WMenuItems may be set as selected. This is normally undertaken by user action, but default selection may be made in the Java API.
// Given WMenu menu and WMenuItem item
// if setting a single item as selected
menu.setSelectedMenuItem(item);
// if multiple selection is enabled then
// a list of WMenuItems may be selected.
// Given List<WMenuItem> items:
menu.setSelectedMenuItems(items);
Each setter may be used whatever selection mode is set. If a list is passed to a menu with a single selection mode then only the last item will remain selected.
To get the current selections (if any) in a WMenu use getSelectedMenuItem
or getSelectedMenuItems
:
// Given WMenu menu.
MenuItemSelectable item = menu.getSelectedMenuItem();
List<MenuItemSelectable> items = menu.getSelectedMenuItems();
Each getter may be used whatever selection mode is set. If a list is acquired from a menu with a single selection mode then the List will consist of at most one item, but it will still be a list. If getSelectedMenuItem
is used with a menu with multiple selection then only the first selected item is returned.
A WMenu may be disabled on page load. When disabled the WMenu will not respond to user input or interaction. All WMenuItem and WSubMenu components in the menu will also be disabled. Individual WSubMenu and WMenuItem components support the disabled state for finer grained control over what menu items need to be disabled for any particular use case.
This property may be used in conjunction with WSubordinateControl controls to enable or disable content without making another server call.
In the main the appearance of a WMenu is determined by the [MenuType][ypes] used. The details of the appearnce will, obviously, be determined by the theme in use, though the verticality or horzontality of menus should not be altered by a theme.
A WMenu may be hidden on page load. When hidden the WMenu is not available to any compliant user agent. It is present in the source and is not obscured in any way. This property is determined by a WSubordinateControl. When a WMenu is hidden all of its content is hidden.
If a WMenu of MenuType.BAR is the first component added to a WSection or a WPanel of Type CHROME
or ACTION
then the menu will 'dock' to the title bar and will not be indented as per the rest of the panel's content.
-
WSection with docked menu:
-
WPanel Type.ACTION with docked menu:
-
WPanel Type.CHROME with docked menu:
By way of comparison, if the menubar did not 'dock' it would appear as shown below:
Sometimes it may not be practical to ensure the WMenu is the first child of a WPanel with no layout therefore this same style can be applied to a WMenu of MenuType.BAR manually using the HTML class value wc-neg-margin
. Using this value will always apply the 'docking' style to the WMenu so it is incumbent on the UI developer to ensure it is appropriate to use this setting. It must only be applied to a WMenu of MenuType.BAR when that WMenu is the first visible component in a WSection's content or the content of a WPanel of Type ACTION
or CHROME
.
To apply the "docking" style to a WMenu:
WMenu bar = new WMenu(MenuType.BAR);
bar.addHtmlClass("wc-neg-margin");
If considering using this facility, it is important to remember that it applies reliably only to WMenus of MenuType.BAR and only when the WMenu is the first visible content in a WSection or WPanel of Type.CHROME
or Type.ACTION
. Any other use is not guaranteed to work. I feel it necessary to echo a note in the Sass source for this feature, which, whilst somewhat tongue-in-cheek, does spell out the dangers of applying this technique:
// #############################################################################
// Negative margins!!!
// WARNING
//
// When a WMenu of MenuType.BAR is the first child of a WPanel of Type.CHROME or
// of Type.ACTION (ie a WPanel with exposed title) _or_ is the first child of a
// WPanel child of WSection; **and** that WPanel does not have a Layout applied
// then a negative margin is applied to make the menu "dock" to the header bar.
// The class `.wc-neg-margin` can be used to force this same negative margin on
// WMenus of MenuType.BAR when it is not feasible to have them in WPanel's with
// no Layout in the appropriate circumstance.
//
// WARNING
//
// This makes no attempt to determine the location of the menu, it just uses the
// section padding as a negative margin. This means it will apply wherever it is
// used.
//
// It is intended that this **only** be used when the menu is the first visible
// component in such a section as described above but I am not going to enforce
// that.
//
// WARNING
//
// Well here is the **WARNING**: if you use this willy-nilly your UI _could_
// look really silly!
The default WComponents theme has a set of simple responsive design styles which can be applied to WMenu (except MenuType.TREE). The in-built responsive design aspects of WMenu are:
- triggered at small screen width as commonly found on phone-like devices;
- all WSubMenus open full-screen with larger text items which are easier to hit;
- open WSubMenus show a close option which contains the text content of the WSubMenu opener.
In addition the WMenu itself may have additional behaviour if the MenuType is MenuType.BAR or MenuType.FLYOUT and the WMenu contains more than one child element. This is invoked by setting the HTML class attribute value wc-respond
on the WMenu. This is best done using HtmlClassProperties.
// given WMenu menu
menu.setHtmlClass(HtmlClassProperties.RESPOND);
If the MenuType is MenuType.BAR or MenuType.FLYOUT and the WMenu contains more than one child element then the menu will be collapsed to a single item. This item with a commonly used icon display. This item will then behave as a WSubMenu.
See Responsive Design for more information about responsive design built in to WComponents.