From 004da88bba17d93309152cd53b20c1cb694c878d Mon Sep 17 00:00:00 2001 From: mpela81 <1140981+mpela81@users.noreply.github.com> Date: Thu, 15 Oct 2020 04:40:44 -0700 Subject: [PATCH] Add Close... option to Tab context menu (#7728) ## Summary of the Pull Request Add a "Close..." option to the tab context menu, with nested entries to close tabs to the right and close other tabs (actions already available) ![immagine](https://user-images.githubusercontent.com/1140981/94178005-c7e03600-fe9a-11ea-9f87-c6f4895d4cf3.png) ## References #1912 ## PR Checklist * [x] Closes #5524 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [ ] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Detailed Description of the Pull Request / Additional comments First contribution :slightly_smiling_face: Tried to follow some suggestions from https://github.com/microsoft/terminal/issues/1912#issuecomment-667079311 ## Validation Steps Performed --- .../Resources/en-US/Resources.resw | 11 ++- src/cascadia/TerminalApp/Tab.cpp | 76 ++++++++++++++++++- src/cascadia/TerminalApp/Tab.h | 16 +++- src/cascadia/TerminalApp/Tab.idl | 3 + src/cascadia/TerminalApp/TerminalPage.cpp | 9 ++- .../TerminalSettingsModel/ActionArgs.cpp | 2 + .../TerminalSettingsModel/ActionArgs.h | 6 ++ .../TerminalSettingsModel/ActionArgs.idl | 2 + 8 files changed, 119 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 325f19ee20b..446bc161976 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -177,8 +177,17 @@ Do you want to close all tabs? + + Close... + + + Close Tabs to the Right + + + Close Other Tabs + - Close + Close Tab Color... diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index f571457d57a..42101c34810 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -593,10 +593,61 @@ namespace winrt::TerminalApp::implementation newTabFlyout.Items().Append(chooseColorMenuItem); newTabFlyout.Items().Append(renameTabMenuItem); newTabFlyout.Items().Append(menuSeparator); + newTabFlyout.Items().Append(_CreateCloseSubMenu()); newTabFlyout.Items().Append(closeTabMenuItem); _tabViewItem.ContextFlyout(newTabFlyout); } + // Method Description: + // - Creates a sub-menu containing menu items to close multiple tabs + // Arguments: + // - + // Return Value: + // - the created MenuFlyoutSubItem + Controls::MenuFlyoutSubItem Tab::_CreateCloseSubMenu() + { + auto weakThis{ get_weak() }; + + // Close tabs after + _closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) { + if (auto tab{ weakThis.get() }) + { + tab->_CloseTabsAfter(); + } + }); + _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter")); + + // Close other tabs + _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) { + if (auto tab{ weakThis.get() }) + { + tab->_CloseOtherTabs(); + } + }); + _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther")); + + Controls::MenuFlyoutSubItem closeSubMenu; + closeSubMenu.Text(RS_(L"TabCloseSubMenu")); + closeSubMenu.Items().Append(_closeTabsAfterMenuItem); + closeSubMenu.Items().Append(_closeOtherTabsMenuItem); + + return closeSubMenu; + } + + // Method Description: + // - Enable the Close menu items based on tab index and total number of tabs + // Arguments: + // - + // Return Value: + // - + void Tab::_EnableCloseMenuItems() + { + // close other tabs is enabled only if there are other tabs + _closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1); + // close tabs after is enabled only if there are other tabs on the right + _closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1); + } + // Method Description: // - This will update the contents of our TabViewItem for our current state. // - If we're not in a rename, we'll set the Header of the TabViewItem to @@ -1053,12 +1104,35 @@ namespace winrt::TerminalApp::implementation SwitchToTabCommand(command); } - void Tab::UpdateTabViewIndex(const uint32_t idx) + void Tab::_CloseTabsAfter() + { + CloseTabsAfterArgs args{ _TabViewIndex }; + ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args }; + + _dispatch.DoAction(closeTabsAfter); + } + + void Tab::_CloseOtherTabs() + { + CloseOtherTabsArgs args{ _TabViewIndex }; + ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args }; + + _dispatch.DoAction(closeOtherTabs); + } + + void Tab::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs) { TabViewIndex(idx); + TabViewNumTabs(numTabs); + _EnableCloseMenuItems(); SwitchToTabCommand().Action().Args().as().TabIndex(idx); } + void Tab::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch) + { + _dispatch = dispatch; + } + DEFINE_EVENT(Tab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>); DEFINE_EVENT(Tab, ColorSelected, _colorSelected, winrt::delegate); DEFINE_EVENT(Tab, ColorCleared, _colorCleared, winrt::delegate<>); diff --git a/src/cascadia/TerminalApp/Tab.h b/src/cascadia/TerminalApp/Tab.h index 02ce977d874..4dae6bc8744 100644 --- a/src/cascadia/TerminalApp/Tab.h +++ b/src/cascadia/TerminalApp/Tab.h @@ -68,7 +68,9 @@ namespace winrt::TerminalApp::implementation int GetLeafPaneCount() const noexcept; - void UpdateTabViewIndex(const uint32_t idx); + void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs); + + void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch); WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); @@ -83,6 +85,8 @@ namespace winrt::TerminalApp::implementation // The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector. // This is needed since Tab is going to be managing its own SwitchToTab command. OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewIndex, _PropertyChangedHandlers, 0); + // The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector. + OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewNumTabs, _PropertyChangedHandlers, 0); private: std::shared_ptr _rootPane{ nullptr }; @@ -92,6 +96,8 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{}; std::optional _themeTabColor{}; std::optional _runtimeTabColor{}; + winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{}; + winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{}; bool _focused{ false }; winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr }; @@ -100,10 +106,15 @@ namespace winrt::TerminalApp::implementation bool _inRename{ false }; winrt::Windows::UI::Xaml::Controls::TextBox::LayoutUpdated_revoker _tabRenameBoxLayoutUpdatedRevoker; + winrt::TerminalApp::ShortcutActionDispatch _dispatch; + void _MakeTabViewItem(); void _Focus(); void _CreateContextMenu(); + winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _CreateCloseSubMenu(); + void _EnableCloseMenuItems(); + void _RefreshVisualState(); void _BindEventHandlers(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control) noexcept; @@ -123,6 +134,9 @@ namespace winrt::TerminalApp::implementation void _MakeSwitchToTabCommand(); + void _CloseTabsAfter(); + void _CloseOtherTabs(); + friend class ::TerminalAppLocalTests::TabTests; }; } diff --git a/src/cascadia/TerminalApp/Tab.idl b/src/cascadia/TerminalApp/Tab.idl index b02e4da0f4b..db9746c99ee 100644 --- a/src/cascadia/TerminalApp/Tab.idl +++ b/src/cascadia/TerminalApp/Tab.idl @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "ShortcutActionDispatch.idl"; namespace TerminalApp { @@ -9,5 +10,7 @@ namespace TerminalApp Windows.UI.Xaml.Controls.IconSource IconSource { get; }; Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand { get; }; UInt32 TabViewIndex { get; }; + + void SetDispatch(ShortcutActionDispatch dispatch); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 12c0fbf106f..68a7462d226 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -673,8 +673,10 @@ namespace winrt::TerminalApp::implementation auto newTabImpl = winrt::make_self(profileGuid, term); _tabs.Append(*newTabImpl); + newTabImpl->SetDispatch(*_actionDispatch); + // Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command. - newTabImpl->UpdateTabViewIndex(_tabs.Size() - 1); + _UpdateTabIndices(); // Hookup our event handlers to the new terminal _RegisterTerminalEvents(term, *newTabImpl); @@ -2542,9 +2544,10 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_UpdateTabIndices() { - for (uint32_t i = 0; i < _tabs.Size(); ++i) + const uint32_t size = _tabs.Size(); + for (uint32_t i = 0; i < size; ++i) { - _GetStrongTabImpl(i)->UpdateTabViewIndex(i); + _GetStrongTabImpl(i)->UpdateTabViewIndex(i, size); } } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 1ce89a1dc6c..cc9b97d83ab 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -20,6 +20,8 @@ #include "SetTabColorArgs.g.cpp" #include "RenameTabArgs.g.cpp" #include "ExecuteCommandlineArgs.g.cpp" +#include "CloseOtherTabsArgs.g.cpp" +#include "CloseTabsAfterArgs.g.cpp" #include diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 209fef24c24..8fc53a4b6e4 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -505,6 +505,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct CloseOtherTabsArgs : public CloseOtherTabsArgsT { CloseOtherTabsArgs() = default; + CloseOtherTabsArgs(uint32_t& tabIndex) : + _Index{ tabIndex } {}; GETSET_PROPERTY(Windows::Foundation::IReference, Index, nullptr); static constexpr std::string_view IndexKey{ "index" }; @@ -533,6 +535,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct CloseTabsAfterArgs : public CloseTabsAfterArgsT { CloseTabsAfterArgs() = default; + CloseTabsAfterArgs(uint32_t& tabIndex) : + _Index{ tabIndex } {}; GETSET_PROPERTY(Windows::Foundation::IReference, Index, nullptr); static constexpr std::string_view IndexKey{ "index" }; @@ -567,4 +571,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation BASIC_FACTORY(NewTabArgs); BASIC_FACTORY(SplitPaneArgs); BASIC_FACTORY(ExecuteCommandlineArgs); + BASIC_FACTORY(CloseOtherTabsArgs); + BASIC_FACTORY(CloseTabsAfterArgs); } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 407dbd6d4cb..3c2e87f37e0 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -141,11 +141,13 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass CloseOtherTabsArgs : IActionArgs { + CloseOtherTabsArgs(UInt32 tabIndex); Windows.Foundation.IReference Index { get; }; }; [default_interface] runtimeclass CloseTabsAfterArgs : IActionArgs { + CloseTabsAfterArgs(UInt32 tabIndex); Windows.Foundation.IReference Index { get; }; }; }