Skip to main content

A well-behaved GlassPane

Posted by alexfromsun on September 20, 2006 at 12:02 PM PDT

I have seen a lot of custom GlassPane tricks, which use GlassPane to "disable" the frame, to provide a visual effects during the lengthy tasks or to give more rich feedback during drag and drop etc...

The distinctive feature of that tricks is that GlassPane is shown only temporarily,
so when the lengthy tast or any other action is completed the custom GlassPane is made hidden and doesn't affect the frame anymore

I am going to examine special cases when you might want to show a GlassPane to provide a constant visual effects,
this way the frame is supposed to function as usual except the big translucent text e.g. "Hello Mom !" is shown on the top of the content pane :-)

So let's create a GlassPane which shows a colored rectangle at the top right corner of the frame and draws the colored circle to follow the mouse's cursor.
As a testing environment please launch this simple application

This application contains two JInternalFrames,
components on the first frame don't have any special features,
only JTextField has a popupMenu,

the second frame contains components which have a rollover effect - their background turns green when you move the mouse cursor to them, moreover each of them has a tooltip

To make it more interesting I implemented a kind of a "focus follow mouse" policy for both internal frames, so when mouse's cursor crosses frames border, the frame becomes selected

Don't close this application we will use it to test

The initial GlassPane version

Most of the similar GlassPane implementations which I tested, copied
the code from the
How to use Root Pane
Swing tutorial.

The main idea is to add mouseListener and mouseMotionListener to the GlassPane, catch the mouseEvent and then redispatch it to the component found with help of SwingUtilities.getDeepestComponentAt()

Based on this solution I created the InitialGlassPane version.

Please return to the testing application and select the "GlassPane is visible" checkBox from the Options menu or simply press Alt+G to activate this menuItem.

Note: Diring the initial glassPane is visible, it is impossible to select any menuItems, use key accelerators instead

Notice the red rectangle appeared at the right top frame's corner and the red translucent circle started follow the mouse.
Let's check how it affects the frame's contents:

Click to the every component from the first internalFrame, as you can see, they seem to work:

You still can select the CheckBox and edit the JTextField

Now play with the components from the second internal frame
Here we can quickly notice some odd things like absence of rollover effects.

Here the list of problems you can see during initial GlassPane is visible:

  1. When Options menu is visible it doesn't react to the mouse,
    the textField's popup doesn't work as well
  2. Component's rollover effect is missed
  3. When you move the mouse cursor between the internal frames they don't get selected as they did before
  4. Tooltips become very unreliable, they might appear in unusual places or not appear at all

There is one more problem which is not so visible:

  • press the JButton
  • drag out of its border
  • release the mouse button
  • The button remains pressed !

Here is a screenshot with the mess you can get with the initial GlassPane

Now hide the glassPane by pressing Alt+G and see the difference,
everything works much better, right ?

Notice one more little feature:

  • press the slider
  • drag out of the *frame* border
  • note that you can still move the slider's thumb

It doesn't work this way for the initial GlassPane, but after all mentioned problems this doesn't look very bad, does it ?

Why it doesn't work

GlassPane doesn't get all events you need, e.g. when you move the mouse inside it you redispatch the mouseMotion events only.
That's why rollover effect didn't work - just because components didn't receive mouseEntered/MouseExited events.

That glassPane also breaks the inner mouseEvents processing so it leads to broken drag and drop, menus and tooltips

Actually redispatching mouseEvents this way is just a bad idea

and I am going to ask to update the Swing tutorial

you can fix some problems for the current implementation but it will never work perfectly

To fix the problems our GlassPane should be absolutely transparent for mouseEvents, it means that we shouldn't add any Mouse/MouseMotion/MouseWheelListeners to it

For more information about mouseEvent transparency please refer to the my my BoF session from the latest JavaOne conference (see the MouseEvents topic)

But wait, how can I process the mouseMotion events if I didn't add any mouseListeners to the glassPane ?

To get the answer please try the

Better GlassPane

We can catch mouseEvents without mouseListeners with AWTEventListener, this way we will keep our GlassPane transparent for mouseEvents and it will not affect the mouseEvents processing

Check out the Better GlassPane implementation

Let's return to the testing application, switch the glassPane implementation to the Better GlassPane and make it visible.

After the yellow rectangle and circle appear you will not find any differences in mouse processing !

Menus, buttons, rollover everything work with no surprises

This is a really better GlassPane, but it also has a little problem
(the first GlassPane has it as well by the way):

  • Hide the better GlassPane (press Alt+G)
  • Move the mouse cursor inside the JTextField
  • The default mouse cursor turns to "text editing" cursor
  • Show the better GlassPane (press Alt+G)
  • Move the mouse cursor inside the JTextField
  • Look that mouse cursor doesn't change

You can see the same problem if you move the cursor to the internal frame border -

the cursor is supposed to change to show that resizing is allowed

This GlassPane prevents mouse cursor to be changed, it is always the same no matter which component you work with

Why it doesn't work

The problem is that AWT system show the cursor of the topmost visible component
and since our topmost component is GlassPane and it has the default cursor it is always the same

The fix is simple and I am happy to present the

Final GlassPane

To fix the "cursor problem" we should make our GlassPane even more transparent,
there is a special method which is used by AWT to detect the target component for the mouseEvents

public boolean contains(int x, int y)

The hot fix is to override this method to alway return false for the GlassPane,
after that you'll start to see proper cursors and your GlassPane will be finally and absolutely transparent,

because even if you add a MouseListener to it, it will never be notified
since such a GlassPane doesn't "contain" any geometrical points

And here it is time to reveal a big secret:

GlassPane might be used by someone else as well

Here is a scenario for our testing application:

  • Hide the current glassPane
  • Resize an internalFrame with help of the mouse
  • Notice that during resizing GlassPane becomes visible
  • When resizing is over GlassPane is made hidden

In this case GlassPane is made visible to provide a proper mouse cursor during internalFrame's resizing

Moreover someone else might implement special logic with help of the GlassPane
e.g. add some MouseListeners to it during drag and drop and than remove them to restore mouseEvents transparency

So, if we prepare our GlassPane for our application only and pretty sure that no one else will use it, it is ok to override contains method to always return false

But this blog is about a well-behaved GlassPane which is supposed to behave well for any situation

So if someone added a mouseListener to the GlassPane or set a new mouse cursor I don't change contains method behaviour

but return false otherwise

We have finally solved all problems and our final GlassPane does well

Conclusion

Here you can find the runnable jar and archive with all source files

I hope this tutorial will help you to better understand Swing and AWT mouseEvents processing and
let your GlassPanes be really transparent !

See also the follow-up blog Enabling/Disabling Swing Containers

Related Topics >>

Comments

This is awesome, but there is a bug and I'm not sure what's ...

This is awesome, but there is a bug and I'm not sure what's the best way to fix it.

If move the mouse over "Options" menu to the left and exit the frame, it doesn't set "point" to null, thus leaving the green cirle on the screen.

Is there a safer way to determine the mouse has exited the frame?

Locking the view.

Hi. This example is very useful to me. The only thing I could not figure out is how do I block the mouse clicks if I want to my frame to to toggle into read-only mode? Thanks. Kirill.

Concur, works well...but I also need a way to consume the ...

Concur, works well...but I also need a way to consume the event in certain cases.

On those cases just consume the mouse ...

On those cases just consume the mouse event.

me.consume();

After that, nothing else will see the event.