The Source for Java Technology Collaboration
User: Password:



Scott Violet

Scott Violet's Blog

The Usefulness of Actions

Posted by zixle on November 01, 2005 at 07:58 AM | Comments (10)

I originally intended to blog on the usefullness of WeakReferences for
client apps. This was to be motivated by the internals of Actions and
why Swing internally needs to use a WeakReference to support
Actions. In writing that blog I ended up spending more time describing
Actions that I've decided to split it into two blogs. This blog
motivates the need for Actions as well as the changes to Actions
in 1.6. Later on I'll followup with why we use WeakReferences to
support Actions and why you might use WeakReferences in similar
situations.

An ActionListener encapsulates application specific functionality that
is usually triggered by a user interface gesture (this isn't always
true, javax.swing.Timer also uses ActionListener). For example
clicking the back button in a browser might notify an implementation
of ActionListener this is responsible for navigating to the previous
page. This might look something like:

private class GoBackActionListener implements ActionListener {
  public void actionPerformed(ActionEvent e) {
    browserComponent.show(getLastURL());
  }
}
backButton.addActionListener(new GoBackActionListener());
backButton.setIcon(...);
backButton.setEnabled(false);

If you've written any Swing/AWT application chances are you have code
like this. It's simple, and it works.

Consider what happens if you want to conditionally enable the button
based on whether you can go back a page. In this case you need a
handle to the backButton so that you can invoke setEnabled on
it. If you also have a menu item with the same functionality you'll
need to keep a handle to that menu item as well, or any other user
interface component that is to use the GoBackActionListener
functionality. This is a bit painful. In particular it leads to all
sorts of references back to the hosting components in your application.

The following code shows configuring a menu item and wiring it to a
GoBackActionListener:

backMenuItem.addActionListener(new GoBackActionListener());
backMenuItem.setText(...);
backMenuItem.setMnemonic(...);
backMenuItem.setDisplayedMnemonicIndex(...);
backMenuItem.setAccelerator(...);
backMenuItem.setEnabled(false);

The Action interface is meant to address these problems. Action not
only encapsulates the functionality part of ActionListener, but also a
description of that functionality as appropriate for user interface
components. Included in that description is whether or not the user
interface component should be enabled. When the enabled state of the
Action changes the user interface components listening to the Action
will update appropriately. If the previous examples were replaced
with Action you would change the enabled state of the Action, and as
a consequence the button and menu item would update appropriately. The
code for this could be something like:

private class GoBackAction extends AbstractAction {
  public GoBackAction() {
    putValue(Action.NAME, "...");
    putValue(Action.LARGE_ICON_KEY, ...);
    putValue(Action.SMALL_ICON, ...);
    putValue(Action.MNEMONIC_KEY, ...);
    putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, ...);
    putValue(Action.ACCELERATOR_KEY, ...);
    putValue(Action.SHORT_DESCRIPTION, ...);
    setEnabled(false);
  }
  public void actionPerformed(ActionEvent e) {
    browserComponent.show(getLastURL());
  }
}
Action goAction = new GoBackAction();
backButton.setHideActionText(true);
backButton.setAction(goAction);
backMenuItem.setAction(goAction);

You'll notice a number of differences with this example and the
earlier ones. In particular rather than configuring the text, icon and
other properties of the user interface component the Action contains
all this information. When the Action is attached to the user interface
component the user interface component configures its state from the
Action. For buttons this includes the icon, text, mnemonic, mnemonic
index, tooltip text, action command key and enabled state. This is
nice in that the description is encapsulated in a single place and
does not need to be replicated to all the hosting components.

Those that haven't followed the latest changes in 1.6 will notice some
new keys and methods. In particular DISPLAYED_MNEMONIC_INDEX_KEY,
LARGE_ICON_KEY and setHideActionText are all new in 1.6. Action got
one more new key in 1.6, that is SELECTED_KEY. Included in the 1.6
additions is centralizing the description of what properties each
component supports. See the class level javadoc of Action for all the
specifics, as well as bugs 4133141 and 4626632.

When using ActionListener we had to explicitly update the enabled
state of the user interface components, eg:

backButton.setEnabled(...);
backMenuItem.setEnabled(...);

With Action the code becomes:

goBackAction.setEnabled(...);

Any user interface components attached to the Action will update
appropriately whenever the Action changes! Now we just reference the
Action and not all the various components.

The ability to change the Action directly and have it reflected
appropriately in the user interface component comes in very handy in a
number of situations. For example, undo/redo menu items often have
their text reflect what the undo/redo operation will do. For example,
'Undo Paste' or 'Undo Edit'... When using Action you change the NAME
property of the Action and the menu item will change appropriately.

If this is the first time you've read about Actions you should read
the Swing tutorial section on Actions. It has more examples and
details. In a later blog I'll cover using the new SELECTED_KEY. It's
interesting enough that it warrants it's own blog.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • For more Actions reading take a look at Hans's article: http://today.java.net/pub/a/today/2005/01/31/controlGUI.html

         -Scott

    Posted by: zixle on November 01, 2005 at 10:23 AM

  • In previous releases, setting the short description (and a few other properties) on an action in a menubar were taken into account when the action-based item was added to the menu, but later changing the value of the property didn't update the menu item accordingly (a tooltip on a menu item wouldn't reflect the new value of SHORT_DESCRIPTION for example), as the property change event was ignored. I could hack around this by adding my own listener for property changes, but it wasn't very elegant. Is this fixed in 1.6 (I understand it is, but it'd be nice to have some confirmation)? I posted a bug report on this, along the lines of 5026829.

    Any chance of ACCELERATOR_KEY bindings being used when the action isn't in a menu? For example, in application where a user has the option to customise menus and toolbars, if he places an action in the toolbar and deletes it from the menu, the keyboard shortcut (say F5) is no longer bound...

    Anyway, keep up the good work. LARGE_ICON is really going to be helpful!

    Posted by: chris_e_brown on November 01, 2005 at 01:46 PM

  • What I miss from the Action is a "VISIBLE" key that when toggled will hide/show any components bound to it. To implement outside the JDK requires a subclass of each component you wish to use, which although I have done is a bit of a pain.

    Posted by: teilo on November 02, 2005 at 02:33 AM

  • chris,
    As far as I know all the components that support action will now properly reset their values when the property changes. This was done as part of 4626632. I encourage you to try your test case and let me know if it doesn't work.
    JMenuItem is the only one that supports accelerators, hence the only one that honors ACCELERATOR_KEY. The other buttons support a mnenonic, is there a reason that isn't enough?
    Thanks for the feedback!
       -Scott

    Posted by: zixle on November 02, 2005 at 05:50 AM

  • teilo,

    You are the first to request support for a VISIBLE property. Please file an RFE.

    Thanks!

    -Scott

    Posted by: zixle on November 02, 2005 at 05:51 AM

  • Scott, the menu tooltips do change as expected (I just tried out my test case against Mustang build 58).

    As for accelerators and mnemonics, using Windows as an example, mnemonics are often accessed by pressing ALT and the mnemonic key, and are usually rendered as an underlined letter in a component such as a JButton, next to an icon if there is one. However, in toolbars, you often don't have that text, so the visual cue isn't there; some actions, such as "press F5 to refresh" may be present in the toolbar only and not in the menu bar, and it'd be nice to be able to encapsulate this (instead of something non-standard such as ALT+F5, just to be "mnemonic-friendly"... and how would you render a mnemonic for F5 in a label anyway?).

    Furthermore, the "accelerators/menu bar" line of thought seems to be assuming that all actions occur in a JFrame (the main or only one of the application)... However, I often want to bind accelerators to actions in JDialogs (say F2 to rename, F3 to search, and so on), but dialogs rarely have menu bars. I'm stuck handling action maps and input maps to implement the key bindings myself...

    I also noticed in the javadoc for Action in Mustang that there's a property called hideActionText. I'd be interested in an explanation of that too in an upcoming blog entry too, if you can. It would seem to allow labels to be displayed beside icons on toolbar buttons (although there doesn't appear to be any way to specify position, such as PAGE_END or TRAILING orientations, or whether labels should be truncated to ensure uniform button size). Also, the documentation for it suggests that it's evaluated once and if it's not true, then changes to the name are always ignored.

    Posted by: chris_e_brown on November 02, 2005 at 07:10 AM

  • Chris,

    I see your point about accelerators. Could you file an RFE against button for this. When we add support to buttons we'll add support to Actions as well.

    I'll cover the hideActionText in a later blog

    Thanks for the feedback!

       -Scott

    Posted by: zixle on November 02, 2005 at 04:01 PM

  • Scott,
    I've posted an RFE which has been assigned an internal review ID of 569069 (not visible outside Sun).
    You're welcome for the feedback: thanks for the code! ;-)
    - Chris

    Posted by: chris_e_brown on November 03, 2005 at 01:22 AM

  • Scott,
    I have posted an RFE for a "visible" property of an action,
    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6346873

    Posted by: teilo on November 08, 2005 at 01:17 AM

  • FYI - I recently donated an open source library for Swing Actions. See SAM - Swing Action Manager .

    Posted by: michaelbushe on November 19, 2005 at 01:13 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds