From a02e8aef3cbea27551fb37ca48c5f7db20a61db8 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 2 Aug 2021 14:42:57 -0500 Subject: [PATCH] Recalculate quake window size when snapping across monitors (#10744) ## Summary of the Pull Request win+shift+arrows can be used to move windows to adjacent monitors. When that happens, we'll new re-calculate the size of the window for the new monitor. ## References * megathread: #8888 ## PR Checklist * [x] Closes #10274 * [x] I work here * [ ] Tests added/passed * [n/a] Requires documentation to be updated ## Detailed Description of the Pull Request / Additional comments In `WM_WINDOWPOSCHANGING`, the OS says "hey, I'm about to do {something} to your window. You cool with that?". We handle that message by: 1. checking if the window was _moved_ as a part of this message 2. getting the monitor that the window will be moved onto 3. If that monitor is different than the monitor the window is currently on, then * calculate how big the quake window should be on that monitor * tell the OS that's where we'd like to be. ## Validation Steps Performed * win+shift+arrows works right now * normal quake summoning still works right --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 98 +++++++++++++++++-- src/cascadia/WindowsTerminal/IslandWindow.h | 2 + 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 9f9d31a085d..067405f08ea 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -274,7 +274,9 @@ LRESULT IslandWindow::_OnSizing(const WPARAM wParam, const LPARAM lParam) LRESULT IslandWindow::_OnMoving(const WPARAM /*wParam*/, const LPARAM lParam) { LPRECT winRect = reinterpret_cast(lParam); - // If we're the quake window, prevent moving the window + + // If we're the quake window, prevent moving the window. If we don't do + // this, then Alt+Space...Move will still be able to move the window. if (IsQuakeWindow()) { // Stuff our current window into the lParam, and return true. This @@ -508,6 +510,61 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize case WM_THEMECHANGED: UpdateWindowIconForActiveMetrics(_window.get()); return 0; + case WM_WINDOWPOSCHANGING: + { + // GH#10274 - if the quake window gets moved to another monitor via aero + // snap (win+shift+arrows), then re-adjust the size for the new monitor. + if (IsQuakeWindow()) + { + // Retrieve the suggested dimensions and make a rect and size. + LPWINDOWPOS lpwpos = (LPWINDOWPOS)lparam; + + // We only need to apply restrictions if the position is changing. + // The SWP_ flags are confusing to read. This is + // "if we're not moving the window, do nothing." + if (WI_IsFlagSet(lpwpos->flags, SWP_NOMOVE)) + { + break; + } + // Figure out the suggested dimensions and position. + RECT rcSuggested; + rcSuggested.left = lpwpos->x; + rcSuggested.top = lpwpos->y; + rcSuggested.right = rcSuggested.left + lpwpos->cx; + rcSuggested.bottom = rcSuggested.top + lpwpos->cy; + + // Find the bounds of the current monitor, and the monitor that + // we're suggested to be on. + + HMONITOR current = MonitorFromWindow(_window.get(), MONITOR_DEFAULTTONEAREST); + MONITORINFO currentInfo; + currentInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(current, ¤tInfo); + + HMONITOR proposed = MonitorFromRect(&rcSuggested, MONITOR_DEFAULTTONEAREST); + MONITORINFO proposedInfo; + proposedInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(proposed, &proposedInfo); + + // If the monitor changed... + if (til::rectangle{ proposedInfo.rcMonitor } != + til::rectangle{ currentInfo.rcMonitor }) + { + const auto newWindowRect{ _getQuakeModeSize(proposed) }; + + // Inform User32 that we want to be placed at the position + // and dimensions that _getQuakeModeSize returned. When we + // snap across monitor boundaries, this will re-evaluate our + // size for the new monitor. + lpwpos->x = newWindowRect.left(); + lpwpos->y = newWindowRect.top(); + lpwpos->cx = newWindowRect.width(); + lpwpos->cy = newWindowRect.height(); + + return 0; + } + } + } case CM_NOTIFY_FROM_TRAY: { switch (LOWORD(lparam)) @@ -1450,6 +1507,13 @@ void IslandWindow::IsQuakeWindow(bool isQuakeWindow) noexcept } } +// Method Description: +// - Enter quake mode for the monitor this window is currently on. This involves +// resizing it to the top half of the monitor. +// Arguments: +// - +// Return Value: +// - void IslandWindow::_enterQuakeMode() { if (!_window) @@ -1459,6 +1523,29 @@ void IslandWindow::_enterQuakeMode() RECT windowRect = GetWindowRect(); HMONITOR hmon = MonitorFromRect(&windowRect, MONITOR_DEFAULTTONEAREST); + + // Get the size and position of the window that we should occupy + const auto newRect{ _getQuakeModeSize(hmon) }; + + SetWindowPos(GetHandle(), + HWND_TOP, + newRect.left(), + newRect.top(), + newRect.width(), + newRect.height(), + SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE); +} + +// Method Description: +// - Get the size and position of the window that a "quake mode" should occupy +// on the given monitor. +// - The window will occupy the top half of the monitor. +// Arguments: +// - +// Return Value: +// - +til::rectangle IslandWindow::_getQuakeModeSize(HMONITOR hmon) +{ MONITORINFO nearestMonitorInfo; UINT dpix = USER_DEFAULT_SCREEN_DPI; @@ -1490,14 +1577,7 @@ void IslandWindow::_enterQuakeMode() availableSpace.height() / 2 }; - const til::rectangle newRect{ origin, dimensions }; - SetWindowPos(GetHandle(), - HWND_TOP, - newRect.left(), - newRect.top(), - newRect.width(), - newRect.height(), - SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE); + return til::rectangle{ origin, dimensions }; } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 0e5859bc4fd..51e57d47d02 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -111,7 +111,9 @@ class IslandWindow : void _moveToMonitor(const MONITORINFO activeMonitor); bool _isQuakeWindow{ false }; + void _enterQuakeMode(); + til::rectangle _getQuakeModeSize(HMONITOR hmon); void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);