Skip to content

Commit

Permalink
Recalculate quake window size when snapping across monitors (#10744)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
zadjii-msft authored and DHowett committed Aug 25, 2021
1 parent 3831d47 commit a02e8ae
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 9 deletions.
98 changes: 89 additions & 9 deletions src/cascadia/WindowsTerminal/IslandWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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, &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))
Expand Down Expand Up @@ -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:
// - <none>
// Return Value:
// - <none>
void IslandWindow::_enterQuakeMode()
{
if (!_window)
Expand All @@ -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<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;
Expand Down Expand Up @@ -1490,14 +1577,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<>);
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/WindowsTerminal/IslandWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit a02e8ae

Please sign in to comment.