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