diff --git a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp index 8e6354bf74cd..765801f29069 100644 --- a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT license. #include "pch.h" -#include "../TerminalApp/TerminalSettings.h" +#include "../TerminalApp/CommandLinePaletteItem.h" #include "../TerminalApp/CommandPalette.h" using namespace Microsoft::Console; @@ -29,32 +29,10 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyHighlighting() { - const std::string settingsJson{ R"( - { - "defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "profiles": [ - { - "name": "profile0", - "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "historySize": 1, - "commandline": "cmd.exe" - } - ], - "keybindings": [ - { "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" }, "name": "AAAAAABBBBBBCCC" } - ] - })" }; - - CascadiaSettings settings{ til::u8u16(settingsJson) }; - const auto commands = settings.GlobalSettings().Commands(); - VERIFY_ARE_EQUAL(1u, commands.Size()); - - const auto command = commands.Lookup(L"AAAAAABBBBBBCCC"); - VERIFY_IS_NOT_NULL(command); - VERIFY_ARE_EQUAL(command.Name(), L"AAAAAABBBBBBCCC"); + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; { Log::Comment(L"Testing command name segmentation with no filter"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); auto segments = filteredCommand->_computeHighlightedName().Segments(); VERIFY_ARE_EQUAL(segments.Size(), 1u); VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); @@ -62,7 +40,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing command name segmentation with empty filter"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L""; auto segments = filteredCommand->_computeHighlightedName().Segments(); VERIFY_ARE_EQUAL(segments.Size(), 1u); @@ -71,7 +49,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing command name segmentation with filter equals to the string"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"AAAAAABBBBBBCCC"; auto segments = filteredCommand->_computeHighlightedName().Segments(); VERIFY_ARE_EQUAL(segments.Size(), 1u); @@ -80,7 +58,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing command name segmentation with filter with first character matching"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"A"; auto segments = filteredCommand->_computeHighlightedName().Segments(); VERIFY_ARE_EQUAL(segments.Size(), 2u); @@ -91,7 +69,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing command name segmentation with filter with other case"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"a"; auto segments = filteredCommand->_computeHighlightedName().Segments(); VERIFY_ARE_EQUAL(segments.Size(), 2u); @@ -102,7 +80,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing command name segmentation with filter matching several characters"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"ab"; auto segments = filteredCommand->_computeHighlightedName().Segments(); VERIFY_ARE_EQUAL(segments.Size(), 4u); @@ -117,7 +95,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing command name segmentation with non matching filter"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"abcd"; auto segments = filteredCommand->_computeHighlightedName().Segments(); VERIFY_ARE_EQUAL(segments.Size(), 1u); @@ -128,39 +106,17 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyWeight() { - const std::string settingsJson{ R"( - { - "defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "profiles": [ - { - "name": "profile0", - "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "historySize": 1, - "commandline": "cmd.exe" - } - ], - "keybindings": [ - { "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" }, "name": "AAAAAABBBBBBCCC" } - ] - })" }; - - CascadiaSettings settings{ til::u8u16(settingsJson) }; - const auto commands = settings.GlobalSettings().Commands(); - VERIFY_ARE_EQUAL(1u, commands.Size()); - - const auto command = commands.Lookup(L"AAAAAABBBBBBCCC"); - VERIFY_IS_NOT_NULL(command); - VERIFY_ARE_EQUAL(command.Name(), L"AAAAAABBBBBBCCC"); + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; { Log::Comment(L"Testing weight of command with no filter"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); auto weight = filteredCommand->_computeWeight(); VERIFY_ARE_EQUAL(weight, 0); } { Log::Comment(L"Testing weight of command with empty filter"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L""; filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); auto weight = filteredCommand->_computeWeight(); @@ -168,7 +124,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing weight of command with filter equals to the string"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"AAAAAABBBBBBCCC"; filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); auto weight = filteredCommand->_computeWeight(); @@ -176,7 +132,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing weight of command with filter with first character matching"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"A"; filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); auto weight = filteredCommand->_computeWeight(); @@ -184,7 +140,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing weight of command with filter with other case"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"a"; filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); auto weight = filteredCommand->_computeWeight(); @@ -192,7 +148,7 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing weight of command with filter matching several characters"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"ab"; filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); auto weight = filteredCommand->_computeWeight(); @@ -202,50 +158,24 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyCompare() { - const std::string settingsJson{ R"( - { - "defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "profiles": [ - { - "name": "profile0", - "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "historySize": 1, - "commandline": "cmd.exe" - } - ], - "keybindings": [ - { "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" }, "name": "AAAAAABBBBBBCCC" }, - { "keys": ["ctrl+b"], "command": { "action": "splitPane", "split": "horizontal" }, "name": "BBBBBCCC" } - ] - })" }; - - CascadiaSettings settings{ til::u8u16(settingsJson) }; - const auto commands = settings.GlobalSettings().Commands(); - VERIFY_ARE_EQUAL(2u, commands.Size()); - - const auto command = commands.Lookup(L"AAAAAABBBBBBCCC"); - VERIFY_IS_NOT_NULL(command); - VERIFY_ARE_EQUAL(command.Name(), L"AAAAAABBBBBBCCC"); - - const auto command2 = commands.Lookup(L"BBBBBCCC"); - VERIFY_IS_NOT_NULL(command2); - VERIFY_ARE_EQUAL(command2.Name(), L"BBBBBCCC"); + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const auto paletteItem2{ winrt::make(L"BBBBBCCC") }; { Log::Comment(L"Testing comparison of commands with no filter"); - const auto filteredCommand = winrt::make_self(command); - const auto filteredCommand2 = winrt::make_self(command2); + const auto filteredCommand = winrt::make_self(paletteItem); + const auto filteredCommand2 = winrt::make_self(paletteItem2); VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight()); VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2)); } { Log::Comment(L"Testing comparison of commands with empty filter"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L""; filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); filteredCommand->_Weight = filteredCommand->_computeWeight(); - const auto filteredCommand2 = winrt::make_self(command2); + const auto filteredCommand2 = winrt::make_self(paletteItem2); filteredCommand2->_Filter = L""; filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName(); filteredCommand2->_Weight = filteredCommand2->_computeWeight(); @@ -255,12 +185,12 @@ namespace TerminalAppLocalTests } { Log::Comment(L"Testing comparison of commands with different weights"); - const auto filteredCommand = winrt::make_self(command); + const auto filteredCommand = winrt::make_self(paletteItem); filteredCommand->_Filter = L"B"; filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); filteredCommand->_Weight = filteredCommand->_computeWeight(); - const auto filteredCommand2 = winrt::make_self(command2); + const auto filteredCommand2 = winrt::make_self(paletteItem2); filteredCommand2->_Filter = L"B"; filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName(); filteredCommand2->_Weight = filteredCommand2->_computeWeight(); @@ -272,32 +202,11 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyCompareIgnoreCase() { - const std::string settingsJson{ R"( - { - "defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "profiles": [ - { - "name": "profile0", - "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", - "historySize": 1, - "commandline": "cmd.exe" - } - ], - "keybindings": [ - { "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" }, "name": "a" }, - { "keys": ["ctrl+b"], "command": { "action": "splitPane", "split": "horizontal" }, "name": "B" } - ] - })" }; - - CascadiaSettings settings{ til::u8u16(settingsJson) }; - const auto commands = settings.GlobalSettings().Commands(); - VERIFY_ARE_EQUAL(2u, commands.Size()); - - const auto command = commands.Lookup(L"a"); - const auto command2 = commands.Lookup(L"B"); + const auto paletteItem{ winrt::make(L"a") }; + const auto paletteItem2{ winrt::make(L"B") }; { - const auto filteredCommand = winrt::make_self(command); - const auto filteredCommand2 = winrt::make_self(command2); + const auto filteredCommand = winrt::make_self(paletteItem); + const auto filteredCommand2 = winrt::make_self(paletteItem2); VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight()); VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2)); diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index bb5003f1757e..b0b391cbd270 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -865,14 +865,14 @@ namespace TerminalAppLocalTests page->_OpenNewTab(newTerminalArgs); page->_OpenNewTab(newTerminalArgs); }); - VERIFY_ARE_EQUAL(4u, page->_mruTabActions.Size()); + VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size()); Log::Comment(L"give alphabetical names to all switch tab actions"); RunOnUIThread([&page]() { - page->_tabs.GetAt(0).SwitchToTabCommand().Name(L"a"); - page->_tabs.GetAt(1).SwitchToTabCommand().Name(L"b"); - page->_tabs.GetAt(2).SwitchToTabCommand().Name(L"c"); - page->_tabs.GetAt(3).SwitchToTabCommand().Name(L"d"); + page->_GetTerminalTabImpl(page->_tabs.GetAt(0))->Title(L"a"); + page->_GetTerminalTabImpl(page->_tabs.GetAt(1))->Title(L"b"); + page->_GetTerminalTabImpl(page->_tabs.GetAt(2))->Title(L"c"); + page->_GetTerminalTabImpl(page->_tabs.GetAt(3))->Title(L"d"); }); Log::Comment(L"Change the tab switch order to MRU switching"); @@ -888,11 +888,11 @@ namespace TerminalAppLocalTests page->_UpdatedSelectedTab(3); }); - VERIFY_ARE_EQUAL(4u, page->_mruTabActions.Size()); - VERIFY_ARE_EQUAL(L"d", page->_mruTabActions.GetAt(0).Name()); - VERIFY_ARE_EQUAL(L"c", page->_mruTabActions.GetAt(1).Name()); - VERIFY_ARE_EQUAL(L"b", page->_mruTabActions.GetAt(2).Name()); - VERIFY_ARE_EQUAL(L"a", page->_mruTabActions.GetAt(3).Name()); + VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size()); + VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title()); + VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title()); + VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title()); + VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title()); Log::Comment(L"Switch to the next MRU tab, which is the third tab"); RunOnUIThread([&page]() { @@ -906,9 +906,9 @@ namespace TerminalAppLocalTests Log::Comment(L"Verify command palette preserves MRU order of tabs"); VERIFY_ARE_EQUAL(4u, palette->_filteredActions.Size()); - VERIFY_ARE_EQUAL(L"d", palette->_filteredActions.GetAt(0).Command().Name()); - VERIFY_ARE_EQUAL(L"c", palette->_filteredActions.GetAt(1).Command().Name()); - VERIFY_ARE_EQUAL(L"b", palette->_filteredActions.GetAt(2).Command().Name()); - VERIFY_ARE_EQUAL(L"a", palette->_filteredActions.GetAt(3).Command().Name()); + VERIFY_ARE_EQUAL(L"d", palette->_filteredActions.GetAt(0).Item().Name()); + VERIFY_ARE_EQUAL(L"c", palette->_filteredActions.GetAt(1).Item().Name()); + VERIFY_ARE_EQUAL(L"b", palette->_filteredActions.GetAt(2).Item().Name()); + VERIFY_ARE_EQUAL(L"a", palette->_filteredActions.GetAt(3).Item().Name()); } } diff --git a/src/cascadia/TerminalApp/ActionPaletteItem.cpp b/src/cascadia/TerminalApp/ActionPaletteItem.cpp new file mode 100644 index 000000000000..2220c2d5d1a1 --- /dev/null +++ b/src/cascadia/TerminalApp/ActionPaletteItem.cpp @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "ActionPaletteItem.h" +#include + +#include "ActionPaletteItem.g.cpp" + +using namespace winrt; +using namespace winrt::TerminalApp; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::System; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +namespace winrt::TerminalApp::implementation +{ + ActionPaletteItem::ActionPaletteItem(Microsoft::Terminal::Settings::Model::Command const& command) : + _Command(command) + { + Name(command.Name()); + KeyChordText(command.KeyChordText()); + Icon(command.Icon()); + + _commandChangedRevoker = command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) { + auto item{ weakThis.get() }; + auto senderCommand{ sender.try_as() }; + + if (item && senderCommand) + { + auto changedProperty = e.PropertyName(); + if (changedProperty == L"Name") + { + item->Name(senderCommand.Name()); + } + else if (changedProperty == L"KeyChordText") + { + item->KeyChordText(senderCommand.KeyChordText()); + } + else if (changedProperty == L"Icon") + { + item->Icon(senderCommand.Icon()); + } + } + }); + } +} diff --git a/src/cascadia/TerminalApp/ActionPaletteItem.h b/src/cascadia/TerminalApp/ActionPaletteItem.h new file mode 100644 index 000000000000..91e0ccb59681 --- /dev/null +++ b/src/cascadia/TerminalApp/ActionPaletteItem.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "PaletteItem.h" +#include "ActionPaletteItem.g.h" +#include "inc/cppwinrt_utils.h" + +namespace winrt::TerminalApp::implementation +{ + struct ActionPaletteItem : ActionPaletteItemT + { + ActionPaletteItem() = default; + ActionPaletteItem(Microsoft::Terminal::Settings::Model::Command const& command); + + GETSET_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr); + + private: + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _commandChangedRevoker; + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(ActionPaletteItem); +} diff --git a/src/cascadia/TerminalApp/ActionPaletteItem.idl b/src/cascadia/TerminalApp/ActionPaletteItem.idl new file mode 100644 index 000000000000..3858ebdfda36 --- /dev/null +++ b/src/cascadia/TerminalApp/ActionPaletteItem.idl @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "PaletteItem.idl"; + +namespace TerminalApp +{ + [default_interface] runtimeclass ActionPaletteItem : PaletteItem + { + ActionPaletteItem(Microsoft.Terminal.Settings.Model.Command command); + + Microsoft.Terminal.Settings.Model.Command Command { get; }; + } +} diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 469d0a119e5d..a558d9e162ca 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -536,7 +536,7 @@ namespace winrt::TerminalApp::implementation const ActionEventArgs& args) { // Tab search is always in-order. - _UpdatePaletteWithInOrderTabs(); + CommandPalette().SetTabs(_tabs, true); auto opt = _GetFocusedTabIndex(); uint32_t startIdx = opt.value_or(0); diff --git a/src/cascadia/TerminalApp/CommandLinePaletteItem.cpp b/src/cascadia/TerminalApp/CommandLinePaletteItem.cpp new file mode 100644 index 000000000000..b8ede1cd9abb --- /dev/null +++ b/src/cascadia/TerminalApp/CommandLinePaletteItem.cpp @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "CommandLinePaletteItem.h" +#include + +#include "CommandLinePaletteItem.g.cpp" + +using namespace winrt; +using namespace winrt::TerminalApp; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::System; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +namespace winrt::TerminalApp::implementation +{ + CommandLinePaletteItem::CommandLinePaletteItem(winrt::hstring const& commandLine) : + _CommandLine(commandLine) + { + Name(commandLine); + } +} diff --git a/src/cascadia/TerminalApp/CommandLinePaletteItem.h b/src/cascadia/TerminalApp/CommandLinePaletteItem.h new file mode 100644 index 000000000000..9251456e5902 --- /dev/null +++ b/src/cascadia/TerminalApp/CommandLinePaletteItem.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "PaletteItem.h" +#include "CommandLinePaletteItem.g.h" +#include "inc/cppwinrt_utils.h" + +namespace winrt::TerminalApp::implementation +{ + struct CommandLinePaletteItem : CommandLinePaletteItemT + { + CommandLinePaletteItem() = default; + CommandLinePaletteItem(winrt::hstring const& commandLine); + + GETSET_PROPERTY(winrt::hstring, CommandLine); + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(CommandLinePaletteItem); +} diff --git a/src/cascadia/TerminalApp/CommandLinePaletteItem.idl b/src/cascadia/TerminalApp/CommandLinePaletteItem.idl new file mode 100644 index 000000000000..5b1244aae999 --- /dev/null +++ b/src/cascadia/TerminalApp/CommandLinePaletteItem.idl @@ -0,0 +1,12 @@ +import "PaletteItem.idl"; +import "TabBase.idl"; + +namespace TerminalApp +{ + [default_interface] runtimeclass CommandLinePaletteItem : PaletteItem + { + CommandLinePaletteItem(String commandLine); + + String CommandLine { get; }; + } +} diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp index 2cd6a8cf54cb..d1886a40a022 100644 --- a/src/cascadia/TerminalApp/CommandPalette.cpp +++ b/src/cascadia/TerminalApp/CommandPalette.cpp @@ -2,6 +2,9 @@ // Licensed under the MIT license. #include "pch.h" +#include "ActionPaletteItem.h" +#include "TabPaletteItem.h" +#include "CommandLinePaletteItem.h" #include "CommandPalette.h" #include @@ -224,11 +227,8 @@ namespace winrt::TerminalApp::implementation if (_currentMode == CommandPaletteMode::TabSwitchMode) { const auto selectedCommand = _filteredActionsView().SelectedItem(); - if (const auto filteredCommand = selectedCommand.try_as()) - { - const auto& actionAndArgs = filteredCommand.Command().Action(); - _dispatch.DoAction(actionAndArgs); - } + const auto filteredCommand{ selectedCommand.try_as() }; + _switchToTab(filteredCommand); } } @@ -600,48 +600,56 @@ namespace winrt::TerminalApp::implementation { _dispatchCommandline(filteredCommand); } + else if (_currentMode == CommandPaletteMode::TabSwitchMode || _currentMode == CommandPaletteMode::TabSearchMode) + { + _switchToTab(filteredCommand); + _close(); + } else if (filteredCommand) { - if (filteredCommand.Command().HasNestedCommands()) + if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) { - // If this Command had subcommands, then don't dispatch the - // action. Instead, display a new list of commands for the user - // to pick from. - _nestedActionStack.Append(filteredCommand); - ParentCommandName(filteredCommand.Command().Name()); - _currentNestedCommands.Clear(); - for (const auto& nameAndCommand : filteredCommand.Command().NestedCommands()) + if (actionPaletteItem.Command().HasNestedCommands()) { - const auto action = nameAndCommand.Value(); - auto nestedFilteredCommand{ winrt::make(action) }; - _currentNestedCommands.Append(nestedFilteredCommand); - } - - _updateUIForStackChange(); - } - else - { - // First stash the search text length, because _close will clear this. - const auto searchTextLength = _searchBox().Text().size(); - - // An action from the root command list has depth=0 - const auto nestedCommandDepth = _nestedActionStack.Size(); - - // Close before we dispatch so that actions that open the command - // palette like the Tab Switcher will be able to have the last laugh. - _close(); - - const auto actionAndArgs = filteredCommand.Command().Action(); - _dispatch.DoAction(actionAndArgs); + // If this Command had subcommands, then don't dispatch the + // action. Instead, display a new list of commands for the user + // to pick from. + _nestedActionStack.Append(filteredCommand); + ParentCommandName(actionPaletteItem.Command().Name()); + _currentNestedCommands.Clear(); + for (const auto& nameAndCommand : actionPaletteItem.Command().NestedCommands()) + { + const auto action = nameAndCommand.Value(); + auto nestedActionPaletteItem{ winrt::make(action) }; + auto nestedFilteredCommand{ winrt::make(nestedActionPaletteItem) }; + _currentNestedCommands.Append(nestedFilteredCommand); + } - TraceLoggingWrite( - g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider - "CommandPaletteDispatchedAction", - TraceLoggingDescription("Event emitted when the user selects an action in the Command Palette"), - TraceLoggingUInt32(searchTextLength, "SearchTextLength", "Number of characters in the search string"), - TraceLoggingUInt32(nestedCommandDepth, "NestedCommandDepth", "the depth in the tree of commands for the dispatched action"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + _updateUIForStackChange(); + } + else + { + // First stash the search text length, because _close will clear this. + const auto searchTextLength = _searchBox().Text().size(); + + // An action from the root command list has depth=0 + const auto nestedCommandDepth = _nestedActionStack.Size(); + + // Close before we dispatch so that actions that open the command + // palette like the Tab Switcher will be able to have the last laugh. + _close(); + + _DispatchCommandRequestedHandlers(*this, actionPaletteItem.Command()); + + TraceLoggingWrite( + g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider + "CommandPaletteDispatchedAction", + TraceLoggingDescription("Event emitted when the user selects an action in the Command Palette"), + TraceLoggingUInt32(searchTextLength, "SearchTextLength", "Number of characters in the search string"), + TraceLoggingUInt32(nestedCommandDepth, "NestedCommandDepth", "the depth in the tree of commands for the dispatched action"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + } } } } @@ -670,6 +678,23 @@ namespace winrt::TerminalApp::implementation return input.substr(firstNonSpace); } + // Method Description: + // - Dispatch switch to tab action. + // Arguments: + // - filteredCommand - Selected filtered command - might be null + // Return Value: + // - + void CommandPalette::_switchToTab(winrt::TerminalApp::FilteredCommand const& filteredCommand) + { + if (filteredCommand) + { + if (const auto tabPaletteItem{ filteredCommand.Item().try_as() }) + { + _SwitchToTabRequestedHandlers(*this, tabPaletteItem.Tab()); + } + } + } + // Method Description: // - Dispatch the current search text as a ExecuteCommandline action. // Arguments: @@ -694,8 +719,9 @@ namespace winrt::TerminalApp::implementation TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); - if (_dispatch.DoAction(filteredCommand.value().Command().Action())) + if (const auto commandLinePaletteItem{ filteredCommand.value().Item().try_as() }) { + _CommandLineExecutionRequestedHandlers(*this, commandLinePaletteItem.CommandLine()); _close(); } } @@ -708,13 +734,9 @@ namespace winrt::TerminalApp::implementation return std::nullopt; } - // Build the ExecuteCommandline action from the values we've parsed on the commandline. - ExecuteCommandlineArgs args{ commandLine }; - ActionAndArgs executeActionAndArgs{ ShortcutAction::ExecuteCommandline, args }; - Command command{}; - command.Action(executeActionAndArgs); - command.Name(commandLine); - return winrt::make(command); + winrt::hstring cl{ commandLine }; + auto commandLinePaletteItem{ winrt::make(cl) }; + return winrt::make(commandLinePaletteItem); } // Method Description: @@ -808,13 +830,27 @@ namespace winrt::TerminalApp::implementation void CommandPalette::SetCommands(Collections::IVector const& actions) { - _populateFilteredActions(_allCommands, actions); + _allCommands.Clear(); + for (const auto& action : actions) + { + auto actionPaletteItem{ winrt::make(action) }; + auto filteredCommand{ winrt::make(actionPaletteItem) }; + _allCommands.Append(filteredCommand); + } + _updateFilteredActions(); } - void CommandPalette::SetTabActions(Collections::IVector const& tabs, const bool clearList) + void CommandPalette::SetTabs(Collections::IVector const& tabs, const bool clearList) { - _populateFilteredActions(_tabActions, tabs); + _tabActions.Clear(); + for (const auto& tab : tabs) + { + auto tabPaletteItem{ winrt::make(tab) }; + auto filteredCommand{ winrt::make(tabPaletteItem) }; + _tabActions.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 @@ -829,24 +865,6 @@ namespace winrt::TerminalApp::implementation _updateFilteredActions(); } - // Method Description: - // - This helper function is responsible to update a collection of filtered commands (e.g., tab switcher commands) - // with the new values - // Arguments: - // - vectorToPopulate - the vector of filtered commands to populate - // - actions - the raw commands to use - // Return Value: - // - - void CommandPalette::_populateFilteredActions(Collections::IVector const& vectorToPopulate, Collections::IVector const& actions) - { - vectorToPopulate.Clear(); - for (const auto& action : actions) - { - auto filteredCommand{ winrt::make(action) }; - vectorToPopulate.Append(filteredCommand); - } - } - void CommandPalette::EnableCommandPaletteMode(CommandPaletteLaunchMode const launchMode) { const auto mode = (launchMode == CommandPaletteLaunchMode::CommandLine) ? @@ -962,7 +980,7 @@ namespace winrt::TerminalApp::implementation { for (uint32_t j = i; j < _filteredActions.Size(); j++) { - if (_filteredActions.GetAt(j).Command() == actions[i].Command()) + if (_filteredActions.GetAt(j).Item() == actions[i].Item()) { for (uint32_t k = i; k < j; k++) { @@ -972,7 +990,7 @@ namespace winrt::TerminalApp::implementation } } - if (_filteredActions.GetAt(i).Command() != actions[i].Command()) + if (_filteredActions.GetAt(i).Item() != actions[i].Item()) { _filteredActions.InsertAt(i, actions[i]); } @@ -991,11 +1009,6 @@ namespace winrt::TerminalApp::implementation } } - void CommandPalette::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch) - { - _dispatch = dispatch; - } - // Method Description: // - Dismiss the command palette. This will: // * select all the current text in the input box diff --git a/src/cascadia/TerminalApp/CommandPalette.h b/src/cascadia/TerminalApp/CommandPalette.h index 731ce0b53876..69b5eb1fba08 100644 --- a/src/cascadia/TerminalApp/CommandPalette.h +++ b/src/cascadia/TerminalApp/CommandPalette.h @@ -30,13 +30,11 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IObservableVector FilteredActions(); void SetCommands(Windows::Foundation::Collections::IVector const& actions); - void SetTabActions(Windows::Foundation::Collections::IVector const& tabs, const bool clearList); + void SetTabs(Windows::Foundation::Collections::IVector const& tabs, const bool clearList); void SetKeyBindings(Microsoft::Terminal::TerminalControl::IKeyBindings bindings); void EnableCommandPaletteMode(Microsoft::Terminal::Settings::Model::CommandPaletteLaunchMode const launchMode); - void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch); - bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); void SelectNextItem(const bool moveDown); @@ -57,6 +55,10 @@ namespace winrt::TerminalApp::implementation OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers); OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers); + TYPED_EVENT(SwitchToTabRequested, winrt::TerminalApp::CommandPalette, winrt::TerminalApp::TabBase); + TYPED_EVENT(CommandLineExecutionRequested, winrt::TerminalApp::CommandPalette, winrt::hstring); + TYPED_EVENT(DispatchCommandRequested, winrt::TerminalApp::CommandPalette, Microsoft::Terminal::Settings::Model::Command); + private: friend struct CommandPaletteT; // for Xaml to bind events @@ -65,7 +67,6 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IObservableVector _filteredActions{ nullptr }; Windows::Foundation::Collections::IVector _nestedActionStack{ nullptr }; - winrt::TerminalApp::ShortcutActionDispatch _dispatch; Windows::Foundation::Collections::IVector _commandsToFilter(); bool _lastFilterTextWasEmpty{ true }; @@ -96,12 +97,8 @@ namespace winrt::TerminalApp::implementation void _updateFilteredActions(); - void _populateFilteredActions(Windows::Foundation::Collections::IVector const& vectorToPopulate, - Windows::Foundation::Collections::IVector const& actions); - std::vector _collectFilteredActions(); - static int _getWeight(const winrt::hstring& searchText, const winrt::hstring& name); void _close(); CommandPaletteMode _currentMode; @@ -121,6 +118,7 @@ namespace winrt::TerminalApp::implementation void _dispatchCommand(winrt::TerminalApp::FilteredCommand const& command); void _dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command); + void _switchToTab(winrt::TerminalApp::FilteredCommand const& command); std::optional _buildCommandLineCommand(std::wstring const& commandLine); void _dismissPalette(); diff --git a/src/cascadia/TerminalApp/CommandPalette.idl b/src/cascadia/TerminalApp/CommandPalette.idl index e75209e76995..ac4eebb7b46e 100644 --- a/src/cascadia/TerminalApp/CommandPalette.idl +++ b/src/cascadia/TerminalApp/CommandPalette.idl @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "TabBase.idl"; import "IDirectKeyListener.idl"; -import "ShortcutActionDispatch.idl"; import "HighlightedTextControl.idl"; import "FilteredCommand.idl"; @@ -21,14 +21,16 @@ namespace TerminalApp Windows.Foundation.Collections.IObservableVector FilteredActions { get; }; void SetCommands(Windows.Foundation.Collections.IVector actions); - void SetTabActions(Windows.Foundation.Collections.IVector tabs, Boolean clearList); + void SetTabs(Windows.Foundation.Collections.IVector tabs, Boolean clearList); void SetKeyBindings(Microsoft.Terminal.TerminalControl.IKeyBindings bindings); void EnableCommandPaletteMode(Microsoft.Terminal.Settings.Model.CommandPaletteLaunchMode launchMode); void SelectNextItem(Boolean moveDown); - void SetDispatch(ShortcutActionDispatch dispatch); - void EnableTabSwitcherMode(Boolean searchMode, UInt32 startIdx); + + event Windows.Foundation.TypedEventHandler SwitchToTabRequested; + event Windows.Foundation.TypedEventHandler DispatchCommandRequested; + event Windows.Foundation.TypedEventHandler CommandLineExecutionRequested; } } diff --git a/src/cascadia/TerminalApp/CommandPalette.xaml b/src/cascadia/TerminalApp/CommandPalette.xaml index b6b7c9107a07..c8515ece5d5e 100644 --- a/src/cascadia/TerminalApp/CommandPalette.xaml +++ b/src/cascadia/TerminalApp/CommandPalette.xaml @@ -246,9 +246,9 @@ the MIT License. See LICENSE in the project root for license information. --> + IsTabStop="False" + AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}" + AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}"> @@ -262,7 +262,7 @@ the MIT License. See LICENSE in the project root for license information. --> Grid.Column="0" Width="16" Height="16" - IconSource="{x:Bind Command.Icon, + IconSource="{x:Bind Item.Icon, Mode=OneWay, Converter={StaticResource IconSourceConverter}}"/> @@ -276,7 +276,7 @@ the MIT License. See LICENSE in the project root for license information. --> CommandKeyChordVisibilityConverter for details. --> + Text="{x:Bind Item.KeyChordText, Mode=OneWay}" /> @@ -295,7 +295,7 @@ the MIT License. See LICENSE in the project root for license information. --> FontFamily="Segoe MDL2 Assets" Glyph="" HorizontalAlignment="Right" - Visibility="{x:Bind Command.HasNestedCommands, + Visibility="{x:Bind Item, Mode=OneWay, Converter={StaticResource HasNestedCommandsVisibilityConverter}}" Grid.Column="2"> diff --git a/src/cascadia/TerminalApp/FilteredCommand.cpp b/src/cascadia/TerminalApp/FilteredCommand.cpp index 208b60330927..afdab9f66f89 100644 --- a/src/cascadia/TerminalApp/FilteredCommand.cpp +++ b/src/cascadia/TerminalApp/FilteredCommand.cpp @@ -19,17 +19,17 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; namespace winrt::TerminalApp::implementation { - // This is a view model class that extends the Command model, - // by managing a highlighted text that is computed by matching search filter characters to command name - FilteredCommand::FilteredCommand(Microsoft::Terminal::Settings::Model::Command const& command) : - _Command(command), + // This class is a wrapper of PaletteItem, that is used as an item of a filterable list in CommandPalette. + // It manages a highlighted text that is computed by matching search filter characters to item name + FilteredCommand::FilteredCommand(winrt::TerminalApp::PaletteItem const& item) : + _Item(item), _Filter(L""), _Weight(0) { _HighlightedName = _computeHighlightedName(); - // Recompute the highlighted name if the command name changes - _commandChangedRevoker = _Command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](Windows::Foundation::IInspectable const& /*sender*/, Data::PropertyChangedEventArgs const& e) { + // Recompute the highlighted name if the item name changes + _itemChangedRevoker = _Item.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& /*sender*/, auto& e) { auto filteredCommand{ weakThis.get() }; if (filteredCommand && e.PropertyName() == L"Name") { @@ -52,13 +52,13 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Looks up the filter characters within the command name. - // Iterating through the filter and the command name it tries to associate the next filter character - // with the first appearance of this character in the command name suffix. + // - Looks up the filter characters within the item name. + // Iterating through the filter and the item name it tries to associate the next filter character + // with the first appearance of this character in the item name suffix. // // E.g., for filter="c l t s" and name="close all tabs after this", the match will be "CLose TabS after this". // - // The command name is then split into segments (groupings of matched and non matched characters). + // The item name is then split into segments (groupings of matched and non matched characters). // // E.g., the segments were the example above will be "CL", "ose ", "T", "ab", "S", "after this". // @@ -73,7 +73,7 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::HighlightedText FilteredCommand::_computeHighlightedName() { const auto segments = winrt::single_threaded_observable_vector(); - auto commandName = _Command.Name(); + auto commandName = _Item.Name(); bool isProcessingMatchedSegment = false; uint32_t nextOffsetToReport = 0; uint32_t currentOffset = 0; @@ -86,7 +86,7 @@ namespace winrt::TerminalApp::implementation if (currentOffset == commandName.size()) { // There are still unmatched filter characters but we finished scanning the name. - // In this case we return the entire command name as unmatched + // In this case we return the entire item name as unmatched auto entireNameSegment{ winrt::make(commandName, false) }; segments.Clear(); segments.Append(entireNameSegment); @@ -120,7 +120,7 @@ namespace winrt::TerminalApp::implementation } } - // Either the filter or the command name were fully processed. + // Either the filter or the item name were fully processed. // If we were in the middle of the matched segment - add it. if (isProcessingMatchedSegment) { @@ -135,7 +135,7 @@ namespace winrt::TerminalApp::implementation } // Now create a segment for all remaining characters. - // We will have remaining characters as long as the filter is shorter than the command name. + // We will have remaining characters as long as the filter is shorter than the item name. auto sizeToReport = commandName.size() - nextOffsetToReport; if (sizeToReport > 0) { @@ -148,7 +148,7 @@ namespace winrt::TerminalApp::implementation } // Function Description: - // - Calculates a "weighting" by which should be used to order a command + // - Calculates a "weighting" by which should be used to order a item // name relative to other names, given a specific search string. // Currently, this is based off of two factors: // * The weight is incremented once for each matched character of the @@ -159,7 +159,7 @@ namespace winrt::TerminalApp::implementation // appear in the list before "Close Pane" // * Consecutive matches will be weighted higher than matches with // characters in between the search characters. - // - This will return 0 if the command should not be shown. If all the + // - This will return 0 if the item should not be shown. If all the // characters of search text appear in order in `name`, then this function // will return a positive number. There can be any number of characters // separating consecutive characters in searchText. @@ -216,10 +216,10 @@ namespace winrt::TerminalApp::implementation // Function Description: // - Implementation of Compare for FilteredCommand interface. - // Compares firs command with the second command, first by weight, then by name. - // In the case of a tie prefers the first command + // Compares first instance of the interface with the second instance, first by weight, then by name. + // In the case of a tie prefers the first instance. // Arguments: - // - other: another instance of Filtered Command interface + // - other: another instance of FilteredCommand interface // Return Value: // - Returns true if the first is "bigger" (aka should appear first) int FilteredCommand::Compare(winrt::TerminalApp::FilteredCommand const& first, winrt::TerminalApp::FilteredCommand const& second) @@ -229,8 +229,8 @@ namespace winrt::TerminalApp::implementation if (firstWeight == secondWeight) { - std::wstring_view firstName{ first.Command().Name() }; - std::wstring_view secondName{ second.Command().Name() }; + std::wstring_view firstName{ first.Item().Name() }; + std::wstring_view secondName{ second.Item().Name() }; return lstrcmpi(firstName.data(), secondName.data()) < 0; } diff --git a/src/cascadia/TerminalApp/FilteredCommand.h b/src/cascadia/TerminalApp/FilteredCommand.h index deb20183a80a..eb1f14436fe1 100644 --- a/src/cascadia/TerminalApp/FilteredCommand.h +++ b/src/cascadia/TerminalApp/FilteredCommand.h @@ -18,14 +18,14 @@ namespace winrt::TerminalApp::implementation struct FilteredCommand : FilteredCommandT { FilteredCommand() = default; - FilteredCommand(Microsoft::Terminal::Settings::Model::Command const& command); + FilteredCommand(winrt::TerminalApp::PaletteItem const& item); void UpdateFilter(winrt::hstring const& filter); static int Compare(winrt::TerminalApp::FilteredCommand const& first, winrt::TerminalApp::FilteredCommand const& second); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - OBSERVABLE_GETSET_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, _PropertyChangedHandlers); + OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::PaletteItem, Item, _PropertyChangedHandlers, nullptr); OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Filter, _PropertyChangedHandlers); OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, _PropertyChangedHandlers); OBSERVABLE_GETSET_PROPERTY(int, Weight, _PropertyChangedHandlers); @@ -33,7 +33,7 @@ namespace winrt::TerminalApp::implementation private: winrt::TerminalApp::HighlightedText _computeHighlightedName(); int _computeWeight(); - Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _commandChangedRevoker; + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _itemChangedRevoker; friend class TerminalAppLocalTests::FilteredCommandTests; }; diff --git a/src/cascadia/TerminalApp/FilteredCommand.idl b/src/cascadia/TerminalApp/FilteredCommand.idl index ee00c9dfda44..ce2b04b92c1b 100644 --- a/src/cascadia/TerminalApp/FilteredCommand.idl +++ b/src/cascadia/TerminalApp/FilteredCommand.idl @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import "IDirectKeyListener.idl"; -import "ShortcutActionDispatch.idl"; +import "PaletteItem.idl"; import "HighlightedTextControl.idl"; namespace TerminalApp @@ -10,9 +9,9 @@ namespace TerminalApp [default_interface] runtimeclass FilteredCommand : Windows.UI.Xaml.Data.INotifyPropertyChanged { FilteredCommand(); - FilteredCommand(Microsoft.Terminal.Settings.Model.Command command); + FilteredCommand(PaletteItem item); - Microsoft.Terminal.Settings.Model.Command Command { get; }; + PaletteItem Item { get; }; String Filter; HighlightedText HighlightedName { get; }; Int32 Weight; diff --git a/src/cascadia/TerminalApp/HasNestedCommandsVisibilityConverter.cpp b/src/cascadia/TerminalApp/HasNestedCommandsVisibilityConverter.cpp index d398dbc40bd1..c72c45b1fa93 100644 --- a/src/cascadia/TerminalApp/HasNestedCommandsVisibilityConverter.cpp +++ b/src/cascadia/TerminalApp/HasNestedCommandsVisibilityConverter.cpp @@ -23,7 +23,8 @@ namespace winrt::TerminalApp::implementation Foundation::IInspectable const& /* parameter */, hstring const& /* language */) { - const auto& hasNestedCommands = winrt::unbox_value_or(value, false); + const auto paletteItem{ value.try_as() }; + const auto& hasNestedCommands = paletteItem && paletteItem.Command().HasNestedCommands(); return winrt::box_value(hasNestedCommands ? Visibility::Visible : Visibility::Collapsed); } diff --git a/src/cascadia/TerminalApp/PaletteItem.cpp b/src/cascadia/TerminalApp/PaletteItem.cpp new file mode 100644 index 000000000000..499fce1d5119 --- /dev/null +++ b/src/cascadia/TerminalApp/PaletteItem.cpp @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include +#include "PaletteItem.h" +#include "PaletteItem.g.cpp" + +using namespace winrt; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Microsoft::Terminal::TerminalControl; +using namespace winrt::Microsoft::Terminal::Settings::Model; +using namespace winrt::Windows::System; + +namespace winrt::TerminalApp::implementation +{ +} diff --git a/src/cascadia/TerminalApp/PaletteItem.h b/src/cascadia/TerminalApp/PaletteItem.h new file mode 100644 index 000000000000..bda50b35f31e --- /dev/null +++ b/src/cascadia/TerminalApp/PaletteItem.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "inc/cppwinrt_utils.h" +#include "PaletteItem.g.h" + +namespace winrt::TerminalApp::implementation +{ + struct PaletteItem : PaletteItemT + { + public: + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + + OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers); + OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers); + OBSERVABLE_GETSET_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers); + }; +} diff --git a/src/cascadia/TerminalApp/PaletteItem.idl b/src/cascadia/TerminalApp/PaletteItem.idl new file mode 100644 index 000000000000..a993dcd5c053 --- /dev/null +++ b/src/cascadia/TerminalApp/PaletteItem.idl @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace TerminalApp +{ + unsealed runtimeclass PaletteItem : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + String Name; + String KeyChordText; + String Icon; + } +} diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp index 84d6a8b4c4de..44f94a10326a 100644 --- a/src/cascadia/TerminalApp/TabBase.cpp +++ b/src/cascadia/TerminalApp/TabBase.cpp @@ -138,7 +138,6 @@ namespace winrt::TerminalApp::implementation TabViewIndex(idx); TabViewNumTabs(numTabs); _EnableCloseMenuItems(); - SwitchToTabCommand().Action().Args().as().TabIndex(idx); } void TabBase::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch) diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h index d4697fadcd61..b0ac7509917a 100644 --- a/src/cascadia/TerminalApp/TabBase.h +++ b/src/cascadia/TerminalApp/TabBase.h @@ -28,14 +28,12 @@ namespace winrt::TerminalApp::implementation WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // 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. GETSET_PROPERTY(uint32_t, TabViewIndex, 0); // The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector. GETSET_PROPERTY(uint32_t, TabViewNumTabs, 0); OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers); OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers); - OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::Command, SwitchToTabCommand, _PropertyChangedHandlers, nullptr); GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr); OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr); diff --git a/src/cascadia/TerminalApp/TabBase.idl b/src/cascadia/TerminalApp/TabBase.idl index 040a25591205..b56121b5e93a 100644 --- a/src/cascadia/TerminalApp/TabBase.idl +++ b/src/cascadia/TerminalApp/TabBase.idl @@ -8,7 +8,7 @@ namespace TerminalApp { String Title { get; }; String Icon { get; }; - Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand; + Microsoft.UI.Xaml.Controls.TabViewItem TabViewItem { get; }; Windows.UI.Xaml.FrameworkElement Content { get; }; Windows.UI.Xaml.FocusState FocusState { get; }; diff --git a/src/cascadia/TerminalApp/TabPaletteItem.cpp b/src/cascadia/TerminalApp/TabPaletteItem.cpp new file mode 100644 index 000000000000..f7f1fd456dc4 --- /dev/null +++ b/src/cascadia/TerminalApp/TabPaletteItem.cpp @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TabPaletteItem.h" +#include + +#include "TabPaletteItem.g.cpp" + +using namespace winrt; +using namespace winrt::TerminalApp; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::System; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +namespace winrt::TerminalApp::implementation +{ + TabPaletteItem::TabPaletteItem(winrt::TerminalApp::TabBase const& tab) : + _Tab(tab) + { + Name(tab.Title()); + Icon(tab.Icon()); + + _tabChangedRevoker = tab.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) { + auto item{ weakThis.get() }; + auto senderTab{ sender.try_as() }; + + if (item && senderTab) + { + auto changedProperty = e.PropertyName(); + if (changedProperty == L"Title") + { + item->Name(senderTab.Title()); + } + else if (changedProperty == L"Icon") + { + item->Icon(senderTab.Icon()); + } + } + }); + } +} diff --git a/src/cascadia/TerminalApp/TabPaletteItem.h b/src/cascadia/TerminalApp/TabPaletteItem.h new file mode 100644 index 000000000000..4a498e6f766e --- /dev/null +++ b/src/cascadia/TerminalApp/TabPaletteItem.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "PaletteItem.h" +#include "TabPaletteItem.g.h" +#include "inc/cppwinrt_utils.h" + +namespace winrt::TerminalApp::implementation +{ + struct TabPaletteItem : TabPaletteItemT + { + TabPaletteItem() = default; + TabPaletteItem(winrt::TerminalApp::TabBase const& tab); + + GETSET_PROPERTY(winrt::TerminalApp::TabBase, Tab, nullptr); + + private: + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _tabChangedRevoker; + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(TabPaletteItem); +} diff --git a/src/cascadia/TerminalApp/TabPaletteItem.idl b/src/cascadia/TerminalApp/TabPaletteItem.idl new file mode 100644 index 000000000000..39abd937849d --- /dev/null +++ b/src/cascadia/TerminalApp/TabPaletteItem.idl @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "PaletteItem.idl"; +import "TabBase.idl"; + +namespace TerminalApp +{ + [default_interface] runtimeclass TabPaletteItem : PaletteItem + { + TabPaletteItem(TabBase tab); + + TabBase Tab { get; }; + } +} diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 2428565b2de8..e4a7559296d6 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -69,16 +69,20 @@ + + MinMaxCloseControl.xaml + TabBase.idl + TerminalTab.idl @@ -137,6 +141,8 @@ + + @@ -144,9 +150,11 @@ MinMaxCloseControl.xaml + TabBase.idl + TerminalTab.idl @@ -214,10 +222,13 @@ + + App.xaml + @@ -226,6 +237,7 @@ Code + TerminalPage.xaml diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters index 3d424c3996ff..d214f1e4eb5b 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters @@ -34,6 +34,18 @@ commandPalette + + commandPalette + + + commandPalette + + + commandPalette + + + commandPalette + highlightedText @@ -65,6 +77,18 @@ commandPalette + + commandPalette + + + commandPalette + + + commandPalette + + + commandPalette + highlightedText @@ -121,6 +145,21 @@ commandPalette + + commandPalette + + + commandPalette + + + commandPalette + + + commandPalette + + + commandPalette + highlightedText diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2f76c68f166b..a7320c31067f 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() }, - _mruTabActions{ winrt::single_threaded_vector() }, + _mruTabs{ winrt::single_threaded_vector() }, _startupActions{ winrt::single_threaded_vector() }, _hostingHwnd{} { @@ -226,7 +226,6 @@ namespace winrt::TerminalApp::implementation _tabContent.SizeChanged({ this, &TerminalPage::_OnContentSizeChanged }); - CommandPalette().SetDispatch(*_actionDispatch); // When the visibility of the command palette changes to "collapsed", // the palette has been closed. Toss focus back to the currently active // control. @@ -236,6 +235,9 @@ namespace winrt::TerminalApp::implementation _CommandPaletteClosed(nullptr, nullptr); } }); + CommandPalette().DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested }); + CommandPalette().CommandLineExecutionRequested({ this, &TerminalPage::_OnCommandLineExecutionRequested }); + CommandPalette().SwitchToTabRequested({ this, &TerminalPage::_OnSwitchToTabRequested }); // Settings AllowDependentAnimations will affect whether animations are // enabled application-wide, so we don't need to check it each time we @@ -252,6 +254,49 @@ namespace winrt::TerminalApp::implementation _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); } + // Method Description: + // - This method is called once command palette action was chosen for dispatching + // We'll use this event to dispatch this command. + // Arguments: + // - command - command to dispatch + // Return Value: + // - + void TerminalPage::_OnDispatchCommandRequested(const IInspectable& /*sender*/, const Microsoft::Terminal::Settings::Model::Command& command) + { + const auto& actionAndArgs = command.Action(); + _actionDispatch->DoAction(actionAndArgs); + } + + // Method Description: + // - This method is called once command palette command line was chosen for execution + // We'll use this event to create a command line execution command and dispatch it. + // Arguments: + // - command - command to dispatch + // Return Value: + // - + void TerminalPage::_OnCommandLineExecutionRequested(const IInspectable& /*sender*/, const winrt::hstring& commandLine) + { + ExecuteCommandlineArgs args{ commandLine }; + ActionAndArgs actionAndArgs{ ShortcutAction::ExecuteCommandline, args }; + _actionDispatch->DoAction(actionAndArgs); + } + + // Method Description: + // - This method is called once a tab was selected in tab switcher + // We'll use this event to select the relevant tab + // Arguments: + // - tab - tab to select + // Return Value: + // - + void TerminalPage::_OnSwitchToTabRequested(const IInspectable& /*sender*/, const winrt::TerminalApp::TabBase& tab) + { + uint32_t index{}; + if (_tabs.IndexOf(tab, index)) + { + _SelectTab(index); + } + } + // Method Description: // - This method is called once on startup, on the first LayoutUpdated event. // We'll use this event to know that we have an ActualWidth and @@ -694,11 +739,10 @@ namespace winrt::TerminalApp::implementation TermControl term{ settings, connection }; auto newTabImpl = winrt::make_self(profileGuid, term); - _MakeSwitchToTabCommand(*newTabImpl, _tabs.Size()); // Add the new tab to the list of our tabs. _tabs.Append(*newTabImpl); - _mruTabActions.Append(newTabImpl->SwitchToTabCommand()); + _mruTabs.Append(*newTabImpl); newTabImpl->SetDispatch(*_actionDispatch); @@ -1078,9 +1122,9 @@ namespace winrt::TerminalApp::implementation tab.Shutdown(); uint32_t mruIndex; - if (_mruTabActions.IndexOf(_tabs.GetAt(tabIndex).SwitchToTabCommand(), mruIndex)) + if (_mruTabs.IndexOf(_tabs.GetAt(tabIndex), mruIndex)) { - _mruTabActions.RemoveAt(mruIndex); + _mruTabs.RemoveAt(mruIndex); } _tabs.RemoveAt(tabIndex); @@ -1222,19 +1266,6 @@ namespace winrt::TerminalApp::implementation // _ClearNewTabButtonColor(); } - // Method Description: - // - Updates the command palette (tab switcher) with a list of actions - // reflecting the current in-order list of tabs. - void TerminalPage::_UpdatePaletteWithInOrderTabs() - { - auto tabCommands = winrt::single_threaded_vector(); - for (const auto& tab : _tabs) - { - tabCommands.Append(tab.SwitchToTabCommand()); - } - CommandPalette().SetTabActions(tabCommands, true); - } - // Method Description: // - Sets focus to the tab to the right or left the currently selected tab. void TerminalPage::_SelectNextTab(const bool bMoveRight) @@ -1272,7 +1303,7 @@ namespace winrt::TerminalApp::implementation // 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 = _mruTabActions.Size(); + uint32_t tabCount = _mruTabs.Size(); newTabIndex = ((tabCount + (bMoveRight ? 1 : -1)) % tabCount); } @@ -1280,16 +1311,7 @@ namespace winrt::TerminalApp::implementation if (useTabSwitcher) { - if (useInOrderTabIndex) - { - // Set up the list of in-order tabs - _UpdatePaletteWithInOrderTabs(); - } - else - { - // Set up the list of MRU tabs - CommandPalette().SetTabActions(_mruTabActions, true); - } + CommandPalette().SetTabs(useInOrderTabIndex ? _tabs : _mruTabs, true); // Otherwise, set up the tab switcher in the selected mode, with // the given ordering, and make it visible. @@ -2772,27 +2794,6 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Initializes a SwitchToTab command object for this Tab instance. - // This should be done before the tab is added to the _tabs vector so that - // controls like the CmdPal that observe the vector changes can always expect - // a SwitchToTab command to be available. - // Arguments: - // - - // Return Value: - // - - void TerminalPage::_MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index) - { - SwitchToTabArgs args{ index }; - ActionAndArgs focusTabAction{ ShortcutAction::SwitchToTab, args }; - - Command command; - command.Action(focusTabAction); - command.Name(tab.Title()); - command.Icon(tab.Icon()); - - tab.SwitchToTabCommand(command); - } - // Method Description: // - Computes the delta for scrolling the tab's viewport. // Arguments: @@ -2835,13 +2836,13 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_UpdateMRUTab(const uint32_t index) { uint32_t mruIndex; - auto command = _tabs.GetAt(index).SwitchToTabCommand(); - if (_mruTabActions.IndexOf(command, mruIndex)) + const auto tab = _tabs.GetAt(index); + if (_mruTabs.IndexOf(tab, mruIndex)) { if (mruIndex > 0) { - _mruTabActions.RemoveAt(mruIndex); - _mruTabActions.InsertAt(0, command); + _mruTabs.RemoveAt(mruIndex); + _mruTabs.InsertAt(0, tab); } } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index cdb5a02b45a7..ed87a877180a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -110,7 +110,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; Windows::Foundation::Collections::IObservableVector _tabs; - Windows::Foundation::Collections::IVector _mruTabActions; + Windows::Foundation::Collections::IVector _mruTabs; winrt::com_ptr _GetTerminalTabImpl(const TerminalApp::TabBase& tab) const; void _UpdateTabIndices(); @@ -162,7 +162,7 @@ namespace winrt::TerminalApp::implementation void _UpdateTabView(); void _UpdateTabWidthMode(); void _UpdateCommandsForPalette(); - void _UpdatePaletteWithInOrderTabs(); + static winrt::Windows::Foundation::Collections::IMap _ExpandCommands(Windows::Foundation::Collections::IMapView commandsToExpand, Windows::Foundation::Collections::IVectorView profiles, Windows::Foundation::Collections::IMapView schemes); @@ -221,6 +221,10 @@ namespace winrt::TerminalApp::implementation void _OnFirstLayout(const IInspectable& sender, const IInspectable& eventArgs); void _UpdatedSelectedTab(const int32_t index); + void _OnDispatchCommandRequested(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::Command& command); + void _OnCommandLineExecutionRequested(const IInspectable& sender, const winrt::hstring& commandLine); + void _OnSwitchToTabRequested(const IInspectable& sender, const winrt::TerminalApp::TabBase& tab); + void _Find(); winrt::fire_and_forget _RefreshUIForSettingsReload(); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index e50c5de89e07..c44b7deebab8 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -185,9 +185,6 @@ namespace winrt::TerminalApp::implementation // The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX... Icon(_lastIconPath); TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath)); - - // Update SwitchToTab command's icon - SwitchToTabCommand().Icon(_lastIconPath); } } @@ -226,9 +223,6 @@ namespace winrt::TerminalApp::implementation // Bubble our current tab text to anyone who's listening for changes. Title(activeTitle); - // Update SwitchToTab command's name - SwitchToTabCommand().Name(Title()); - // Update the control to reflect the changed title _headerControl.Title(activeTitle); }