Chapter 12. Menus, Toolbars, and Actions
Subpages: 1. JMenus, Toolbars, and Actions overview
2. Basic text editor: part I - menus
3. Basic text editor: part II - Toolbars and Actions
4. Basic text editor: part III - Custom toolbar components
5. Basic text editor: part IV- Custom menu components
In this chapter:
- Menus, Toolbars, and Actions overview
- Basic text editor: part I - Menus
- Basic text editor: part II - Toolbars and Actions
- Basic text editor: part III - Custom toolbar components
- Basic text editor: part IV- Custom menu components
12.1 Menus, toolbars, and actions overview
Drop-down menu bars, context-sensitive popup menus, and draggable toolbars have become commonplace in many modern applications. It is no surprise that Swing offers these features, and in this section we will discuss the classes and interfaces that underly them. The remainder of this chapter is then devoted to the stepwise construction of a basic text editor application to demonstrate each feature discussed here.
12.1.1 The SingleSelectionModel interface
abstract interface javax.swing.SingleSelectionModel
This simple interface describes a model which maintains a single selected element from a given collection. Methods to assign, retrieve, and clear a selected index are declared, as well as methods for attaching and removing ChangeListeners. Implementations are responsible for the storage and manipulation of the collection to be selected from, maintaining an int property representing the selected element, maintaining a boolean property specifying whether or not an element is selected, and are expected to fire ChangeEvents whenever the selected index changes.
12.1.2 DefaultSingleSelectionModel
class javax.swing.DefaultSelectionModel
This is the default implementation of SingleSelectionModel used by JMenuBar and JMenuItem. The selectedIndex property represents the selected index at any given time and is -1 when nothing is selected. As expected we can add and remove ChangeListeners, and the protected fireStateChanged() method is responsible for dispatching ChangeEvents whenever the selectedIndex property changes.
12.1.3 JMenuBar
class javax.swing.JMenuBar
JMenuBar is a container for JMenus layed out horizontally in a row, typically residing at the top of a frame or applet. We use the add(JMenu menu) method to add a new JMenu to a JMenuBar. We use the setJMenuBar() method in JFrame, JDialog, JApplet, JRootPane, and JInternalFrame to set the menu bar for these containers (recall that each of these containers implements RootPaneContainer, which enforces the definition of setJMenuBar()--see chapter 3). JMenuBar uses a DefaultSingleSelectionModel to enforce the selection of only one child at any given time.
A JMenuBar is a JComponent subclass and, as such, can be placed anywhere in a container just as any other Swing component (this functionality is not available with AWT menu bars).
Warning: JMenuBar defines the method setHelpMenu(JMenu menu) which is intened to mark a single menu contained in a JMenuBar as the designated 'help' menu. The JMenuBar UI delegate may be responsible for positioning and somehow treating this menu differently than others. Hoewever, this is not implemented as of Java 2 FCS, and generates an exception if used.
Note: One lacking feature in the current JMenuBar implementation, or its UI delegate, is the ability to easily control the spacing between its JMenu children. As of Java 2 FCS, the easiest way to control this is by overriding JMenuBar and manually taking control of its layout. By default JMenubar uses an x-oriented BoxLayout.
JMenuBar provides several methods to retrieve its child components, set/get the currently selected item, register/unregister with the current KeyBoardManager (see chapter 2, section 2.13), and the isManagingFocus() method which simply returns true to indicate that JMenuBar handles focus management internally. Public methods processKeyEvent() and processMouseEvent() are implemented only to satisfy the MenuElement interface (see below) requirements, and do nothing by default.
12.1.4 JMenuItem
class javax.swing.JMenuItem
This class extends AbstractButton (see chapter 4, section 4.1) and represents a single menu item. We can assign icons and keyboard mnemonics just as we can with buttons. A mnemonic is represented graphically by underlining the first instance of the corresponding character, just as it is in buttons. Icon and text placement can be dealt with identically to how we deal with this functionality in buttons.
We can also attach keyboard accelerators to a JMenuItem.(i.e. we can register keyboard actions with a JMenuItem -- see chapter 2 section 2.13). Every JComponent decendent inherits similar functionality. When assigned to a JMenuItem, the accelerator will appear as small text to the right of the menu item text. An accelerator is a key or combination of keys that can be used to activate a menu item. Contrary to a mnemonic, an accelerator will invoke a menu item even when the popup containing it is not visible. The only necessary condition for accelerator activation is that the window containing the target menu item is currently active. To add an accelerator corresponding to CTRL+A we can do the following:
myJMenuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_A, KeyEvent.CTRL_MASK, false);
Note: JMenuItem is the only Swing component that graphically displays an assigned keyboard accelerator.
We normally attach an ActionListener to a menu item. As any button, whenever the menu item is clicked the ActionListener is notified. Alternatively we can use Actions (discussed below and briefly in 2.13) which provide a convenient means of creating a menu item as well as definining the corresponding action handling code. A single Action instance can be used to create an arbitrary number of JMenuItems and JButtons with identical action handling code. We will see how this is done soon enough. It suffices to say here that when an Action is disabled, all JMenuItems associated with that Action are disabled and, as buttons always do in the disabled state, appear grayed out.
As any AbstractButton decendent, JMenuItem fires ActionEvents and ChangeEvents and allows attachment of ActionListeners and ChangeListeners accordingly. JMenuItem will also fire MenuDragMouseEvents (see below) when the mouse enters, exits, is dragged, or a mouse button is released inside its bounds, and MenuKeyEvents when a key is pressed, typed, or released. Both of these Swing-specific events will only be fired when the popup containing the corresponding menu item is visible. As expected, we can add MenuDragMouseListeners and MenuKeyEventListeners for notification of these events. Several public processXXEvent() methods are also provided to receive and respond to events dispatched to a JMenuItem, some of which are forewarded from the current MenuSelectionManager (see below).
12.1.5 JMenu
class javax.swing.JMenu
This class extends JMenuItem and is usually added to a JMenuBar or to another JMenu. In the former case it will act as a menu item which pops up a JPopupMenu containing child menu items. If a JMenu is added to another JMenu it will appear in that menu's corresponding popup as a menu item with an arrow on its right side. When that menu item is activated by mouse movement or keyboard selection a popup will appear displaying its corresponding child menu items. Each JMenu maintains a topLevelMenu property which is false for sub-menus and true otherwise.
JMenu uses a DefaultButtonModel to manage its state, and it holds a private instance of JPopupMenu (see below) used to display its associated menu items when it is activated with the mouse or a keyboard mnemonic.
Note: Unlike its JMenuItem parent, JMenu specifically overrides setAccelerator() with an empty implementation to disallow keyboard accelerators. This is because it assumes that we will only want to activate a menu (i.e. display its popup) when it is already visible; and for this we can use a mnemonic.
We can display/hide the associated popup programmatically by setting the popupMenuVisible property, and we can access the popup using getPopupMenu(). We can set the coordinate location where the popup is displayed with setMenuLocation(). We can assign a specific delay time in milliseconds using setDelay() to specify how long a JMenu should wait before displaying its popup when activated.
We use the overloaded add() method to add JMenuItems, Components, Actions (see below) or Strings to a JMenu. (Adding a String simply creates a JMenuItem child with the given text.) Similarly we can use several variations of overloaded insert() and remove() methods to insert and remove existing children. JMenu also directly supports creation and insertion of separator components in its popup, using addSeparator(), which provides a convenient means of visually organizing child components into groups.
The protected createActionChangeListener() method is used when an Action is added to a JMenu to create a PropertyChangeListener for internal use in responding to bound property changes that occur in that Action (see below). The createWinListener() method is used to create an instance of the protected inner class JMenu.WinListener which is used to deselect a menu when its corresponding popup closes. We are rarely concerned with these methods, and only subclasses desiring a more complete customization will override them.
Along with event dispatching/handling inherited from JMenuItem, JMenu adds functionality for firing and capturing MenuEvents (see below) used to notify attached MenuListeners when its current selection changes.
UI Guideline : Flat and wide design
Recent research in usability has shown that menus with too many levels of hierachy don't work well. Features get buried too many layers deep. Some operating systems restrict menus to 3 levels i.e. the main menu bar, a pull down menu and a single walking pop-up menu.
A maximum 3 Levels would be appear to be a good rule of thumb. Don't be tempted to use popup menus to create a complex series of hierarchical choices. Keep menus flatter.
For each menu, another good rule of thumb is to provide 7 +/- 2 options. However, if you have too many choices, it is better to break this rule and go to 10 or more than to introduce additional hierarchy.
12.1.6 JPopupMenu
class javax.swing.JPopupMenu
This class represents a small window which pops up and contains a collection of components layed out in a single column by default using, suprisingly, a GridBagLayout (note that there is nothing stopping us from changing JPopupMenu's layout manager). JPopupMenu uses a DefaultSingleSelectionModel to enforce the selection of only one child at any given time.
JMenu simply delegates all its add(), remove(), insert(), addSeparator(), etc., calls to its internal JPopupMenu. As expected, JPopupMenu provides similar methods. The addSeparator() method inserts an instance of the inner class JPopupMenu.Separator (a subclass of JSeparator -- discussed below). The show() method displays a JPopupMenu at a given position within the corrdinate system of a given component. This component is referred to as the invoker component, and JPopupMenu can be assigned an invoker by setting its invoker property. JComponent's setVisible() method is overriden to display a JPopupMenu with respect to its current invoker, and we can change the location it will appear using setLocation(). We can also control a JPopupMenu's size with the overloaded setPopupSize() methods, and we can use the pack() method (similar to the java.awt.Window method of the same name) to request that a popup change size to the minimum required for correct display of its child components.
Note: JComboBox's UI delegate uses a JPopupMenu subclass to display its popup list.
When the need arises to display our own JPopupMenu, it is customary, but certainly not necessary, to do so in response to a platform-dependent mouse gesture (e.g. a right-click on Windows platforms). Thus, the java.awt.event.MouseEvent class provides a simple method we can use in a platform-independent manner to check whether a platform-dependent popup gesture has occurred. This method, isPopupTrigger(), will return true if the MouseEvent it is called on represents the current operating system's popup trigger gesture.
JPopupMenu has the unique ability to act as either a heavyweight or lighweight component. It is smart enough to detect when it will be displayed completely within a Swing container or not and adjust itself accordingly. However, there may be cases in which the default behavior may not be acceptable. Recall from chapter 2 that we must set JPopupMenu's lightWeightPopupEnabled property to false to force it to be heavyweight and allow overlapping of other heavyweight components that might reside in the same container. Setting this property to true will force a JPopupMenu to remain lightweight. The static setDefaultLightWeightPopupEnabled() method serves the same purpose, but affects all JPopupMenu's created from that point on (in the current implementation all popups existing before this method is called will retain their previous lightweight/heavyweight settings).
Bug Altert! Due to an AWT bug, all popups are forced into lightweight mode when displayed in modal dialogs (regardless of the state of the lightWeightPopupEnabled property).
The protected createActionChangeListener() method is used when an Action (see below) is added to a JPopupMenu to create a PropertyChangeListener for internal use in responding to bound property changes that occur in that Action.
A JPopupMenu fires PopupMenuEvents (discussed below) whenever it is made visible, hidden, and cancelled. As expected we can attatch PopupMenuListeners to capture these events.
12.1.7 JSeparator
class javax.swing.JSeparator
This class represents a simple separator component with a UI delegate responsible for displaying a horizontal or vertical line. We can specify which orientation a JSeparator should use by changing its orientation property. This class is most often used in menus and toolbars, however, it is a standard Swing component and there is nothing stopping us from using JSeparators anywhere we like.
We normally do not use JSeparator explicitly. Rather, we use the addSeparator() method of JMenu, JPopupMenu, and JToolBar. JMenu delegates this call to its JPopupMenu which, as we know, uses an instance of its own custom JSeparator subclass which is rendered as a horizontal line. JToolBar also uses its own custom JSeparator sub-class which has no graphical representation, and appears as just an empty region. Unlike menu separators, however, JToolBar's separator allows explicit instantiation and provides a method for assigning a new size in the form of a Dimension.
UI Guideline : Use of a separator
Use a separator to group related menu choices and separate them from others. This gives better visual communication and better usability by providing a space between the target areas for groups of choices. This reduces the chance of an error when making a selection with the mouse.
12.1.8 JCheckBoxMenuItem
class javax.swing.JCheckBoxMenuItem
This class extends JMenuItem and can be selected, deselected, and rendered identical to JCheckBox (see chapter 4). We use the isSelected()/setSelected() or getState()/setState() methods to determine/set the selection state respectively. ActionListeners and ChangeListeners can be attached to a JCheckBoxMenuItem for notification about changes in its state (see JMenuItem discussion for inherited functionality). We often use JCheckBoxMenuItems in ButtonGroups to enforce the selection of only one item in a group at any given time.
12.1.9 JRadioButtonMenuItem
class javax.swing.JRadioButtonMenuItem
This class extends JMenuItem and can be selected, deselected, and rendered identical to JRadioButton (see chapter 4). We use the isSelected()/setSelected() or getState()/setState() methods to determine/set the selection state respectively. ActionListeners and ChangeListeners can be attached to a JRadioButtonMenuItem for notification about changes in its state (see JMenuItem discussion for inherited functionality). We often use JRadioButtonMenuItems in ButtonGroups to enforce the selection of only one item in a group at any given time.
UI Guideline : Widget overloading
As a general rule in UI Design, it is not desirable to overload components and use them for two purposes. By adding Checkboxes or Radio Buttons to a menu, you are changing the purpose of a menu from one of navigation to one of selection. This is an important point to understand.
Making this change is an acceptable design technique when it will speed operation and enhance usability by removing the need for a cumbersome dialog or option pane. However, it is important to assess that it does not otherwise adversely affect usability.
Groups of Radio Button or Checkbox menu items are probably best isolated by using a JSeparator.
12.1.10 The MenuElement interface
abstract interface javax.swing.MenuElement
This interface must be implemented by all components that wish to act as menu items. By implementing the methods of this interface any components can act as a menu item, making it quite easy to build our own.
The getSubElements() method returns an array of MenuElements containing the given item's sub-elements. The processKeyEvent() and processMouseEvent() methods are called to process keyboard and mouse events respectively when the implementing component has the focus. Unlike methods with the same name in the java.awt.Component class, these two methods receive three parameters: the KeyEvent or MouseEvent, respectively, which should be processed, an array of MenuElements which forms the menu path to the implementing component, and the current MenuSelectionManager (see below). The menuSelectionChanged() method is called by the MenuSelectionManager when the implementing component is added or removed from its current selection state. The getComponent() method returns a reference to a component that is responsible for the rendering of the implementing component.
Note: The getComponent() method is interesting, as it allows classes that are not Components themselves to implement the MenuElement interface and act as menu elements when necessary. Such a class must provide a Component used for display in a menu, and this Component would be returned by getComponent(). This design has curious implications, allowing us to design robust JavaBeans that encapsulate an optional GUI representation. We can imagine a complex spell-checker or dictionary class implementing the MenuElement interface and providing a custom component for display in a menu (a powerful and highly object-oriented bean indeed).
JMenuItem, JMenuBar, JPopupMenu, and JMenu all implement this interface. Note that each of their getComponent() methods simply return a this reference. Also note that by extending any of these implementing classes, we inherit MenuElement functionality and are therefore not required to implement it. (We won't explicitly use this interface in any examples, as the custom component we will build at the end of this chapter is an extension of JMenu.)
12.1.11 MenuSelectionManager
class javax.swing.MenuSelectionManager
MenuSelectionManager is a service class responsible for managing menu selection throughout a single Java session. (Note that unlike most other service classes in Swing, MenuSelectionManager does not register its shared instance with AppContext--see chapter 2.) When MenuElement implementations receive MouseEvents or KeyEvents, these events should not be processed directly. Rather, they should be handed off to the MenuSelectionManager so that it may forward them to sub-components automatically. For instance, whenever a JMenuItem is activated by keyboard or mouse, or whenever a JMenuItem selection occurs, the menu item UI delegate is responsible for forwarding the corresponding event to the MenuSelectionManager if necessary. The following code shows how BasicMenuItemUI deals with mouse releases:
public void mouseReleased(MouseEvent e) {
MenuSelectionManager manager =
MenuSelectionManager.defaultManager();
Point p = e.getPoint();
if(p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
manager.clearSelectedPath();
menuItem.doClick(0);
}
else {
manager.processMouseEvent(e);
}
}
The static defaultManager() method returns the MenuSelectionManager shared instance, and the clearSelectedPath() method tells the currently active menu hierarchy to close and unselect all menu components. In the code shown above, clearSelectedPath() will only be called if the mouse release occurs within the corresponding JMenuItem (in which case there is no need for the event to propogate any further). If this is not the case, the event is sent to MenuSelectionManager's processMouseEvent() method which forwards it to other sub-components. JMenuItem doesn't have any sub-components by default so not much interesting happens in this case. However, in the case of JMenu, which considers its popup menu a sub-component, sending a mouse released event to the MenuSelectionManager is expected no matter what (from BasicMenuUI):
public void mouseReleased(MouseEvent e) {
MenuSelectionManager manager =
MenuSelectionManager.defaultManager();
manager.processMouseEvent(e);
if (!e.isConsumed())
manager.clearSelectedPath();
}
MenuSelectionManager will fire ChangeEvents whenever its setSelectedPath() method is called (i.e. each time a menu selection changes). As expected, we can attach ChangeListeners to listen for these events.
12.1.12 The MenuDragMouseListener interface
abstract interface javax.swing.event.MenuDragMouseListener
This listener receives notification when the mouse cursor enters, exits, is released, or is moved over a menu item.
12.1.13 MenuDragMouseEvent
class javax.swing.event.MenuDragMouseEvent
This event class is used to deliver information to MenuDragMouseListeners. It encapsulates the component source, event id, time of the event, bitwise or-masked int specifying which mouse button and/or keys (CTRL, SHIFT, ALT, or META) were pressed at the time of the event, x and y mouse coordinates, number of clicks immediately preceding the event, whether or not the event represents the platform-dependent popup trigger, an array of MenuElements leading to the source of the event, and the current MenuSelectionManager. This event inherits all MouseEvent functionality (see API docs) and adds two methods for retrieving the array of MenuElements and the MenuSelectionManager.
12.1.14 The MenuKeyListener interface
abstract interface javax.swing.event.MenuKeyListener
This listener is notified when a menu item receives a key event corresponding to a key press, release, or type. These events don't necessarily correspond to mnemonics or accelerators, and are received whenever a menu item is simply visible on the screen.
12.1.15 MenuKeyEvent
class javax.swing.event.MenuKeyEvent
This event class is used to deliver information to MenuKeyListeners. It encapsulates the component source, event id, time of the event, bitwise or-masked int specifying which mouse button and/or keys (CTRL, SHIFT, or ALT) were pressed at the time of the event, an int and char identifying the source key that caused the event, an array of MenuElements leading to the source of the event, and the current MenuSelectionManager. This event inherits all KeyEvent functionality (see API docs) and adds two methods for retrieving the array of MenuElements and the MenuSelectionManager.
12.1.16 The MenuListener interface
abstract interface javax.swing.event.MenuListener
This listener receives notification when a menu is selected, deselected, or cancelled. Three methods must be implemented by MenuListeners, and each takes a MouseEvent parameter: menuCanceled(), menuDeselected(), and menuSelected().
12.1.17 MenuEvent
class javax.swing.event.MenuEvent
This event class is used to deliver information to MenuListeners. It simply encapsulates a reference to its source Object.
12.1.18 The PopupMenuListener interface
abstract interface javax.swing.event.PopupMenuListener
This listener receives notification when a JPopupMenu is about to become visible, hidden, or when it is cancelled. Canceling a JPopupMenu also causes it to be hidden, so two PopupMenuEvents are fired in this case. A cancel occurs when the invoker component is resized or the window containing the invoker changes size or location. Three methods must be implemented by PopupMenuListeners, and each takes a PopupMenuEvent parameter: popupMenuCanceled(), popupMenuWillBecomeVisible(), and popupMenuWillBecomeInvisible().
12.1.19 PopupMenuEvent
class javax.swing.event.PopupMenuEvent
This event class is used to deliver information to PopupMenuListeners. It simply encapsulates a reference to its source Object.
12.1.20 JToolBar
class javax.swing.JToolBar
This class represents the Swing implementation of a toolbar. Toolbars are often placed directly below menu bars at the top of a frame or applet, and act as a container for any component (buttons and combo boxes are most common). The most convenient way to add buttons to a JToolBar is to use Actions (discussed below).
Note: Components often need their alignment setting tweaked to provide uniform positioning within JToolBar. This can be accomplished through use of the setAlignmentY() and setAlignmentX() methods. We will see that this is necessary in the final example of this chapter.
JToolBar also allows convenient addition of an inner JSeparator sub-class, JToolBar.Separator, to provide an empty space for visually grouping components. These separators can be added with either of the overloaded addSeparator() methods, one of which takes a Dimension parameter specifying the size of the separator.
Two orientations are supported, VERTICAL and HORIZONTAL, and managed by JToolBar's orientation property. It uses a BoxLayout layout manager which is dynamically changed between Y_AXIS and X_AXIS when the orientation property changes.
JToolBar can be dragged in and out of its parent container if its floatable property is set to true. When dragged out of its parent, a JToolBar appears as a floating window and its border changes color depending on whether it can re-dock in its parent at a given location. If a JToolBar is dragged outside of its parent and released, it will be placed in its own JFrame which is fully maximizable, minimizable, and closable. When this frame is closed JToolBar will jump back into its most recent dock position in its original parent, and the floating JFrame will disappear. It is recommended that JToolBar is placed in one of the four sides of a container using a BorderLayout, leaving the other sides unused. This allows the JToolBar to be docked in any of that container's side regions.
The protected createActionChangeListener() method is used when an Action (see below) is added to a JToolBar to create a PropertyChangeListener for internal use in responding to bound property changes that occur in that Action.
UI Guideline : 3 Uses for a Toolbar
Toolbars have become ubiquitious in modern software. However, they are often overused or misused and fail to achieve their objective of increased usability. There are three key uses which have subtle differences and implications.
Tool Selection or Mode Selection
Perhaps the most effective use of a toolbar is, as the name suggests, for the selection of a tool or operational mode. This is most common in drawing or image manipulation packages. The user selects the toolbar button to change the mode from "paintbrush" to "filler" to "draw box" to "cut" etc.. This is a highly effective use of toolbar, as the small icons are usually sufficient to render a suitable tool image. Many images for this purpose have been adopted as a defacto standard. If you are developing such a tool selection toolbar, it would be advisable to stick closely to icons which have been used by similar existing products.
Functional Selection
The earliest use of toolbar was to replace the selection of a specific function from the menu. This led to them being called "speedbars" or "menubars". The idea was that the small icon button was faster and easier to acquire than the menu selection and that usability was enhanced as a result. This worked well for many common functions in file oriented applications, such as "open file", "new file", "save", "cut", "copy" and "paste". In fact, most of us would recognise the small icons for all of these functions. However, with other more application specific functions, it has been much harder for icon designers to come up with appropriate designs. This often leads to applications which have a confusing and intimidating array of icons across the top of the screen. This can detract from usability. As a general rule of thumb, stick to common cross application functions when overloading menu selections with toolbar buttons. If you do need to break the rule then consider selecting annotated buttons for the toolbar.
Navigational Selection
The 3rd use for toolbars has been for navigational selection. This often means replacing or overloading menu options from a "Window", "Go" or "Form" menu. These menu options are used to select a specific screen to come to the front. The toolbar buttons replace or overload the menu option and allow the navigational selection to be made supposedly by faster means. However, again this suffers from the problem of appropriate icon design. It is usually too difficult to devise a suitable set of icons which have clear and unambiguous meaning. Therefore, as a rule of thumb, consider the use of annotated buttons on the toolbar.
12.1.21 Custom JToolBar separators
Unfortunately Swing does not include a toolbar-specific separator component that will display a vertical or horizontal line depending on current toolbar orientation. The following psuedo-code shows how we can build such a component under the assumption that it will always have a JToolBar as direct parent:
public class MyToolBarSeparator extends JComponent
{
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (getParent() instanceof JToolBar) {
if (((JToolBar) getParent()).getOrientation()
== JToolBar.HORIZONTAL) {
// paint a vertical line
}
else {
// paint a horizontal line
}
}
}
public Dimension getPreferredSize() {
if (getParent() instanceof JToolBar) {
if (((JToolBar) getParent()).getOrientation()
== JToolBar.HORIZONTAL) {
// return size of vertical bar
}
else {
// return size of horizontal bar
}
}
}
}
UI Guideline : Use of a separator
The failure to include a separator for toolbars really was an oversight by the Swing designers. Again, use the separator to group related functions or tools. For example, if the function all belong on the same menu then group them together, or if the tools (or modes) are related such as "cut", "copy", "paste" then group them together and separate them from others with a separator.
Visual grouping like this improves visual separation by introducing a visual layer. The viewer can first acquire a group of buttons and then a specific button. They will also learn with directional memory the approximate position of each group. By separating them you will improve the usability by helping them to acquire the target better when using the mouse.
12.1.22 Changing JToolBar's floating frame behavior
The behavior of JToolBar's floating JFrame is certainly useful, but it is arguable whether the maximization and resizability should be allowed. Though we cannot control whether or not a JFrame can be maximized, we can control whether or not it can be resized. To enforce non-resizability in JToolBar's floating JFrame (and set its dispayed title while we're at it) we need to override its UI delegate and customize the createFloatingFrame() method as follows:
public class MyToolBarUI
extends javax.swing.plaf.metal.MetalToolBarUI {
protected JFrame createFloatingFrame(JToolBar toolbar) {
JFrame frame = new JFrame(toolbar.getName());
frame.setTitle("My toolbar");
frame.setResizable(false);
WindowListener wl = createFrameListener();
frame.addWindowListener(wl);
return frame;
}
}
To assign MyToolBarUI as a JToolBar's UI delegate we can do the following:
mytoolbar.setUI(new MyToolBarUI());
To force use of this delegate on a global basis we can do the following before any JToolBars are instantiated:
UIManager.getDefaults().put(
"ToolBarUI","com.mycompany.MyToolBarUI");
Note that we may also have to add an associated Class instance to the UIDefaults table for this to work (see chapter 21).
UI Guideline : Use of a Floating Frame
It is probably best to restrict the use of a floating toolbar frame to toolbars being used for tool or mode selection (see guideline in 12.1.20).
12.1.23 The Action interface
abstract interface javax.swing.Action
This interface describes a helper object which extends ActionListener and which supports a set of bound properties. We use appropriate add() methods in the JMenu, JPopupMenu, and JToolBar classes to add an Action which will use information from the given instance to create and return a component that is appropriate for that container (a JMenuItem in the case of the first two, a JButton in the case of the latter). The same Action instance can be used to create an arbitrary number of menu items or toolbar buttons.
Because Action extends ActionListener, the actionPerformed() method is inherited and can be used to encapsulte appropriate ActionEvent handling code. When a menu item or toolbar button is created using an Action, the resulting component is registered as a PropertyChangeListener with the Action, and the Action is registered as an ActionListener with the component. Thus, whenever a change occurs to one of that Action's bound properties, all components with registered PropertyChangeListeners will receive notification. This provides a convenient means for allowing identical functionality in menus, toolbars, and popup menus with minimum code repetition and object creation.
The putValue() and getValue() methods are intended to work with a Hashtable-like structure to maintain an Action's bound properties. Whenever the value of a property changes, we are expected to fire PropertyChangeEvents to all registered listeners. As expected, methods to add and remove PropertyChangeListeners are provided.
The Action interface defines five static property keys intended for use by JMenuItems and JButtons created with an Action instance:
String DEFAULT: [not used]
String LONG_DESCRIPTION: Used for a lengthy description of an Action.
String NAME: Used as the text in associated and displayed in JMenuItems and JButtons.
String SHORT_DESCRIPTION: Used for the tooltip text of associated JMenuItems and JButtons.
String SMALL_ICON: Used as the icon in associated JMenuItems and JButtons.
12.1.24 AbstractAction
class javax.swing.AbstractAction
This class is an abstract implementation of the Action interface. Along with the properties inherited from Action, AbstractAction defines the enabled property which provides a means of enabling/disabling all associated components registered as PropertyChangeListeners. A SwingPropertyChangeSupport instance is used to manage the firing of PropertyChangeEvents to all registered PropertyChangeListeners (see chapter 2 for more about SwingPropertyChangeSupport).


RSS feed Java FAQ News