Greg has posted 18 posts at DZone. You can read more from them at their website. View Full User Profile

Using Decorators in Pivot

08.22.2008
| 4027 views |
  • submit to reddit

I've recently been reading through the excellent Filthy Rich Clients book by Chet Haase and Romain Guy. I picked it up a couple of weeks ago to see how I might apply some cool Java2D effects to a Pivot application. Since both Pivot and Swing are based on Java2D, I was hoping it wouldn't be too tough. Turns out that it was actually pretty easy.

For example, the book contains a section on creating reflections. In Swing, this requires a custom repaint manager. In Pivot, it can be done with a decorator. Decorators allow a caller to attach additional rendering behavior to a component. The decorator interface is defined as follows:

public interface Decorator {
public Graphics2D prepare(Component component, Graphics2D graphics);
public void update();

public Rectangle getBounds(Component component);
public void repaint(Component component, int x, int y, int width, int height);
}

The prepare() method is called immediately before the component's paint() method and allows the caller to modify or replace the graphics object on which the component will be painted. For example, Pivot's DropShadowDecorator class paints a semi-transparent black rectangle underneath a component to simulate a drop shadow. The FadeDecorator sets an AlphaComposite on the graphics before the component is called, making the entire component appear translucent.

The update() method is called just after painting the component. This allows a caller to paint on top of a component after the component itself is done painting. For example, the ShadeDecorator class paints a semi-transparent colored rectangle over an entire component, and the WatermarkDecorator class paints a string of text over a component, simulating a paper watermark.

The prepare() and update() methods can also be used together to achieve interesting visual effects. BlurDecorator and GrayscaleDecorator take a "snapshot" of the component by returning a BufferedImage graphics object from prepare(), then paint the buffered image to the original graphics object in update(). ReflectionDecorator also works this way, and uses a technique similar to that described in the book to paint the reflection of an undecorated window containing a JPEG image.

Multiple decorators can be attached to a component, and decorators are "chained". The decorators' prepare() methods are called in descending order, passing the graphics output of each to the prior decorator. The update() methods are called in ascending order after the component is painted. This allows a caller to create a window whose contents are rendered in black and white and also paints a drop shadow, for example.

In addition to trying out some of the examples in Filthy Rich Clients, I was also inspired to see how I might implement the translucent window effect described in Josh Marinacci's web log a few weeks back. An instance of FadeDecorator attached to a Frame window did the trick. The result of both experiements is shown in the applet below:

As in Josh's demo, mousing over the frame changes the opacity to 0.9, and mousing out changes it back to 0.5. Fortunately, it is a bit easier to implement the mouse handler logic in Pivot: simply attach a ComponentMouseListener to the frame:

fadeFrame.getComponentMouseListeners().add(new ComponentMouseListener() {
public void mouseMove(Component component, int x, int y) {
// No-op
}

public void mouseOver(Component component) {
fadeDecorator.setOpacity(0.9f);
component.repaint();
}

public void mouseOut(Component component) {
fadeDecorator.setOpacity(0.5f);
component.repaint();
}
});

The Pivot version also doesn't require any OS-specific hacks - the same code works on OSX, Linux, and Windows.

The full source for the demo can be found here.

From http://weblogs.java.net/blog/gkbrown/

Published at DZone with permission of its author, Greg Brown.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: