Sunday, May 27, 2012

TreeUIs: Adding Decorations to Cells

This article discusses a modified TreeUI that lets you add decorations on the right side of your tree. (See this other article for a similar discussion focusing on JLists.)

What do I mean by "Decoration"?

The short answer is, "I'm not exactly sure." It's similar to a button or a label (depending on which one you want), and that's how most users will perceive it, but it is not actually a JComponent, and it does not exist in the Swing hierarchy of your window.

In a JTree: every cell is rendered via a TreeCellRenderer. The same renderer is consulted for every row in the tree, so the intended usage is for one component to be used to display potentially thousands of different values. This is referred to as "stamping", because the renderer is repeatedly painting to the tree without actually belonging to it.

This is a wonderful design regarding memory use, but it becomes difficult to add functionality to trees the same way you can add functionality to text fields and other Swing components -- because you don't have a solid grasp on the hierarchy of elements involved.

The decoration model partially addresses this issue. Swing components are (usually) very well suited to this kind of augmentation. There is nothing sacred about the current implementation of the JTree and how it renders: these components are designed to flexible and, when possible, improved to meet your needs.

Examples

To help paint a better picture of what I'm describing, here is an applet demonstrating tree decorations:

This applet (source code included) is available here. Here is a summary of each decoration:

  • Progress Indicator: the root node takes a few seconds to load. While it is loading: a progress indicator appears on the right side of the tree. As soon as the tree is fully loaded: the indicator is hidden.
  • Refresh Button: Once the progress indicator is dismissed, the root node will show a refresh button when it is selected. (When clicked, the node re-loads like it did before.)
  • Play/Pause Buttons: when the "Playable" node is selected a play/pause toggle is present. (In this demo nothing actually plays, but this was designed with audio playback in mind.)
  • Warning Indicator: this node shows a pulsing warning icon when selected. (Thanks to the Silk icon set for the image!)
  • Rating Stars: this node shows 5 rating stars when selected. This is a good demonstration of the current limits of the decoration implementation: clicking and dragging does not work like I would want it to.
  • Delete Button: this removes the selected node from the list.
  • The delete button is actually the original motivation for this project: I didn't want the user to have to click a tree node and then navigate somewhere else in the UI to the delete button. In my case this saved several pixels, cleaned up the layout, and made the user's task more efficient. Also because the button is adjacent to what it modifies: there is little room for confusion about which object the user is acting on.

    However I am also worried that this model can introduce too much visual clutter (aka "over-stimulation"). To limit the amount of controls the user needs to process I made most of the decorations only appear in a row when that row is selected. Also this helps guard against accidentally clicking the wrong button, which becomes increasingly important when your controls become smaller. Restraining the number of decorations is just a suggestion, though: you can make all your decorations visible all the time if you want to. (For example: if you're always dealing with very advanced users, or if your tree is usually going to be relatively small.)

    And lastly: as a personal preference I added an option to highlight the width of the entire row. By default the BasicTreeUI only highlights the text of the tree node when it is selected, but this tree is a kind of hybrid between a tree and a list (or a tree and a table?): so highlighting the entire row seemed like a good idea.

    Implementation

    The DecoratedTreeUI is an extension of the BasicTreeUI, so it inherits most of the standard tree functionality.

    It takes the existing TreeCellRenderer (that the JTree stores) and places it inside its own renderer. On the right side of that renderer it adds all the appropriate decorations as JLabels. As the demo above shows: different tree nodes can have different decorations.

    The decoration object you need to supply is represented with three methods:

    public abstract static class TreeDecoration {
    public abstract Icon getIcon(JTree tree, Object value, boolean selected, boolean expanded,
    boolean leaf, int row, boolean isRollover,boolean isPressed);

    public abstract boolean isVisible(JTree tree, Object value, boolean selected, boolean expanded,
    boolean leaf, int row, boolean hasFocus);

    public abstract ActionListener getActionListener(JTree tree, Object value, boolean selected, boolean expanded,
    boolean leaf, int row, boolean hasFocus);
    }

    These are the only methods you need to implement to add decorations to your tree. And there is also built-in support for the BasicTreeDecoration (for simple labels/buttons) and RepaintingTreeDecoration (for decorations that continually need to repaint).

    To install decorations, call:

    myTree.setUI(new DecoratedTreeUI());
    myTree.putClientProperty( DecoratedTreeUI.KEY_DECORATIONS, myArrayOfDecorations );

    Also to give you complete control over how the icons are positioned: there is no padding between decorations. That's not because I think zero padding is a good idea (it's not), but this lets you pad your icons with however many pixels you think is appropriate (instead of an arbitrary amount I made up).

    The rest of the implementation is probably relatively boring: the hard parts included identifying the simplest methods I needed to override to add the functionality I wanted, and adding a simple model to arm/disarm the decorations so they really behaved like buttons. As of this writing: the final implementation is about 500 lines of code (not counting comments), so it's not that daunting in the end.

    Thursday, May 24, 2012

    Liens pratiques de la semaine

    Vous trouverez dans ce billet une s�lection de liens pratiques autour des technologies Eclipse qui m'ont particuli�rement int�ress�es ces derni�res semaines.   

    Eclipse
    Java
    • Sonar 3.0 est disponible (nouveaut�s) et page de t�l�chargement
    • Un billet qui pr�sente une comparaison entre SQL et JPQL.
    Divers
    • Vagrant est une solution permettant de faciliter la virtualisation de son environnement de d�veloppement.