JDK-6526631 : RFE: JScrollPane behaves wrong when a child component has RIGHT_TO_LEFT orientation
  • Type: Enhancement
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 7
  • Priority: P5
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2007-02-20
  • Updated: 2017-05-23
Related Reports
Relates :  
Description
JScrollPane behaves wrong when a child component has RIGHT_TO_LEFT orientation.
There are two problems. To see them, use the test case below (you can also find it in the attachment) and follow the instructions:

1. Aligned to the wrong direction while expanding the tree.
Steps to reproduce:
 - run the test application
 - press Switch Orientation button
 - press Expand All button
You can see that the tree is aligned by the left border so the root element
is moved to the right and becomes invisible.
You can see a related issue here:
   http://www.netbeans.org/issues/show_bug.cgi?id=93577


2. Aligned to the wrong direction while resizing the container (frame). 
Steps to reproduce:
 - run the test application
 - press Switch Orientation button
 - press Expand All button
 - scroll to right so the root node becomes visible
 - increase size of the frame 
 - decrease size of the frame
Now you can note that the root element becomes overlapped by the frame's border.
If you try to do almost the same scenario for the LEFT_TO_RIGHT orientation, 
then you can note that root element tends to remain visible. It's more user friendly behaviour.


====================== Source Begin ==========================
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;

/**
 * @author nk160297
 */
public class ScrollTest extends JPanel {

    private JButton btnCollapse;
    private JButton btnExpand;
    private JToggleButton btnSwitchOrientation;
    private JScrollPane jScrollPane1;
    private JTree jTree1;

    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame("Test wrong scrolling while resizing");
        frame.getContentPane().add(new ScrollTest());
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public ScrollTest() {
        initComponents();
        jTree1.setModel(new WideTreeModel());
        //
        btnSwitchOrientation.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ComponentOrientation orientation = jTree1.getComponentOrientation();
                if (orientation.isLeftToRight()) {
                    jTree1.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
                } else {
                    jTree1.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
                }
            }
        });
        //
        btnExpand.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                expandNodeAndSubnodes(new TreePath(jTree1.getModel().getRoot()), true);
            }
        });
        //
        btnCollapse.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                expandNodeAndSubnodes(new TreePath(jTree1.getModel().getRoot()), false);
            }
        });
    }

    private void expandNodeAndSubnodes(TreePath path, boolean expand) {
        if (expand) {
            jTree1.expandPath(path);
        } else {
            jTree1.collapsePath(path);
        }
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        Enumeration subnodes = node.children();
        while (subnodes.hasMoreElements()) {
            DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) subnodes.nextElement();
            if (!childNode.isLeaf()) {
                TreePath childPath = new TreePath(childNode.getPath());
                expandNodeAndSubnodes(childPath, expand);
            }
        }
    }

    private void initComponents() {
        jScrollPane1 = new javax.swing.JScrollPane();
        jTree1 = new javax.swing.JTree();
        btnSwitchOrientation = new javax.swing.JToggleButton();
        btnExpand = new javax.swing.JButton();
        btnCollapse = new javax.swing.JButton();

        jScrollPane1.setViewportView(jTree1);

        btnSwitchOrientation.setText("Switch Orientation");

        btnExpand.setText("Expand All");

        btnCollapse.setText("Collapse All");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE)
                                .addComponent(btnExpand, javax.swing.GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE)
                                .addComponent(btnSwitchOrientation, javax.swing.GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE)
                                .addComponent(btnCollapse, javax.swing.GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE))
                        .addContainerGap())
        );
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 200, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btnSwitchOrientation)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btnExpand)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btnCollapse)
                        .addContainerGap())
        );
    }

    static class WideTreeModel extends DefaultTreeModel {
        public WideTreeModel() {
            super(new DefaultMutableTreeNode("root"));
            DefaultMutableTreeNode root = (DefaultMutableTreeNode) getRoot();
            DefaultMutableTreeNode parent;
            //
            parent = new DefaultMutableTreeNode("colors");
            root.add(parent);
            parent.add(new DefaultMutableTreeNode("blue"));
            parent.add(new DefaultMutableTreeNode("violet"));
            parent.add(new DefaultMutableTreeNode("red"));
            parent.add(new DefaultMutableTreeNode("yellow"));
            parent.add(new DefaultMutableTreeNode("cccccccccccccccccccccccccccccccccccccccccccccccccccccccc"));
            //
            parent = new DefaultMutableTreeNode("sports");
            root.add(parent);
            parent.add(new DefaultMutableTreeNode("basketball"));
            parent.add(new DefaultMutableTreeNode("soccer"));
            parent.add(new DefaultMutableTreeNode("football"));
            parent.add(new DefaultMutableTreeNode("hockey"));
            parent.add(new DefaultMutableTreeNode("sssssssssssssssssssssssssssssssssssssssssssssssssssssss"));
            //
            parent = new DefaultMutableTreeNode("food");
            root.add(parent);
            parent.add(new DefaultMutableTreeNode("hot dogs"));
            parent.add(new DefaultMutableTreeNode("pizza"));
            parent.add(new DefaultMutableTreeNode("ravioli"));
            parent.add(new DefaultMutableTreeNode("bananas"));
            parent.add(new DefaultMutableTreeNode("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
        }
    }
}
====================== Source End ============================

Comments
EVALUATION The fix should include detailed description how to use the componentOrientation properties of all components in hierarchy: scroll pane, horizontal scroll bar, viewport and view (inner component).
28-07-2009

WORK AROUND The componentOrientation property should be changed not only for inner component, but for outer scroll pane too.
28-07-2009

EVALUATION The problem with the alignment are moved to the 6864297 CR.
24-07-2009

EVALUATION the bug is reproducible
03-07-2009

EVALUATION I tried the test case under JDK 7 and JDK 6. The problem appears. Most likely JScrollPane should take into account the orientation of child component, i.e. JScrollPane.setComponentOrientation() should be made more complex.
20-02-2007