As I mentioned back here, my collegue, Jared MacDonald, also spoke at JavaOne 2009. Besides being an entertaining speaker, he delivered a great message about applying Test Driven Development (TDD) to user interface development. TDD is oft regarded as too difficult to apply in the UI space. Jared did a great job demonstrating why that’s simply not the case.

pdf Bullet Proof User Interface slides (PDF)

Until next JavaOne!

If you’ve ever extended BasicTableUI, you may have struggled a bit with it’s inability to hook into the cell rendering process. JTable has the prepareRenderer method through which you can inject yourself into the cell painting pipeline. BasicTableUI, however, has no such mechanism.

Why would we even need this capability from a BasicTableUI? In ITunesTableUI, for example, I install a special border on the containing JScrollPane, which paints the row striping (the subject of a future blog entry). In order for the striping to show through, each renderer must be non-opaque (transparent). By default, renderers are opaque, which results in a table that looks like this:

iTunesTable-bad
We have a couple of options to work around this. First, we could simply grab each of the default renderers and make it non-opaque. This isn’t a great solution, though, because there’s no easy way to get a list of all these renderers — we’d end up harding coding a set into our UI delegate. And if downstream consumers added their own renderers, they’d be responsible for making sure they were non-opaque.

The second (and better) option, is to create a custom CellRendererPane that essentially gives us a prepareRenderer method. CellRendererPane is the class used to stamp out each table cell onto the screen. The renderer for the cell is prepared, and then added to the CellRendererPane, which manually invokes paintComponent. Overriding paintComponent gives us the hook into the cell painting process that we’re looking for. Here’s what the custom CellRendererPane looks like:

    /**
     * Creates a custom {@link CellRendererPane} that sets the renderer component to
     * be non-opaque if the associated row isn't selected. This custom
     * {@code CellRendererPane} is needed because a table UI delegate has no prepare
     * renderer like {@link JTable} has.
     */
    private CellRendererPane createCustomCellRendererPane() {
        return new CellRendererPane() {
            @Override
            public void paintComponent(Graphics graphics, Component component,
                                       Container container, int x, int y, int w, int h,
                                       boolean shouldValidate) {
                // figure out what row we're rendering a cell for.
                int rowAtPoint = table.rowAtPoint(new Point(x, y));
                boolean isSelected = table.isRowSelected(rowAtPoint);
                // if the component to render is a JComponent, add our tweaks.
                if (component instanceof JComponent) {
                    JComponent jcomponent = (JComponent) component;
                    jcomponent.setOpaque(isSelected);
                }

                super.paintComponent(graphics, component, container, x, y, w, h,
                    shouldValidate);
            }
        };
    }

Here’s how we create and install this custom CellRendererPane in our extension of BasicTableUI:

CustomTableUI extends BasicTableUI {
    // ...
    @Override
    public void installUI(JComponent c) {
        super.installUI(c);

        table.remove(rendererPane);
        rendererPane = createCustomCellRendererPane();
        table.add(rendererPane);

        // ...
    }
}

and that lets us make all our non-selected cells non-opaque!

iTunesTable-good

iTunes_Table-promo-new
HUDControlsUpdate1-promo-updated
I’ve just released Mac Widgets for Java 0.9.5 (download it here). The major addition in this release is an iTunes style table. I’ve also added a HUD style radio button, made HUDs transparent on non-Mac platforms and completed a number of SourceList enhancements and bug fixes.

Users of the Unified Tool Bar and Bottom Bar will note that these components have been promoted to full classes rather than the awkward factory methods (which had been located in MacWidgetFactory). The API is still evolving, most liking breaking API consumers on upgrade, which is why I’m keeping the library pre-1.0.

You’ll find the full list of enhancements and fixes here, or you can browse the API here. Also, you can see an example that uses the new iTunes table here.

hud_on_windows

I finally got around to making the Heads Up Display (HUD) use transparency on non-Mac platforms. I wanted to keep compatibility with Java 5, so I decided to use reflection to try and gracefully use the com.sun.awt.AWTUtilities.setWindowOpaque method. Here are the two utility methods I use to make a window non-opque:

    /**
     * Try's to make the given {@link Window} non-opqaue (transparent) across
     * platforms and JREs. This method is not guaranteed to succeed, and will fail
     * silently if the given {@code Window} cannot be made non-opaque.
     *
     * This method is useful, for example, when creating a HUD style window that
     * is semi-transparent, and thus doesn't want the window background to be
     * drawn.
     * @param window the {@code Window} to make non-opaque.
     */
    public static void makeWindowNonOpaque(Window window) {
        // on the mac, simply setting the window's background color to be fully
        // transparent makes the window non-opaque.
        window.setBackground(new Color(0, 0, 0, 0));
        // on non-mac platforms, try to use the facilities of Java 6 update 10.
        if (!PlatformUtils.isMac()) {
            quietlyTryToMakeWindowNonOqaque(window);
        }
    }

    /**
     * Trys to invoke {@code com.sun.awt.AWTUtilities.setWindowOpaque(window, false)}
     * on the given {@link Window}. This will only work when running with JRE 6 update 10
     * or higher. This method will silently fail if the method cannot be invoked.
     * @param window the {@code Window} to try and make non-opaque.
     */
    private static void quietlyTryToMakeWindowNonOqaque(Window window) {
        try {
            Class clazz = Class.forName("com.sun.awt.AWTUtilities");
            Method method =
                    clazz.getMethod("setWindowOpaque", java.awt.Window.class, Boolean.TYPE);
            method.invoke(clazz, window, false);
        } catch (Exception e) {
            // silently ignore this exception.
        }
    }

This enhancement will be part of Mac Widgets for Java 0.9.5, which will hopefully be out in the next few weeks. If you want access earlier, you can either download the code and build it from the Subversion repository, or you can email me and I’ll send you the latest jar file.

JWebPane screen shots

June 16, 2009

Alexey Ushakov has just posted screen shots of the JWebPane Java web browser that he showed in his BOF at JavaOne 2009 (which I talked about here). Below is a screen shot I pulled from Alexey’s latest blog post:
JWPScr1
Seems like we’re getting closer!

I thought Safari 4 beta was headed in the right direction with it’s tab location. The Safari design team had moved the tabs to the the top of the window and thus made them up-right like this:
safari_4_beta
Now I realize there was a bit of a usability problem, as the tabs were now doubling as the mechanism to move the window, but I liked the direction the UI was moving in. I greatly disliked the upside down tabs of Safari 3, because they make no visual sense — the tabs are completely disconnected from the content they are tabbing.

As you can imagine, I was dismayed when I installed the final version of Safari 4 only to see the tabs revert to their version 3 visuals:
safari_4
I’m not sure why the Safari visual design team chose this broken tab metaphor to begin with as it’s never made any sense, and it’s clear that they realize this. So why keep around this broken metaphor and make us suffer for another full version?

This is not a hard visual design problem to solve. Google Chrome has a great solution that meets the visual demands of the Mac platform:
chrome_beta
Come on Apple — lets get this fixed.

Here are the slides for my JavaOne 2009 presentation, Simply Sweet Components, TS-4559 (read the abstract). Enjoy!

keynote Keynote version (the format I authored the presentation in)
quick_time QuickTime version (with full animations)
pdf PDF version (with notes, but no animations)

To get the most out of the presentation, I’d recommend clicking through the QuickTime version (the one with the animations), with the PDF version up on the side (which has the notes).

JWebPane update

June 5, 2009

I was one of the few attendees of the late-night JWebPane BOF (BOF-3992). Artem Ananiev and Alexey Ushakov gave a fantastic demo of a Java web browser based on the JWebPane component (I’m trying to get ahold of a screen shot of this). I did learn that WebKit doesn’t actually provide a rendering engine, but instead provides hooks such that you can be told when and what to paint. This works out well, because JWebPane’s content can be completely painted using Java 2D and Swing.

The team was of course asked when JWebPane would be released (this was my only motivation for attending!). As expected, no date was provided, but they did suggest that it may be available by the end of the year (2009). They also said that an actual date would be put forth within the next couple of months.

One interesting tidbit of information that slipped out was that JWebPane’s release date is being aligned with JavaFX. It’s not clear to exactly why this connection to JavaFX exists, but I’m sure we’ll find out soon.

** Update: screen shots available here. **

JavaFX 1.2 arrived silently last night in order to coincide with the opening of JavaOne 2009. I’m happy to see many more UI controls included in this latest release of JavaFX, most notably ScrollBar, which is so painful to re-implement yourself.

Here’s a look at some of the new controls based on a Sun web-start app I found here (run it):
javafx_1_2_ui_controlsHere’s the full list of new UI controls:

  • Button
  • Label
  • CheckBox
  • ToggleButton
  • RadioButton
  • Hyperlink
  • ProgressBar
  • ProgressIndicator
  • Slider
  • ScrollBar
  • ListView
  • TextBox

I wasn’t expecting the controls to have a platform neutral look, though I think this was a good idea (similar to how Adobe Flex controls have an platform agnostic look).

Check out the full JavaFX 1.2 API — note how much fuller it is than in JavaFX 1.1.

I’m interested in hearing you’re thoughts on JavaFX 1.2, specifically about the new UI controls.

ticket

And the winners of the IntelliJ raffle are…

Animesh Jain
Craig Kelley
Greg Gerard

Congratulations to the winners! If you didn’t win, and are still interested in trying out IntelliJ, I encourage you to download their 30 day trial.