Monday, September 10, 2007

Creating Non-Rectangular Visual Components in Verdantium

Subclasses of java.awt.Component have rectangular bounds, but within those bounds a component can have a non-rectangular shape. Sun documented this capability but I am not aware of Sun releasing any demonstration classes that take advantage of it. Non-rectangularly shaped components fit perfectly into the Verdantium paradigm, so I do provide example code for this seldom-used feature of the JDK. See the NonRectangular package of the Verdantium download.

In the NonRectangular demo component, I chose a circular shape for the non-rectangular component because it was the simplest non-rectangular shape to implement (I try to keep things as simple as possible). However, any arbitrary shape is possible. A non-rectangular JComponent usually has three things:

* A transparent background.

* An overridden paint() method that draws the non-rectangular shape (and possibly an overridden update() method that calls the paint() method).

* Optionally, an overridden contains() method that returns whether the mouse position is inside the shape.

Because of the transparency of the background, the isOpaque() method of the JComponent should return false. For some JComponents, calling setOpaque() will work. For others, isOpaque() may need to be overridden to return false. Transparency essentially means that the JComponent element doesn't do an initial fillRect() on its extent before calling paint()-- there isn't much point to making something non-rectangular if the surrounding rectangular bounds get filled anyway.

The paint() method is where the non-rectangular shape is shown to the world. Java-2D provides general APIs for doing this.

The contains() method is a rather strange and interesting invention from Sun. In several other APIs that perform similar tasks (e.g. collision detection in Sun's own Java-3D), the geometry is passed to the framework and then the framework solves the inside/outside/collision relationships using a high-efficiency algorithm (e.g. binary space partitioning). Instead, AWT polls your JComponent every time the mouse is moved to determine if it is inside or outside the non-rectangular shape. If your shape contains complex geometry, then you get to write your own space partitioning framework in order to make inside/outside calculations efficient. What you need to do is the following: override contains() so that it returns true iff. the X-Y position in the parameters is inside your non-rectangular shape. If this is properly implemented, then AWT will send your JComponent mouse entry/exit events when the mouse enters and leaves your non-rectangular shape.

That covers it. The ability to display non-rectangular components has been a differentiator for component frameworks such as OpenDoc, so give this a try.

No comments: