From 97722d3efe077e3285a691eb938e5f346b11feb1 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Fri, 10 Sep 2021 11:25:43 -0700 Subject: [PATCH] Add an openSystemMenu keybinding (#11086) ## Summary of the Pull Request Basically undoes #10988 in favour of implementing it as described in #11018 ## PR Checklist * [x] Closes #11018 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [X] Tests added/passed * [X] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [X] Schema updated. * [x] I work here ## Validation Steps Performed - alt+space opens the system menu by default - when alt+space is bound, the keys do not get send to terminal - right-click on the tab bar didn't break (still opens system menu at the location of the cursor) --- doc/cascadia/profiles.schema.json | 1 + .../TerminalApp/AppActionHandlers.cpp | 7 +++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 1 + src/cascadia/TerminalApp/TerminalPage.h | 1 + src/cascadia/TerminalApp/TerminalPage.idl | 1 + src/cascadia/TerminalControl/TermControl.cpp | 18 ------ .../TerminalSettingsModel/ActionAndArgs.cpp | 2 + .../AllShortcutActions.h | 1 + .../Resources/en-US/Resources.resw | 3 + .../TerminalSettingsModel/defaults.json | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 7 +++ src/cascadia/WindowsTerminal/AppHost.h | 3 + src/cascadia/WindowsTerminal/IslandWindow.cpp | 58 +++++++++++++++++++ src/cascadia/WindowsTerminal/IslandWindow.h | 2 + .../WindowsTerminal/NonClientIslandWindow.cpp | 46 +-------------- .../WindowsTerminal/NonClientIslandWindow.h | 2 - src/cascadia/WindowsTerminal/main.cpp | 13 ++--- 18 files changed, 95 insertions(+), 73 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 1d387a9e4..6b989a249 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -279,6 +279,7 @@ "paste", "prevTab", "renameTab", + "openSystemMenu", "openTabRenamer", "quakeMode", "resetFontSize", diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 5bd5e56ed..9e1933615 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -882,6 +882,13 @@ namespace winrt::TerminalApp::implementation } } + void TerminalPage::_HandleOpenSystemMenu(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + _OpenSystemMenuHandlers(*this, nullptr); + args.Handled(true); + } + void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/, const ActionEventArgs& args) { diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 3e9a77bc1..5f8c87be1 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -175,6 +175,7 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested); FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged); FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested); + FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu); FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested); #ifdef UNIT_TESTING diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 9ff673b41..7f4c2121e 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -99,6 +99,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SettingsChanged; event Windows.Foundation.TypedEventHandler IsQuakeWindowChanged; event Windows.Foundation.TypedEventHandler SummonWindowRequested; + event Windows.Foundation.TypedEventHandler OpenSystemMenu; event Windows.Foundation.TypedEventHandler QuitRequested; } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 5ffee2fcb..e29676405 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -132,6 +132,7 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs); TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable); TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable); + TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable); TYPED_EVENT(QuitRequested, IInspectable, IInspectable); private: diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index a57b1aada..dd2777add 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -58,5 +58,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RenameWindowRequested; event Windows.Foundation.TypedEventHandler IsQuakeWindowChanged; event Windows.Foundation.TypedEventHandler SummonWindowRequested; + event Windows.Foundation.TypedEventHandler OpenSystemMenu; } } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 1c698d8b4..115f9afc5 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -904,24 +904,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - if (vkey == VK_SPACE && modifiers.IsAltPressed()) - { - if (const auto bindings = _settings.KeyBindings()) - { - if (!bindings.IsKeyChordExplicitlyUnbound({ modifiers.IsCtrlPressed(), modifiers.IsAltPressed(), modifiers.IsShiftPressed(), modifiers.IsWinPressed(), vkey, scanCode })) - { - // If we get here, it means that - // 1. we do not have a command bound to alt+space - // 2. alt+space was not explicitly unbound - // That means that XAML handled the alt+space to open up the context menu, and - // so we don't want to send anything to the terminal - // TODO GH#11018: Add a new "openSystemMenu" keybinding - e.Handled(true); - return; - } - } - } - if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown)) { e.Handled(true); diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 4e2f3a742..2ae6bb6da 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -65,6 +65,7 @@ static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" }; static constexpr std::string_view GlobalSummonKey{ "globalSummon" }; static constexpr std::string_view QuakeModeKey{ "quakeMode" }; static constexpr std::string_view FocusPaneKey{ "focusPane" }; +static constexpr std::string_view OpenSystemMenuKey{ "openSystemMenu" }; static constexpr std::string_view ClearBufferKey{ "clearBuffer" }; static constexpr std::string_view MultipleActionsKey{ "multipleActions" }; static constexpr std::string_view QuitKey{ "quit" }; @@ -369,6 +370,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") }, { ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName + { ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") }, { ShortcutAction::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::Quit, RS_(L"QuitCommandKey") }, diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h index c7e1bd272..aa06ae70c 100644 --- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h +++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h @@ -79,6 +79,7 @@ ON_ALL_ACTIONS(GlobalSummon) \ ON_ALL_ACTIONS(QuakeMode) \ ON_ALL_ACTIONS(FocusPane) \ + ON_ALL_ACTIONS(OpenSystemMenu) \ ON_ALL_ACTIONS(ClearBuffer) \ ON_ALL_ACTIONS(MultipleActions) \ ON_ALL_ACTIONS(Quit) diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index a5e6368ff..acd619ab5 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -178,6 +178,9 @@ Close window + + Open system menu + Command Prompt This is the name of "Command Prompt", as localized in Windows. The localization here should match the one in the Windows product for "Command Prompt" diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 8cf8dd87d..a246d52b9 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -301,6 +301,7 @@ { "command": "identifyWindow" }, { "command": "openWindowRenamer" }, { "command": "quakeMode", "keys":"win+sc(41)" }, + { "command": "openSystemMenu", "keys": "alt+space" }, { "command": "quit" }, // Tab Management diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 19118df68..0e489ca71 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -279,6 +279,7 @@ void AppHost::Initialize() _logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged }); _logic.IsQuakeWindowChanged({ this, &AppHost::_IsQuakeWindowChanged }); _logic.SummonWindowRequested({ this, &AppHost::_SummonWindowRequested }); + _logic.OpenSystemMenu({ this, &AppHost::_OpenSystemMenu }); _logic.QuitRequested({ this, &AppHost::_RequestQuitAll }); _window->UpdateTitle(_logic.Title()); @@ -1062,6 +1063,12 @@ void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspecta _HandleSummon(sender, summonArgs); } +void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&, + const winrt::Windows::Foundation::IInspectable&) +{ + _window->OpenSystemMenu(std::nullopt, std::nullopt); +} + // Method Description: // - Creates a Tray Icon and hooks up its handlers // Arguments: diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index df8042828..ebf38dd17 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -85,6 +85,9 @@ private: void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _OpenSystemMenu(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + winrt::fire_and_forget _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index ac281880c..879e1c007 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -1645,5 +1645,63 @@ void IslandWindow::SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept _minimizeToTray = minimizeToTray; } +// Method Description: +// - Opens the window's system menu. +// - The system menu is the menu that opens when the user presses Alt+Space or +// right clicks on the title bar. +// - Before updating the menu, we update the buttons like "Maximize" and +// "Restore" so that they are grayed out depending on the window's state. +// Arguments: +// - cursorX: the cursor's X position in screen coordinates +// - cursorY: the cursor's Y position in screen coordinates +void IslandWindow::OpenSystemMenu(const std::optional mouseX, const std::optional mouseY) const noexcept +{ + const auto systemMenu = GetSystemMenu(_window.get(), FALSE); + + WINDOWPLACEMENT placement; + if (!GetWindowPlacement(_window.get(), &placement)) + { + return; + } + const bool isMaximized = placement.showCmd == SW_SHOWMAXIMIZED; + + // Update the options based on window state. + MENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_STATE; + mii.fType = MFT_STRING; + auto setState = [&](UINT item, bool enabled) { + mii.fState = enabled ? MF_ENABLED : MF_DISABLED; + SetMenuItemInfo(systemMenu, item, FALSE, &mii); + }; + setState(SC_RESTORE, isMaximized); + setState(SC_MOVE, !isMaximized); + setState(SC_SIZE, !isMaximized); + setState(SC_MINIMIZE, true); + setState(SC_MAXIMIZE, !isMaximized); + setState(SC_CLOSE, true); + SetMenuDefaultItem(systemMenu, UINT_MAX, FALSE); + + int xPos; + int yPos; + if (mouseX && mouseY) + { + xPos = mouseX.value(); + yPos = mouseY.value(); + } + else + { + RECT windowPos; + ::GetWindowRect(GetHandle(), &windowPos); + xPos = windowPos.left; + yPos = windowPos.top; + } + const auto ret = TrackPopupMenu(systemMenu, TPM_RETURNCMD, xPos, yPos, 0, _window.get(), nullptr); + if (ret != 0) + { + PostMessage(_window.get(), WM_SYSCOMMAND, ret, 0); + } +} + DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 773119329..ad8c54de9 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -51,6 +51,8 @@ public: void SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept; + void OpenSystemMenu(const std::optional mouseX, const std::optional mouseY) const noexcept; + DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); WINRT_CALLBACK(MouseScrolled, winrt::delegate); diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index 9b9cbd468..c00347708 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -750,7 +750,7 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept // reason so we have to do it ourselves. if (wParam == HTCAPTION) { - _OpenSystemMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + OpenSystemMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); } break; } @@ -947,47 +947,3 @@ bool NonClientIslandWindow::_IsTitlebarVisible() const { return !(_fullscreen || _borderless); } - -// Method Description: -// - Opens the window's system menu. -// - The system menu is the menu that opens when the user presses Alt+Space or -// right clicks on the title bar. -// - Before updating the menu, we update the buttons like "Maximize" and -// "Restore" so that they are grayed out depending on the window's state. -// Arguments: -// - cursorX: the cursor's X position in screen coordinates -// - cursorY: the cursor's Y position in screen coordinates -void NonClientIslandWindow::_OpenSystemMenu(const int cursorX, const int cursorY) const noexcept -{ - const auto systemMenu = GetSystemMenu(_window.get(), FALSE); - - WINDOWPLACEMENT placement; - if (!GetWindowPlacement(_window.get(), &placement)) - { - return; - } - const bool isMaximized = placement.showCmd == SW_SHOWMAXIMIZED; - - // Update the options based on window state. - MENUITEMINFO mii; - mii.cbSize = sizeof(MENUITEMINFO); - mii.fMask = MIIM_STATE; - mii.fType = MFT_STRING; - auto setState = [&](UINT item, bool enabled) { - mii.fState = enabled ? MF_ENABLED : MF_DISABLED; - SetMenuItemInfo(systemMenu, item, FALSE, &mii); - }; - setState(SC_RESTORE, isMaximized); - setState(SC_MOVE, !isMaximized); - setState(SC_SIZE, !isMaximized); - setState(SC_MINIMIZE, true); - setState(SC_MAXIMIZE, !isMaximized); - setState(SC_CLOSE, true); - SetMenuDefaultItem(systemMenu, UINT_MAX, FALSE); - - const auto ret = TrackPopupMenu(systemMenu, TPM_RETURNCMD, cursorX, cursorY, 0, _window.get(), nullptr); - if (ret != 0) - { - PostMessage(_window.get(), WM_SYSCOMMAND, ret, 0); - } -} diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index 02e59cc7a..cae2bc0da 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -87,6 +87,4 @@ private: void _UpdateFrameMargins() const noexcept; void _UpdateMaximizedState(); void _UpdateIslandPosition(const UINT windowWidth, const UINT windowHeight); - - void _OpenSystemMenu(const int mouseX, const int mouseY) const noexcept; }; diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index ddac884fa..c53f885b1 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -184,16 +184,13 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) } } - // GH#7125 = System XAML will show a system dialog on Alt Space. We might want to - // explicitly prevent that - for example when an action is bound to it. So similar to - // above, we steal the event and hand it off to the host. When the host does not process - // it, we will still dispatch like normal. + // GH#7125 = System XAML will show a system dialog on Alt Space. We want to + // explicitly prevent that because we handle that ourselves. So similar to + // above, we steal the event and hand it off to the host. if (_messageIsAltSpaceKeypress(message)) { - if (host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true)) - { - continue; - } + host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true); + continue; } TranslateMessage(&message);