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.
August 4, 2008 at 4:52 pm
[…] 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 […]
December 10, 2008 at 1:03 pm
I should note that this *only* works on the Mac. I’m currently looking into a better cross-platform solution.