Question

I've got a JTree with icons on some of the nodes within the tree. They appear and work fine but when I select a node with a icon, the renderer does not render the entire node selected but appears to have an offset applied to it, as if it thinks the icon is still to the left of the node as below:

Example of the rendering fault

The code for the renderer (which extends DefaultTreeCellRenderer) is below:

public ProfileTreeRenderer() {
    super.setLeafIcon(null);
    super.setClosedIcon(null);
    super.setOpenIcon(null);
}

@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
    Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
    if (c instanceof JLabel) {
        JLabel label = (JLabel) c;
        label.setHorizontalTextPosition(SwingConstants.LEADING);
    }

    if(sel && !hasFocus) {
        setBackgroundSelectionColor(UIManager.getColor("Panel.background"));
        setTextSelectionColor(UIManager.getColor("Panel.foreground"));
    } else {
        setTextSelectionColor(UIManager.getColor("Tree.selectionForeground"));
        setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground"));
    } 

    if (value instanceof ProfileNode) {
        ProfileNode node = (ProfileNode) value;
        if (node.isUsed() && !sel) {
            c.setForeground(Color.GRAY);
        }

        if (node.getIcon() != null) {
            setIcon(node.getIcon());
        }
    }
}

I cannot see why the renderer would apply this offset, so can anyone offer a way to get the node fully selected with the icon? The SSCCE code for the tree itself is below.

public class Example extends JDialog {
    public Example() {
        JTree tree = new JTree(createModel());
        tree.setCellRenderer(new ProfileTreeRenderer());

       setLayout(new BorderLayout());
       add(tree, BorderLayout.CENTER);
    }

    private TreeModel createModel() {
        ProfileNode root = new ProfileNode("Profiles");

        ProfileNode userA = new ProfileNode("Example User A");
        ProfileNode userB = new ProfileNode("Example User B");

        // You'll need to subsitute your own 16x16 icons here
        userA.setIcon(ImageSet.USER_ICON);
        userB.setIcon(ImageSet.USER_ICON);

        root.add(userA);
        root.add(userB);

        return new DefaultTreeModel(root);
    }

    public static void main(String[] args) {
       EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Example().setVisible(true);
            }
        });
    }
}

The ProfileNode class:

public class ProfileNode extends DefaultMutableTreeNode {

   @Getter private String labelDisplay;
   @Getter @Setter private ImageIcon icon;

   @Getter @Setter private boolean isUsed = false;

   public ProfileNode(String labelDisplay) {
       this.labelDisplay = labelDisplay;
   }

   @Override
   public String toString() {
      return labelDisplay;
   }
}

Thanks in advance.

Was it helpful?

Solution

The problem is that the DefaultTreeCellRenderer uses its icon property exclusively for the open/leaf/close icons: it assumes that - if the icon != null - it's at the start of the component (even if it isn't) and adjusts the selection accordingly. You need to re-adjust ... or use SwingX renderers :-)

Something like:

JXTree tree = new JXTree();
tree.expandAll();
IconValue iv = new IconValue() {

    Icon icon = XTestUtils.loadDefaultIcon("green-orb.png");
    @Override
    public Icon getIcon(Object value) {
        return value.toString().contains("s") ? icon : null;
    }

};
StringValue converter = new MappedValue(StringValues.TO_STRING, iv);
WrappingProvider provider = new WrappingProvider(IconValues.NONE, converter);
// hacking around missing api ...
LabelProvider wrappee = (LabelProvider) provider.getWrappee();
wrappee.getRendererComponent(null).setHorizontalTextPosition(JLabel.LEADING);
TreeCellRenderer r = new DefaultTreeRenderer(provider);
tree.setCellRenderer(r);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top