6 Replies Latest reply: Mar 14, 2012 1:07 PM by 829050 RSS

    JSplitPane keeps right-hand component same size

    829050
      I have a JInternalFrame... on its content pane sits a JScrollPane. Viewport view of JScrollPane is set to a JSplitPane.

      I want to make it so that, when the split pane divider is pulled either way (left or right), the right-hand component stays the same size... i.e. the width of the JSplitPane grows or diminishes accordingly. Obviously you would then see this reflected in the width and position of the slider of the horizontal scroll bar of the JScrollPane.

      I'm finding this very difficult to implement. To the point that I'm wondering whether I have to subclass BasicSplitPaneUI.BasicHorizontalLayoutManager ... which itself is not clear because the ** horizontal ** layout manager can't be instantiated (although its subclass the ** vertical ** layout manager can be).

      Alternatively I'm wondering if maybe JSplitPane is just not designed for such a purpose... in which case which layout manager would be appropriate? I'm not an expert on them... but do any of them actually give you a bar of some kind by which to resize one of their components? This is the attraction of JSplitPane for me...

      Another possibility might I suppose be a 1 row x 2 col JTable... but I'd like to find a split pane solution ideally
        • 1. Re: JSplitPane keeps right-hand component same size
          StanislavL
          Could you provide SSCCE?
          • 2. Re: JSplitPane keeps right-hand component same size
            829050
            import java.awt.BorderLayout;
            import java.awt.Color;
            import java.awt.Component;
            import java.awt.Dimension;
            import java.awt.EventQueue;
            import java.awt.Point;
            import java.awt.Rectangle;
            import java.awt.event.AdjustmentEvent;
            import java.awt.event.AdjustmentListener;
            import java.awt.event.ComponentAdapter;
            import java.awt.event.ComponentEvent;
            import java.beans.PropertyChangeEvent;
            import java.beans.PropertyChangeListener;
            
            import javax.swing.JFrame;
            import javax.swing.JPanel;
            import javax.swing.JScrollBar;
            import javax.swing.JScrollPane;
            import javax.swing.JDesktopPane;
            import javax.swing.JInternalFrame;
            import javax.swing.JSplitPane;
            
            public class SimpleJSplitPaneTest {
                 public static void main(String[] a_args) {
                      EventQueue.invokeLater(new Runnable() {
                           public void run() {
                                new SimpleJSplitPaneTest();
                           }
                      });}
            
                 SimpleJSplitPaneTest() {
                      final JFrame f_mainFrame = new JFrame( "Simple JSplitPane test");
                      f_mainFrame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
                      f_mainFrame.setSize(800, 400);
                      JDesktopPane desktopPane = new JDesktopPane();
                      f_mainFrame.getContentPane().add(desktopPane, BorderLayout.CENTER );
                      desktopPane.setBackground( Color.blue );
                      final JInternalFrame f_internalFrame = new JInternalFrame("New JInternalFrame", true, true, true, true );
                      f_internalFrame.setResizable(true);
                      f_internalFrame.setBounds(0, 0, 450, 250 );
                      desktopPane.add(f_internalFrame);
                      JScrollPane mainScrollPane = new JScrollPane();
                      f_internalFrame.getContentPane().add(mainScrollPane, BorderLayout.CENTER );
                      
                      // rh panel of main split pane
                      final JPanel f_rHPanel = new JPanel();
                      
                      // main split pane
                      final JSplitPane f_mainSplitPane = new JSplitPane(){
                           public boolean isValidateRoot(){
                                boolean result = super.isValidateRoot();
                                p( "== isValidateRoot - result: " + result );
                                return result;
                           }
                           
                           public void resetToPreferredSizes(){
                                p( "== resetToPreferredSizes" );
                                super.resetToPreferredSizes();
                           }
                           
                           public void doLayout(){
                                int lastDivLoc = this.getLastDividerLocation();
                                int presDivLoc = this.getDividerLocation();
                                int diff = presDivLoc - lastDivLoc;
                                p( "== diff " + diff );
                                if( diff != -1 ){
                                     Dimension mainPrefSize = this.getPreferredSize();
                                     Dimension mainSize = this.getSize();
            //                         this.setPreferredSize( new Dimension( mainPrefSize.width + diff, mainPrefSize.height ));
            // THIS DOESN'T WORK:
            //                         this.setSize( new Dimension( mainSize.width + diff, mainSize.height ));
                                     
                                     
                                     Dimension rHPrefSize = f_rHPanel.getPreferredSize();
                                     Dimension rHSize = f_rHPanel.getSize();
            //                         f_rHPanel.setPreferredSize( new Dimension( rHPrefSize.width + diff, rHPrefSize.height ));
            // THIS DOESN'T WORK:
            //  Whoops! Of course it's the LEFT panel which needs to have its "pref size" or "size" altered after a divider
            // movement... but that doesn't work either... thing is, I have been experimenting with code quite a bit... best thing 
            // is to ignore this "remnant" code ... 
            //                         f_rHPanel.setSize( new Dimension( rHSize.width + diff, rHSize.height ));
                                }
                                p( "== doLayout" );
                                super.doLayout();
                           }
                           
                           public void validate(){
                                p( "== validate" );
                                super.validate();
                           }
                      };
                      mainScrollPane.setViewportView(f_mainSplitPane);
                      // lh panel of main split pane
                      final JPanel f_lHPanel = new JPanel();
                      f_lHPanel.setBackground( Color.red );
                      f_mainSplitPane.setLeftComponent(f_lHPanel);
                      f_lHPanel.setPreferredSize( new Dimension( 300, 200 ));
                      f_lHPanel.setSize( new Dimension( 300, 200 ));
                      f_mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
                           @Override
                           public void propertyChange(PropertyChangeEvent pce) {
                                System.out.println( "PROP CHANGE: main div changed");
                                int lastDivLoc = f_mainSplitPane.getLastDividerLocation();
                                int presDivLoc = f_mainSplitPane.getDividerLocation();
                                int diff = presDivLoc - lastDivLoc;
            
            // NOTHING HERE SEEMED TO WORK
                                
                                printSizes( "  f_lHPanel", f_lHPanel );
                                printSizes( "  f_rHPanel", f_rHPanel );
                                printSizes( "  f_mainSplitPane", f_mainSplitPane );
                                
                      }});
                      
                      
                      f_rHPanel.addComponentListener( new ComponentAdapter(){
                      public void componentResized(ComponentEvent e){
                           System.out.println( "RHPanel resized" );
                           int lastDivLoc = f_mainSplitPane.getLastDividerLocation();
                           int presDivLoc = f_mainSplitPane.getDividerLocation();
                           int diff = presDivLoc - lastDivLoc;
                           p( "  diff: " + diff );
                           Dimension mainPrefSize = f_mainSplitPane.getPreferredSize();
                           Dimension mainSize = f_mainSplitPane.getSize();
            //               f_mainSplitPane.setPreferredSize( new Dimension( mainPrefSize.width + diff, mainPrefSize.height ));
            // THIS DOESN'T WORK:
            //               f_mainSplitPane.setSize( new Dimension( mainSize.width + diff, mainSize.height ));
                           
                           printSizes( "  f_lHPanel", f_lHPanel );
                           printSizes( "  f_rHPanel", f_rHPanel );
                           printSizes( "  f_mainSplitPane", f_mainSplitPane );
                      }});
                      
                      f_mainSplitPane.setRightComponent(f_rHPanel);
                      f_rHPanel.setPreferredSize( new Dimension( 500, 200 ));
                      f_rHPanel.setSize( new Dimension( 500, 200 ));
                      f_internalFrame.setVisible(true);
                      f_mainFrame.setVisible(true);
                 }
                 
                 public static void printSizes( String name, Component comp ){
                      System.out.println( name + ": size: " + comp.getSize() + "; pref size: " + comp.getPreferredSize() );
                 }
            
                 private static void p(String s) {
                      System.out.println(s);
                 }
                 
                 
            }
            as the main JSplitPane's divider is dragged right or left, I have found no way of keeping the main JSplitPane's RH component (JPanel) the same size... the problem is that if you, for example, find out the pixel change of the divider, and alter the size of main JSplitPane accordingly, it appears that a "do layout" event is called...

            I've tried quite a few permutations and various listeners... either I get an endless loop of events calling events, or the divider simply snaps back to where it was... or the "printSizes" print-outs show that things are not the sizes they appear (which of course simply means that a subsequent event occurs which changes the sizes)...

            And, as I said, I don't know how to subclass BasicSplitPaneUI.BasicHorizontalLayoutManager
            • 3. Re: JSplitPane keeps right-hand component same size
              Darryl Burke
              It looks to me like you're overthinking this one. This meets your requirement. The paintComponent override is just so you can more easily observe the size of the right panel. There was a 1-pixel discrepancy I couldn't track down so I included it as a magic number.

              Note: tested only with the default MetalLookAndFeel.
              import java.awt.Color;
              import java.awt.Dimension;
              import java.awt.Graphics;
              import java.beans.PropertyChangeEvent;
              import java.beans.PropertyChangeListener;
              import javax.swing.*;
              
              public class FixedRightSplitPane {
              
                public static void main(String[] args) throws Exception {
                  SwingUtilities.invokeLater(new Runnable() {
              
                    @Override
                    public void run() {
                      new FixedRightSplitPane().makeUI();
                    }
                  });
                }
              
                public void makeUI() {
                  JPanel left = new JPanel();
                  left.setPreferredSize(new Dimension(300, 300));
                  final JPanel right = new JPanel() {
              
                    @Override
                    protected void paintComponent(Graphics g) {
                      super.paintComponent(g);
                      g.setColor(Color.RED);
                      g.drawLine(299, 0, 299, getHeight());
                    }
                  };
                  right.setPreferredSize(new Dimension(300, 300));
                  final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
                  final JScrollPane scrollPane = new JScrollPane(splitPane);
                  JInternalFrame internalFrame = new JInternalFrame("Internal Frame", true, true, true, true);
                  internalFrame.add(scrollPane);
                  internalFrame.pack();
                  internalFrame.setVisible(true);
                  JDesktopPane desktopPane = new JDesktopPane();
                  desktopPane.add(internalFrame);
              
                  splitPane.addPropertyChangeListener("dividerLocation", new PropertyChangeListener() {
              
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                      Dimension d = splitPane.getPreferredSize();
                      d.width = (Integer) evt.getNewValue()
                              + splitPane.getDividerSize()
                              + right.getPreferredSize().width
                              + 1; // magic number
                      splitPane.setPreferredSize(d);
                      scrollPane.revalidate();
                    }
                  });
              
                  JFrame frame = new JFrame();
                  frame.add(desktopPane);
              
                  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                  frame.setSize(800, 400);
                  frame.setLocationRelativeTo(null);
                  frame.setVisible(true);
                }
              }
              db
              • 4. Re: JSplitPane keeps right-hand component same size
                829050
                Thanks very much!

                Might the magic number might be due to insets?... i.e. border .left + .right of one of the JPanels or of the JSplitPane or of the JScrollPane?

                I briefly did a few experiments with thick borders but nothing conclusive came up... I'm not sure how you worked out that you needed this magic number... I tried without it and visually it looked OK... were you measuring "getSize()" or something?
                • 5. Re: JSplitPane keeps right-hand component same size
                  Darryl Burke
                  how you worked out that you needed this magic number... I tried without it and visually it looked OK... were you measuring "getSize()" or something?
                  I already told you what the paintComponent(...) override was for.

                  Try my code with and without the + 1

                  db
                  • 6. Re: JSplitPane keeps right-hand component same size
                    829050
                    yes, OK, understand... disappears off to the right

                    with a thicker border (e.g. LineBorder) given to splitPane, 1 is not enough, according to my tests...

                    I humbly suggest replacing your code line with the following (though the culprit might be insets.left for all I know).
                            d.width = (Integer) evt.getNewValue()
                                + splitPane.getDividerSize()
                                + right.getPreferredSize().width
                    //            + 1 // magic number
                                + splitPane.getInsets().right;