From 0ffe5948a53ca6681ff95ef6d703870f04fabb5a Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 30 Jul 2024 12:29:12 +0100 Subject: [PATCH] Windows: Fix issue where maximised windows with non-native titlebars could slightly exceed the screen bounds When handling WM_NCCALCSIZE, we reduce the size of the client area when the window is maximised. If we don't do this, then the client area matches the top-level window area, which is larger than the display's safe area. As a result, the window appears too large, and the edges are obscured under the edges of the display. After reducing the client area in WM_NCCALCSIZE, the client area is no longer equal to the top-level window area, so getBounds() must be updated to return the window's *client* area rather than the top-level window area. --- .../native/juce_Windowing_windows.cpp | 73 ++++++++++++------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp index c5aa812ced..314e00a40e 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp @@ -1662,20 +1662,32 @@ public: { if (parentToAddTo == nullptr) { - // Depending on the desktop scale factor, the physical size of the window may not map to - // an integral client-area size. - // In this case, we always round the width and height of the client area up to the next - // integer. - // This means that we may end up clipping off up to one logical pixel under the physical - // window border, but this is preferable to displaying an uninitialised/unpainted - // region of the client area. - const auto physicalBorder = findPhysicalBorderSize(); + if (hasTitleBar()) + { + // Depending on the desktop scale factor, the physical size of the window may not map to + // an integral client-area size. + // In this case, we always round the width and height of the client area up to the next + // integer. + // This means that we may end up clipping off up to one logical pixel under the physical + // window border, but this is preferable to displaying an uninitialised/unpainted + // region of the client area. + const auto physicalBorder = findPhysicalBorderSize(); - const auto physicalBounds = D2DUtilities::toRectangle (getWindowScreenRect (hwnd)); - const auto physicalClient = physicalBorder.subtractedFrom (physicalBounds); - const auto logicalClient = convertPhysicalScreenRectangleToLogical (physicalClient.toFloat(), hwnd); - const auto snapped = logicalClient.withPosition (logicalClient.getPosition().roundToInt().toFloat()).getSmallestIntegerContainer(); - return snapped; + const auto physicalBounds = D2DUtilities::toRectangle (getWindowScreenRect (hwnd)); + const auto physicalClient = physicalBorder.subtractedFrom (physicalBounds); + const auto logicalClient = convertPhysicalScreenRectangleToLogical (physicalClient.toFloat(), hwnd); + const auto snapped = logicalClient.withPosition (logicalClient.getPosition().roundToInt().toFloat()).getSmallestIntegerContainer(); + return snapped; + } + + RECT rect{}; + GetClientRect (hwnd, &rect); + auto points = readUnaligned> (&rect); + MapWindowPoints (hwnd, nullptr, points.data(), (UINT) points.size()); + + const auto mappedRect = readUnaligned (points.data()); + const auto logicalClient = convertPhysicalScreenRectangleToLogical (D2DUtilities::toRectangle (mappedRect), hwnd); + return logicalClient; } auto localBounds = D2DUtilities::toRectangle (getWindowClientRect (hwnd)); @@ -3481,6 +3493,7 @@ private: return true; } + updateBorderSize(); handleMovedOrResized(); updateCurrentMonitorAndRefreshVBlankDispatcher(); @@ -3876,18 +3889,29 @@ private: case WM_NCCALCSIZE: { - auto* rect = (RECT*) lParam; - - if (renderContext != nullptr) - renderContext->setSize (rect->right - rect->left, rect->bottom - rect->top); - - if (! wParam) + // If we're using the native titlebar, then the default window proc behaviour will + // do the right thing. + if (hasTitleBar()) break; - if (! hasTitleBar() && windowUsesNativeShadow()) - return 0; + auto* param = (RECT*) lParam; - break; + // If we're not using a native titlebar, and the window is maximised, then the + // proposed window may be a bit bigger than the available space. Remove the padding + // so that the client area exactly fills the available space. + if (isFullScreen()) + { + const auto padX = -param->left; + const auto padY = -param->top; + + param->left += padX; + param->right -= padX; + + param->top += padY; + param->bottom -= padY; + } + + return 0; } //============================================================================== @@ -3978,9 +4002,8 @@ private: if ((wPos.flags & SWP_NOMOVE) != 0 && (wPos.flags & SWP_NOSIZE) != 0) startTimer (100); - else - if (handlePositionChanged()) - return 0; + else if (handlePositionChanged()) + return 0; } break;