diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index ede4229f169..f5019fa5527 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -554,6 +554,7 @@ exemain EXETYPE exeuwp exewin +EXITSIZEMOVE exitwin EXPUNGECOMMANDHISTORY EXSTYLE diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.h b/src/cascadia/TerminalSettingsModel/ApplicationState.h index f998fc5ead4..afdee9f991a 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.h +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.h @@ -42,7 +42,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation X(FileSource::Shared, Windows::Foundation::Collections::IVector, DismissedMessages, "dismissedMessages") \ X(FileSource::Local, Windows::Foundation::Collections::IVector, AllowedCommandlines, "allowedCommandlines") \ X(FileSource::Local, std::unordered_set, DismissedBadges, "dismissedBadges") \ - X(FileSource::Shared, bool, SSHFolderGenerated, "sshFolderGenerated", false) + X(FileSource::Shared, bool, SSHFolderGenerated, "sshFolderGenerated", false) \ + X(FileSource::Shared, double, QuakeWindowSizePercent, "quakeWindowSizePercent", 0.5) struct WindowLayout : WindowLayoutT { diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.idl b/src/cascadia/TerminalSettingsModel/ApplicationState.idl index f4e85d3a0bf..de8d4673c74 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.idl +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.idl @@ -41,5 +41,6 @@ namespace Microsoft.Terminal.Settings.Model Windows.Foundation.Collections.IVector RecentCommands; Windows.Foundation.Collections.IVector DismissedMessages; Windows.Foundation.Collections.IVector AllowedCommandlines; + Double QuakeWindowSizePercent; } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 522e90c7d28..3fff4fa08d1 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -65,6 +65,14 @@ AppHost::AppHost(WindowEmperor* manager, const winrt::TerminalApp::AppLogic& log // Update our own internal state tracking if we're in quake mode or not. _IsQuakeWindowChanged(nullptr, nullptr); + const auto state = ApplicationState::SharedInstance(); + const auto savedPercent = static_cast(state.QuakeWindowSizePercent()); + _window->SetQuakeWindowSizePercent(savedPercent); + +#ifdef _DEBUG + OutputDebugStringW(wil::str_printf(L"[IslandWindow] Loaded size percent from state: %.1f%%\n", savedPercent * 100.0f).c_str()); +#endif + _window->SetMinimizeToNotificationAreaBehavior(_windowLogic.GetMinimizeToNotificationArea()); // Tell the window to callback to us when it's about to handle a WM_CREATE @@ -75,6 +83,7 @@ AppHost::AppHost(WindowEmperor* manager, const winrt::TerminalApp::AppLogic& log _windowCallbacks.WindowActivated = _window->WindowActivated({ this, &AppHost::_WindowActivated }); _windowCallbacks.WindowMoved = _window->WindowMoved({ this, &AppHost::_WindowMoved }); _windowCallbacks.ShouldExitFullscreen = _window->ShouldExitFullscreen({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen }); + _windowCallbacks.QuakeWindowSizeChanged = _window->QuakeWindowSizeChanged({ this, &AppHost::_QuakeWindowSizeChanged }); _window->MakeWindow(); @@ -391,6 +400,7 @@ void AppHost::_revokeWindowCallbacks() _window->DragRegionClicked(_windowCallbacks.DragRegionClicked); _window->WindowVisibilityChanged(_windowCallbacks.WindowVisibilityChanged); _window->MaximizeChanged(_windowCallbacks.MaximizeChanged); + _window->QuakeWindowSizeChanged(_windowCallbacks.QuakeWindowSizeChanged); } // Method Description: @@ -558,17 +568,32 @@ void AppHost::_initialResizeAndRepositionWindow(const HWND hwnd, til::rect propo if (_windowLogic.IsQuakeWindow()) { - // If we just use rcWork by itself, we'll fail to account for the invisible - // space reserved for the resize handles. So retrieve that size here. - const auto availableSpace = desktopDimensions + nonClientSize; + // Get cursor monitor for quake windows - the window should appear + // on the monitor where the cursor is, not the proposed window location + POINT cursorPos{}; + GetCursorPos(&cursorPos); + auto hmon = MonitorFromPoint(cursorPos, MONITOR_DEFAULTTONEAREST); + + MONITORINFO cursorMonitorInfo{}; + cursorMonitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(hmon, &cursorMonitorInfo); + + UINT dpiX = USER_DEFAULT_SCREEN_DPI; + UINT dpiY = USER_DEFAULT_SCREEN_DPI; + GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + + const til::rect workArea{ cursorMonitorInfo.rcWork }; + const auto nonClientSize = _window->GetTotalNonClientExclusiveSize(dpiX); + const auto availableSpace = til::size{ workArea.width(), workArea.height() } + nonClientSize; + const auto savedPercent = ApplicationState::SharedInstance().QuakeWindowSizePercent(); origin = { - (nearestMonitorInfo.rcWork.left - (nonClientSize.width / 2)), - (nearestMonitorInfo.rcWork.top) + (cursorMonitorInfo.rcWork.left - (nonClientSize.width / 2)) + 1, + cursorMonitorInfo.rcWork.top }; dimensions = { - availableSpace.width, - availableSpace.height / 2 + availableSpace.width - 2, + static_cast(availableSpace.height * savedPercent) }; launchMode = LaunchMode::FocusMode; } @@ -1030,6 +1055,16 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab _window->IsQuakeWindow(_windowLogic.IsQuakeWindow()); } +void AppHost::_QuakeWindowSizeChanged(float sizePercent) +{ + // Persist the new quake window size to ApplicationState + ApplicationState::SharedInstance().QuakeWindowSizePercent(static_cast(sizePercent)); + +#ifdef _DEBUG + OutputDebugStringW(wil::str_printf(L"[IslandWindow] Persisting size percent to state: %.1f%%\n", sizePercent * 100.0f).c_str()); +#endif +} + // Raised from TerminalWindow. We handle by bubbling the request to the window manager. void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 379f876b943..19802b7935b 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -88,6 +88,8 @@ class AppHost : public std::enable_shared_from_this void _IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _QuakeWindowSizeChanged(float sizePercent); + void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); @@ -176,6 +178,7 @@ class AppHost : public std::enable_shared_from_this winrt::event_token DragRegionClicked; winrt::event_token WindowVisibilityChanged; winrt::event_token MaximizeChanged; + winrt::event_token QuakeWindowSizeChanged; // LOAD BEARING!! //If you add events here, make sure they're revoked in AppHost::_revokeWindowCallbacks } _windowCallbacks{}; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 55d46abd68c..fc4aa3faae4 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -587,6 +587,45 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam WindowMoved.raise(); break; } + case WM_EXITSIZEMOVE: + { + if (IsQuakeWindow()) + { + const til::rect windowRect{ GetWindowRect() }; + const auto hmon = MonitorFromWindow(_window.get(), MONITOR_DEFAULTTONEAREST); + + MONITORINFO monitorInfo{}; + monitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(hmon, &monitorInfo); + + UINT dpix = USER_DEFAULT_SCREEN_DPI; + UINT dpiy = USER_DEFAULT_SCREEN_DPI; + GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + + const auto ncSize = GetTotalNonClientExclusiveSize(dpix); + const til::size desktopDimensions{ + (monitorInfo.rcWork.right - monitorInfo.rcWork.left), + (monitorInfo.rcWork.bottom - monitorInfo.rcWork.top) + }; + const auto availableHeight = desktopDimensions.height + ncSize.height; + const auto currentHeight = windowRect.height(); + const auto newPercent = static_cast(currentHeight) / static_cast(availableHeight); + const auto clampedPercent = std::clamp(newPercent, 0.1f, 1.0f); + + if (clampedPercent != _quakeWindowSizePercent) + { + _quakeWindowSizePercent = clampedPercent; + +#ifdef _DEBUG + OutputDebugStringW(wil::str_printf(L"[IslandWindow] WM_EXITSIZEMOVE: New size percent: %.1f%% (height: %d, available: %d)\n", + clampedPercent * 100.0f, currentHeight, availableHeight).c_str()); +#endif + + QuakeWindowSizeChanged.raise(clampedPercent); + } + } + break; + } case WM_CLOSE: { // If the user wants to close the app by clicking 'X' button, @@ -1646,6 +1685,15 @@ void IslandWindow::SetAutoHideWindow(bool autoHideWindow) noexcept _autoHideWindow = autoHideWindow; } +void IslandWindow::SetQuakeWindowSizePercent(float percent) noexcept +{ + _quakeWindowSizePercent = std::clamp(percent, 0.1f, 1.0f); + +#ifdef _DEBUG + OutputDebugStringW(wil::str_printf(L"[IslandWindow] Size percent set to: %.1f%%\n", _quakeWindowSizePercent * 100.0f).c_str()); +#endif +} + // Method Description: // - Enter quake mode for the monitor this window is currently on. This involves // resizing it to the top half of the monitor. @@ -1713,7 +1761,7 @@ til::rect IslandWindow::_getQuakeModeSize(HMONITOR hmon) }; const til::size dimensions{ availableSpace.width - 2, - availableSpace.height / 2 + static_cast(availableSpace.height * _quakeWindowSizePercent) }; return { origin, dimensions }; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 83afeaf44f5..d924d6564df 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -60,6 +60,7 @@ class IslandWindow : bool IsQuakeWindow() const noexcept; void IsQuakeWindow(bool isQuakeWindow) noexcept; void SetAutoHideWindow(bool autoHideWindow) noexcept; + void SetQuakeWindowSizePercent(float percent) noexcept; void HideWindow(); @@ -85,6 +86,7 @@ class IslandWindow : til::event> WindowMoved; til::event> WindowVisibilityChanged; + til::event> QuakeWindowSizeChanged; protected: void ForceResize() @@ -143,6 +145,7 @@ class IslandWindow : bool _isQuakeWindow{ false }; bool _autoHideWindow{ false }; + float _quakeWindowSizePercent{ 0.5f }; void _enterQuakeMode(); til::rect _getQuakeModeSize(HMONITOR hmon);