Skip to content
Mark Reeves edited this page Jan 3, 2018 · 24 revisions

WMenu is a component for creating menus, navigators and toolbars.

Why use WMenu

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.

Creating a WMenu

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);

Usability and accessibility

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.

Access without a mouse

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.

Menus or Buttons?

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.

HTML output

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.

Types

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

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.

Closed bar menu

Open bar menu

MenuType COLUMN

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.

Closed column menu

Open column menu

MenuType FLYOUT

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.

Closed flyout menu

Open flyout menu

MenuType TREE

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.

Closed tree menu

Open tree menu

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.

Item selection

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);

Setting selected items

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.

Inspecting selection

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.

Disabling

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.

Appearance

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.

Hiding

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.

'Docking' menu bars

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.

By way of comparison, if the menubar did not 'dock' it would appear as shown below:

WSection with an undocked WMenu

Forcing the 'docking' style

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!

Responsive design

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.

Related Components

Further information

Clone this wiki locally