Reacting to Frame focus

August 3, 2008

Thanks to Jeremy Wood’s comment on my Sexy Swing App – Unified Toolbar post, I’ve gone back and removed the extension of JPanel I was using in order to paint a focus-state-based-border, and replaced it with a client property listener. I really favor this technique over extension, as it reduces coupling and dependencies, namely on JPanel.

    public static void installWindowFocusListener(
            WindowFocusListener focusListener, JComponent component) {
        component.addPropertyChangeListener("Frame.active",
                createFrameFocusPropertyChangeListener(focusListener, component));
    }

    private static PropertyChangeListener createFrameFocusPropertyChangeListener(
            final WindowFocusListener focusListener, final JComponent component) {
        return new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                Window window = SwingUtilities.getWindowAncestor(component);
                // use the client property that initiated this this
                // property change event, as the actual window's
                // isFocused method may not return the correct value
                // because the window is in transition.
                boolean hasFocus = (Boolean) component.getClientProperty("Frame.active");
                if (hasFocus) {
                    focusListener.windowGainedFocus(
                            new WindowEvent(window, WindowEvent.WINDOW_GAINED_FOCUS));
                } else {
                    focusListener.windowLostFocus(
                            new WindowEvent(window, WindowEvent.WINDOW_LOST_FOCUS));
                }
            }
        };
    }

Simply call installWindowFocusListener with a WindowFocusListener and the JComponent to listen to the focus state of, and the listener will be notified of focusGained and focusLost events.

There’s also another technique you could use – a slightly dirtier approach. Your painting code and/or painting support methods could simply handle the current focus state, and rely on an outside mechanism to generate a repaint when the window focus changes. For example, getBackgroundColor might return color A when in focus, and color B when not in focus.

What makes this technique less robust, is that your painting code must trust that a repaint event will occur, and you must be able to install a listener on the containing window in order to cause the the repaint to be generated. But if you have control over the window, this is a rather simple approach:

    public static WindowFocusListener createAndInstallRepaintWindowFocusListener(
            Window window) {
        // create a WindowFocusListener that repaints the window on focus
        // changes.
        WindowFocusListener windowFocusListener = new WindowFocusListener() {
            public void windowGainedFocus(WindowEvent e) {
                e.getWindow().repaint();
            }
            public void windowLostFocus(WindowEvent e) {
                e.getWindow().repaint();
            }
        };
        window.addWindowFocusListener(windowFocusListener);
        return windowFocusListener;
    }

Again, this is not as robust as the first technique, but it does the job. It would be nice if Swing caused a full window repaint on window focus gained and lost, though I haven’t thought through the full implications of this.

2 Responses to “Reacting to Frame focus”


  1. […] finally, Ken Orr tracks an interesting problem of providing different visuals for controls in active and inactive windows. His solutions include checking Window.isFocused in […]

  2. Ken Says:

    I should note that this *only* works on the Mac. I’m currently looking into a better cross-platform solution.


Leave a comment