Skip to content

Commit b5ce8af

Browse files
committed
8256373: [Windows/HiDPI] The Frame#setBounds does not work in a minimized state
Reviewed-by: kizune, aivanov
1 parent 0eaf0bb commit b5ce8af

File tree

2 files changed

+117
-19
lines changed

2 files changed

+117
-19
lines changed

src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -651,39 +651,46 @@ MsgRouting AwtFrame::WmNcMouseDown(WPARAM hitTest, int x, int y, int button) {
651651

652652
// Override AwtWindow::Reshape() to handle minimized/maximized
653653
// frames (see 6525850, 4065534)
654-
void AwtFrame::Reshape(int x, int y, int width, int height)
654+
void AwtFrame::Reshape(int x, int y, int w, int h)
655655
{
656656
if (isIconic()) {
657657
// normal AwtComponent::Reshape will not work for iconified windows so...
658+
POINT pt = {x + w / 2, y + h / 2};
659+
Devices::InstanceAccess devices;
660+
HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
661+
int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor);
662+
AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
663+
// Try to set the correct size and jump to the correct location, even if
664+
// it is on the different monitor. Note that for the "size" we use the
665+
// current monitor, so the WM_DPICHANGED will adjust it for the "target"
666+
// monitor.
667+
MONITORINFO *miInfo = AwtWin32GraphicsDevice::GetMonitorInfo(screen);
668+
x = device == NULL ? x : device->ScaleUpAbsX(x);
669+
y = device == NULL ? y : device->ScaleUpAbsY(y);
670+
w = ScaleUpX(w);
671+
h = ScaleUpY(h);
672+
// SetWindowPlacement takes workspace coordinates, but if taskbar is at
673+
// top/left of screen, workspace coords != screen coords, so offset by
674+
// workspace origin
675+
x = x - (miInfo->rcWork.left - miInfo->rcMonitor.left);
676+
y = y - (miInfo->rcWork.top - miInfo->rcMonitor.top);
658677
WINDOWPLACEMENT wp;
659-
POINT ptMinPosition = {x,y};
660-
POINT ptMaxPosition = {0,0};
661-
RECT rcNormalPosition = {x,y,x+width,y+height};
662-
RECT rcWorkspace;
663-
HWND hWndDesktop = GetDesktopWindow();
664-
HWND hWndSelf = GetHWnd();
665-
666-
// SetWindowPlacement takes workspace coordinates, but
667-
// if taskbar is at top of screen, workspace coords !=
668-
// screen coords, so offset by workspace origin
669-
VERIFY(::SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rcWorkspace, 0));
670-
::OffsetRect(&rcNormalPosition, -rcWorkspace.left, -rcWorkspace.top);
671-
678+
::ZeroMemory(&wp, sizeof(WINDOWPLACEMENT));
672679
// set the window size for when it is not-iconified
673680
wp.length = sizeof(wp);
674681
wp.flags = WPF_SETMINPOSITION;
675682
wp.showCmd = IsVisible() ? SW_SHOWMINIMIZED : SW_HIDE;
676-
wp.ptMinPosition = ptMinPosition;
677-
wp.ptMaxPosition = ptMaxPosition;
678-
wp.rcNormalPosition = rcNormalPosition;
683+
wp.ptMinPosition = {x, y};
684+
wp.ptMaxPosition = {0, 0};
685+
wp.rcNormalPosition = {x, y, x + w, y + h};
679686

680687
// If the call is not guarded with ignoreWmSize,
681688
// a regression for bug 4851435 appears.
682689
// Having this call guarded also prevents
683690
// changing the iconified state of the frame
684691
// while calling the Frame.setBounds() method.
685692
m_ignoreWmSize = TRUE;
686-
::SetWindowPlacement(hWndSelf, &wp);
693+
::SetWindowPlacement(GetHWnd(), &wp);
687694
m_ignoreWmSize = FALSE;
688695

689696
return;
@@ -703,7 +710,7 @@ void AwtFrame::Reshape(int x, int y, int width, int height)
703710
}
704711
}
705712

706-
AwtWindow::Reshape(x, y, width, height);
713+
AwtWindow::Reshape(x, y, w, h);
707714
}
708715

709716

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2020, 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.Frame;
25+
import java.awt.GraphicsDevice;
26+
import java.awt.GraphicsEnvironment;
27+
import java.awt.Rectangle;
28+
import java.awt.Toolkit;
29+
30+
/**
31+
* @test
32+
* @bug 8256373
33+
* @key headful
34+
* @summary setBounds() should work if the frame is minimized
35+
*/
36+
public final class RestoreToOppositeScreen {
37+
38+
public static void main(String[] args) throws Exception {
39+
Toolkit toolkit = Toolkit.getDefaultToolkit();
40+
if (!toolkit.isFrameStateSupported(Frame.ICONIFIED)) {
41+
return;
42+
}
43+
44+
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
45+
GraphicsDevice[] gds = ge.getScreenDevices();
46+
for (GraphicsDevice gd1 : gds) {
47+
Rectangle screen1 = gd1.getDefaultConfiguration().getBounds();
48+
int x1 = (int) screen1.getCenterX();
49+
int y1 = (int) screen1.getCenterY();
50+
for (GraphicsDevice gd2 : gds) {
51+
Rectangle screen2 = gd2.getDefaultConfiguration().getBounds();
52+
// tweak the (x2, y2) point so even if the screen1 and screen2
53+
// are the same, we will use different bounds, otherwise
54+
// setBounds() will be ignored
55+
int x2 = (int) screen2.getCenterX() - 50;
56+
int y2 = (int) screen2.getCenterY() - 50;
57+
Frame frame = new Frame();
58+
try {
59+
// show the frame on one monitor, and then move it to
60+
// another while the frame minimized
61+
frame.setBounds(x1, y1, 400, 400);
62+
frame.setVisible(true);
63+
Thread.sleep(2000);
64+
frame.setExtendedState(Frame.ICONIFIED);
65+
Thread.sleep(2000);
66+
Rectangle before = new Rectangle(x2, y2, 380, 380);
67+
frame.setBounds(before);
68+
Thread.sleep(2000);
69+
frame.setExtendedState(Frame.NORMAL);
70+
Thread.sleep(2000);
71+
Rectangle after = frame.getBounds();
72+
checkSize(after.x, before.x, "x");
73+
checkSize(after.y, before.y, "y");
74+
checkSize(after.width, before.width, "width");
75+
checkSize(after.height, before.height, "height");
76+
} finally {
77+
frame.dispose();
78+
}
79+
}
80+
}
81+
}
82+
83+
private static void checkSize(int actual, int expected, String prop) {
84+
if (Math.abs(actual - expected) > 10) { // let's allow size variation,
85+
// the bug is reproduced anyway
86+
System.err.println("Expected: " + expected);
87+
System.err.println("Actual: " + actual);
88+
throw new RuntimeException(prop + " is wrong");
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)