changing how Nimbus LaF handles JTree node highlighting
Asked Answered
J

1

8

I have been working to transition a Java application from WindowsLookAndFeel to Nimbus, largely successfully, despite Nimbus foibles. My users overall like the Nimbus LaF but didn't like some details, some of which I changed by consulting previous questions on this site. Example: I copied the LeafIcon, ClosedIcon and OpenIcon from Windows LaF (which they liked) and use them in the Nimbus version, for a nice combination of LaFs.

Stuck on one last (?) problem.

I have a JTree with a subclassed DefaultCellRenderer to create the appropriate node displays. This works fine under WindowsLookAndFeel.

Problem: Under WindowsLaF when a node is selected the text of the node is highlighted, and the effect is visually easy to understand. Under Nimbus when a node is selected the highlighting is done with a bar of (fairly dark) color that runs the width of the tree window (not just the width of the text), and the effect is disconcerting.

So: I simply want WindowsLaF treatment of JTree node highlighting in the Nimbus LaF (ie colored background only the width of the text, and preferably in a better color that I can choose). I suspect this means I need to assign the right sort Painter to "Tree:TreeCell[Focused+Selected].backgroundPainter", but I don't know how to write it.

Suggestions most welcome.


EDIT

See the strange selected node highlight with Java 7!

enter image description here

public class TreeBorder {
    public static void main( String[] args ) {
        try{
            for( UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels() ) {
                if( "Nimbus".equals( info.getName() ) ) {
                    UIManager.setLookAndFeel( info.getClassName() );
                    break;
                }
            }
        } catch( Exception e ) {
            e.printStackTrace();
        }
        SwingUtilities.invokeLater( new Runnable() {
            @Override
            public void run() {
                JFrame f = new JFrame();
                f.setLocationRelativeTo( null );
                f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                f.getContentPane().add( getJTree() );
                f.pack();
                f.setVisible( true );
            }
            private JTree getJTree() {
                JTree jTree = new JTree();
                jTree.setCellRenderer( new LocalRenderer() );
                return jTree;
            }
        } );
    }

    private static class LocalRenderer extends DefaultTreeCellRenderer {
        @Override
        public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasfocus ) {
            DefaultTreeCellRenderer result = (DefaultTreeCellRenderer)super.getTreeCellRendererComponent( tree, value, sel, expanded, leaf, row, hasfocus );
                if( true ) {
                    result.setFont( new JLabel().getFont() );
                    Icon icon = UIManager.getIcon("FileView.floppyDriveIcon");
                    result.setIcon( icon );
                }
            return(result);
        }
    }
}
Jarrod answered 26/4, 2012 at 19:28 Comment(5)
Addendum to my posting: of course if there's an easier way than a painter, that would be even better. Tried just changing Tree.selectionBackground to a less distracting color, but Nimbus seems to ignore the modification.Jarrod
Further to my comment. You can't set Tree.selectionBackground directly, but this color is derived from numbusSelectionBackground, so nimbUID.put("nimbusSelectionBackground", new ColorUIResource(205,208,216)); produces some of the desired effect (a lighter Tree.selectionBackground). Still getting a row highlighted rather than just the text, so help on a painter would still be appreciated.Jarrod
@oliholz please can you commenting your bounty, sure I'm never to saw this question, because classic Renderers concept overloading Nimbus properties and settings in most of casesInhalator
@Inhalator eee Edit. With the a DefaultTreeCellRenderer and Java7 I got the annoying highlight under Nimbus.Quintin
The Nimbus bug is fixed in Java 8 and the fix doesn't work anymore.Quintin
P
6

Edit

The "Tree.selectionBackground" key is what controls the highlight on the JTree - it's done on the tree level, not on the TreeCellRenderer level (which is why it's a little confusing to manage). This code will get you a Tree where you can control the highlighting:

private JTree getJTree() {

    JTree jTree = new JTree();
    jTree.setOpaque(true);
    jTree.setBackground(Color.white);
    UIDefaults paneDefaults = new UIDefaults();
    paneDefaults.put("Tree.selectionBackground",null);

    JTextPane pane = new JTextPane();
    jTree.putClientProperty("Nimbus.Overrides",paneDefaults);
    jTree.putClientProperty("Nimbus.Overrides.InheritDefaults",false);

    jTree.setCellRenderer( new LocalRenderer() );
    return jTree;
}

And here's an example of changing the highlighting to Red. Please note that the Icon's background will be highlighted too - this is the default behavior for non-nimbus L&F too. If you don't want the icon to be highlighted, you're going to have to use something fancier than the default JLabel to render the TreeCell:

    public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasfocus ) {
        DefaultTreeCellRenderer result = (DefaultTreeCellRenderer)super.getTreeCellRendererComponent( tree, value, sel, expanded, leaf, row, hasfocus );
        result.setOpaque(true);
            if( true ) {
                result.setFont( new JLabel().getFont() );
                Icon icon = UIManager.getIcon("FileView.floppyDriveIcon");
                result.setIcon( icon );
            }
            if(sel){
                result.setBackground(Color.red);
            } else{
                result.setBackground(Color.white);
            }
        return(result);
    }

Original Answer

One of the easiest ways to fix this is to set the selected background color to transparent. The problem is that it's trying to paint the background of the label - which doesn't have the cool Nimbus painter used by the JTree's selection. So add this line to getTreeCellRendererComponent method:

result.setBackgroundSelectionColor(new Color(0,0,0,0));

Another option is to use the nimbus painter for the background of the TreeCellRenderer - but that seems like overkill in this situation.

Peggy answered 17/12, 2012 at 15:31 Comment(6)
hmm ... not really, as I understood the question it's about preventing the background coloring outside of the renderer (while keeping it inside) Using an invisible color (if it has any effect at all) would prevent both.Terebinthine
@Terebinthine Hehe... maybe I'll read the full question next time. Good catch.Peggy
OP here; just checked back in after a long time, delighted to see the question answered, and it works. Many thanks for the code and the info about it being handled on the tree level.Jarrod
works no more with Java 8. But the Nimbus bug is fixed.Quintin
OP here again. @oliholz: Sorry but I don't see that the Nimbus bug is fixed. Tried building a trivial tree; selecting gets different highlight behavior in the default L&F and in Numbus. Default highlights only the text of the node while Nimbus highlights the width of the tree frame (which was my original problem). Borrowed the idea in the last answer at #28691772 and used a painter that simply lightened the color, which looks better. Still would like to constrain highlighting to just the node text. Ideas welcome.Jarrod
OP adding to my comment above. Interesting (?) irony: https://mcmap.net/q/1206531/-color-row-in-jtree asks essentially the inverse question -- how to get a full band of color in the default L&F instead of just highlighting the node text.Jarrod

© 2022 - 2024 — McMap. All rights reserved.