-
Notifications
You must be signed in to change notification settings - Fork 5.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8265586: [windows] last button is not shown in AWT Frame with BorderLayout and MenuBar set. #9118
Conversation
👋 Welcome back honkar! A progress list of the required criteria for merging this PR into |
@honkar-jdk The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
// add extra padded border only when Win OS version is 6+ | ||
int extraPaddedBorderInsets = info.dwMajorVersion >= 6 ? | ||
::GetSystemMetrics(SM_CXPADDEDBORDER) : 0; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The OS version check is added since SM_CXPADDEDBORDER metric is not supported on Win XP & 2000. If the current JDK Window target OS version is set to Windows Vista, probably this check is not required or redundant. Suggestions would be helpful as to whether to retain this check or not
Screenshot source: GetSystemMetrics Doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can look at the oficially supported versions of operating systems by different Java versions here: https://www.oracle.com/java/technologies/javase-subscription/documentation.html#sysconfig
Nothing even remotely relevant supports Windows XP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@azuev-java Thank you for clarifying and sharing the link. It's a good reference for future as well.
Webrevs
|
OSVERSIONINFOEX info; | ||
ZeroMemory(&info, sizeof(OSVERSIONINFOEX)); | ||
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); | ||
GetVersionEx((LPOSVERSIONINFO) &info); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to check Windows version. Windows XP and 2000 are unsupported, java.exe doesn't even start there because its exe file header requires version 6.0 (Vista).
The lowest version supported by Microsoft is Windows 10, Windows 8.1 will reach its end of extended support in January 2023.
Thus, it's safe to call ::GetSystemMetrics(SM_CXPADDEDBORDER)
.
This issue came up after Windows SDK had been upgraded, which led to /subsystem version being set to 6.0. When targeting to previous versions, the SM_CXPADDEDBORDER
is excluded from the non-client area of a window.
frame.setMenuBar(mb); | ||
|
||
frame.pack(); | ||
frame.setVisible(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should call Robot.waitForIdle()
after making the frame visible. Calling it before you created the frame has no effect.
BufferedImage image = robot.createScreenCapture( | ||
new Rectangle(0, 0, 500, 500)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to capture frame.getBounds()
.
BufferedImage image = robot.createScreenCapture( | ||
new Rectangle(0, 0, 500, 500)); | ||
try { | ||
ImageIO.write(image, "png", new File("Frame")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ImageIO.write(image, "png", new File("Frame")); | |
ImageIO.write(image, "png", new File("Frame.png")); |
Add extension to the image file.
* @test | ||
* @bug 8265586 | ||
* @key headful | ||
* @summary Tests the functionality of AWT frame.pack() on Windows. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you be more specific? It tests the insets of a frame are calculated correctly on Windows.
* @key headful | ||
* @summary Tests the functionality of AWT frame.pack() on Windows. | ||
* @run main AwtFramePackTest | ||
* @requires (os.family == "windows") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually, @run
is the latest tag in a test description; @requires
usually goes before @summary
.
/* | ||
* @test | ||
* @bug 8265586 | ||
* @key headful | ||
* @summary Tests the functionality of AWT frame.pack() on Windows. | ||
* @run main AwtFramePackTest | ||
* @requires (os.family == "windows") | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am for placing the test tags before the class declaration, after imports. They're easier to see when you open the file in the IDE because they aren't collapsed.
m_insets.left = m_insets.right = | ||
::GetSystemMetrics(SM_CXDLGFRAME); | ||
m_insets.top = m_insets.bottom = | ||
::GetSystemMetrics(SM_CYDLGFRAME); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's likely needed here as well.
Make the frame non-resizeable to get into this block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added SM_CXPADDEDBORDER
to Non-Resizable Frames. After the addition of padded border the second button is being shown similar to Resizable frames. But unlike resizable frames, for the non-resizable frames - the actual frame size and the preferred size are different. Ideally the frame.getSize() should be equal to preferred size when frame.pack() is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested the AwtWindow::UpdateInsets()
method for Resizable and Non-Resizable frame.
Non-Resizable UI Scale=1
Outside Rect 0 56 0 198
Inside Rect 0 0 0 176
MenuBar Added
BEFORE Scale Down 73 9 9 9
AFTER Scale Down 73 9 9 9
Expected frame size: java.awt.Dimension[width=241,height=119]
Actual frame size: java.awt.Dimension[width=237,height=115]
Panel size: java.awt.Dimension[width=215,height=29]
Frame Insets: java.awt.Insets[top=75,left=11,bottom=11,right=11]
Non-Resizable
Outside Rect 0 56 0 198
Inside Rect 0 0 0 176
MenuBar Added
BEFORE Scale Down 73 9 9 9
AFTER Scale Down 49 6 6 6
Expected frame size: java.awt.Dimension[width=231,height=90]
Actual frame size: java.awt.Dimension[width=229,height=88]
Panel size: java.awt.Dimension[width=215,height=31]
Frame Insets: java.awt.Insets[top=50,left=7,bottom=7,right=7]
Resizable UI Scale=1
Outside Rect 0 56 0 198
Inside Rect 0 0 0 176
MenuBar Added
BEFORE Scale Down 75 11 11 11
AFTER Scale Down 75 11 11 11
Expected frame size: java.awt.Dimension[width=241,height=119]
Actual frame size: java.awt.Dimension[width=241,height=119]
Panel size: java.awt.Dimension[width=219,height=33]
Frame Insets: java.awt.Insets[top=75,left=11,bottom=11,right=11]
Resizable
Outside Rect 0 56 0 198
Inside Rect 0 0 0 176
MenuBar Added
BEFORE Scale Down 75 11 11 11
AFTER Scale Down 50 7 7 7
Expected frame size: java.awt.Dimension[width=231,height=90]
Actual frame size: java.awt.Dimension[width=231,height=90]
Panel size: java.awt.Dimension[width=217,height=33]
Frame Insets: java.awt.Insets[top=50,left=7,bottom=7,right=7]
Based on these logs, I observed that the final frame insets for non-resizable frame obtained by frame.getInsets() does not match with the scaled down insets where as for resizable frame it does.
Clarification Point - should the preferred size of a resizable and non-resizable frame be the same when frame.pack() is used? The default insets obtained for both are from different system metrics - ::GetSystemMetrics(SM_CYSIZEFRAME) & ::GetSystemMetrics(SM_CYDLGFRAME) respectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarification Point - should the preferred size of a resizable and non-resizable frame be the same when frame.pack() is used? The default insets obtained for both are from different system metrics - ::GetSystemMetrics(SM_CYSIZEFRAME) & ::GetSystemMetrics(SM_CYDLGFRAME) respectively.
Do ::GetSystemMetrics(SM_CYSIZEFRAME)
and ::GetSystemMetrics(SM_CYDLGFRAME)
return the same value? If yes, then I expect the size of the frame should be the same for resizable and non-resizable case.
Let's put it another way, I expect the pack()
makes the panel size to its preferred size.
In either case, the frame.getPreferredSize()
returns the same result but the actual size differs.
I don't consider it a blocker for this integration: the resizable case is handled perfectly now; the non-resizable case also ensures the second button is visible now.
However, I think we should find out what contributes to the difference in the actual size under a separate bugid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aivanov-jdk Thank you for clarifying. Yes ::GetSystemMetrics(SM_CYSIZEFRAME) and ::GetSystemMetrics(SM_CYDLGFRAME) return different values, thus we get different actual frame sizes for resizable and non-resizable frames.
As you rightly pointed out : the frame.getPreferredSize() returns the same result but the actual size differs for Non-Resizable frame.
As suggested I will create a new bug to track actual and preferred size difference for non-resizable frame.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aivanov-jdk Thank you for clarifying. Yes ::GetSystemMetrics(SM_CYSIZEFRAME) and ::GetSystemMetrics(SM_CYDLGFRAME) return different values, thus we get different actual frame sizes for resizable and non-resizable frames.
If these are different, why does the preferred size remain the same?
If these parameters return different values, the preferred size should be different.
As you rightly pointed out : the frame.getPreferredSize() returns the same result but the actual size differs for Non-Resizable frame.
As suggested I will create a new bug to track actual and preferred size difference for non-resizable frame.
Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these are different, why does the preferred size remain the same?
The updateInsets() is called multiple times depending on how many child components are present such as panel and buttons, each time the insets are updated accordingly. I believe the insets obtained for non-resizable frame is being overridden, thus causing preferred size to return wrong values for non-resizable frame.
My guess is it might be due to old or stale value of m_old_insets in this part of the code. Investigating it further.
insetsChanged = !::EqualRect( &m_old_insets, &m_insets );
::CopyRect( &m_old_insets, &m_insets );
if (insetsChanged) {
// Since insets are changed we need to update the surfaceData object
// to reflect that change
env->CallVoidMethod(peer, AwtComponent::replaceSurfaceDataLaterMID);
}
return insetsChanged;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, I think we should find out what contributes to the difference in the actual size under a separate bugid.
Tracked by JDK-8288325
saveScreenCapture(); | ||
throw new RuntimeException("Expected and Actual frame size" + | ||
" are different. frame.pack() does not work!!"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One space of indent is missing before the closing brace.
robot.delay(500); | ||
robot.waitForIdle(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think another delay
and waitForIdle
are required here.
The frame should be properly displayed before frame.getSize()
is called.
@aivanov-jdk Thank you for reviewing. I'll have the suggested changes fixed in the next commit. |
::GetSystemMetrics(SM_CXSIZEFRAME) + | ||
extraPaddedBorderInsets; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you put them on the same line, it nearly fits 80-column limit. Does it make sense to do so?
#include <jlong.h> | ||
#include <windows.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably windows.h
is not required… It compiled successfully without it, it should continue to compile now
@honkar-jdk This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 68 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@aivanov-jdk, @dmarkov20, @azvegint) but any other Committer may sponsor as well. ➡️ To flag this PR as ready for integration with the above commit message, type |
/sponsor |
@aivanov-jdk The change author (@honkar-jdk) must issue an |
I was under the impression that the |
/integrate |
@honkar-jdk |
/sponsor |
Going to push as commit bbaeacb.
Your commit was automatically rebased without conflicts. |
@dmarkov20 @honkar-jdk Pushed as commit bbaeacb. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
* @test | ||
* @bug 8265586 | ||
* @key headful | ||
* @requires (os.family == "windows") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why the test is windows specific? It does not have the platform specific code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mrserb I wanted to clarify if you meant - Though the fix is windows specific, the condition used in test case frame.getSize() == frame.getPreferredSize() should hold on all platforms when frame.pack() is used and the test case can be made generic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mrserb Since the PR was already integrated before the review suggestion was made to make the test generic, a new PR has been created to remove the @requires
tag from the test.
PR link: #9248
JBS bug: https://bugs.openjdk.org/browse/JDK-8288993
Due to incorrect AWT Frame inset values being returned from native code, few of the components in the frame were not being shown completely on Windows. With the proposed fix, correct insets are obtained which in turn sizes and displays the frame correctly.
The default insets obtained from the Win system was adding only
::GetSystemMetrics(SM_CXSIZEFRAME)
for WS_THICKFRAME and the insets were off by few pixels from the expected value.::GetSystemMetrics(SM_CXPADDEDBORDER)
is additionally added to top, bottom, left and right insets to account for the 6px. GetSystemMetric() DocumentA test case is added which checks if the actual frame size is equal to the expected frame size (frame.getSize() == frame.getPreferredSize()), thus checking if frame.pack() works as expected.
Following are before and after screenshots -
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk pull/9118/head:pull/9118
$ git checkout pull/9118
Update a local copy of the PR:
$ git checkout pull/9118
$ git pull https://git.openjdk.org/jdk pull/9118/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 9118
View PR using the GUI difftool:
$ git pr show -t 9118
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/9118.diff