UI Design

Watch in full 720p. This is a very simple demo that shows how to use some of the new Android 3.0 APIs.

Android 3.0 animations demo

I’ve been following the development of Flash Catalyst, formerly known as Thermo, ever since it was announced and I was really happy when Adobe finally released Flash Catalyst beta 1 earlier this month. I played with it a little and I am really excited by what this tool will become.

Flash Catalyst

To give Catalyst a try, I decided to write a very simple application to search for photos on Flickr. After 10 minutes in Photoshop, 15 minutes in Catalyst and 15 minutes in Flash Builder 4 I had a working application and I had written only a dozen lines of code.

Despite its qualities, Flash Catalyst has a long way to go. I know this is only the first beta of a 1.0 product but here is a short list of what limitations I encountered after writing a few simple applications:

  • The tool sometimes doesn’t feel responsive; I kind of expected this from an Eclipse-based Flex-based application in full screen mode on a 30″ monitor.
  • All sizes and positions are hardcoded in absolute coordinates. The lack of support for layouts will definitely make Catalyst a lot less interesting for stand-alone AIR apps.
  • There are only a few properties you can manipulate for each widget.
  • Round-tripping between Catalyst and Flash Builder is worrisome if you’re not careful.
  • Integration with Photoshop is definitely lacking and far from what was announced (or at least I thought was announced.)

The Photoshop integration is the only real issue I have with Catalyst. You can apparently use Illustrator or Fireworks but these are tools I’m not very familiar with. This also means getting new licenses because only CS4 is supported (too bad for my CS3 copy of Illustrator.)

I can’t wait to see what Flash Catalyst 1.0 will end up becoming and what Adobe has in store for the future releases.

Flash Catalyst and Flash Builder 4

A few months ago, I showed Shelves, an application to manage a collection of books on Android powered phones. I am an avid user of Delicious Library on MacOS X and I since no similar application existed at the time for Android, I decided to write my own. Needless to say, Shelves was largely inspired by Delicious Library.

Even though Shelves is not quite ready to be release on the Android Market, I decided to publish the source code under the Apache 2.0 license. The application is already usable and lets you add books by searching Google Books or by scanning their barcode (if you have the application called Barcode Scanner installed), search your collection by keywords or barcodes, import books from a CSV file and delete individual books. The only two features I need to implement before releasing Shelves are the ability to manually add/edit a book and to view a book’s details.

If you are curious, you can see Shelves in action in the following video. You can also visit Shelve’s project page for screenshots showcasing several features.

Note: the colors on the video are different from what you would see on an Android device. I created the assets specifically for mobile devices and the colors appear therefore more saturated on a computer monitor.

Shelves, an Open Source Android application

A few months ago I stumbled upon what came to be one of the most interesting Swing blogs I’ve ever read. Actually, it is the most interesting Swing blog I know of. Exploding Pixels, by Ken Orr, shows how to create beautiful looking Swing applications for Mac OS X.

Ken has a great sense of design, pays great attention to details and his examples are really well written. Even if you do not work with Mac OS X, you’ll find a lot of valuable information on his blog, in particular how a UI developer should deal with details and pixel-perfect designs. I highly recommend you read all of Ken’s posts right now :)

Sexy Swing Apps on Mac OS X

Thanks to the hard work of Kouichi Matsuda, Filthy Rich Clients is now available in Japanese. Now I wish I didn’t drop out of my Japanese class after only one year :)

Filthy Rich Clients in Japanese

Chet Haase, a good friend and fellow co-author, just opened his new blog. I won’t spoil you the surprise but you might be interested to know what Chet is up to nowadays :)

A New Blog

Chet and I are going to present another installment of Filthy Rich Clients at JavaOne 2008. We have a bunch of cool ideas for techniques and demos we would like to show you but I am very interested in hearing what you want to know about. If you have any visual effect, animation or graphics technique you would like to learn, please tell me and we’ll try to put it in our presentation.

What do you want to hear about?

Chet finally released his AnimatedTransition library. This library is discussed in details in Chapter 18 of our book. Given its name, I won’t insult you by explaining what this library does :)

Animated Transitions Made Easy

Adobe just released the AIF Toolkit which lets you create GPU-accelerated visual effects by using some sort of a pixel shader programming language. Apparently this technology, Hydra, will be included in Flash. Hmm, time to learn Flash and Flex more deeply?

More Awesomeness From Adobe

The more I play with Flex, the more I like it. I’ve been following this technology for a few months now and today I just stumbled upon a very exciting article. It’s a report of Adobe MAX where a new tool, called Thermo, was showcased. Apparently it lets you turn a Photoshop mockup into a working UI in a matter of minutes. I have been dreaming of such an app for a very long time now and it seems that Adobe is on track to deliver something awesome. I can’t way to give it a try!

Adobe Thermo

InfoQ just published a review of Filthy Rich Clients. This review contains a link to chapter 14 of the book, Timing Framework Fundamentals. Enjoy!

Another Free Chapter

Palantir guys are doing the kind of applications I wish I worked on (at least on the UI.) Check out their screenshots.

Beautiful Swing

Jasper just posted a blog entry in which he explains that Nimbus is almost done. That is exciting. Even more exciting is how Nimbus was implemented. Nimbus was built entirely with shapes and vectors in a visual editing tool built as a NetBeans module. This let Jasper and Richard draw every single component, even the icons, rather than hand-coding them… and the tool simply generates the Java 2D code. As bonus, everything is based on Painters, so you can easily replace parts of the look and feel with your own painters. Or you can reuse the painters for totally different purposes. I’ve been hearing about this tool for a while now and it has lots of great features, like layers, layers effects (drop shadows or glows), and so on.

That means we will all soon be able to create look and feels from scratch without writing any line of code and still get resolution independence and great performance. I can only hope this will lead to the release of many high-quality look and feels.

The Secret Behind Nimbus

Synthetica now supports a new theme called WhiteVision. It’s a beautiful modern theme that I really encourage you to try. Screenshots can be found on Synthetica’s web site but you can also try the new Synthetica Office demo to check it out.

#ProgX Image

New Look and Feel

If you’re still not sure whether you should buy a copy of Filthy Rich Clients, take a look at our first review by Gregg Bolinger. Thanks Gregg!

First review for Filthy Rich Clients

James Gosling just put online a video of the project Green user interface (141 MB). Green, and its language Oak which later became Java, was running on the Star7, some sort of PDA/remote control Sun Microsystems was working on. It was in 1992. Enjoy, it’s worth the look.

Java Roots

Some applications, like Eclipse, show a progress bar in their splash screen during the startup phase. This is a useful way to show the user that the application is actually working in the background. It helps the user to wait for the end of the process without kicking. Without such a progress indicator, the application could appear to be frozen.

During this year’s Extreme GUI Makeover session we demonstrated a nice replacement for common progress indicator. Rather than using a progress bar, a percentage or even a status text, we chose to use colors instead.

Download the following video to see how our splash screen conveys the progress information.

Alternatively, you can take a look at the screenshot below. The splash screen first appear as a sepia image. As the application loads, the image gradually turns to a fully colored picture.

Extreme GUI Makeover

Implementing this effect is very simple with Java SE 6 and the Timing Framework to fake the loading process. The first state of the splash screen is shown by using a new command line flag which lets you pick any PNG file to use it as your application’s splash screen. This is really nice because this command supports the alpha channel of the PNG file format, thus letting you create splash screens with drop shadows and other translucency effects.

java -splash:./lib/splash-sepia.png [...]

The biggest advantage of this flag over the use of a Java window is that the splash screen shows up as soon as the JVM is started. The user will therefore see the splash screen during your application’s startup and during the JVM’s startup.

Even though the JVM is responsible for creating and drawing the splash screen, our application can get a handle on a Graphics2D object which lets us paint over the splash screen. In this example, we will fade the colored version of the splash screen over the original sepia image. By doing so, we will mimic the effect of coloring the sepia image.

SplashScreen splash = SplashScreen.getSplashScreen();
if (splash != null) {
    // Load the sepia and colored images
    colorSplashImage = null;
    try {
        colorSplashImage = GraphicsUtilities.loadCompatibleImage(
            getClass().getResource("/reality/resources/splash-color.png"));
        sepiaSplashImage = GraphicsUtilities.loadCompatibleImage(
            new File("./lib/splash-sepia.png").toURI().toURL());
    } catch (IOException e) {
    }

    // Emulates the progress
    Animator animator = PropertySetter.createAnimator(
        6000, this, "colorSplash", 1.0f);
    animator.addTarget(new TimingTargetAdapter() {
        @Override
        public void end() {
            show(mainPanel);
        }
    });
    animator.start();
}

This piece of code grabs the SplashScreen instance to make sure we have a splash screen. The null test is mandatory in case your application is started without the -splash flag. The code then proceeds with loading the sepia and colored images and finally starts an animation to simulate a lengthy startup process. In this example, we simulate a startup sequence of 6 seconds.

The actual work is performed in the method setColorSplash(), which would be the equivalent of a progress bar’s setProgress() method. The method stores the current progress value, grabs the splash screen’s graphics surface and paints the effect:

public float getColorSplash() {
    return colorSplash;
}

public void setColorSplash(float colorSplash) {
    this.colorSplash = colorSplash;

    SplashScreen splash = SplashScreen.getSplashScreen();
    Graphics2D g2 = splash.createGraphics();

    g2.setComposite(AlphaComposite.Clear);
    g2.fillRect(0, 0, splash.getSize().width, splash.getSize().height);

    g2.setPaintMode();
    g2.drawImage(sepiaSplashImage, 0, 0, null);

    g2.setComposite(AlphaComposite.SrcOver.derive(colorSplash));
    g2.drawImage(colorSplashImage, 0, 0, null);

    g2.dispose();
    splash.update();
}

You might be surprised by the first painting commands. The first action here is to clear the splash screen. On the contrary to Swing components, the splash screen is never updated automatically and everything you draw on the splash screen is persistent. Without the clear operation, our drawings would add up and the colorization would happen a lot faster than expected. The rest of the code is straightforward. We first draw the sepia image, then we set an alpha composite to draw a translucent copy of the colored image. Do not forget the call to update() to synchronize the splash screen with the changes you made on the graphics surface.

This is naturally only one example of what you can do with a splash screen to replace a dull progress bar. The translucency trick will work in many situations but you might want to consider SwingX’s blending modes. The BlendComposite class mimics all of Photoshop’s blending modes and lets you create beautiful lighting and coloring effects.

Progress Bars Are Boring (Extreme GUI Makeover)

I blogged a while ago about Artemis. This simple tool uses a blurry background to draw user’s attention onto a modal progress bar. Artemis is a good example of how you can use a blurry background. It emphasizes the importance of the modal dialog which doesn’t require any context.

You can watch a video of Artemis in action. Note that I did not take the time to polish the UI. Therefore, the blurry background does not appear progressively but instantly (and the indeterminate progress bar clearly needs some work.)

Artemis

The full source code of Artemis is part of chapter 16 of Filthy Rich Clients. It will be released at the same time as the book will be available in book stores.

Putting Blurry Background to Good Use

At JavaOne 2007, the Extreme GUI Makeover demo showed a semi-translucent dialog over a blurry background. This effect was explained in a previous entry on this blog. However, I received a few requests asking how the dialog itself was implemented.

This dialog is shown in the screenshot below. Not only is it translucent, but it also sports rounded corners and a subtle drop shadow. Implementing such a dialog is fairly easy and I will describe you what techniques you can use.

Extreme GUI Makeover

Before we delve into the code, remember that this “dialog” is in fact a simple JPanel (more specifically a JXPanel from SwingX) added to the frame’s glass pane. To draw the rounded corners and the translucent background we first need to make our component non-opaque:

public class DetailPanel extends JPanel {
    public DetailPanel() {
        setOpaque(false);
    }
}

The next step is simply to override the paintComponent() method:

@Override
protected void paintComponent(Graphics g) {
    int x = 34;
    int y = 34;
    int w = getWidth() - 68;
    int h = getHeight() - 68;
    int arc = 30;

    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

    g2.setColor(new Color(0, 0, 0, 220));
    g2.fillRoundRect(x, y, w, h, arc, arc);

    g2.setStroke(new BasicStroke(3f));
    g2.setColor(Color.WHITE);
    g2.drawRoundRect(x, y, w, h, arc, arc); 

    g2.dispose();
}

This implementation fills a black, translucent rounded rectangle to paint the background. Then it proceeds with drawing a white, opaque rounded rectangle using the exact same dimension, location and arcs. The border effect is rendered by setting a thick stroke on the graphics surface, 3 pixels in this case. As you can see you can create good-looking custom components with just a few lines of code.

Rendering the drop shadow is a bit more complicated though. SwingX provides a DropShadowBorder that works in most situation but that fits only rectangular components. Because we are using a rounded rectangle component, we need another approach. Hopefully, SwingX contains a very handy class named ShadowRenderer. As its name suggests, it can be used to render shadows. Just pass an image and you get a shadow for it.

Shadow rendering is performed in the setBounds() methods, called whenever the component changes dimension and/or location. The idea is to create an image buffer in which we draw a rounded rectangle of the same dimension as the one we used at painting time. This image is then passed to the shadow renderer to create the shadow we want to paint behind the component:

@Override
public void setBounds(int x, int y, int width, int height) {
    super.setBounds(x, y, width, height);

    int w = getWidth() - 68;
    int h = getHeight() - 68;
    int arc = 30;
    int shadowSize = 20;

    shadow = GraphicsUtilities.createCompatibleTranslucentImage(w, h);
    Graphics2D g2 = shadow.createGraphics();
    g2.setColor(Color.WHITE);
    g2.fillRoundRect(0, 0, w, h, arc, arc);
    g2.dispose();

    ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f, Color.BLACK);
    shadow = renderer.createShadow(shadow);
}

Once again, very easy. Unfortunately, this technique does not work very well. Because our component uses a translucent background, we can see the shadow through. The result of the blending produces an almost opaque effect which defeats the purpose of having a semi-translucent dialog in the first place. This is why tools like Photoshop do not draw shadows behind the layers.

The solution is to get rid of the part of the shadow that lies within the area in which the component paints its background. This could be achieved at painting time but I chose to do it when generating the shadow. To the previous code snippet, just add the following:

g2 = shadow.createGraphics();
// The color does not matter, red is used for debugging
g2.setColor(Color.RED);
g2.setComposite(AlphaComposite.Clear);
g2.fillRoundRect(shadowSize, shadowSize, w, h, arc, arc);
g2.dispose();

When the “clear” alpha composite is set on the graphics surface, everything we draw erases the existing pixels. We thus cut a hole into our shadow. The final step is simply to paint the shadow in paintComponent(), just before the filling and drawing of the rounded rectangles occurs:

if (shadow != null) {
    int xOffset = (shadow.getWidth()  - w) / 2;
    int yOffset = (shadow.getHeight() - h) / 2;
    g2.drawImage(shadow, x - xOffset, y - yOffset, null);
}

This code is not the most efficient nor elegant way to achieve this effect but it is clear, easy to implement and fast enough. This technique can be used in a variety of situations too. For instance, you could add a drop shadow to an icon by generating the shadow, then “clearing” with the icon itself. Note that the “clearing” step is required only when you are or might be drawing a translucent element on top of the shadow.

Rounded Corners and Shadow for Dialogs (Extreme GUI Makeover)

JavaDesktop.org just published a new Swing Sighting. The application is called Palantir and sports a very clean, very modern-looking Swing user interface. They even emulated Office 2007 menu/toolbar. I really urge you to go take a look at the screenshots.

Palantir

Another Pretty Java Application

Shannon, Chris and I showed many cool effects during this year’s Extreme GUI Makeover session, at JavaOne 2007. Since we cannot release the source code yet, I will explain some of the effects in detail. I will start with the blurred background displayed when a modal dialog shows up.

Blurring a dialog’s or a menu’s background is often used in video games to attract user’s attention to the front most widgets. Using this effect allows you to keep the context without suffering from the visual clutter it might induce. Adobe Flex offers a similar effect when you bring up a modal dialog in a Flex application. To better understand this effect, you can take a look at one of these two videos:

You can also take a look at the screenshot below. Note that the screenshot does not show the fade in and fade out animations used to bring up and dismiss the dialog. The background gradually blurs while the dialog progressively becomes visible.

Extreme GUI Makeover

Writing this effect is surprisingly easy with the help of the Timing Framework and SwingX. The former is used to drive the animations while the latter offers support for alpha translucency and blur.

The actual “dialog” is made of two classes, DetailsView and DetailPanel. The first class, DetailsView is used as a glass pane and is responsible for managing the animations and blurring the background. The second class contains the actual dialog, the black rounded rectangle in the screenshot. While DetailPanel contains interesting pieces of code, we will not spend any time on it in this entry. Simply note, however, that DetailPanel extends the JXPane class from SwingX. This class offers a public property called alpha which can be used to change the translucency of the container and its children.

Here is what the DetailsView class looks like:

public class DetailsView extends JPanel {
    private DetailPanel detailPanel;
    private BufferedImage blurBuffer;
    private BufferedImage backBuffer;
    private float alpha = 0.0f;

    DetailsView(DetailPanel detailPanel) {
        setLayout(new GridBagLayout());

        this.detailPanel = detailPanel;
        this.detailPanel.setAlpha(0.0f);
        add(detailPanel, new GridBagConstraints());

        // Should also disable key events...
        addMouseListener(new MouseAdapter() { });
    }
}

You can notice that we use two image buffers. They are used to create the animation of a gradually blurring background. The basic idea is to capture the frame’s content into a buffer, blur this buffer and paint it in the glass pane, behind the “dialog.” Unfortunately, doing so would be impractical for an animation. Even though SwingX provides an efficient blur filter, it would be very difficult to render a smooth animation of that size with it. Instead, we keep the frame’s content into an original buffer and blur that buffer into another buffer. At drawing time, we first paint the frame’s content, then the blurred copy. By progressively changing the opacity of the blurred copy, we can simulate an increasing blur effect.

Our first step is therefore to create our buffers:

private void createBlur() {
    JRootPane root = SwingUtilities.getRootPane(this);
    blurBuffer = GraphicsUtilities.createCompatibleImage(
        getWidth(), getHeight());
    Graphics2D g2 = blurBuffer.createGraphics();
    root.paint(g2);
    g2.dispose();

    backBuffer = blurBuffer;

    blurBuffer = GraphicsUtilities.createThumbnailFast(
        blurBuffer, getWidth() / 2);
    blurBuffer = new GaussianBlurFilter(5).filter(blurBuffer, null);
}

This method relies on GraphicsUtilities and GaussianBlurFilter from SwingX to render a good-looking blur efficiently. Notice how the blurred buffer is downscaled before the filter is applied. This trick lets us reduce the blur’s radius, thus making it a lot faster. We just need to scale this image back to its original size at drawing time.

Drawing the back buffer and the blur buffer does not involve any complicated trick. We just set the bilinear rendering hint on the graphics context and rely on an alpha composite to perform the translucent drawing:

@Override
protected void paintComponent(Graphics g) {
    if (isVisible() && blurBuffer != null) {
        Graphics2D g2 = (Graphics2D) g.create();

        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2.drawImage(backBuffer, 0, 0, null);

        g2.setComposite(AlphaComposite.SrcOver.derive(alpha));
        g2.drawImage(blurBuffer, 0, 0, getWidth(), getHeight(), null);
        g2.dispose();
    }
}

The most interesting part of this piece of code is the use of the field called alpha, that you saw in the first code snippet. This field is a float which holds a value ranging from 0 (no blur, transparent dialog) to 1 (blurred background, opaque dialog.) The animated effect is generated by modifying this value over time. To do this, we will use the trusty Timing Framework:

public float getAlpha() {
    return alpha;
}

public void setAlpha(float alpha) {
    this.alpha = alpha;
    repaint();
}

public void fadeIn() {
    createBlur();

    setVisible(true);
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            Animator animator = PropertySetter.createAnimator(
                400, detailPanel, "alpha", 1.0f);
            animator.setAcceleration(0.2f);
            animator.setDeceleration(0.3f);
            animator.addTarget(
                new PropertySetter(DetailsView.this, "alpha", 1.0f));
            animator.start();
        }
    });
}

For those of you who don’t know the Timing Framework, we are simply instructing it to animate two properties, both called “alpha,” on two different targets, this and the DetailPanel (remember, its alpha property is provided by its superclass JXPanel.) These properties are animated from their current value, 0.0f, to 1.0f over 400 milliseconds. The acceleration and deceleration are used to create a non-linear interpolation of the values over time and generate a more natural rendering.

That’s all there is to it! Dismissing the dialog is done with the fadeOut() method, whose code is almost exactly the same as fadeIn(). Instead of changing the properties from 0.0f to 1.0f, fadeOut() changes them from 1.0f to 0.0f. Remember to set DetailsView as the glass pane on your frame and your effect is ready to go.

Blurred Background for Dialogs (Extreme GUI Makeover 2007)

Groovy is a very appealing language that I really enjoy using from time to time. Unfortunately, as many other languages, Groovy lacks decent development environments. Even though this shortcoming is being addressed by the Eclipse and IntelliJ, the Groovy Console remains a valuable tool to quickly test an expression or a script.

The console works fine and achieve its purpose really well. However, its GUI is somewhat lacking and integrates badly with Mac OS X. The following screenshot shows what the ordinary Groovy Console looks like on Mac OS X:

Groovy Console on Mac OS X

Mac OS X users will easily spot the problems with this user interface. The menu bar belong to the window rather than to the screen and the shortcuts are wrong (using Ctrl instead of Cmd.) Also, not shown in the screenshot, the About and Quit menu items are not in the appropriate location. After a few tweaks, here is the new UI:

New Groovy Console on Mac OS X

There are three steps required to achieve this kind of integration with Mac OS X, two of them which you are probably already aware of. First, you need to tell Apple’s VM to move the menu bar to the top of screen, out of the application’s frame. At this stage, you also need to give your application its real name, otherwise Mac OS X will use the main class’ fully qualified name (in this case, groovy.ui.Console.) The Groovy code to achieve this is the following:

System.setProperty("apple.laf.useScreenMenuBar", "true")
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "GroovyConsole")
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())

Make sure to set the properties before you initialize any frame and you will get the desired result. The second part of our “Mac OS Xification” is to use the appropriate shortcut keys in the menus. The java.awt.Toolkit class offers the getMenuShortcutKeyMask() method which returns the system’s default shortcut modifier (Ctrl on Windows and Linux, Cmd on Mac OS X.) Again, using this in your application is quite simple:

def createShortcutWithModifier = { key, modifier -> KeyStroke.getKeyStroke(key,
    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | modifier) }
def createShortcut = { key -> createShortcutWithModifier(key, 0) }

// Uses Cmd-Shift-N/Ctrl+Shift+N
def newWindowAction = action(
    name:'New Window', closure: this.&fileNewWindow, mnemonic: 'W',
    accelerator: createShortcutWithModifier(KeyEvent.VK_N, KeyEvent.SHIFT_DOWN_MASK)
)

// Uses Cmd-O/Ctrl+O
def openAction = action(
    name:'Open', closure: this.&fileOpen, mnemonic: 'O',
    accelerator: createShortcut(KeyEvent.VK_O)
)

In this sample code, we just defined Swing actions with correct menu shortcuts. Creating a menu bar with those actions is easy with Groovy’s SwingBuilder. Building the menus is also the opportunity for us to introduce the last trick regarding Mac OS X integration: handling the About and Quit menu items correctly.

def isMac = { System.getProperty("mrj.version") != null }

menuBar {
    menu(text:'File', mnemonic: 'F') {
        menuItem() { action(newFileAction) }
        menuItem() { action(newWindowAction) }
        menuItem() { action(openAction) }
        separator()
        menuItem() { action(saveAction) }
        if (!isMac()) {
            separator()
            menuItem() { action(exitAction) }
        }
    }
    // More menus...
}

This piece of code does not add the exit menu item when the application is running on Mac OS X. Every Mac OS X application shows this menu item in the system controlled menu named after the application itself. This menu appears in bold on the second screenshot. The main problem is to figure out to hook our code to this menu. In my modified version of the Groovy console, here is how I did it:

if (isMac()) {
    ConsoleMacOsSupport.handleMacOs(this.&exit, this.&showAbout)
}

The isMac() could be avoided if ConsoleMacOsSupport was cleverer, but I decided to go with a simple yet effective implementation. As you can see, this class exposes a static method to which you can pass two method references. The methods bound that way will be invoked by the system when the user clicks either the About or Quit menu item in the application’s menu. The support class is also written in Groovy:

package groovy.ui

import com.apple.mrj.*

class ConsoleMacOsSupport implements MRJQuitHandler, MRJAboutHandler {
    static initialized = false
	
    def quitHandler
    def aboutHandler

    public void handleAbout() {
        aboutHandler()
    }

    public void handleQuit() {
        quitHandler()
    }

    public static void handleMacOs(quit, about) {
        if (!initialized) {
            initialized = true
            def handler = new ConsoleMacOsSupport(quitHandler:quit, aboutHandler:about)
            MRJApplicationUtils.registerAboutHandler(handler)
            MRJApplicationUtils.registerQuitHandler(handler)
        }
    }
}

ConsoleMacOsSupport simply delegates two Apple VM’s specific handlers to our Groovy methods. Since Groovy won’t even bother with this class at runtime if it’s not required, it is safe to keep it in the Windows and Linux distribution. This kind of work would usually be achieved through reflection in Java to avoid compile-time headaches.

Groovy makes the use of those handles even sweeter thanks to optional method parameters. For instance, Mac OS X’s quit handler does not expect any argument when invoked. However, we would like to bind the same method to a regular JMenuItem when the application is executed on Linux or Windows. In this case we are actually writing an ActionListener, and a parameter of type EventObject is therefore expected. Groovy is here to make it simple:

// The action's closure (action listener) is bound to the exit(EventObject) method
def exitAction = action(
    name:'Quit', closure: this.&exit, mnemonic: 'Q',
    accelerator: createShortcut(KeyEvent.VK_Q)
)

// The MRJQuitHandler is bound to the exit() method
ConsoleMacOsSupport.handleMacOs(this.&exit, this.&showAbout)

// Optional evt argument
void exit(EventObject evt = null) {
    // Cleanup, etc.
}

Even though I did not used this feature in the Groovy Console, you can also install a handler to hook into Mac OS X’s Preferences menu items. If your application offers some sort of options panel or configuration dialog, make sure to bind it to this menu item because that’s where users expect to find this feature. Last but not least, all those examples work also in pure Java and should not required much work to transpose.

Improving Groovy Console’s GUI

The source code, demos and examples for the chapter 11 of Filthy Rich Clients are available online. This chapter presents the repaint manager.

Chapter 11 presents two demos, TranslucentPanel and ReflectionPanel The first one is an exercise for the reader showing why and when the user of a RepaintManager is necessary. The second one presents a component that can render real-time reflections of its children. The demo requires QuickTime for Java to add a reflection effect to a QuickTime movie streamed from the web. When QuickTime is not available, the demo reverts to regular Swing components.

Update: The final PDF of the book is available for purchase online \o/

Both screenshots below show what the demo looks like. As you can see, the reflection panel can be used with any component you fancy. All you have to do is add() your component inside the panel and you’re done.

Real-time reflection on a movie

Real-time reflection on Swing components

Repaint Manager Demos (Chapter 11)

Chet just uploaded www.filthyrichclients.org, the web site I designed for our upcoming book, Filthy Rich Clients.

Filthy Rich Clients Web Site

We will try to make the demos and their source code available on this web site as zip archives. For the time being, the web site offers a link to the project page where you can find the CVS repository. This web site is still very much a work in progress and has been tested only with a few web browsers so please forgive me would you encounter any problem.

Update: The web site has now been tested, fixed and tested again with Internet Explorer 6. You should now be able to view the web site in good conditions.

New Web Site