swingwindow-resizemiglayout

Nested MigLayouts stretch out of bounds when restoring from fullscreen


I have a non-trivial Swing app running on Windows that has an issue with window resizing: when I maximize the window and then restore it, the main panel doesn't resize properly with the window. The panel stays between the fullscreen size and the current size until I drag the window border around and force a few more resize attempts. I found that the minimal repro case is close to the following:

JAVA

        JFrame mainWindow = new JFrame("TEST");
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
        JPanel outerPanel = new JPanel();
        outerPanel.setLayout(new MigLayout("fill, insets 0", "[grow]", "[grow]"));
        
        JPanel innerPanel = new JPanel();
        innerPanel.setLayout(new MigLayout("fill, insets 10", "[75%:75%:100%]", "[grow]"));
        JTextArea text = new JTextArea("text 1");
        innerPanel.add(text, "cell 0 0, grow");
        
        outerPanel.add(innerPanel, "cell 0 0, grow");
        
        mainWindow.getContentPane().add(outerPanel);
    
        mainWindow.pack();
        mainWindow.setLocationRelativeTo(null);
        mainWindow.setVisible(true);

If I put ComponentListeners on text, innerPanel, and outerPanel here and print out each component's width on resize, I can see that outerPanel resizes properly, but innerPanel and text both only shrink by an amount proportionate to how far they are from the correct size each update. This seems to be caused by the minimum-size column constraint on innerPanel being set to a value such that the minimum width when in fullscreen is larger than the maximum width of the window when not in fullscreen. When I run this code, I get a 120px-wide window that maximizes to 2560px; it doesn't repro if the minimum-size constraint is 1% (25.6px when maximized), but it does if the minimum-size is 5% (128px).

My question is, why? Why does the resize not initially work properly but eventually converge to the right value if I make enough incremental changes to the stretched dimension? And how can I get my minimum-size constraints to work when maximizing and restoring the window?

For the places in my actual program that are exhibiting the problem, there's a RecordingListPanel inside of a JTabbedPane inside of the mainPanel JPanel in MainWindow inside of the metaPanel JPanel in MainWindow. Here are the relevant excerpts:

JAVA

// RecordingListPanel
setLayout(new MigLayout("fill, insets 2", "[75%:75%:90%]2[10%::300]", "[][grow]"));

//tabbedPane
tabbedPane.add("Recordings", recordingListPanel);

// mainPanel
mainLayout = new CardLayout();
mainPanel.setLayout(mainLayout);
mainPanel.add(tabbedPane, "mainView");

// metaPanel
metaPanel.setLayout(new MigLayout("fill, insets 0 0 0 0", "[grow]", "[grow][30!]"));
metaPanel.add(mainPanel, "cell 0 0, grow");
mainFrame.getContentPane().add(metaPanel);

Solution

  • I didn't find the root cause, but I was able to work around the issue. I added a ComponentListener to the inner panel (mainPanel), which has a componentResized() method that checks if its current size is larger than the outer panel (metaPanel) and resizes again if it is.

    mainPanel.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e)
        {
            int width = mainPanel.getWidth();
            int height = mainPanel.getHeight();
            boolean shouldAdjust = false;
            if(mainPanel.getWidth() > metaPanel.getWidth())
            {
                width = metaPanel.getWidth();
                shouldAdjust = true;
            }
            if(mainPanel.getHeight() > metaPanel.getHeight())
            {
                height = metaPanel.getHeight() - 30;
                shouldAdjust = true;
            }
    
            if(shouldAdjust)
            {
                mainPanel.setSize(width, height);
                tabbedPane.setSize(width, height);
            }
        }
    });