2

I'm trying to implement a quite simple UI using SpringLayout (partly because I, as opposed to most tutorial writers I find on the net, quite like the coding interface compared to other layout managers and partly because I want to learn how to use it). The UI basically looks like this:

UI template

This is all well. The UI resizes the way I want (keeping the welcome text centered and expanding the text area to fill all the new available space) if I increase the window size. However, below a certain point (more specifically when the window becomes too narrow for the welcome text):

UI when shrunk

I would like the window to not allow further shrinking, so that if the user tries to shrink the window to a size smaller than enough to house the components, it simply stops. How do I accomplish this, using the SpringLayout layout manager?

I know I could probably do this by handling some resize-event and checking if the minimum size is reach, and then just set the size to the minimum size. But this requires me to a) know, or know how to calculate, the minimum size of the window, even before it renders, b) write a bunch of event-handling code just to get some UI rendering right, and c) write a bunch of code for things that I expect a good layout manager to take care of ;)

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402

2 Answers2

3
  1. you can override MinimumSize for TopLevelContainer

  2. you have put JTextArea to the JScrollPane

  3. easiest way is mixing LayoutManagers (called as NestedLayout) by spliting GUI to the parts (separated JPanels with same or different LayoutManager), rather than implements some most sofisticated LayoutManager (GridBagLayout or SpringLayout) for whole Container

  4. some LayoutManagers pretty ignore setXxxSize

  5. SpringLayout isn't my cup of Java

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

public class MinSizeForContainer {

    private JFrame frame = new JFrame("some frame title");

    public MinSizeForContainer() {
        JTextArea textArea = new JTextArea(15, 30);
        JScrollPane scrollPane = new JScrollPane(textArea);

        CustomJPanel fatherPanel = new CustomJPanel();
        fatherPanel.setLayout(new SpringLayout());

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(fatherPanel, BorderLayout.CENTER);
        frame.setLocation(20, 20);
        frame.setMinimumSize(fatherPanel.getMinimumSize());
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                MinSizeForContainer Mpgp = new MinSizeForContainer();
            }
        });
    }
}

class CustomJPanel extends JPanel {

    private static final long serialVersionUID = 1L;

    @Override
    public Dimension getMinimumSize() {
        return new Dimension(400, 400);
    }
}
msrd0
  • 7,816
  • 9
  • 47
  • 82
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • See, the problem here is that the minimum size is ultimately hard-coded. I want to avoid that entirely, so I can later decide to add another UI element (say, a long but narrow list on the right) and not have to rewrite any of that in my code. – Tomas Aschan Jan 03 '12 at 23:40
  • +1 `TextLayout`, shown [here](http://stackoverflow.com/a/8282330/230513), is a way to get the minimum bounds, but the validated preferred size should be good, too. – trashgod Jan 04 '12 at 03:12
  • hard-coding sizes in getXXSize is nearly as bad as using setXXSize ;-) – kleopatra Jan 05 '12 at 12:45
  • forgot: +1 for detecting the size guaranteeing (as far as the OS allows) contract of window.setMinimumSize - wasn't aware of it until yesterday :-) – kleopatra Jan 06 '12 at 09:59
  • @kleopatra one kind lesson by (@Andrew Thompson) – mKorbel Jan 06 '12 at 10:39
3

There are several issues to achieve a "real" (that is not shrinkable beyond) min size:

  • the child components must return some reasonable (based on their content) min size, many core components don't
  • the layoutManager must respect the compounded min of all children, no matter how little space is available
  • the top-level container (here the JFrame) must not allow shrinking beyond the min

The first is true for a JLabel, the second is met for SpringLayout (that's why the label is truncated) - which leaves the third as the underlying problem, the solution to which isn't obvious, actually I wasn't aware it's even possible before running @mKorbel's example. The relevant line indeed is

frame.setMinimumSize(someSize);

With that line in place, it's not possible to shrink the frame below. Without, it is. Starting from that observation, some digging turns out the doc for its override in Window

Sets the minimum size of this window to a constant value. [..] If current window's size is less than minimumSize the size of the window is automatically enlarged to honor the minimum size. If the setSize or setBounds methods are called afterwards with a width or height less [...] is automatically enlarged to honor the minimumSize value. Resizing operation may be restricted if the user tries to resize window below the minimumSize value. This behaviour is platform-dependent.

Looking at the code, there are two (implementation, don't rely on them :-) details related to the min size

 Dimension minSize;
 boolean minSizeSet;

and public api to access

 public Dimension getMinimumSize()
 public boolean isMininumSizeSet()

the first rather oldish (jdk1.1), the latter rather newish (jdk1.5) - implying that the first can't rely on the latter but internally has to check for a null minSize. The overridden sizing methods (with their guarantee to doing their best to respect a manually set minSize) on Window are the latest (jdk6) and do rely on the latter. Or in other words: overriding isMinimumSizeSet does the trick.

Some code snippet (beware: it's a hack, untested, might well be OS dependent with undesirable side-effects!):

    // JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame("some frame title") {

        /**
         * Overridden to tricks sizing to respect the min.
         */
        @Override
        public boolean isMinimumSizeSet() {
            return true; //super.isMinimumSizeSet();
        }

        /**
         * Overridden to adjust for insets if tricksing and not using 
         * LAF decorations.
         */
        @Override
        public Dimension getMinimumSize() {
            Dimension dim = super.getMinimumSize();
            // adjust for insets if we are faking the isMinSet
            if (!super.isMinimumSizeSet() && !isDefaultLookAndFeelDecorated()) {
               Insets insets = getInsets();
               dim.width += insets.left + insets.right;
               dim.height += insets.bottom + insets.top;
            }
            return dim;
        }



    };
    // add a component which reports a content-related min
    JLabel label = new JLabel("Welcome to my application!");
    // make it a big min
    label.setFont(label.getFont().deriveFont(40f));
    frame.add(label); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
kleopatra
  • 51,061
  • 28
  • 99
  • 211