1 /* 2 * Copyright (c) 2017, 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.Color; 25 import java.awt.FlowLayout; 26 import java.awt.Font; 27 import java.awt.Frame; 28 import java.awt.Graphics; 29 import java.awt.Graphics2D; 30 import java.awt.GridBagConstraints; 31 import java.awt.GridBagLayout; 32 import java.awt.HeadlessException; 33 import java.awt.Image; 34 import java.awt.Insets; 35 import java.awt.Panel; 36 import java.awt.event.ActionEvent; 37 import java.awt.event.ActionListener; 38 import java.awt.event.WindowAdapter; 39 import java.awt.event.WindowEvent; 40 import java.awt.geom.AffineTransform; 41 import java.awt.image.AbstractMultiResolutionImage; 42 import java.awt.image.BufferedImage; 43 import java.awt.image.ImageObserver; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.concurrent.CountDownLatch; 48 import java.util.concurrent.TimeUnit; 49 import javax.swing.JButton; 50 import javax.swing.JFrame; 51 import javax.swing.JPanel; 52 import javax.swing.JTextArea; 53 import javax.swing.SwingUtilities; 54 55 /* @test 56 * @bug 8147440 8147016 57 * @summary HiDPI (Windows): Swing components have incorrect sizes after 58 * changing display resolution 59 * @author Alexander Scherbatiy 60 * @run main/manual/othervm WindowResizingOnMovingToAnotherDisplay 61 */ 62 public class WindowResizingOnMovingToAnotherDisplay { 63 64 private static volatile boolean testResult = false; 65 private static volatile CountDownLatch countDownLatch; 66 private static TestFrame frame; 67 private static JFrame mainFrame; 68 69 private static final String INSTRUCTIONS = "INSTRUCTIONS:\n" 70 + "Verify that a window is properly resized after moving to a display" 71 + " with different DPI.\n" 72 + "\n" 73 + "The test is applicable for a multi-monitor system where displays" 74 + " are configured to have different DPI\n" 75 + "\n" 76 + "1. Press Show Frame button\n" 77 + "The frame appear.\n" 78 + "2. Check that the string \"scales [ScaleX, ScaleY]\" is painted on the window" 79 + " where ScaleX and ScaleY are the scales for current display.\n" 80 + "The scales are calculated as DPI / 96 and are 1 for the DPI value 96" 81 + " and 2 for the DPI value 192.\n" 82 + "3. Move the frame to the second display.\n" 83 + "4. Check that the string \"scales [ScaleX, ScaleY]\" is updated" 84 + " to show the right display scales.\n" 85 + "5. Check that the window is properly resized.\n" 86 + "6. Check that the window is properly repainted and does not contain drawing artifacts\n" 87 + "Try different display positions (left, right, top, bottom).\n" 88 + "If all tests are passed, press PASS, else press FAIL.\n"; 89 90 public static void main(String args[]) throws Exception { 91 92 countDownLatch = new CountDownLatch(1); 93 SwingUtilities.invokeLater(WindowResizingOnMovingToAnotherDisplay::createUI); 94 countDownLatch.await(15, TimeUnit.MINUTES); 95 if (!testResult) { 96 throw new RuntimeException("Test fails!"); 97 } 98 } 99 100 private static void createUI() { 101 102 mainFrame = new JFrame("DPI change test"); 103 GridBagLayout layout = new GridBagLayout(); 104 JPanel mainControlPanel = new JPanel(layout); 105 JPanel resultButtonPanel = new JPanel(layout); 106 107 GridBagConstraints gbc = new GridBagConstraints(); 108 109 JPanel testPanel = new JPanel(new FlowLayout()); 110 JButton frameButton = new JButton("Show Frame"); 111 frameButton.addActionListener((e) -> { 112 int x = 20; 113 int y = 10; 114 int w = 400; 115 int h = 300; 116 117 frame = new TestFrame(w, h); 118 frame.setLocation(x, y); 119 frame.setVisible(true); 120 121 }); 122 testPanel.add(frameButton); 123 124 gbc.gridx = 0; 125 gbc.gridy = 0; 126 gbc.fill = GridBagConstraints.HORIZONTAL; 127 mainControlPanel.add(testPanel, gbc); 128 129 JTextArea instructionTextArea = new JTextArea(); 130 instructionTextArea.setText(INSTRUCTIONS); 131 instructionTextArea.setEditable(false); 132 instructionTextArea.setBackground(Color.white); 133 134 gbc.gridx = 0; 135 gbc.gridy = 1; 136 gbc.fill = GridBagConstraints.HORIZONTAL; 137 mainControlPanel.add(instructionTextArea, gbc); 138 139 JButton passButton = new JButton("Pass"); 140 passButton.setActionCommand("Pass"); 141 passButton.addActionListener((ActionEvent e) -> { 142 testResult = true; 143 disposeFrames(); 144 countDownLatch.countDown(); 145 146 }); 147 148 JButton failButton = new JButton("Fail"); 149 failButton.setActionCommand("Fail"); 150 failButton.addActionListener(new ActionListener() { 151 @Override 152 public void actionPerformed(ActionEvent e) { 153 disposeFrames(); 154 countDownLatch.countDown(); 155 } 156 }); 157 158 gbc.gridx = 0; 159 gbc.gridy = 0; 160 resultButtonPanel.add(passButton, gbc); 161 162 gbc.gridx = 1; 163 gbc.gridy = 0; 164 resultButtonPanel.add(failButton, gbc); 165 166 gbc.gridx = 0; 167 gbc.gridy = 2; 168 mainControlPanel.add(resultButtonPanel, gbc); 169 170 mainFrame.add(mainControlPanel); 171 mainFrame.pack(); 172 173 mainFrame.addWindowListener(new WindowAdapter() { 174 175 @Override 176 public void windowClosing(WindowEvent e) { 177 disposeFrames(); 178 countDownLatch.countDown(); 179 } 180 }); 181 mainFrame.setVisible(true); 182 } 183 184 private static void disposeFrames() { 185 if (frame != null && frame.isVisible()) { 186 frame.dispose(); 187 } 188 189 if (mainFrame != null && mainFrame.isVisible()) { 190 mainFrame.dispose(); 191 } 192 } 193 194 static class TestFrame extends Frame { 195 196 private final TestMultiResolutionImage mrImage; 197 198 public TestFrame(int width, int height) throws HeadlessException { 199 super("Test Frame"); 200 setSize(width, height); 201 mrImage = new TestMultiResolutionImage(width, height); 202 203 Panel panel = new Panel(new FlowLayout()) { 204 @Override 205 public void paint(Graphics g) { 206 super.paint(g); 207 AffineTransform tx = ((Graphics2D) g).getTransform(); 208 mrImage.scaleX = tx.getScaleX(); 209 mrImage.scaleY = tx.getScaleY(); 210 Insets insets = getInsets(); 211 g.drawImage(mrImage, insets.left, insets.bottom, null); 212 } 213 }; 214 add(panel); 215 } 216 } 217 218 static class TestMultiResolutionImage extends AbstractMultiResolutionImage { 219 220 final int width; 221 final int height; 222 double scaleX; 223 double scaleY; 224 225 public TestMultiResolutionImage(int width, int height) { 226 this.width = width; 227 this.height = height; 228 } 229 230 @Override 231 public int getWidth(ImageObserver observer) { 232 return width; 233 } 234 235 @Override 236 public int getHeight(ImageObserver observer) { 237 return height; 238 } 239 240 @Override 241 protected Image getBaseImage() { 242 return getResolutionVariant(width, height); 243 } 244 245 @Override 246 public Image getResolutionVariant(double destImageWidth, double destImageHeight) { 247 248 int w = (int) destImageWidth; 249 int h = (int) destImageHeight; 250 251 BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 252 Graphics2D g = img.createGraphics(); 253 g.scale(scaleX, scaleY); 254 int red = (int) (255 / scaleX); 255 int green = (int) (250 / scaleX); 256 int blue = (int) (20 / scaleX); 257 g.setColor(new Color(red, green, blue)); 258 g.fillRect(0, 0, width, height); 259 260 g.setColor(Color.decode("#87CEFA")); 261 Font f = g.getFont(); 262 g.setFont(new Font(f.getName(), Font.BOLD, 24)); 263 g.drawString(String.format("scales: [%1.2fx, %1.2fx]", scaleX, scaleY), 264 width / 6, height / 2); 265 266 g.dispose(); 267 return img; 268 } 269 270 @Override 271 public List<Image> getResolutionVariants() { 272 return Collections.unmodifiableList(Arrays.asList(getBaseImage())); 273 } 274 } 275 }