JScrollPane minimum width inside JSplitPane

313 views Asked by At

I'm trying out the JSplitPane with a couple of scrollable side-by-side JTables.
However I'm experiment a behavior where the JScrollPane gets shrinked too much, as per gif.
Notice the left component, that besides having a minimum width of 250px, continues shrinking.

enter image description here

The relevant code is

final var objetsTable = new JTable();
final var objectsScrollPane = new JScrollPane(objetsTable);
objectsScrollPane.setMinimumSize(new Dimension(250, 0));
objectsScrollPane.setPreferredSize(new Dimension(400, 300));

final var stepsTable = new JTable();
final var stepsScrollPane = new JScrollPane(stepsTable);
stepsScrollPane.setMinimumSize(new Dimension(150, 0));
stepsScrollPane.setPreferredSize(new Dimension(200, 300));

final var splitPane = new JSplitPane();
splitPane.setLeftComponent(objectsScrollPane);
splitPane.setRightComponent(stepsScrollPane);
splitPane.setResizeWeight(1.0);

How can I avoid the JScrollPanes getting shrinked too much in this case?

1

There are 1 answers

6
gthanop On BEST ANSWER

The getMinimumSize called on a JSplitPane returns a size which is actually taking into account the minimum size of its left and right Components, plus the divider size. So one way to maybe solve your problem would be to make your JSplitPane implement Scrollable (in order to make it respect the minimum size of itself) and add it to a JScrollPane. This way you can ensure that the minimum size is respected and when the user continues shrinking the Scrollable JSplitPane past its minimum size, then the scroll bars will show up.

Here is some working code:

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class Main {
    
    private static class MyScrollableSplitPane extends JSplitPane implements Scrollable {
        
        private int maxUnitIncrement = 10;
        
        public void setMaxUnitIncrement(final int pixels) {
            maxUnitIncrement = pixels;
        }
        
        public int getMaxUnitIncrement() {
            return maxUnitIncrement;
        }
        
        /**
         * This is being constantly checked by the scroll pane instead of the
         * getPreferredScrollableViewportSize...
         */
        @Override
        public Dimension getPreferredSize() {
            final Dimension minSz = getMinimumSize(),
                            curSz = getSize();
            curSz.width = Math.max(curSz.width, minSz.width);
            curSz.height = Math.max(curSz.height, minSz.height);
            return curSz;
        }
        
        /**
         * This is only checked once (at the beginning).
         */
        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return super.getPreferredSize();
        }

        /**
         * Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
         */
        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect,
                                              int orientation,
                                              int direction) {
            //Get the current position.
            int currentPosition;
            if (orientation == SwingConstants.HORIZONTAL) {
                currentPosition = visibleRect.x;
            } else {
                currentPosition = visibleRect.y;
            }

            //Return the number of pixels between currentPosition
            //and the nearest tick mark in the indicated direction.
            if (direction < 0) {
                int newPosition = currentPosition -
                                 (currentPosition / maxUnitIncrement)
                                  * maxUnitIncrement;
                return (newPosition == 0) ? maxUnitIncrement : newPosition;
            } else {
                return ((currentPosition / maxUnitIncrement) + 1)
                       * maxUnitIncrement
                       - currentPosition;
            }
        }

        /**
         * Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
         */
        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect,
                                               int orientation,
                                               int direction) {
            if (orientation == SwingConstants.HORIZONTAL) {
                return visibleRect.width - maxUnitIncrement;
            } else {
                return visibleRect.height - maxUnitIncrement;
            }
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            final Container parent = getParent();
            return (parent instanceof JViewport) && (getMinimumSize().width < ((JViewport) parent).getWidth());
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            final Container parent = getParent();
            return (parent instanceof JViewport) && (getMinimumSize().height < ((JViewport) parent).getHeight());
        }
    }
    
    private static void createAndShowGUI() {
        
        /*Since I don't add any Components to the 'left' and 'right' panels, I am going to set the
        preferred size of them. This is only for demonstrating the concept. Setting the minimum size
        though is somewhat required by the JSplitPane itself.*/
        
        final JPanel left = new JPanel();
        left.setMinimumSize(new Dimension(150, 100));
        left.setPreferredSize(new Dimension(200, 200));
        
        final JPanel right = new JPanel();
        right.setMinimumSize(new Dimension(300, 100));
        right.setPreferredSize(new Dimension(400, 200));
        
        final JSplitPane split = new MyScrollableSplitPane();
        split.setBorder(BorderFactory.createLineBorder(Color.CYAN.darker(), 3));
        split.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
        split.setLeftComponent(left);
        split.setRightComponent(right);
        
        final JFrame frame = new JFrame("MyScrollableSplitPane demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new JScrollPane(split));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    public static void main(final String[] args) {
        SwingUtilities.invokeLater(Main::createAndShowGUI);
    }
}