Search code examples
javaswingscrollbarjtree

Java : JTree and BasicTreeUI reference doesn't show scrollbars


Working with the JTreeWithScrollbar example, but scaled it back significantly to focus on the issue.

The original code would have the vertical scrollbars appear as needed.

Here, there is plenty of space and no scrollbars are needed.

enter image description here

If the panel is moved enough, the scrollbar will appear.

enter image description here

Once the following line of code was added, the scrollbars stopped appearing.

        tree.setUI(new MyTreeUI());

Notice no scrollbar.

enter image description here

If the above line of code is commented out, the vertical scrollbar appears.

Checking the documentation for BasicTreeUI and there isn't anything related to showing/hiding scrollbars.

2 Questions

1 - When utilizing the BasicTreeUI object, what is required to ensure the scrollbars still function?

2 - Why is it the Horizontal scrollbar never appears even if the line of code is commented out?

import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.Dimension;

public class JTreeWithScrollbar extends JPanel { 
    private JEditorPane htmlPane;
    private JTree tree;

    public JTreeWithScrollbar() 
    {
        //Create the nodes. 
        DefaultMutableTreeNode top =  new DefaultMutableTreeNode("The Java Series");
        DefaultMutableTreeNode book1Node = new DefaultMutableTreeNode("Book 1");
        DefaultMutableTreeNode book2Node = new DefaultMutableTreeNode("Book 2");
        top.add(book1Node);
        top.add(book2Node);     

        tree = new JTree(top);
        tree.setUI(new MyTreeUI());  ///Comment out this line of code and the vertical scrollbar appears.

        JScrollPane treeView = new JScrollPane(tree);

        JScrollPane htmlView = new JScrollPane(htmlPane);

        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        splitPane.setTopComponent(treeView);
        splitPane.setBottomComponent(htmlView);

        Dimension minimumSize = new Dimension(100, 50);
        htmlView.setMinimumSize(minimumSize);
        splitPane.setDividerLocation(100); 
        splitPane.setPreferredSize(new Dimension(500, 300));
        add(splitPane);
    }
    
    public static void main(String[] args) 
    {
        //Create and set up the window.
        JFrame frame = new JFrame("TreeDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel jp = new JPanel();
        jp.add(new JTreeWithScrollbar());
        frame.add(jp);


        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }
    
    
    private static class MyTreeUI extends BasicTreeUI 
    {
        public MyTreeUI() 
        {
            super();
        }

        @Override
        protected void updateCachedPreferredSize() {
            treeState.invalidateSizes();
            tree.treeDidChange();
        }
        
    }
}

Solution

    1. When utilizing the BasicTreeUI object, what is required to ensure the scrollbars still function?

    As shown in the minimal example below, BasicTreeUI correctly shows each scroll bar when needed; resize the frame to see the effect.

    1. Why does the horizontal scrollbar never appear even if the line of code is commented out?

    After pack() the frame has been resized to adopt the preferred size of it content. Making the frame slightly smaller illustrates the effect. Your example adds the tree to a JPanel having a default FlowLayout which ignores preferred sizes; the example below adds the tree to the center of the frame's default BorderLayout which responds to preferred sizes.

    1. I am assuming the updateCachedPreferredSize() must be doing other stuff behind the scenes…

    Exactly. Each invocation of updateCachedPreferredSize() updates the component's preferred size to reflect any change in state (resize, expand, etc.); when the preferred size exceeds the viewport size, the scroll bars appear. As you observed, invoking super.updateCachedPreferredSize() allows normal operation, and any further customization must preserve that functionality.

    In addition,

    • Expand rows as need like this.

    • Construct and manipulate Swing GUI objects only on the event dispatch thread.

    • Don't use setSize() when you really mean to override getPreferredSize() or illustrates a resize effect; more here.

    Tree

    import java.awt.EventQueue;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTree;
    import javax.swing.plaf.basic.BasicTreeUI;
    
    public class JTreeWithScrollbar {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(() -> {
                //Create and set up the window.
                JFrame frame = new JFrame("TreeDemo");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
                JTree tree = new JTree(); //default model
                for (int i = 0; i < tree.getRowCount(); i++) {
                    tree.expandRow(i);
                }
                tree.setUI(new MyTreeUI());
                frame.add(new JScrollPane(tree));
    
                //Display the window.
                frame.pack();
                frame.setSize(frame.getWidth() - 10, frame.getHeight() - 100);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            });
        }
    
        private static class MyTreeUI extends BasicTreeUI {
        }
    }