-
Notifications
You must be signed in to change notification settings - Fork 235
How to use FocusManager
Available since: WebLaF v1.28 release
Required Java version: Java 6 update 30 or any later
Module: ui
One of the things lacking in Swing is ability to globally track various component states. One of such states is focused
- it is a simple state informing that user has keyboard focus on the component that has the state.
There are various solutions to how focused
state can be implemented for Swing components - most popular is simply registering FocusListener
on the component. While FocusListener
is sufficient for tracking a single component focused
state in most common cases - it will not be of any help if you want to track it application-wide.
To provide such application-wide focus tracking and solve some other minor issues with default FocusListener
I've created FocusManager
that continuously focused component through AWTEventListener
and makes sure that various listeners you can register in FocusManager
are informed about every change they might be interested in.
The only way to use FocusManager
is to register one of the available listeners, so first you need to decide what exactly you want to listen for - all focus change events or only ones for a particular component.
All listeners receive events from the same source, but they are filtered and delivered differently.
Can be registered to listen to all focus change events:
FocusManager.registerGlobalFocusListener ( new GlobalFocusListener ()
{
@Override
public void focusChanged ( @Nullable final Component oldFocus, @Nullable final Component newFocus )
{
...
}
} );
It can also be registered using an existing component:
FocusManager.registerGlobalFocusListener ( component, new GlobalFocusListener ()
{
@Override
public void focusChanged ( @Nullable final Component oldFocus, @Nullable final Component newFocus )
{
...
}
} );
That method is particularly useful if you have a component that will be listening to provided events. Events will not be limited to that component, but listener will be registered in FocusManager
differently to help prevent possible memory leaks through the listener itself.
I recommend using second option or registering FocusTracker
for particular component as described in the next section.
Here is a small practical example of GlobalFocusListener
usage:
public final class GlobalFocusListenerExample
{
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
WebLookAndFeel.install ();
final WebLabel label = new WebLabel ( "...", WebLabel.CENTER );
TestFrame.show (
10, false, 10,
label,
new WebTextField ( "Sample", 20 ),
new WebButton ( "Sample" ),
new WebCheckBox ( "Sample" ),
new WebRadioButton ( "Sample" )
);
FocusManager.registerGlobalFocusListener ( label, new GlobalFocusListener ()
{
@Override
public void focusChanged ( @Nullable final Component oldFocus, @Nullable final Component newFocus )
{
final String prev = oldFocus != null ? ReflectUtils.getClassName ( oldFocus ) : "none";
final String next = newFocus != null ? ReflectUtils.getClassName ( newFocus ) : "none";
label.setText ( prev + " -> " + next );
}
} );
}
} );
}
}
It will display a small frame with a few focusable components and label showing what component has the focus:
DefaultFocusTracker
implementation covers most common cases of focused
state tracking - when component itself or one of it's children is focused. Additional related components can also be added to be considered sharing the focus with the tracked component which can be convenient for some cases.
It is generally recommended to be use DefaultFocusTracker
instead of writing a custom FocusTracker
implementation.
Any FocusTracker
can be registered in FocusManager
like this:
FocusManager.addFocusTracker ( component, new DefaultFocusTracker ( component, true )
{
@Override
public void focusChanged ( final boolean focused )
{
...
}
} );
Here is a small practical example of FocusTracker
usage:
public final class FocusTrackerExample
{
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
WebLookAndFeel.install ();
final WebLabel label = new WebLabel ( "Panel is not focused", WebLabel.CENTER );
final WebCheckBox directFocusOnly = new WebCheckBox ( "Direct focus only", false );
directFocusOnly.setHorizontalAlignment ( WebCheckBox.CENTER );
final WebButton button = new WebButton ( "Sample button" );
final WebPanel panel = new WebPanel ( StyleId.panelDecorated, new BorderLayout (), button );
panel.setBackground ( Color.WHITE );
panel.setPadding ( 20 );
panel.setFocusable ( true );
panel.onMousePress ( new MouseEventRunnable ()
{
@Override
public void run ( final MouseEvent e )
{
panel.requestFocusInWindow ();
}
} );
TestFrame.show ( 20, false, 20, label, directFocusOnly, panel );
final DefaultFocusTracker focusTracker = new DefaultFocusTracker ( panel, true )
{
@Override
public void focusChanged ( final boolean focused )
{
label.setText ( focused ? "Panel is focused" : "Panel is not focused" );
}
};
FocusManager.addFocusTracker ( panel, focusTracker );
directFocusOnly.addActionListener ( new ActionListener ()
{
@Override
public void actionPerformed ( final ActionEvent e )
{
focusTracker.setUniteWithChildren ( !directFocusOnly.isSelected () );
}
} );
}
} );
}
}
It will display a small frame with label showing whether or not panel is currently focused:
You can also enable or disable uniteWithChildren
setting in the DefaultFocusTracker
that switches tracking mode, affecting whether or not focused children cause component to be counted as focused as well.
Now if you actually want to provide different set of conditions for deciding whether or not component is focused based on the actual focused component - you may actually want to write your own FocusTracker
implementation and specify your conditions in isInvolved ( tracked, component )
method where tracked
is your component and component
is the actual focused component.
That's all you need to know to start using this feature.
I will update FocusManager
with more options in the future, so stay tuned!
If you found any mistakes or inconsistency in this article, feel that it is lacking explanation or simply want to request an additional wiki article covering some topic:
I will do my best to answer and provide assistance as soon as possible!