1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.awt.Component;
  25 import java.awt.Dimension;
  26 import java.awt.EventQueue;
  27 import java.awt.FlowLayout;
  28 import java.awt.Font;
  29 import java.util.List;
  30 import java.util.Objects;
  31 import java.util.concurrent.Callable;
  32 
  33 import javax.swing.JButton;
  34 import javax.swing.JCheckBox;
  35 import javax.swing.JComboBox;
  36 import javax.swing.JEditorPane;
  37 import javax.swing.JFormattedTextField;
  38 import javax.swing.JFrame;
  39 import javax.swing.JLabel;
  40 import javax.swing.JList;
  41 import javax.swing.JMenu;
  42 import javax.swing.JMenuItem;
  43 import javax.swing.JRadioButton;
  44 import javax.swing.JScrollPane;
  45 import javax.swing.JSpinner;
  46 import javax.swing.JTable;
  47 import javax.swing.JTextArea;
  48 import javax.swing.JTextField;
  49 import javax.swing.JToolTip;
  50 import javax.swing.JTree;
  51 import javax.swing.SpinnerListModel;
  52 import javax.swing.SwingUtilities;
  53 import javax.swing.UIManager;
  54 import javax.swing.UnsupportedLookAndFeelException;
  55 import javax.swing.tree.DefaultMutableTreeNode;
  56 
  57 import static javax.swing.UIManager.getInstalledLookAndFeels;
  58 
  59 /**
  60  * @test
  61  * @key headful
  62  * @bug 8201552
  63  * @summary Initial layout of the component should use correct graphics config.
  64  *          It is checked by SwingUtilities.updateComponentTreeUI(), if layout
  65  *          was correct the call to updateComponentTreeUI() will be no-op.
  66  * @compile -encoding utf-8 StalePreferredSize.java
  67  * @run main/othervm/timeout=200 StalePreferredSize
  68  * @run main/othervm/timeout=200 -Dsun.java2d.uiScale=1 StalePreferredSize
  69  * @run main/othervm/timeout=200 -Dsun.java2d.uiScale=2.25 StalePreferredSize
  70  */
  71 public final class StalePreferredSize {
  72 
  73     // Some text to be tested
  74     static final String TEXT[] = new String[]{
  75             "<span>A few words to get started before the "
  76                     + "bug</span><span>overlapping text</span>",
  77             "A quick brown fox jumps over the lazy dog",
  78             "El veloz murciélago hindú comía feliz cardillo y kiwi. La cigüeña "
  79                     + "tocaba el saxofón detrás del palenque de paja",
  80             "Voix ambiguë d’un cœur qui au zéphyr préfère les jattes de kiwis",
  81             "다람쥐 헌 쳇바퀴에 타고파",
  82             "Съешь ещё этих мягких французских булок да выпей же чаю"};
  83 
  84     static JFrame frame;
  85     static Component component;
  86     static int typeFont = 0; // 0 - default, 1 - bold, 2 - italic
  87 
  88     public static void main(final String[] args) throws Exception {
  89         for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
  90             EventQueue.invokeAndWait(() -> setLookAndFeel(laf));
  91             for (typeFont = 1; typeFont < 3; typeFont++) {
  92                 System.err.println("typeFont = " + typeFont);
  93                 for (final boolean html : new boolean[]{true, false}) {
  94                     for (String text : TEXT) {
  95                         if (html) {
  96                             text = "<html>" + text + "</html>";
  97                         }
  98                         test(text);
  99                     }
 100                 }
 101             }
 102         }
 103     }
 104 
 105     private static void test(String text) throws Exception {
 106         System.err.println("text = " + text);
 107         // Each Callable create a component to be tested
 108         final List<Callable<Component>> comps = List.of(
 109                 () -> new JLabel(text),
 110                 () -> new JButton(text),
 111                 () -> new JMenuItem(text),
 112                 () -> new JMenu(text),
 113                 () -> new JList<>(new String[]{text}),
 114                 () -> new JComboBox<>(new String[]{text}),
 115                 () -> new JTextField(text),
 116                 () -> new JTextArea(text),
 117                 () -> new JCheckBox(text),
 118                 () -> new JFormattedTextField(text),
 119                 () -> new JRadioButton(text),
 120                 () -> new JTree(new DefaultMutableTreeNode(text)),
 121                 () -> new JSpinner(new SpinnerListModel(new String[]{text})),
 122                 () -> {
 123                     JToolTip tip = new JToolTip();
 124                     tip.setTipText(text);
 125                     return tip;
 126                     },
 127                 () -> {
 128                     JEditorPane pane = new JEditorPane();
 129                     pane.setText(text);
 130                     return pane;
 131                     },
 132                 () -> {
 133                     JTable table = new JTable(1, 1);
 134                     table.getModel().setValueAt(text, 0, 0);
 135                     return table;
 136                     }
 137         );
 138 
 139         for (final Callable<Component> creator : comps) {
 140             checkComponent(creator);
 141         }
 142     }
 143 
 144     static void checkComponent(Callable<Component> creator) throws Exception {
 145         EventQueue.invokeAndWait(() -> {
 146 
 147             try {
 148                 component = creator.call();
 149             } catch (Exception e) {
 150                 throw new RuntimeException(e);
 151             }
 152 
 153             Font font = component.getFont();
 154             if (typeFont == 1) {
 155                 component.setFont(new Font(font.deriveFont(Font.BOLD).getAttributes()));
 156             }
 157             if (typeFont == 2) {
 158                 component.setFont(new Font(font.deriveFont(Font.ITALIC).getAttributes()));
 159             }
 160 
 161             frame = new JFrame();
 162             frame.setLayout(new FlowLayout());
 163             frame.add(new JScrollPane(component));
 164             frame.setSize(300, 100);
 165             frame.setLocationRelativeTo(null);
 166             frame.setVisible(true);
 167         });
 168 
 169         EventQueue.invokeAndWait(() -> {
 170 
 171             // After the frame was shown we change nothing, so current layout
 172             // should be optimal and updateComponentTreeUI() should be no-op
 173             Dimension before = component.getPreferredSize();
 174             SwingUtilities.updateComponentTreeUI(frame);
 175             Dimension after = component.getPreferredSize();
 176 
 177             // We change the font size to some big value, as a result the
 178             // layout and preferredSize of the component should be changed
 179             component.setFont(component.getFont().deriveFont(35f));
 180             Dimension last = component.getPreferredSize();
 181 
 182             frame.dispose();
 183 
 184             if (!Objects.equals(before, after)) {
 185                 System.err.println("Component: " + component);
 186                 System.err.println("Before: " + before);
 187                 System.err.println("After: " + after);
 188                 throw new RuntimeException("Wrong PreferredSize");
 189             }
 190             // TODO JDK-8206024
 191 //            if (Objects.equals(after, last)) {
 192 //                System.err.println("Component: " + component);
 193 //                System.err.println("After: " + after);
 194 //                System.err.println("Last: " + last);
 195 //                throw new RuntimeException("Wrong PreferredSize");
 196 //            }
 197         });
 198     }
 199 
 200     private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) {
 201         try {
 202             UIManager.setLookAndFeel(laf.getClassName());
 203             System.err.println("LookAndFeel: " + laf.getClassName());
 204         } catch (final UnsupportedLookAndFeelException ignored) {
 205             System.err.println(
 206                     "Unsupported LookAndFeel: " + laf.getClassName());
 207         } catch (ClassNotFoundException | InstantiationException |
 208                 IllegalAccessException e) {
 209             throw new RuntimeException(e);
 210         }
 211     }
 212 }