diff --git a/.github/actions/spell-check/expect/expect.txt b/.github/actions/spell-check/expect/expect.txt index 9bb806aa3f4..1b53779d74b 100644 --- a/.github/actions/spell-check/expect/expect.txt +++ b/.github/actions/spell-check/expect/expect.txt @@ -2830,3 +2830,5 @@ AAAAABBBBBBCCC AAAAA BBBBBCCC abcd +LPMINMAXINFO +MINMAXINFO \ No newline at end of file diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 4b1cba8fa73..ec56cdbab67 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1752,7 +1752,7 @@ namespace winrt::TerminalApp::implementation // - See Pane::CalcSnappedDimension float TerminalPage::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const { - if (_settings.GlobalSettings().SnapToGridOnResize()) + if (_settings && _settings.GlobalSettings().SnapToGridOnResize()) { if (auto index{ _GetFocusedTabIndex() }) { diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index f7beb5cd584..0e8cc1a82c3 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -276,10 +276,77 @@ void IslandWindow::OnSize(const UINT width, const UINT height) } } +// Method Description: +// - Handles a WM_GETMINMAXINFO message, issued before the window sizing starts. +// This message allows to modify the minimal and maximal dimensions of the window. +// We focus on minimal dimensions here +// (the maximal dimension will be calculate upon maximizing) +// Our goal is to protect against to downsizing to less than minimal allowed dimensions, +// that might occur in the scenarios where _OnSizing is bypassed. +// An example of such scenario is anchoring the window to the top/bottom screen border +// in order to maximize window height (GH# 8026). +// The computation is similar to what we do in _OnSizing: +// we need to consider both the client area and non-client exclusive area sizes, +// while taking DPI into account as well. +// Arguments: +// - lParam: Pointer to the requested MINMAXINFO struct, +// a ptMinTrackSize field of which we want to update with the computed dimensions. +// It also acts as the return value (it's a ref parameter). +// Return Value: +// - + +void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam) +{ + // Without a callback we don't know to snap the dimensions of the client area. + // Should not be a problem, the callback is not set early in the startup + // The initial dimensions will be set later on + if (!_pfnSnapDimensionCallback) + { + return; + } + + HMONITOR hmon = MonitorFromWindow(GetHandle(), MONITOR_DEFAULTTONEAREST); + if (hmon == NULL) + { + return; + } + + UINT dpix = USER_DEFAULT_SCREEN_DPI; + UINT dpiy = USER_DEFAULT_SCREEN_DPI; + GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + + // From now we use dpix for all computations (same as in _OnSizing). + const auto nonClientSizeScaled = GetTotalNonClientExclusiveSize(dpix); + const auto scale = base::ClampedNumeric(dpix) / USER_DEFAULT_SCREEN_DPI; + + auto lpMinMaxInfo = reinterpret_cast(lParam); + lpMinMaxInfo->ptMinTrackSize.x = _calculateTotalSize(true, minimumWidth * scale, nonClientSizeScaled.cx); + lpMinMaxInfo->ptMinTrackSize.y = _calculateTotalSize(false, minimumHeight * scale, nonClientSizeScaled.cy); +} + +// Method Description: +// - Helper function that calculates a singe dimension value, given initialWindow and nonClientSizes +// Arguments: +// - isWidth: parameter to pass to SnapDimensionCallback. +// True if the method is invoked for width computation, false if for height. +// - clientSize: the size of the client area (already) +// - nonClientSizeScaled: the exclusive non-client size (already scaled) +// Return Value: +// - The total dimension +long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize) +{ + return gsl::narrow_cast(_pfnSnapDimensionCallback(isWidth, gsl::narrow_cast(clientSize)) + nonClientSize); +} + [[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { switch (message) { + case WM_GETMINMAXINFO: + { + _OnGetMinMaxInfo(wparam, lparam); + return 0; + } case WM_CREATE: { _HandleCreateWindow(wparam, lparam); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 8e6a4fcc943..f2ccd4a93e5 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -74,7 +74,15 @@ class IslandWindow : LONG _getDesiredWindowStyle() const; + void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam); + long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize); + private: // This minimum width allows for width the tabs fit static constexpr long minimumWidth = 460L; + + // We run with no height requirement for client area, + // though the total height will take into account the non-client area + // and the requirements of components hosted in the client area + static constexpr long minimumHeight = 0L; }; diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 7d82654a5e6..e899da5e1c0 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -69,5 +69,5 @@ TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider); // For commandline argument processing #include #include - +#include #include "til.h"