From e9a3d16286e8cfebd487be5665753cd5852e2126 Mon Sep 17 00:00:00 2001 From: Tim Heuer Date: Wed, 22 May 2019 13:01:33 -0700 Subject: [PATCH] Adding auto-UI shortcuts to menu based on keymappings (#924) * Adding vsconfig file for VS2019 help to prompt for missing components requried. * Adding a keybinding for launching the settings. Suggested fix for #683 * Modified to comma per PR feedback * Implements 791 for profile and settings shortcuts (most frequent and have shortcuts) * Quick change for consistency (missed in first checkin due to using ENUM) on using 'Ctrl' instead of 'Control' * Adding UI shortcut generation to new keybinding mappings. Resolving #791 * Making a few changes on reviewer feedback for shortcut UI. * Additional reviewer feedback on variable name change (not a member var) --- src/cascadia/TerminalApp/App.cpp | 55 ++++++++++++++++++ src/cascadia/TerminalApp/App.h | 2 + src/cascadia/TerminalApp/AppKeyBindings.cpp | 62 +++++++++++++++++++++ src/cascadia/TerminalApp/AppKeyBindings.h | 3 + src/cascadia/TerminalApp/AppKeyBindings.idl | 1 + 5 files changed, 123 insertions(+) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 5c4f80a9003..e5846810b63 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -215,11 +215,26 @@ namespace winrt::TerminalApp::implementation void App::_CreateNewTabFlyout() { auto newTabFlyout = Controls::MenuFlyout{}; + auto keyBindings = _settings->GetKeybindings(); + for (int profileIndex = 0; profileIndex < _settings->GetProfiles().size(); profileIndex++) { const auto& profile = _settings->GetProfiles()[profileIndex]; auto profileMenuItem = Controls::MenuFlyoutItem{}; + // add the keyboard shortcuts for the first 9 profiles + if (profileIndex < 9) + { + // enum value for ShortcutAction::NewTabProfileX; 0==NewTabProfile0 + auto profileKeyChord = keyBindings.GetKeyBinding(static_cast(profileIndex + static_cast(ShortcutAction::NewTabProfile0))); + + // make sure we find one to display + if (profileKeyChord) + { + _SetAcceleratorForMenuItem(profileMenuItem, profileKeyChord); + } + } + auto profileName = profile.GetName(); winrt::hstring hName{ profileName }; profileMenuItem.Text(hName); @@ -254,6 +269,12 @@ namespace winrt::TerminalApp::implementation settingsItem.Click({ this, &App::_SettingsButtonOnClick }); newTabFlyout.Items().Append(settingsItem); + auto settingsKeyChord = keyBindings.GetKeyBinding(ShortcutAction::OpenSettings); + if (settingsKeyChord) + { + _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord); + } + // Create the feedback button. auto feedbackFlyout = Controls::MenuFlyoutItem{}; feedbackFlyout.Text(L"Feedback"); @@ -899,6 +920,40 @@ namespace winrt::TerminalApp::implementation } } + + // Method Description: + // - Takes a MenuFlyoutItem and a corresponding KeyChord value and creates the accelerator for UI display. + // Takes into account a special case for an error condition for a comma + // Arguments: + // - MenuFlyoutItem that will be displayed, and a KeyChord to map an accelerator + void App::_SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord) + { + // work around https://github.com/microsoft/microsoft-ui-xaml/issues/708 in case of VK_OEM_COMMA + if (keyChord.Vkey() != VK_OEM_COMMA) + { + // use the XAML shortcut to give us the automatic capabilities + auto menuShortcut = Windows::UI::Xaml::Input::KeyboardAccelerator{}; + + // TODO: Modify this when https://github.com/microsoft/terminal/issues/877 is resolved + menuShortcut.Key(static_cast(keyChord.Vkey())); + + // inspect the modifiers from the KeyChord and set the flags int he XAML value + auto modifiers = AppKeyBindings::ConvertVKModifiers(keyChord.Modifiers()); + + // add the modifiers to the shortcut + menuShortcut.Modifiers(modifiers); + + // add to the menu + menuItem.KeyboardAccelerators().Append(menuShortcut); + } + else // we've got a comma, so need to just use the alternate method + { + // extract the modifier and key to a nice format + auto overrideString = AppKeyBindings::FormatOverrideShortcutText(keyChord.Modifiers()); + menuItem.KeyboardAcceleratorTextOverride(overrideString + L" ,"); + } + } + // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 358a43a69ed..3e2ad18e50e 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -64,6 +64,7 @@ namespace winrt::TerminalApp::implementation std::vector> _tabs; std::unique_ptr<::TerminalApp::CascadiaSettings> _settings; + std::unique_ptr _keyBindings; bool _loadedInitialSettings; @@ -111,6 +112,7 @@ namespace winrt::TerminalApp::implementation void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme); static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile); + static void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord); }; } diff --git a/src/cascadia/TerminalApp/AppKeyBindings.cpp b/src/cascadia/TerminalApp/AppKeyBindings.cpp index d9a00d42e21..cb9beb8e5f9 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindings.cpp @@ -82,6 +82,7 @@ static const std::map commandNames { { SWITCHTOTAB6_KEY, ShortcutAction::SwitchToTab6 }, { SWITCHTOTAB7_KEY, ShortcutAction::SwitchToTab7 }, { SWITCHTOTAB8_KEY, ShortcutAction::SwitchToTab8 }, + { OPENSETTINGS_KEY, ShortcutAction::OpenSettings }, }; namespace winrt::TerminalApp::implementation @@ -92,6 +93,15 @@ namespace winrt::TerminalApp::implementation _keyShortcuts[chord] = action; } + Microsoft::Terminal::Settings::KeyChord AppKeyBindings::GetKeyBinding(TerminalApp::ShortcutAction const& action) + { + for (auto& kv : _keyShortcuts) + { + if (kv.second == action) return kv.first; + } + return { nullptr }; + } + bool AppKeyBindings::TryKeyChord(const Settings::KeyChord& kc) { const auto keyIter = _keyShortcuts.find(kc); @@ -344,4 +354,56 @@ namespace winrt::TerminalApp::implementation return bindingsArray; } + + // Method Description: + // - Takes the KeyModifier flags from Terminal and maps them to the WinRT types which are used by XAML + // Return Value: + // - a Windows::System::VirtualKeyModifiers object with the flags of which modifiers used. + Windows::System::VirtualKeyModifiers AppKeyBindings::ConvertVKModifiers(Settings::KeyModifiers modifiers) + { + Windows::System::VirtualKeyModifiers keyModifiers = Windows::System::VirtualKeyModifiers::None; + + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl)) + { + keyModifiers |= Windows::System::VirtualKeyModifiers::Control; + } + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift)) + { + keyModifiers |= Windows::System::VirtualKeyModifiers::Shift; + } + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt)) + { + // note: Menu is the Alt VK_MENU + keyModifiers |= Windows::System::VirtualKeyModifiers::Menu; + } + + return keyModifiers; + } + + // Method Description: + // - Handles the special case of providing a text override for the UI shortcut due to VK_OEM_COMMA issue. + // Looks at the flags from the KeyChord modifiers and provides a concatenated string value of all + // in the same order that XAML would put them as well. + // Return Value: + // - a WinRT hstring representation of the key modifiers for the shortcut + //NOTE: This needs to be localized with https://github.com/microsoft/terminal/issues/794 if XAML framework issue not resolved before then + winrt::hstring AppKeyBindings::FormatOverrideShortcutText(Settings::KeyModifiers modifiers) + { + std::wstring buffer{ L"" }; + + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl)) + { + buffer += L"Ctrl+"; + } + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift)) + { + buffer += L"Shift+"; + } + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt)) + { + buffer += L"Alt+"; + } + + return winrt::hstring{ buffer }; + } } diff --git a/src/cascadia/TerminalApp/AppKeyBindings.h b/src/cascadia/TerminalApp/AppKeyBindings.h index d3f0290e1c1..5fe8030bf9a 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.h +++ b/src/cascadia/TerminalApp/AppKeyBindings.h @@ -34,9 +34,12 @@ namespace winrt::TerminalApp::implementation static TerminalApp::AppKeyBindings FromJson(Windows::Data::Json::JsonArray const& json); Windows::Data::Json::JsonArray ToJson(); + static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers); + static winrt::hstring FormatOverrideShortcutText(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers); bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc); void SetKeyBinding(TerminalApp::ShortcutAction const& action, winrt::Microsoft::Terminal::Settings::KeyChord const& chord); + Microsoft::Terminal::Settings::KeyChord GetKeyBinding(TerminalApp::ShortcutAction const& action); DECLARE_EVENT(CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs); DECLARE_EVENT(PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs); diff --git a/src/cascadia/TerminalApp/AppKeyBindings.idl b/src/cascadia/TerminalApp/AppKeyBindings.idl index b2d5144790c..7fb23d2e237 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.idl +++ b/src/cascadia/TerminalApp/AppKeyBindings.idl @@ -67,6 +67,7 @@ namespace TerminalApp static AppKeyBindings FromJson(Windows.Data.Json.JsonArray json); void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord); + Microsoft.Terminal.Settings.KeyChord GetKeyBinding(ShortcutAction action); event CopyTextEventArgs CopyText; event PasteTextEventArgs PasteText;