-
Notifications
You must be signed in to change notification settings - Fork 235
How to use HoverManager
Available since: WebLaF v1.2.10 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 hover
- it is a simple state informing that user has mouse cursor hovering over the component that has the state.
There are various solutions to how hover
state can be implemented for Swing components - most popular is simply registering MouseListener
on the component and tracking mouse enter and exit events. But there are multiple issues with that approach - hover state won't be properly removed in some cases because you simply won't receive "exit" event until mouse is moved. Also it can be completely skipped when component is hidden or removed. It is also not sufficient to track hover
state application-wide.
To solve these and some other problems I've created HoverManager
that continuously tracks hovered component through multiple registered AWTEventListener
s and makes sure that various listeners you can register in HoverManager
are informed about every change they might be interested in.
The only way to use HoverManager
is to register one of the available listeners, so first you need to decide what exactly you want to listen for - all hover 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 hover change events:
HoverManager.registerGlobalHoverListener ( new GlobalHoverListener ()
{
@Override
public void hoverChanged ( @Nullable final Component oldHover, @Nullable final Component newHover )
{
...
}
} );
It can also be registered using an existing component:
HoverManager.registerGlobalHoverListener ( component, new GlobalHoverListener ()
{
@Override
public void hoverChanged ( @Nullable final Component oldHover, @Nullable final Component newHover )
{
...
}
} );
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 HoverManager
differently to help prevent possible memory leaks through the listener itself.
I recommend using second option or registering HoverTracker
for particular component as described in the next section.
Here is a small practical example of GlobalHoverListener
usage:
public final class GlobalHoverListenerExample
{
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
WebLookAndFeel.install ();
final WebLabel label = new WebLabel ( "...", WebLabel.CENTER );
final WebColorChooserPanel panel = new WebColorChooserPanel ();
TestFrame.show ( 10, false, 10, label, panel );
HoverManager.registerGlobalHoverListener ( label, new GlobalHoverListener ()
{
@Override
public void hoverChanged ( @Nullable final Component oldHover, @Nullable final Component newHover )
{
final String prev = oldHover != null ? ReflectUtils.getClassName ( oldHover ) : "none";
final String next = newHover != null ? ReflectUtils.getClassName ( newHover ) : "none";
label.setText ( prev + " -> " + next );
}
} );
}
} );
}
}
It will display a small frame with color chooser and label showing what component is currently under the cursor:
DefaultHoverTracker
implementation covers most common cases of hover
state tracking - when cursor is on top of the component or one of the component children within it's bounds.
It is generally recommended to use DefaultHoverTracker
instead of writing a custom HoverTracker
implementation.
Any HoverTracker
can be registered in HoverManager
like this:
HoverManager.addHoverTracker ( component, new DefaultHoverTracker ( component, false )
{
@Override
public void hoverChanged ( final boolean hover )
{
...
}
} );
Here is a small practical example of HoverTracker
usage:
public final class HoverTrackerExample
{
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 hovered", WebLabel.CENTER );
final WebCheckBox directHoverOnly = new WebCheckBox ( "Direct hover only", false );
directHoverOnly.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 );
TestFrame.show ( 20, false, 20, label, directHoverOnly, panel );
final DefaultHoverTracker hoverTracker = new DefaultHoverTracker ( panel, false )
{
@Override
public void hoverChanged ( final boolean hover )
{
label.setText ( hover ? "Panel is hovered" : "Panel is not hovered" );
}
};
HoverManager.addHoverTracker ( panel, hoverTracker );
directHoverOnly.addActionListener ( new ActionListener ()
{
@Override
public void actionPerformed ( final ActionEvent e )
{
hoverTracker.setDirectHoverOnly ( directHoverOnly.isSelected () );
}
} );
}
} );
}
}
It will display a small frame with label showing whether or not panel is currently hovered:
You can also enable or disable directHoverOnly
setting in the DefaultHoverTracker
that switches tracking mode, affecting whether or not cursor hovering any of the component's children within it's bounds should also count as hovering the component itself.
Now if you actually want to provide different set of conditions for deciding whether or not component is hovered based on the actual hovered component - you may actually want to write your own HoverTracker
implementation and specify your conditions in isInvolved ( tracked, component )
method where tracked
is your component and component
is the actual hovered component.
That's all you need to know to start using this feature.
I will update HoverManager
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!