New Java client properties on Mac

August 22, 2009

new_client_properties_focusednew_client_properties_unfocused
There are a few new client properties on the Mac, that haven’t yet been documented (though I’ve been assured that they’re safe to use). The great thing about the way Apple has been using client properties on the Mac, is that they make it easy for you to get closer to being a great Mac app, while not breaking your fidelity on other platforms. They’re really great if you want to fit in on your target platform, whereas in Mac Widgets for Java, I’m aiming for always looking like a Mac app.

The screenshots above show the new client properties that give you access to SourceList style selection painters (demo’d in the source code below). Note that these painters accurately pick up whether the user is using Aqua or Graphite — that’s a big bonus.

Here’s a listing of the new client properties:

List.sourceListBackgroundPainter
List.sourceListSelectionBackgroundPainter
List.sourceListFocusedSelectionBackgroundPainter
List.evenRowBackgroundPainter
List.oddRowBackgroundPainter

Here’s a little bit of code that puts a few of the new SourceList client properties to work (seen above):

public class NewClientProperties {

    /**
     * Create a SourceList style JList.
     */
    private static JList createMacSourceList() {
        JList list = new SourceList();
        // install a custom renderer that wraps the already installed renderer.
        list.setCellRenderer(new CustomListCellRenderer(list.getCellRenderer()));
        return list;
    }

    /**
     * A custom JList that renders like a Mac SourceList.
     */
    public static class SourceList extends JList {

        public SourceList() {
            // make the component non-opaque so that we can paint the background in
            // paintComponent.
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            // paint the background of the component using the special Mac border
            // painter.
            Border backgroundPainter =
                    UIManager.getBorder("List.sourceListBackgroundPainter");
            backgroundPainter.paintBorder(this, g, 0, 0, getWidth(), getHeight());
            super.paintComponent(g);
        }
    }

    /**
     * A custom ListCellRenderrer that wraps a delegate renderer.
     */
    public static class CustomListCellRenderer extends JPanel
            implements ListCellRenderer {

        private ListCellRenderer fDelegate;
        private boolean fIsSelected;
        private boolean fIsFocused;

        public CustomListCellRenderer(ListCellRenderer delegate) {
            this.setOpaque(false);
            this.setLayout(new BorderLayout());
            this.setBorder(BorderFactory.createEmptyBorder(1,5,1,5));
            fDelegate = delegate;
        }

        public Component getListCellRendererComponent(
                JList list, Object value, int index,boolean isSelected,
                boolean cellHasFocus) {

            this.removeAll();
            // remember the isSelected and cellHasFocus state so that we can use those
            // values in the paintComponent method.
            fIsSelected = isSelected;
            fIsFocused = cellHasFocus;
            // call the delegate renderer
            JComponent component = (JComponent) fDelegate.getListCellRendererComponent(
                    list, value, index, isSelected, false);
            // make the delegate rendere non-opqaue so that the background shows through.
            component.setOpaque(false);
            this.add(component, BorderLayout.CENTER);

            return this;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            // if the item was selected, then paint the custom Mac selection background.
            if (fIsSelected) {
                Border backgroundPainter = fIsFocused
                        ? UIManager.getBorder("List.sourceListFocusedSelectionBackgroundPainter")
                        : UIManager.getBorder("List.sourceListSelectionBackgroundPainter");
                backgroundPainter.paintBorder(this, g, 0, 0, getWidth(), getHeight());
            }
        }
    }

    public static void main(String[] args) {

        JList list = createMacSourceList();
        list.setListData(new String[]{
                "BMW", "Chevy", "Dodge", "Infiniti", "Nissan", "Porsche"});

        JScrollPane scrollPane = new JScrollPane(list);
        scrollPane.setBorder(BorderFactory.createEmptyBorder());

        JFrame frame = new JFrame();
        frame.add(scrollPane);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(200,200);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

12 Responses to “New Java client properties on Mac”

  1. Jamison Hope Says:

    Ken,

    Would this work with a JTree as well? Also, I imagine (but am too lazy^H^H^H^H busy at the moment to test for myself) that you’d get a NullPointerException in SourceList#paintComponent on another OS where these client properties are not defined, right?

    Thanks,
    Jamie

  2. Ken Says:

    Hi Jamison,

    This would work in any component. You’d need to protect from null on other platforms — in fact, I wouldn’t’ even try to do the painting on other platforms.

    -Ken

    • Daniel Says:

      How can I apply this to JTrees? I’ve changed the ListCellRenderer to TreeCellRenderer, but only the first “cell” of JTrees (that one with the icons) is rendered. The other cell containing the value/text is not rendered in the sourclist-style…

      • Ken Says:

        Hi Daniel,

        Applying this to a JTree will be a bit trickier. Try following my instructions here, which will show you how to make JTree fill it’s width. Also, you can look at SourceListTreeUI to see how I install renderers to create the SourceList effect.

        -Ken

  3. hendrik Says:

    neat! I gotta check this out. thanks for posting.


  4. […] Orr has a post up detailing some new Java client properties available on Mac platforms. These properties mean that components can get ever-closer to looking like proper Mac […]

  5. Daniel Says:

    do you have included this feature already in the latest dev-build?

    • Ken Says:

      Hi Daniel,

      I’m not currently using these client properties in Mac Widgets for Java. I may cut over to them though, so that on the Mac the user’s choice of Aqua or Graphite is picked up.

      -Ken


  6. […] des barre de header à la iTunes , faire des listes au look Apple bref que du […]

  7. trejkaz Says:

    Do we have a list of these somewhere? I just discovered this one in particular in the source code and am amazed that it wasn’t documented in the same doc as all the other custom component styles.


Leave a comment