Search code examples
javaswingawtjdialog

Enforcing an implicit minimum size on a JDialog?


Question

I have an application-modal JDialog which I want to have an enforced minimum size (i.e. the OS prevents the user from making the window any smaller than the minimum size). But the window should still be resizable (i.e. setResizable(false) is not an option). Preferably, the enforced minimum size should be implicitly defined (i.e. dynamically determined by the minimum sizes of the child components, and not a fixed size set explicitly using setMinimumSize)

The problem I've run into is that it seems if I don't explicitly set a minimum size on the JDialog via setMinimumSize, the OS will not enforce it. So even though the JDialog implicitly knows the correct minimum size to use at any given time, that size won't be enforced unless I call something like dlg.setMinimumSize(dlg.getMinimumSize()). See the SSCCE below.

In many cases, explicitly setting the minimum size just before opening the dialog is sufficient. However, sometimes I have a situation where the implicit minimum size can change while the dialog is open, e.g. a child component may be dynamically added or removed from the dialog. And if I set an explicit minimum size on the dialog, that size may not be sufficiently large for the new layout.

Is there any way to configure a JDialog to enforce the implicit minimum size of the dialog? If not, is there some other elegant way to enforce a dynamically changing minimum size on an open dialog?

SSCCE

In this SSCCE, the goal is to create a dialog that cannot be made any smaller than 300x300 pixels. This goal is only met if the minimum size is explicitly set.

public class SSCCE {
    public static void main(String[] args) {
        JDialog dlg = new JDialog(null, ModalityType.APPLICATION_MODAL);
        dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

        JPanel pnl = new JPanel(new BorderLayout());
        pnl.add(new JLabel("300x300"), BorderLayout.CENTER);

        // The panel is given a hard-coded minimum size just to make the
        // problem easier to see
        pnl.setMinimumSize(new Dimension(300,300));

        dlg.add(pnl);

        System.out.println("Dialog minimum size: " + dlg.getMinimumSize());
        // Minimum size is not enforced by the OS without this call
        dlg.setMinimumSize(dlg.getMinimumSize());
        System.out.println("Dialog minimum size: " + dlg.getMinimumSize());

        dlg.pack();
        dlg.setVisible(true);
    }
}

With explicit setMinimumSize call:

Properly sized 300x300 JDialog

Without explicit setMinimumSize call:

Improperly sized JDialog

Output in both cases:

Dialog minimum size: java.awt.Dimension[width=300,height=300]
Dialog minimum size: java.awt.Dimension[width=300,height=300]

For what it's worth - I observed this on Windows 8.1 with jdk1.8.0_91.


Solution

  • However, this is less than ideal because it causes the minimum dialog size to be fixed

    Yes you should not be hardcoding values.

    You could override the getMinimumSize() method of your panel to:

    1. just return the prefeferred size, or
    2. return some ratio of the preferred size.

    Edit;

    is there some other elegant way to allow for a dynamically changing minimum size on an open dialog?

    You could add an AncestorListener to your panel. The ancestorAdded event is generated when the panel is added to a visible window (so basically the event is generated after the setVisible() on the dialog). Then you can use the SwingUtilities.windowForComponent(...) method to get the window for the panel. Then you can invoked the setMinimumSize() on the dialog.