Swing: restrict keyboard focus traversal to specific widgets

Go To StackoverFlow.com

1

I have a JFrame containing the usual assortment of panels and widgets, and I have a JPanel which I'm using as that JFrame's glassPane. I'd like to be able to restrict keyboard focus traversal to the components in the glassPane when it's visible.

My problem may or may not be compounded by the fact that a background thread launches a process which results in a progress dialog appearing and subsequently disappearing, which steals the focus from a widget in my glassPane but returns it to some widget beneath my glassPane.

I've tried setting the JFrame's focus traversal policy to one which only allows the glassPane to be focused, but that didn't seem to have any effect. (maybe I did it incorrectly?)

Any help would be appreciated.

2012-04-03 20:48
by Dan O
Your application sounds to be quite complex. Out of curiosity what sort of components are you displaying in the glasspane and why the glasspane - Hovercraft Full Of Eels 2012-04-03 21:07
the glassPane contains a scrolling TextPane, a JCheckBox, and a JButton. It's being used to display a EULA, forcing the user to accept its terms before they can use the application. My users complained when I used a modal JDialog, so I'm investigating alternatives - Dan O 2012-04-03 21:17
maybe I did it incorrectly - maybe, hard to tell without seeing your code :-) That said, managing the glasspane is tricky (wouldn't expect a custom ftp to be overly helpful as it isn't responsible for mouseEvents), so you might consider to use JLayer (as of jdk7, or JXLayer for jdk6) instead - kleopatra 2012-04-04 08:46


3

As mentioned in my comment, I would probably go for a J/X/Layer - but here's a solution if indeed a custom FTP is the only missing piece in your context.

To exclude components from focus traversal, reject them in the accept method of a custom FTP. Below is an example which rejects all components which are not children of the glassPane if the glassPane is visible (note: this is overly simplistic, as it handles direct children only, real-world code must walk up the parent chain until it either hit the glasspane or not)

public static class FTP extends LayoutFocusTraversalPolicy {

    @Override
    protected boolean accept(Component comp) {
        JFrame window = (JFrame) SwingUtilities.windowForComponent(comp);
        if (hasVisibleGlassPane(window)) {
            return comp.getParent() == window.getGlassPane();
        }
        return super.accept(comp);
    }

    private boolean hasVisibleGlassPane(JFrame window) {
        return window != null && window.getGlassPane() != null
                && window.getGlassPane().isVisible();
    }

}
2012-04-04 09:27
by kleopatra
Great, thanks. I completely forgot to investigate subclasses of FocusTraversalPolicy , and just extended it directly. For the record I ended up going with another solution, but this actually answers the question as asked - Dan O 2012-04-04 13:53


3

I'm going to make my comment an answer as I believe that it is the solution to your problem:

Perhaps a better option is to use a CardLayout to simply swap views rather than a glass pane or dialog since this situation does not seem the best suited for glass pane use. If you use CardLayout, you don't have to come up with a kludge (fiddle with focus traversal), to fix the side effects of another kludge (using glass pane for things it wasn't intended to be used for).

If you're not familiar with it, a CardLayout will allow you to easily swap components in a GUI, and is often used to swap JPanels that hold complex GUI's that occurs when the user goes from one major program state to another. I think that it would be perfect for your purposes and would prevent you from having to worry about your focus issue.

2012-04-03 21:35
by Hovercraft Full Of Eels


2

Try calling .setFocusable(false) on the JFrame you want to ignore (or at worst, on all the components within it) while the glassPane is visible.

2012-04-03 20:56
by Peter Sobot


0

When I had discussion with client he gave me the same requirement. So I decided to use the JLayer and LayeredPane in my project and with all of these components as a simple solution I implemented following code, may be it will help you in your project.

public YourConstructor() {
    yes.addFocusListener(new FocusAdapter() {
        public void focusLost(FocusEvent fe) {
            if (!no.hasFocus()) {
                no.requestFocusInWindow();
            }
        }
    });

    no.addFocusListener(new FocusAdapter() {
        public void focusLost(FocusEvent fe) {
            if (!yes.hasFocus()) {
                yes.requestFocusInWindow();
            }
        }
    });
}

@Override
public void setVisible(boolean visibility) {
    super.setVisible(visibility);
    if (visibility) {
        yes.requestFocusInWindow();
    }
}
2013-04-23 17:00
by Sudhir Dhumal