3

I have a simple GUI using GridBagLayout with a button panel at the top, and a custom resizable component taking up the rest of the space, as shown in the following image:

The GUI at start-up.

The custom component (the red one) has a preferred size of (400, 300) and a minimum size of (40, 30) and is happy to be resized to any size greater than that. However, I would like my frame to respect the minimum size of the button panel and not allow the frame to be resized such that any of the buttons are not fully shown on the screen. This is not currently the behaviour as I can resize it far past those boundaries as seen here:

The GUI resized ridiculously small.

My current code is as follows:

import javax.swing.*;
import java.awt.*;

public class Example {
    public static void main(String[] args) {
        // Setup JFrame and GridBagLayout.
        JFrame frame = new JFrame("Example");
        Container contentPane = frame.getContentPane();
        GridBagLayout layout = new GridBagLayout();
        contentPane.setLayout(layout);
        layout.rowWeights = new double[] {0.0, 1.0};
        layout.columnWeights = new double[] {1.0};
        GridBagConstraints cons = new GridBagConstraints();

        // Add button panel with a BoxLayout.
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
        panel.add(new JButton("Button 1"));
        panel.add(new JButton("Button 2"));
        panel.add(new JButton("Button 3"));
        cons.anchor = GridBagConstraints.NORTHWEST;
        cons.gridx = 0;
        cons.gridy = 0;
        layout.setConstraints(panel, cons);
        contentPane.add(panel);

        // Add custom component, resizable.
        JComponent custom = new JComponent() {
            public Dimension getPreferredSize() {
                return new Dimension(400, 300);
            }

            public Dimension getMinimumSize() {
                return new Dimension(40, 30);
            }

            public void paintComponent(Graphics g) {
                g.setColor(Color.RED);
                g.fillRect(0, 0, getWidth(), getHeight());
            }
        };
        cons.gridx = 0;
        cons.gridy = 1;
        cons.fill = GridBagConstraints.BOTH;
        layout.setConstraints(custom, cons);
        contentPane.add(custom);

        // Pack and show frame.
        frame.pack();
        frame.setVisible(true);
    }
}

I have tested this on both Mac OS X 10.8 (Java 6) and Ubuntu 3.2.8 (Java 6) and observed the same thing.

How can I prevent the frame from being resized to cover any of the buttons? More generally, how can I get GridBagLayout to actually respect the minimum sizes of my components? When I print out the minimum size of my frame I get (291, 81), which is exactly what I want, but when I resize the frame, it goes beyond that.

Note: I have looked at this related question but it doesn't appear to answer my question.

Community
  • 1
  • 1
DanielGibbs
  • 9,910
  • 11
  • 76
  • 121
  • Try setting the frame's minimum size – MadProgrammer Oct 22 '13 at 01:33
  • What to? I don't know the dimensions of the minimum size. It should be the size of the button panel container. And in my actual problem (given that this is a SSCCE), the overall minimum size would be a rather complicated calculation of multiple components. Ideally I want `GridBagLayout` to respect the minimum size for each component. – DanielGibbs Oct 22 '13 at 01:38
  • Take the preferred size of the button panel as starting point and see where it takes you... – MadProgrammer Oct 22 '13 at 01:39
  • That restricts the width, but not the height. And even then, it's not respecting the minimum size of the custom component either. I'm looking for a general solution where minimum sizes are properly respected. – DanielGibbs Oct 22 '13 at 01:43
  • Additionally, `frame.getMinimumSize()` returns the correct minimum size without having to do anything. However this minimum size does not seem to be enforced when resizing the frame. – DanielGibbs Oct 22 '13 at 01:49
  • I don't know why, but if I use `frame.setMinimumSize(frame.getContentPane().getMinimumSize());` it seems to work – MadProgrammer Oct 22 '13 at 02:09
  • Out of interest, looking at this [Bug Report](http://bugs.sun.com/view_bug.do?bug_id=6464548) Does setting the max and min size result in the reported behavior? – Java Devil Oct 22 '13 at 02:17
  • @JavaDevil I experience the same behaviour as in the bug report. – DanielGibbs Oct 22 '13 at 02:23
  • Thanks, looks like you found a solution anyway. – Java Devil Oct 22 '13 at 02:33

3 Answers3

4

I'm not sure why, but if I use...

frame.setMinimumSize(frame.getMinimumSize());

After the UI has being created (obviously), it works.

I "think" it has something to do with ((WindowPeer)peer).updateMinimumSize(); in the setMinimumSize size method of Window...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Bingo! Exactly what I'm looking for. Very odd that this is what causes what I'd expect to be the default behaviour though. – DanielGibbs Oct 22 '13 at 02:17
  • 1
    The problem (I think), is the fact that the layout manager isn't applied to the window at all, but either to the content pane or it's content. It may also be a restriction of the underlying framework (peer) that it doesn't probe the parent window for size restriction information, but instead uses what ever values have previously being applied...but I'd be guessing. – MadProgrammer Oct 22 '13 at 02:19
2

If the minimum size is set on your frame then you can add a ComponentListener to the frame and implement the componentResized method.

Like this example

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
public class Test {
    public static void main(String[] args) 
    {
        final JFrame frame = new JFrame("My Frame");
        frame.setMinimumSize(new Dimension(200,200));
        frame.addComponentListener( new ComponentAdapter()
        {
            public void componentResized(ComponentEvent evt)
            {
                Dimension size = frame.getSize();
                Dimension min = frame.getMinimumSize();
                if (size.getWidth() < min.getWidth())
                {
                    frame.setSize((int)min.getWidth() ,(int) size.getHeight());
                }
                if (size.getHeight() < min.getHeight())
                {
                    frame.setSize((int)size.getWidth() ,(int) min.getHeight());
                }
            }
        });
        frame.setSize(300,300);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
Java Devil
  • 10,629
  • 7
  • 33
  • 48
  • This is a partial solution, but closest so far. It looks a bit ugly because you can resize the frame too small, but it will jump back to the minimum size when you let go of the mouse. Ideally I would be able to prevent resizing smaller than the minimum size completely. – DanielGibbs Oct 22 '13 at 02:06
-1

I often have this sort of trouble with GridBagConstraints defaults: I long ago got in the habit of setting them (weightx, gridwidth, etc.) all explicitly. That usually gets GridBagLayout to respect minimum sizes for me. If anyone knows which default I'm overriding to achieve this, I'm curious :-)