8372415: Stage size should match visual window bounds
On Windows, the Stage.width and Stage.height correspond to the window size as returned by GetWindowRect.
Up until Windows 10, the size of a window was identical to its visual borders. However, since Windows 10 has introduced thin visual window borders, the window manager adds an invisible border of a few pixels around the window to make it easier to resize the window. Since GetWindowRect returns the window size including these invisible borders, the location and size of a Stage isn't exactly what we'd expect.
For example, if we place a Stage at setX(0) and setY(0), the window appears with a small distance from the screen edge, and the window size extends a few pixels beyond its visual borders (in the following images, the screenshot size corresponds to the window size; note the invisible padding around the edges):
What we actually want is to have the visual borders line up with the edges of the screen, and have the window size correspond to the visual borders:
The implementation is quite simple: instead of GetWindowRect, we use DwmGetWindowAttribute(DWMA_EXTENDED_FRAME_BOUNDS). This gives us the bounds of the visual window borders. If this function fails, we fall back to GetWindowRect (now, I don't know why DwmGetWindowAttribute(DWMA_EXTENDED_FRAME_BOUNDS) would ever fail... maybe an old Windows version in a remote desktop scenario?).
Progress
- [x] Change must not contain extraneous whitespace
- [x] Commit message must refer to an issue
- [ ] Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)
Issue
- JDK-8372415: Stage size should match visual window bounds (Bug - P4)
Reviewing
Using git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1982/head:pull/1982
$ git checkout pull/1982
Update a local copy of the PR:
$ git checkout pull/1982
$ git pull https://git.openjdk.org/jfx.git pull/1982/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 1982
View PR using the GUI difftool:
$ git pr show -t 1982
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1982.diff
Using Webrev
/reviewers 2
:wave: Welcome back mstrauss! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.
❗ This change is not yet ready to be integrated. See the Progress checklist in the description for automated requirements.
@mstr2 The total number of required reviews for this PR (including the jcheck configuration and the last /reviewers command) is now set to 2 (with at least 1 Reviewer, 1 Author).
I haven't uncovered any problems so far but it took me a while to get my bearings. You're working with two coordinate systems, the one used by JavaFX which doesn't include the invisible border and the one used by Windows which does. The naming convention is confusing since the "extended" rectangle is actually the smaller of the two. It would also be good to see some comments clarifying that m_insets, m_maxSize, and m_minSize are all specified in the JavaFX system.
I haven't uncovered any problems so far but it took me a while to get my bearings. You're working with two coordinate systems, the one used by JavaFX which doesn't include the invisible border and the one used by Windows which does. The naming convention is confusing since the "extended" rectangle is actually the smaller of the two. It would also be good to see some comments clarifying that
m_insets,m_maxSize, andm_minSizeare all specified in the JavaFX system.
I don’t think it’s quite "two coordinate systems" (JavaFX vs Windows). Stage.width/height are intended to include the normal system decorations (title bar + borders), which historically also matches what people mean by "window size": it includes the non-client area, but not purely visual effects like drop shadows. The weirdness on Windows 10+ is that GetWindowRect started including an extra invisible resize border that’s not part of the visible frame, so the window ends up being reported a few pixels larger than it looks. Using DwmGetWindowAttribute(DWMA_EXTENDED_FRAME_BOUNDS) is basically a way to get the visually perceived bounds back.
I was thinking that JavaFX and Windows have different ideas of where the window origin (0, 0) lies; Windows thinks it's at the top left of drop shadow area and JavaFX thinks it's at the top left of the title bar (in this PR). But you're right, what's relevant is that there are two rectangles of different sizes. I still think it could be clearer that, say, m_insets applies to the smaller of the two (which is the extended one).
I've added a comment to clarify that m_minSize, m_maxSize, and m_insets are specified with respect to the extended frame bounds.
With this fix applied, the problem described in JDK-8285983 disappears and the reported position and size of a full-screen window is as a sane person would expect it to be (position = 0,0 and size = visual bounds of the screen).
I suggest we re-open the bug (which was closed as "not an issue") and add it to this PR.
I've looked at every call to GetWindowRect and, where appropriate, replaced it with GetExtendedFrameBounds.
In addition to that, I also tested the case when JavaFX is running in a DPI-unaware process. I've added code that detects this situation and applies the correct mapping from physical coordinates into virtualized coordinates.
/issue add JDK-8285983
@mstr2
Adding additional issue to issue list: 8285983: JavaFX reports maximized window size larger than the physical screen.