Recalculate quake window size when snapping across monitors (#10744)

## Summary of the Pull Request

<kbd>win+shift+arrows</kbd> 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

* <kbd>win+shift+arrows</kbd> works right now
* normal quake summoning still works right
This commit is contained in:
Mike Griese 2021-08-02 14:42:57 -05:00 committed by GitHub
parent fc64ff3029
commit a151607c79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 9 deletions

View file

@ -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<LPRECT>(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
@ -506,6 +508,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, &currentInfo);
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<int>();
lpwpos->y = newWindowRect.top<int>();
lpwpos->cx = newWindowRect.width<int>();
lpwpos->cy = newWindowRect.height<int>();
return 0;
}
}
}
case CM_NOTIFY_FROM_TRAY:
{
switch (LOWORD(lparam))
@ -1448,6 +1505,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:
// - <none>
// Return Value:
// - <none>
void IslandWindow::_enterQuakeMode()
{
if (!_window)
@ -1457,6 +1521,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<int>(),
newRect.top<int>(),
newRect.width<int>(),
newRect.height<int>(),
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:
// - <none>
// Return Value:
// - <none>
til::rectangle IslandWindow::_getQuakeModeSize(HMONITOR hmon)
{
MONITORINFO nearestMonitorInfo;
UINT dpix = USER_DEFAULT_SCREEN_DPI;
@ -1488,14 +1575,7 @@ void IslandWindow::_enterQuakeMode()
availableSpace.height() / 2
};
const til::rectangle newRect{ origin, dimensions };
SetWindowPos(GetHandle(),
HWND_TOP,
newRect.left<int>(),
newRect.top<int>(),
newRect.width<int>(),
newRect.height<int>(),
SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE);
return til::rectangle{ origin, dimensions };
}
DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);

View file

@ -109,7 +109,9 @@ protected:
void _moveToMonitor(const MONITORINFO activeMonitor);
bool _isQuakeWindow{ false };
void _enterQuakeMode();
til::rectangle _getQuakeModeSize(HMONITOR hmon);
void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);