Posted by alexfromsun
on October 9, 2007 at 4:50 AM PDT
Overview of hacks in validation overlays implementations
In one of my previous blogs
I answered to Kirill's comment that some of techniques he described in his Swing painting pipeline
overview are "not good enough for me",
here I want to explain my point by brifely commenting some of the techinques he covered.
Implementing validation overlays
First of all I want to mention my requirements to the validation overlays implementation:
- Solution shouldn't change global settings, it has to be as compatible with any other projects as possible
- It should work well inside JScrollPane
- Validation icons should respect Z-order of sibling components
When a component requires a custom RepaintManager to be installed
it sounds for me as strange as if I require to customize your operating system for my application.
RepaintManager is a global resouce and changing it to support a particular component is not just awkward but it may cause problems if another component wants to install its own RepaintManager at the same time.
Since Swing repaints components asynchronously it is impossible to fix this potential conflict by saving and restoring the old RM after repaint() is called.
uses custom RM for their needs but it's a special case.
SwingX is a project which contains extentions for the Swing GUI toolkit which may become a part of the JDK.
If we decide to include e.g. translucency to Swing, the default RM will be the best choice to add support for this feature and no doubt, in this case their experience will be very useful.
Since a custom RepaintManager works well if you are sure that no one else will change it,
I take RepaintManagerX
as the only recommended alternative implementation
For me it is quite obvious that GlassPane is not the best solution for validation overlays.
I know that with GlassPane it is impossible to write a robust code which will quickly and correctly
- move/hide/show validation icon when its component is moved/hidden/shown
- crop the icon when its component is overlapped by another component
I don't even need to repeat that GlassPane is a global resource etc, because all mentioned issues together with the popups issue
make GlassPane unusable for validation overlays implementation
It is the most interesting hack and it deserves special attention because of two remarkable projects.
Long time ago I came across the Decorating/Overpainting Swing Components
blog with an impressive demo by Timothy Wall.
He transparently adds all kinds of custom components to the frame's default JLayeredPane to decorate existing components.
The demo is really cool, but...
It fails to meet my second requirement about quick painting inside JScrollPane
(I can see the labels' flickering when I scroll the JScrollPane in the demo, sorry about that)
It also fail to meet the third requrement because
if two sibling non-opaque components are overlapped it is impossible to detect which component's pixels are transparent to correctly mix their decorations
(I'll give an example later on)
In addition in the AbstractComponentDecorator.java
code I found a bunch of FIXME and TODO notes which reminds the situation with my JXTransformer
- you can make it work for a demo, but it will never be completely ready for production use
by Karsten Lentzsch is another tricky solution. I wrote a simple test with a JTextField inside IconFeedbackPanel with a validation icon,
then I wrapped the JTextField with JScrollPane and the icon unexpectedly was gone.
My first though was that IconFeedbackPanel doesn't support inner scrollPanes at all but then I read this note from its constructor javaDoc:
Note: Typically you should wrap component trees with getWrappedComponentTree(ValidationResultModel, JComponent)}, not this constructor.
When I tried the magic getWrappedComponentTree method the validation icon appeared in the inner JScrollPane and were repainted very quickly during scrolling.
After a few minitues I found the reason - this method traverses the component's hierarchy and wraps JScrollPane's views with additional IconFeedbackPanels!
This is an important detail, I'll return to it later
The Karsten's code is clear and well-written as usual, only a few things about IconFeedbackPanel caught my attention
- It removes all validation icons, adds them again and repaints the whole panel during every layout change, which is pretty expensive
- Quote from javadoc:
you must ensure that the wrapped content
provides enough space to display the overlaid components.
Since the current implementation positions the overlay components
in the lower left, just make sure that there are about 6 pixel to the left
and bottom of the input components that can be marked.
But what if I don't want to think about 6 pixel to the left and bottom and like to have a solution which will make it right?
Anyway, IconFeedbackPanel works well with JScrollPane, what about the second requirement?
Okay, you can already guess that this solution doesn't work well for overlapped sibling components either,
to make it work you have to wrap every JTextField with a kind of an IconFeedbackPanel
Nevertheless, among all mentioned techniques, IconFeedbackPanel is probably the best one
In his comments Kirill mentioned an interesting term - "component-level techniques", comparing JXLayer with GlassPanes and LayeredPanes.
Actually if you need to decorate an existing component you have only three recommended choices:
- Subclass a component
- Make a custom UI delegate
- Wrap it with a special decorator
All of them are "component-level techniques" and there is no other valid tecniques in Swing, because only in that cases your components will work in predictable way for all common cases
JXLayer is a component's wrapper, which meets all my requirements:
- It doesn't change any global resources
- Its decorations are painted quickly and correctly inside JScrollPane
- It respects Z-order of sibling components
(move the decorated component and note that decorations correctly overlaps with the top circular component)
Decorated components behave exactly like any other Swing components and there are no unexpected "special cases" where something might go wrong.
That what I meant when I said that solutions like GlassPanes or LayeredPanes are not good enough for me.
I want to be sure that my components will work well with all existing layouts, like StackLayout
Try to make a component which is decorated with JLayeredPane, correctly overlap a non-opaque sibling component
In the previous blog I showed two examples of validation decorations:
when it is completely within the bounds of wrapped component, and JXLayer has the same size as its inner component;
the second variant is when decorations are partially outside (the little red icon on the corner).
I personally like the first one, because it doesn't affect component's layout and doesn't require a bunch of listeners to be added to the TextField's document.
If I needed to support that fancy outer icon for some TextFields I would use Karsten's trick - he automatically wraps JScrollPane's views only when I'd wrap all TextFields to set them identical insets
Sometimes the "no-hacks rule" is not easy to follow and may require some extra work,
but use it or not is always up to you