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.