« Movable Type 3 Upgrade | Main | Swing and Groovy: Low-Carb Coding, not Syntactic Sugar »

Extending Swing via Listeners

With a framework as nimble as Swing there are many ways to skin a cat. This is possibly one of the sources of many complains about Swing. For example Sam Dalton wrote a class that implements a dialog that closes when the escape key is pressed. Subclassing JDialog is certainly one way t do it, but I probably would have simply wrote a utility method...

static void makeDisposeOnEscapeKey(final RootPaneContainer rootPane) { Action action = new AbstractAction() { public void actionPerformed(ActionEvent arg0) { ((java.awt.Window)rootPane).dispose(); } }; KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); rootPane.getRootPane() .getActionMap() .put(action,action); rootPane.getRootPane() .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(stroke, action); }

A couple of changes reflect my swing codeing preferences. First, I call dispose() instead of hide(). My reasons are twofold: first until a window is disposed it takes up native graphics resources, and can essentially be a big memory leak until the object becomes inaccessible and is finalized. The second reason has to do with a clean JVM exit. While any AWT resources are realized (i.e. have native OS resources allocated) the AWT event thread will keep running and the JVM will not exit. If all of the windows are disposed() then the AWT event thread will enter a state that will allow the JVM to terminate. No more calls to System.exit(0).

The second thing I changed was which action map I put the escape key in. Sam used WHEN_FOCUSED which is fine if there are no focusable components in the dialog. But throw in a button or text field and the action may never get fired. There are actually two options here, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT and WHEN_IN_FOCUSED_WINDOW. I chose the latter because this is also how the JMenuItems register their accelerator keys and the desired operation is essentially the same. Since we are adding the action to the JRootPane there are no differences between the two.

By using a utility method we can also add this behavior to any free standing component. One of the issues Swing has to deal with is that higher up in the AWT hierarchy dialogs and frames were separated. To deal with this they created an interface javax.swing.RootPaneContainer so that they can treat all root level components the same. Hence this behavior can be added to any JFrame, JDialog, JWindow, JApplet, or JInternalFrame.

And please, if you have obnoxious splash screens either have a user preference to turn it off or add this to the JWindow so the user can click it and press escape. Wouldn't that be nice.

Comments (1)

Eric Snell:

I also use this style. One thing, I don't dispose of the window, I instead send it a close event. That keeps the logic of what to do with the close in the setDefaultCloseOperation() setting.

eg. frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));

Post a comment


About

This page contains a single entry from the blog posted on May 25, 2004 9:40 AM.

The previous post in this blog was Movable Type 3 Upgrade.

The next post in this blog is Swing and Groovy: Low-Carb Coding, not Syntactic Sugar.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.33