Wednesday, June 11, 2008

Creating a Swing Dashboard Widget - Summary

So the original intention was to create a Dashboard style widget UI using Swing.

In nine fairly simple steps we've gone from a blue square:

...to a nice looking widget that hopefully looks at home on the modern Desktop:

The only shortcut I took was not to pull out the maths book and write my own transform filter.

I'm very pleased with the result, it was a lot easier than I thought it would be. Hopefully this exercise proves (again) that Swing can be more than just a boring, gray, toolkit suitable for boring, gray, form-filling applications. The new transparency features especially allow vibrant, modern looking UI's that are relatively simple and quick to create.

So that's really it for the UI.

A few non-UI remarks...

Java widgets have a number of benefits over other widgets systems:- No framework or widget manager needed- Platform independent- Installed from a web-page- The extensive JDK library available as standard- Sandboxed and secure automatically- Familiar language with powerful UI toolkit- Customizable, fast, accelerated painting (try that in Javascript)

There are still a few non-UI things to do before making this code truly useful: preferences and desktop startup spring immediately to mind.

Performance is also an issue. On my machine the two widgets take about 20 MB of RAM each and 0 CPU cycles when just sitting there. 20MB is considerably more than a simple Widget in Apple's Dashboard for example.However, in modern machines with RAMs of 1GB+ as standard, and depending who you're asking, maybe this is an increasingly unimportant issue. Especially if the widget is useful.

Feel free to use the Source Code. Don't forget you'll need the filters classes from JHLabs.

The project was to see if it was possible to make the ui effects of a dashboard widget, so I didn't concentrate too much on speed. So, yes there's lots of things to do to speed it up. 600x600 is quite a big widget though, and larger areas will need a lot of optimizing as the spinning filter doesn't have the help of graphics card acceleration.

Hi,I'm not sure what has changed in the last year but I get the following error trying to flip any widget on mac and linux. However, the "flippable component" example still works fine via jnlp. Do you jnow whether any recent change in the jhlab's filter library could have caused this error?

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: Coordinate out of bounds! at sun.awt.image.IntegerInterleavedRaster.setDataElements(IntegerInterleavedRaster.java:416) at com.jhlabs.image.AbstractBufferedImageOp.setRGB(AbstractBufferedImageOp.java:82) at com.jhlabs.image.TransformFilter.filter(TransformFilter.java:189) at com.jhlabs.image.PerspectiveFilter.filter(PerspectiveFilter.java:198) at widget.FlipTransition.updateInterImage(FlipTransition.java:128)

I also get the Array index out of bounds error (Windows). I tried to debug it through the JH filter code but could not figure out what it was trying to do. I have sent an email to Jerry Huxtable, I'll post here if I get a solution.

Got the note from JHLabs and changed the FlipTransition class to use the default PerspectiveFilter c'tor followed by a setCorners() call. That fixed the transformation problem.

One last thing does not work quite right when I use this technique in a Swing app. As the image turns, the corners move outside the bounds of the ReversibleComponent (above and below). This can be seen clearly in the 2nd image in the May 24, 2008 blog posting. The image even overlays a bit of the JLabel north of it.

In my swing app using the ReversibleComponent, the images are clipped at the bounds of the ReversibleComponent. That seems to make sense as the layered pane on which the image is drawn is the same size as the component itself. So how can this ever work (but obviously it can). Can a swing component draw outside its own bounds?

where maxHeight was calculated as the max of the starting and ending component heights (which would usually be the same). This would seem to prevent the rendered intermediate image from ever being larger than the start/end components. Indeed if I use "maxHeight+50" and offset the X inset by -25 it draws correctly (although with a black background that overwrites adjoining components... not sure why).

I don't think it's possible (without hacking) to get a swing widget can draw outside of it's bounds.

The trick used in the example (if I remember correctly) is to add a transparent border around the component, so if this is clipped when spinning no one will see it anyway.

Of course a limitation of this trick is that in a normal layout the border will still be there adding (possibly) unwanted space around the component. One solution to counter this would be to use a specialized layout manager.