Search code examples
javaswingjcomboboxlayout-managerboxlayout

Swing BoxLayout problem with JComboBox without using setXXXSize


here's an SSCCE:

import java.awt.Color;
import java.awt.Dimension;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class BoxLayoutTest extends JFrame {

    public BoxLayoutTest(){
        JPanel main = new JPanel();
        main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
        main.setBackground(Color.red);
        this.add(main);
        JPanel northPanel = new JPanel();

        JPanel middle = new JPanel();
        middle.setLayout(new BoxLayout(middle, BoxLayout.X_AXIS));
        middle.add(new JButton("FOO"));
        middle.add(Box.createHorizontalGlue());

        JPanel aPanel = new JPanel();
        aPanel.setBackground(Color.black);

            JComboBox b = new JComboBox();
    //b.setPreferredSize(new Dimension(100,16)); //uncomment this to see the layout I would like to achieve
    //b.setMaximumSize(new Dimension(100,16));
        //middle.add(b); //uncomment this line 

        middle.setBackground(Color.green);
        northPanel.setBackground(Color.blue);

        main.add(northPanel);
        main.add(middle);
        main.add(Box.createVerticalGlue());

        this.setSize(800,600);
        this.setResizable(true);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        new BoxLayoutTest();
    }

}

I'm trying to refactor some classes I wrote some time ago, when I didn't know that using setXXXSize methods on components is wrong. Using a resizable frame ,the result I want to achieve is the following:

  1. The northPanel should stay on top and change it's size accordingly to the frame size modifications (seems to work fine)
  2. The green panel where I put the JButton should keep the maximum dimension of the JButton and stay just below the blue panel above (this works fine if I only put JButtons inside that panel).

The problem arise if I put a JComboBox inside the green panel (try to uncomment the line in the SSCCE). I guess JComboBox hasn't a maximum size specified, so it stretches with the frame. In the previous wrong version of my code I was using setxxxSize methods on the JComboBox to limit it's dimension(try to uncomment the line on setXXXSize methods to see it).

My question are:

  1. Is it possible to achieve the same result using BoxLayout without invoking setXXXSize() methods?
  2. If yes, how?
  3. Is there any other LayoutManager that can I use to get that effect?

Please put me in the right direction


Solution

  • JComboBox is misbehaving (the same as JTextField) in reporting an unbounded max height: should never show more than a single line. Remedy is the same: subclass and return a reasonable height

            JComboBox b = new JComboBox() {
    
                /** 
                 * @inherited <p>
                 */
                @Override
                public Dimension getMaximumSize() {
                    Dimension max = super.getMaximumSize();
                    max.height = getPreferredSize().height;
                    return max;
                }
    
            };
    

    just for fun, here's a snippet using MigLayout (which is my personal favorite currently :-)

        // two panels as placeholders
        JPanel northPanel = new JPanel();
        northPanel.setBackground(Color.YELLOW);
        JPanel southPanel = new JPanel();
        southPanel.setBackground(Color.YELLOW);
        // layout with two content columns
        LC layoutContraints = new LC().wrapAfter(2)
                .debug(1000);
        AC columnContraints = new AC()
        // first column pref, followed by greedy gap
                .size("pref").gap("push")
                // second
                .size("pref");
        // three rows, top/bottom growing, middle pref
        AC rowContraints = new AC()
           .grow().gap().size("pref").gap().grow();
        MigLayout layout = new MigLayout(layoutContraints, columnContraints,
                rowContraints);
        JPanel main = new JPanel(layout);
        main.setBackground(Color.WHITE);
        // add top spanning columns and growing
        main.add(northPanel, "spanx, grow");
        main.add(new JButton("FOO"));
    
        // well-behaved combo: max height == pref height
        JComboBox combo = new JComboBox() {
    
            @Override
            public Dimension getMaximumSize() {
                Dimension max = super.getMaximumSize();
                max.height = getPreferredSize().height;
                return max;
            }
    
        };
        // set a prototype to keep it from constantly adjusting
        combo.setPrototypeDisplayValue("somethingaslongasIwant");
    
        main.add(combo);
        // add top spanning columns and growing
        main.add(southPanel, "spanx, grow");