Validation overlays using paint()

July 30th, 2007

In the previous entries on painting validation overlays using repaint manager and borders i discussed why it it important to find the best painting pipeline hook that matches the specific requirements. The repaint manager is an “overshoot” since it allows painting only after all the dirty components have been painted. The borders are “undershoot” since they are painted before the child components are painted. In this entry i’m going to discuss the <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/Component.html#paint(java.awt.Graphics)">paint()</a> method which is one of the best hooks fit for our requirements.

While it is not recommended to override the paint() method (specifically because if you don’t call the super implementation, you “lose” the border and the children), doing it correctly is a very useful tool in any Swing developer’s arsenal. In our case, we want to paint an overlay icon on top of the component (which might have child components in it). Well, as long as the overlay icon is painted inside the component bounds, we can override the paint() method, call the super implementation (that will paint the component, its border and its children) and then paint our icon. There should be no special cases except one – extending the dirty region with a custom repaint manager (more on that later).

Let’s try implementing the validation overlays with custom paint() implementation. Note that all classes in this entry are located in the org.pushingpixels.validation.custompaint package.

The major disadvantage of overriding the paint() method is that you have to do it for every component that can display validation errors. Of course, you can come up with a factory that returns a preconfigured instance of such a component (sample implementation in the CustomPaintComponentFactory class), but it still “pollutes” your UI code with the calls to that factory. In addition, this might prove problematic with visual editors such as Matisse. Furthermore, you will have to provide a factory method for each component class (for those components that can have validation errors) and repeat the same painting code in each one of these.

The end result, however, is exactly what we’re looking for – the validation errors that are displayed on top of the components (including editable combobox that has an inner textfield implementation child):

Validation overlay, paint()

The only thing that must be mentioned (and was already mentioned in the previous entries) is the case of complex components. Here is how our UI looks like when we click the editable combobox (without a custom repaint manager installed):

Validation overlay, paint() without repaint manager

Swing is smart enough to repaint only the editor (and hence paint the validation icon in the editor bounds), but not smart enough to figure out that this editor is an integral part of a “larger” component (which means that the validation icon is not be painted outside the editor bounds, resulting in a clipped indication). Without a custom repaint manager (with its “globalness” shortcoming addressed in the relevant entry) you can end up with temporary visual artifacts (until some event that triggers the repaint of the combobox itself).

There are two possible solutions. The first is to install a custom repaint manager that extends the dirty region from the inner text field to the combobox itself. The second is to trigger the repaint of the combobox from the inner text field. The later is much more complicated, since you will have to override the paint() method of the inner text field and guard against an infinite repaint loop (repaint of combo triggering repaint of text field that trigger the repaint of combo in turn). While this can be done, it is not trivial.

As we have seen, while the custom paint() implementation is a good fit for painting the validation overlays, it a has a few disadvantages:

  • Need to override paint() for each component class that can have validation errors
  • Need to install a custom repaint manager to trigger repaints of complex components

In the following entries, i will discuss a few alternatives that try to address these issues.