Search |
||
A well-behaved GlassPanePosted 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,
I am going to examine special cases when you might want to show a GlassPane to provide a constant visual effects,
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.
This application contains two JInternalFrames,
components on the first frame don't have any special features,
only JTextField has a popupMenu, 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. 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. 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:
There is one more problem which is not so visible:
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:
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 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 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 GlassPaneWe 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. 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):
You can see the same problem if you move the cursor to the internal frame border - 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 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, 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:
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
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
|
|