diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index e9dd00e11..9fb190ed7 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -521,13 +521,8 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleOpenTabSearch(const IInspectable& /*sender*/, const ActionEventArgs& args) { - // Tab search is always in-order. - CommandPalette().SetTabs(_tabs, true); - - auto opt = _GetFocusedTabIndex(); - uint32_t startIdx = opt.value_or(0); - - CommandPalette().EnableTabSwitcherMode(true, startIdx); + CommandPalette().SetTabs(_tabs, _mruTabs); + CommandPalette().EnableTabSearchMode(); CommandPalette().Visibility(Visibility::Visible); args.Handled(true); diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp index 8b55a0e7b..2279b50cc 100644 --- a/src/cascadia/TerminalApp/CommandPalette.cpp +++ b/src/cascadia/TerminalApp/CommandPalette.cpp @@ -31,6 +31,7 @@ namespace winrt::TerminalApp::implementation _currentNestedCommands = winrt::single_threaded_vector(); _allCommands = winrt::single_threaded_vector(); _tabActions = winrt::single_threaded_vector(); + _mruTabActions = winrt::single_threaded_vector(); _commandLineHistory = winrt::single_threaded_vector(); _switchToMode(CommandPaletteMode::ActionMode); @@ -51,6 +52,12 @@ namespace winrt::TerminalApp::implementation RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) { if (Visibility() == Visibility::Visible) { + if (_filteredActionsView().Items().Size() == 0 && _filteredActions.Size() > 0) + { + // Force immediate binding update so we can select an item + Bindings->Update(); + } + if (_currentMode == CommandPaletteMode::TabSwitchMode) { _searchBox().Visibility(Visibility::Collapsed); @@ -66,7 +73,6 @@ namespace winrt::TerminalApp::implementation { _filteredActionsView().SelectedIndex(0); _searchBox().Focus(FocusState::Programmatic); - _updateFilteredActions(); } TraceLoggingWrite( @@ -112,7 +118,7 @@ namespace winrt::TerminalApp::implementation // - void CommandPalette::SelectNextItem(const bool moveDown) { - const auto selected = _filteredActionsView().SelectedIndex(); + auto selected = _filteredActionsView().SelectedIndex(); const int numItems = ::base::saturated_cast(_filteredActionsView().Items().Size()); // Do not try to select an item if @@ -561,7 +567,7 @@ namespace winrt::TerminalApp::implementation case CommandPaletteMode::TabSearchMode: return _tabActions; case CommandPaletteMode::TabSwitchMode: - return _tabActions; + return _tabSwitcherMode == TabSwitcherMode::MostRecentlyUsed ? _mruTabActions : _tabActions; case CommandPaletteMode::CommandlineMode: return _commandLineHistory; default: @@ -854,31 +860,40 @@ namespace winrt::TerminalApp::implementation _allCommands.Append(filteredCommand); } - _updateFilteredActions(); + if (Visibility() == Visibility::Visible && _currentMode == CommandPaletteMode::ActionMode) + { + _updateFilteredActions(); + } } - void CommandPalette::SetTabs(Collections::IVector const& tabs, const bool clearList) + // Method Description: + // - Replaces a list of filtered commands in the target collection with new + // commands based on the tabs in the source collection. + // Although the source observable we still don't register on it, + // so the palette user will need to reset the binding manually every time + // the source collection changes + // Arguments: + // - source: the tabs to use for creation filtered commands + // - target: the collection to store newly created filtered commands + // Return Value: + // - + void CommandPalette::_bindTabs( + Windows::Foundation::Collections::IObservableVector const& source, + Windows::Foundation::Collections::IVector const& target) { - _tabActions.Clear(); - for (const auto& tab : tabs) + target.Clear(); + for (const auto& tab : source) { auto tabPaletteItem{ winrt::make(tab) }; auto filteredCommand{ winrt::make(tabPaletteItem) }; - _tabActions.Append(filteredCommand); + target.Append(filteredCommand); } + } - // The smooth remove/add animations that happen during - // UpdateFilteredActions don't work very well with changing the tab - // order, because of the sheer amount of remove/adds. So, let's just - // clear & rebuild the list when we change the set of tabs. - // - // Some callers might actually want smooth updating, like when the list - // of tabs changes. - if (clearList && _currentMode == CommandPaletteMode::TabSwitchMode) - { - _filteredActions.Clear(); - } - _updateFilteredActions(); + void CommandPalette::SetTabs(Collections::IObservableVector const& tabs, Collections::IObservableVector const& mruTabs) + { + _bindTabs(tabs, _tabActions); + _bindTabs(mruTabs, _mruTabActions); } void CommandPalette::EnableCommandPaletteMode(CommandPaletteLaunchMode const launchMode) @@ -888,26 +903,11 @@ namespace winrt::TerminalApp::implementation CommandPaletteMode::ActionMode; _switchToMode(mode); - _updateFilteredActions(); } void CommandPalette::_switchToMode(CommandPaletteMode mode) { - // The smooth remove/add animations that happen during - // UpdateFilteredActions don't work very well when switching between - // modes because of the sheer amount of remove/adds. So, let's just - // clear + append when switching between modes. - if (mode != _currentMode) - { - _currentMode = mode; - _filteredActions.Clear(); - auto commandsToFilter = _commandsToFilter(); - - for (auto action : commandsToFilter) - { - _filteredActions.Append(action); - } - } + _currentMode = mode; ParsedCommandLineText(L""); _searchBox().Text(L""); @@ -940,6 +940,13 @@ namespace winrt::TerminalApp::implementation PrefixCharacter(L">"); break; } + + // The smooth remove/add animations that happen during + // UpdateFilteredActions don't work very well when switching between + // modes because of the sheer amount of remove/adds. So, let's just + // clear + append when switching between modes. + _filteredActions.Clear(); + _updateFilteredActions(); } // Method Description: @@ -956,27 +963,34 @@ namespace winrt::TerminalApp::implementation winrt::hstring searchText{ _getTrimmedInput() }; auto commandsToFilter = _commandsToFilter(); - for (const auto& action : commandsToFilter) - { - // Update filter for all commands - // This will modify the highlighting but will also lead to re-computation of weight (and consequently sorting). - // Pay attention that it already updates the highlighting in the UI - action.UpdateFilter(searchText); - // if there is active search we skip commands with 0 weight - if (searchText.empty() || action.Weight() > 0) + if (_currentMode == CommandPaletteMode::TabSwitchMode) + { + std::copy(begin(commandsToFilter), end(commandsToFilter), std::back_inserter(actions)); + } + else if (_currentMode == CommandPaletteMode::TabSearchMode || _currentMode == CommandPaletteMode::ActionMode || _currentMode == CommandPaletteMode::CommandlineMode) + { + for (const auto& action : commandsToFilter) { - actions.push_back(action); + // Update filter for all commands + // This will modify the highlighting but will also lead to re-computation of weight (and consequently sorting). + // Pay attention that it already updates the highlighting in the UI + action.UpdateFilter(searchText); + + // if there is active search we skip commands with 0 weight + if (searchText.empty() || action.Weight() > 0) + { + actions.push_back(action); + } } } - // We want to present the commands sorted, - // unless we are in the TabSwitcherMode and TabSearchMode, - // in which we want to preserve the original order (to be aligned with the tab view) - if (_currentMode != CommandPaletteMode::TabSearchMode && _currentMode != CommandPaletteMode::TabSwitchMode && _currentMode != CommandPaletteMode::CommandlineMode) + // We want to present the commands sorted + if (_currentMode == CommandPaletteMode::ActionMode) { std::sort(actions.begin(), actions.end(), FilteredCommand::Compare); } + return actions; } @@ -1051,20 +1065,18 @@ namespace winrt::TerminalApp::implementation _currentNestedCommands.Clear(); } - void CommandPalette::EnableTabSwitcherMode(const bool searchMode, const uint32_t startIdx) + void CommandPalette::EnableTabSwitcherMode(const uint32_t startIdx, TabSwitcherMode tabSwitcherMode) { - _switcherStartIdx = startIdx; - - if (searchMode) - { - _switchToMode(CommandPaletteMode::TabSearchMode); - } - else - { - _switchToMode(CommandPaletteMode::TabSwitchMode); - } - - _updateFilteredActions(); + // The _switcherStartIdx field allows us to select the current tab + // We need to take it into account only in the in-order mode, + // as an MRU mode the current tab is on top by definition. + _switcherStartIdx = tabSwitcherMode == TabSwitcherMode::InOrder ? startIdx : 0; + _tabSwitcherMode = tabSwitcherMode; + _switchToMode(CommandPaletteMode::TabSwitchMode); } + void CommandPalette::EnableTabSearchMode() + { + _switchToMode(CommandPaletteMode::TabSearchMode); + } } diff --git a/src/cascadia/TerminalApp/CommandPalette.h b/src/cascadia/TerminalApp/CommandPalette.h index 39f655a64..108c67bb8 100644 --- a/src/cascadia/TerminalApp/CommandPalette.h +++ b/src/cascadia/TerminalApp/CommandPalette.h @@ -31,11 +31,9 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IObservableVector FilteredActions(); void SetCommands(Windows::Foundation::Collections::IVector const& actions); - void SetTabs(Windows::Foundation::Collections::IVector const& tabs, const bool clearList); + void SetTabs(Windows::Foundation::Collections::IObservableVector const& tabs, Windows::Foundation::Collections::IObservableVector const& mruTabs); void SetKeyMap(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap); - void EnableCommandPaletteMode(Microsoft::Terminal::Settings::Model::CommandPaletteLaunchMode const launchMode); - bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); void SelectNextItem(const bool moveDown); @@ -45,8 +43,9 @@ namespace winrt::TerminalApp::implementation void ScrollToTop(); void ScrollToBottom(); - // Tab Switcher - void EnableTabSwitcherMode(const bool searchMode, const uint32_t startIdx); + void EnableCommandPaletteMode(Microsoft::Terminal::Settings::Model::CommandPaletteLaunchMode const launchMode); + void EnableTabSwitcherMode(const uint32_t startIdx, Microsoft::Terminal::Settings::Model::TabSwitcherMode tabSwitcherMode); + void EnableTabSearchMode(); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); OBSERVABLE_GETSET_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers); @@ -112,7 +111,11 @@ namespace winrt::TerminalApp::implementation // Tab Switcher Windows::Foundation::Collections::IVector _tabActions{ nullptr }; + Windows::Foundation::Collections::IVector _mruTabActions{ nullptr }; + Microsoft::Terminal::Settings::Model::TabSwitcherMode _tabSwitcherMode; uint32_t _switcherStartIdx; + + void _bindTabs(Windows::Foundation::Collections::IObservableVector const& source, Windows::Foundation::Collections::IVector const& target); void _anchorKeyUpHandler(); winrt::Windows::UI::Xaml::Controls::ListView::SizeChanged_revoker _sizeChangedRevoker; diff --git a/src/cascadia/TerminalApp/CommandPalette.idl b/src/cascadia/TerminalApp/CommandPalette.idl index 28f0f89c0..a806e8e30 100644 --- a/src/cascadia/TerminalApp/CommandPalette.idl +++ b/src/cascadia/TerminalApp/CommandPalette.idl @@ -22,13 +22,16 @@ namespace TerminalApp Windows.Foundation.Collections.IObservableVector FilteredActions { get; }; void SetCommands(Windows.Foundation.Collections.IVector actions); - void SetTabs(Windows.Foundation.Collections.IVector tabs, Boolean clearList); + + void SetTabs(Windows.Foundation.Collections.IObservableVector tabs, Windows.Foundation.Collections.IObservableVector mruTabs); + void SetKeyMap(Microsoft.Terminal.Settings.Model.KeyMapping keymap); - void EnableCommandPaletteMode(Microsoft.Terminal.Settings.Model.CommandPaletteLaunchMode launchMode); void SelectNextItem(Boolean moveDown); - void EnableTabSwitcherMode(Boolean searchMode, UInt32 startIdx); + void EnableCommandPaletteMode(Microsoft.Terminal.Settings.Model.CommandPaletteLaunchMode launchMode); + void EnableTabSwitcherMode(UInt32 startIdx, Microsoft.Terminal.Settings.Model.TabSwitcherMode tabSwitcherMode); + void EnableTabSearchMode(); event Windows.Foundation.TypedEventHandler SwitchToTabRequested; event Windows.Foundation.TypedEventHandler DispatchCommandRequested; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 58a050366..209bb9095 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -43,7 +43,7 @@ namespace winrt::TerminalApp::implementation { TerminalPage::TerminalPage() : _tabs{ winrt::single_threaded_observable_vector() }, - _mruTabs{ winrt::single_threaded_vector() }, + _mruTabs{ winrt::single_threaded_observable_vector() }, _startupActions{ winrt::single_threaded_vector() }, _hostingHwnd{} { @@ -1323,49 +1323,27 @@ namespace winrt::TerminalApp::implementation // - Sets focus to the tab to the right or left the currently selected tab. void TerminalPage::_SelectNextTab(const bool bMoveRight) { + const auto index{ _GetFocusedTabIndex().value_or(0) }; const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode(); - const bool useInOrderTabIndex = tabSwitchMode != TabSwitcherMode::MostRecentlyUsed; - - // First, determine what the index of the newly selected tab should be. - // This changes if we're doing an in-order traversal vs a MRU traversal. - auto newTabIndex = 0; - if (useInOrderTabIndex) + if (tabSwitchMode == TabSwitcherMode::Disabled) { - // Determine what the next in-order tab index is - if (auto index{ _GetFocusedTabIndex() }) - { - uint32_t tabCount = _tabs.Size(); - // Wraparound math. By adding tabCount and then calculating - // modulo tabCount, we clamp the values to the range [0, - // tabCount) while still supporting moving leftward from 0 to - // tabCount - 1. - newTabIndex = ((tabCount + *index + (bMoveRight ? 1 : -1)) % tabCount); - } + uint32_t tabCount = _tabs.Size(); + // Wraparound math. By adding tabCount and then calculating + // modulo tabCount, we clamp the values to the range [0, + // tabCount) while still supporting moving leftward from 0 to + // tabCount - 1. + const auto newTabIndex = ((tabCount + index + (bMoveRight ? 1 : -1)) % tabCount); + _SelectTab(newTabIndex); } else { - // Determine what the next "most recently used" index is. - // In this case, our focused tab index (in the MRU ordering) is - // always 0. So, going next should go to index 1, and going prev - // should wrap to the end. - uint32_t tabCount = _mruTabs.Size(); - newTabIndex = ((tabCount + (bMoveRight ? 1 : -1)) % tabCount); - } - - const bool useTabSwitcher = tabSwitchMode != TabSwitcherMode::Disabled; - - if (useTabSwitcher) - { - CommandPalette().SetTabs(useInOrderTabIndex ? _tabs : _mruTabs, true); + CommandPalette().SetTabs(_tabs, _mruTabs); // Otherwise, set up the tab switcher in the selected mode, with // the given ordering, and make it visible. - CommandPalette().EnableTabSwitcherMode(false, newTabIndex); + CommandPalette().EnableTabSwitcherMode(index, tabSwitchMode); CommandPalette().Visibility(Visibility::Visible); - } - else if (auto index{ _GetFocusedTabIndex() }) - { - _SelectTab(newTabIndex); + CommandPalette().SelectNextItem(bMoveRight); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 58709e86e..468f70178 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -112,7 +112,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; Windows::Foundation::Collections::IObservableVector _tabs; - Windows::Foundation::Collections::IVector _mruTabs; + Windows::Foundation::Collections::IObservableVector _mruTabs; static winrt::com_ptr _GetTerminalTabImpl(const TerminalApp::TabBase& tab); void _UpdateTabIndices();