JDK-7027013 : Regression: JComponent.revalidate() has no effect on invisible components
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2011-03-12
  • Updated: 2011-05-25
  • Resolved: 2011-05-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b140Fixed
Related Reports
Relates :  
Description
SYNOPSIS
--------
Regression: JComponent.revalidate() has no effect on invisible components

JDK VERSION
-----------
JDK 7 b77 and later (does not occur with b76 or earlier)
Does not occur with 5.0 or 6.

DESCRIPTION
-----------
Calls to JComponent.revalidate() have no effect on components
that are not showing at the moment of this call but will become visible
before the end of the current AWT event handling.

In all releases of 5.0 and 6, a revalidate() call has an effect if the Swing component has become visible by the time the delayed revalidation is executed, so this seems to be a regression (although the specification for revalidate() is not completely clear on the correct behaviour).

REPRODUCTION INSTRUCTIONS
-------------------------
1. Compile and run the attached test case.
2. Press the "Show/hide dialog" button. A dialog appears.
3. Close the dialog, either by pressing the button again or through the close icon.
4. Press the "Show/hide dialog" button again. The dialog reappears.

With 5.0 and 6 the dialog is painted correctly, but in JDK 7 it is incomplete. Only after resizing or typing some characters in the text field does it repaint itself correctly.

WORKAROUND
----------
Make any invisible components visible by calling setVisible(true) immediately before calling revalidate().

TESTCASE
--------
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class TestJDK7RevalidateBug {

    static final boolean WORKAROUND = false;

    static class MyDialog extends JDialog {
        JPanel mainPanel;
        JLabel title;
        int counter;
        Box inputPanel;
        MyDialog() {
            mainPanel = new JPanel();
            mainPanel.setLayout(new BorderLayout());
            title = new JLabel();
            mainPanel.add(title, BorderLayout.NORTH);
            getContentPane().add(mainPanel);
            setSize(150, 150);
        }
        void showMe() {
            inputPanel = new Box(BoxLayout.PAGE_AXIS);
            inputPanel.add(new JLabel("Please type something here:"));
            inputPanel.add(new JTextArea());
            mainPanel.add(inputPanel);
            counter++;
            title.setText("Dialog #"+counter);
            if (WORKAROUND)
                setVisible(true);
            mainPanel.revalidate();
            mainPanel.repaint();
            if (!WORKAROUND)
                setVisible(true);
        }
        @Override
        public void setVisible(boolean visible) {
            super.setVisible(visible);
            if (!visible) {
                if (inputPanel != null) {
                    mainPanel.remove(inputPanel);
                    inputPanel = null;
                }
            }
        }
    }

    static class MainFrame extends JFrame {
        MyDialog dialog;
        MainFrame() {
            dialog = new MyDialog();
            final JButton button = new JButton();
            button.addActionListener(
                                    new ActionListener() {
                                        public void actionPerformed(ActionEvent event) {
                                            if (dialog.isShowing()) {
                                                dialog.setVisible(false);
                                            } else {
                                                dialog.showMe();
                                            }
                                        }
                                    });
            button.setText("Show/hide dialog");
            getContentPane().add(button);
            setSize(200, 150);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(
                                  new Runnable() {
                                      public void run() {
                                          MainFrame frame = new MainFrame();
                                          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                                          frame.setVisible(true);
                                      }
                                  });
    }
}

Comments
SUGGESTED FIX --- old/src/share/classes/java/awt/Dialog.java 2011-04-06 18:22:07.000000000 +0400 +++ new/src/share/classes/java/awt/Dialog.java 2011-04-06 18:22:06.000000000 +0400 @@ -903,7 +903,7 @@ if (peer == null) { addNotify(); } - validate(); + validateUnconditionally(); if (visible) { toFront(); retval = false; --- /dev/null 2011-04-04 22:09:25.851006711 +0400 +++ new/test/java/awt/Dialog/ValidateOnShow/ValidateOnShow.java 2011-04-06 18:22:08.000000000 +0400 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 7027013 + @summary Dialog.show() should validate the window unconditionally + @author ###@###.###: area=awt.toplevel + @run main ValidateOnShow +*/ + +import java.awt.*; + +public class ValidateOnShow { + private static Dialog dialog = new Dialog((Frame)null); + private static Panel panel = new Panel() { + @Override + public boolean isValidateRoot() { + return true; + } + }; + private static Button button = new Button("Test"); + + private static void sleep() { + try { Thread.sleep(500); } catch (Exception e) {} + } + + private static void test() { + System.out.println("Before showing: panel.isValid=" + panel.isValid() + " dialog.isValid=" + dialog.isValid()); + dialog.setVisible(true); + sleep(); + System.out.println("After showing: panel.isValid=" + panel.isValid() + " dialog.isValid=" + dialog.isValid()); + + if (!panel.isValid()) { + dialog.dispose(); + throw new RuntimeException("The panel hasn't been validated upon showing the dialog"); + } + + dialog.setVisible(false); + sleep(); + } + + public static void main(String[] args) { + // setup + dialog.add(panel); + panel.add(button); + + dialog.setBounds(200, 200, 300, 200); + + // The first test should always succeed since the dialog is invalid initially + test(); + + // now invalidate the button and the panel + button.setBounds(1, 1, 30, 30); + sleep(); + // since the panel is a validate root, the dialog is still valid + + // w/o a fix this would fail + test(); + + // cleanup + dialog.dispose(); + } +}
06-04-2011

EVALUATION The validateUnconditionally() call is present in the Window.show() method, however, it's absent in the Dialog.show(). Therefore, windows get unconditionally validated upon showing, but dialogs don't. The fix is as simple as adding the call to the show() method of the Dialog class.
06-04-2011

EVALUATION A top level window must unconditionally validate its content when it is shown, for some reason it doesn't happen when the dialog is shown after the first time. Assigned to AWT
14-03-2011