Swap the command palette modes for the prefix >
(#7935)
VsCode uses `>` as its "prefix" for the equivalent of their "action mode". This PR aligns the Terminal with their logic here. We have to be tricky - if we use the `>` in the actual input as the indicator for action mode, we can't display any placeholder text in the input to tell users to type a command. This wasn't an issue for the commandline mode previously, because we'd stick the "prompt" in the "no matches text" space. However, we can't do that for action mode. Instead, we'll stick a floating text block over the input box, and when the user's in action mode, we'll manually place a `>` into that space. When the user backspaces the `>`, we'll remove it from that block, and switch into commandline mode. ## Validation Steps Performed Played with the cmdpal in lots of different modes, this finally feels good Closes #7736
This commit is contained in:
parent
30e363e7ac
commit
bd7cd5512d
|
@ -213,41 +213,27 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
else if (key == VirtualKey::Escape)
|
||||
{
|
||||
// Action, TabSearch, TabSwitch Mode: Dismiss the palette if the
|
||||
// text is empty, otherwise clear the search string.
|
||||
if (_currentMode != CommandPaletteMode::CommandlineMode)
|
||||
// Dismiss the palette if the text is empty, otherwise clear the
|
||||
// search string.
|
||||
if (_searchBox().Text().empty())
|
||||
{
|
||||
if (_searchBox().Text().empty())
|
||||
{
|
||||
_dismissPalette();
|
||||
}
|
||||
else
|
||||
{
|
||||
_searchBox().Text(L"");
|
||||
}
|
||||
_dismissPalette();
|
||||
}
|
||||
else if (_currentMode == CommandPaletteMode::CommandlineMode)
|
||||
else
|
||||
{
|
||||
const auto currentInput = _getPostPrefixInput();
|
||||
if (currentInput.empty())
|
||||
{
|
||||
// The user's only input "> " so far. We should just dismiss
|
||||
// the palette. This is like dismissing the Action mode with
|
||||
// empty input.
|
||||
_dismissPalette();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear out the current input. We'll leave a ">" in the
|
||||
// input (to stay in commandline mode), and a leading space
|
||||
// (if they currently had one).
|
||||
const bool hasLeadingSpace = (_searchBox().Text().size()) - (currentInput.size()) > 1;
|
||||
_searchBox().Text(hasLeadingSpace ? L"> " : L">");
|
||||
_searchBox().Text(L"");
|
||||
}
|
||||
|
||||
// This will conveniently move the cursor to the end of the
|
||||
// text input for us.
|
||||
_searchBox().Select(_searchBox().Text().size(), 0);
|
||||
}
|
||||
e.Handled(true);
|
||||
}
|
||||
else if (key == VirtualKey::Back)
|
||||
{
|
||||
// If the last filter text was empty, and we're backspacing from
|
||||
// that state, then the user "backspaced" the virtual '>' we're
|
||||
// using as the action mode indicator. Switch into commandline mode.
|
||||
if (_searchBox().Text().empty() && _lastFilterTextWasEmpty && _currentMode == CommandPaletteMode::ActionMode)
|
||||
{
|
||||
_switchToMode(CommandPaletteMode::CommandlineMode);
|
||||
}
|
||||
|
||||
e.Handled(true);
|
||||
|
@ -480,18 +466,13 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get all the input text in _searchBox that follows the prefix character
|
||||
// and any whitespace following that prefix character. This can be used in
|
||||
// commandline mode to get all the useful input that the user input after
|
||||
// the leading ">" prefix.
|
||||
// - Note that this will behave unexpectedly in Action Mode.
|
||||
// - Get all the input text in _searchBox that follows any leading spaces.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the string of input following the prefix character.
|
||||
std::wstring CommandPalette::_getPostPrefixInput()
|
||||
// - the string of input following any number of leading spaces
|
||||
std::wstring CommandPalette::_getTrimmedInput()
|
||||
{
|
||||
const std::wstring input{ _searchBox().Text() };
|
||||
if (input.empty())
|
||||
|
@ -499,17 +480,15 @@ namespace winrt::TerminalApp::implementation
|
|||
return input;
|
||||
}
|
||||
|
||||
const auto rawCmdline{ input.substr(1) };
|
||||
|
||||
// Trim leading whitespace
|
||||
const auto firstNonSpace = rawCmdline.find_first_not_of(L" ");
|
||||
const auto firstNonSpace = input.find_first_not_of(L" ");
|
||||
if (firstNonSpace == std::wstring::npos)
|
||||
{
|
||||
// All the following characters are whitespace.
|
||||
return L"";
|
||||
}
|
||||
|
||||
return rawCmdline.substr(firstNonSpace);
|
||||
return input.substr(firstNonSpace);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -520,12 +499,11 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void CommandPalette::_dispatchCommandline()
|
||||
{
|
||||
const auto input = _getPostPrefixInput();
|
||||
if (input.empty())
|
||||
auto cmdline{ _getTrimmedInput() };
|
||||
if (cmdline.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
winrt::hstring cmdline{ input };
|
||||
|
||||
// Build the ExecuteCommandline action from the values we've parsed on the commandline.
|
||||
ExecuteCommandlineArgs args{ cmdline };
|
||||
|
@ -574,32 +552,49 @@ namespace winrt::TerminalApp::implementation
|
|||
void CommandPalette::_filterTextChanged(IInspectable const& /*sender*/,
|
||||
Windows::UI::Xaml::RoutedEventArgs const& /*args*/)
|
||||
{
|
||||
if (_currentMode == CommandPaletteMode::CommandlineMode || _currentMode == CommandPaletteMode::ActionMode)
|
||||
if (_currentMode == CommandPaletteMode::CommandlineMode)
|
||||
{
|
||||
_evaluatePrefix();
|
||||
}
|
||||
|
||||
// We're setting _lastFilterTextWasEmpty here, because if the user tries
|
||||
// to backspace the last character in the input, the Backspace KeyDown
|
||||
// event will fire _before_ _filterTextChanged does. Updating the value
|
||||
// here will ensure that we can check this case appropriately.
|
||||
_lastFilterTextWasEmpty = _searchBox().Text().empty();
|
||||
|
||||
_updateFilteredActions();
|
||||
_filteredActionsView().SelectedIndex(0);
|
||||
|
||||
_noMatchesText().Visibility(_filteredActions.Size() > 0 ? Visibility::Collapsed : Visibility::Visible);
|
||||
if (_currentMode == CommandPaletteMode::TabSearchMode || _currentMode == CommandPaletteMode::ActionMode)
|
||||
{
|
||||
_noMatchesText().Visibility(_filteredActions.Size() > 0 ? Visibility::Collapsed : Visibility::Visible);
|
||||
}
|
||||
else
|
||||
{
|
||||
_noMatchesText().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandPalette::_evaluatePrefix()
|
||||
{
|
||||
auto newMode = CommandPaletteMode::ActionMode;
|
||||
// This will take you from commandline mode, into action mode. The
|
||||
// backspace handler in _keyDownHandler will handle taking us from
|
||||
// action mode to commandline mode.
|
||||
auto newMode = CommandPaletteMode::CommandlineMode;
|
||||
|
||||
auto inputText = _searchBox().Text();
|
||||
auto inputText = _getTrimmedInput();
|
||||
if (inputText.size() > 0)
|
||||
{
|
||||
if (inputText[0] == L'>')
|
||||
{
|
||||
newMode = CommandPaletteMode::CommandlineMode;
|
||||
newMode = CommandPaletteMode::ActionMode;
|
||||
}
|
||||
}
|
||||
|
||||
if (newMode != _currentMode)
|
||||
{
|
||||
//_switchToMode will remove the '>' character from the input.
|
||||
_switchToMode(newMode);
|
||||
}
|
||||
}
|
||||
|
@ -644,6 +639,8 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
_searchBox().Text(L"");
|
||||
_searchBox().Select(_searchBox().Text().size(), 0);
|
||||
// Leaving this block of code outside the above if-statement
|
||||
// guarantees that the correct text is shown for the mode
|
||||
// whenever _switchToMode is called.
|
||||
|
@ -652,20 +649,24 @@ namespace winrt::TerminalApp::implementation
|
|||
case CommandPaletteMode::TabSearchMode:
|
||||
case CommandPaletteMode::TabSwitchMode:
|
||||
{
|
||||
SearchBoxText(RS_(L"TabSwitcher_SearchBoxText"));
|
||||
SearchBoxPlaceholderText(RS_(L"TabSwitcher_SearchBoxText"));
|
||||
NoMatchesText(RS_(L"TabSwitcher_NoMatchesText"));
|
||||
ControlName(RS_(L"TabSwitcherControlName"));
|
||||
PrefixCharacter(L"");
|
||||
break;
|
||||
}
|
||||
case CommandPaletteMode::CommandlineMode:
|
||||
NoMatchesText(RS_(L"CmdPalCommandlinePrompt"));
|
||||
SearchBoxPlaceholderText(RS_(L"CmdPalCommandlinePrompt"));
|
||||
NoMatchesText(L"");
|
||||
ControlName(RS_(L"CommandPaletteControlName"));
|
||||
PrefixCharacter(L"");
|
||||
break;
|
||||
case CommandPaletteMode::ActionMode:
|
||||
default:
|
||||
SearchBoxText(RS_(L"CommandPalette_SearchBox/PlaceholderText"));
|
||||
SearchBoxPlaceholderText(RS_(L"CommandPalette_SearchBox/PlaceholderText"));
|
||||
NoMatchesText(RS_(L"CommandPalette_NoMatchesText/Text"));
|
||||
ControlName(RS_(L"CommandPaletteControlName"));
|
||||
PrefixCharacter(L">");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -717,7 +718,7 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
std::vector<Command> actions;
|
||||
|
||||
auto searchText = _searchBox().Text();
|
||||
winrt::hstring searchText{ _getTrimmedInput() };
|
||||
const bool addAll = searchText.empty();
|
||||
|
||||
auto commandsToFilter = _commandsToFilter();
|
||||
|
|
|
@ -39,7 +39,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, SearchBoxText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
|
||||
|
||||
|
@ -55,6 +56,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _commandsToFilter();
|
||||
|
||||
bool _lastFilterTextWasEmpty{ true };
|
||||
|
||||
void _filterTextChanged(Windows::Foundation::IInspectable const& sender,
|
||||
Windows::UI::Xaml::RoutedEventArgs const& args);
|
||||
void _previewKeyDownHandler(Windows::Foundation::IInspectable const& sender,
|
||||
|
@ -84,8 +87,8 @@ namespace winrt::TerminalApp::implementation
|
|||
CommandPaletteMode _currentMode;
|
||||
void _switchToMode(CommandPaletteMode mode);
|
||||
|
||||
std::wstring _getTrimmedInput();
|
||||
void _evaluatePrefix();
|
||||
std::wstring _getPostPrefixInput();
|
||||
|
||||
Microsoft::Terminal::TerminalControl::IKeyBindings _bindings;
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ namespace TerminalApp
|
|||
CommandPalette();
|
||||
|
||||
String NoMatchesText { get; };
|
||||
String SearchBoxText { get; };
|
||||
String SearchBoxPlaceholderText { get; };
|
||||
String PrefixCharacter { get; };
|
||||
String ControlName { get; };
|
||||
String ParentCommandName { get; };
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:Windows10version1903="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 8)"
|
||||
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
|
||||
xmlns:Windows10version1903="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 8)"
|
||||
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
|
||||
TabNavigation="Cycle"
|
||||
IsTabStop="True"
|
||||
AllowFocusOnInteraction="True"
|
||||
|
@ -135,27 +135,27 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
to receive clicks _anywhere_ in its bounds. -->
|
||||
|
||||
<Grid
|
||||
x:Name="_shadowBackdrop"
|
||||
Background="Transparent"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
x:Name="_shadowBackdrop"
|
||||
Background="Transparent"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
x:Name="_backdrop"
|
||||
Style="{ThemeResource CommandPaletteBackground}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||
PointerPressed="_backdropPointerPressed"
|
||||
Margin="8"
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Windows10version1903:Shadow="{StaticResource CommandPaletteShadow}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top">
|
||||
x:Name="_backdrop"
|
||||
Style="{ThemeResource CommandPaletteBackground}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||
PointerPressed="_backdropPointerPressed"
|
||||
Margin="8"
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Windows10version1903:Shadow="{StaticResource CommandPaletteShadow}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
|
@ -164,15 +164,28 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBox
|
||||
Grid.Row="0"
|
||||
x:Name="_searchBox"
|
||||
Margin="8"
|
||||
IsSpellCheckEnabled="False"
|
||||
TextChanged="_filterTextChanged"
|
||||
PlaceholderText="{x:Bind SearchBoxText, Mode=OneWay}"
|
||||
Text="">
|
||||
Grid.Row="0"
|
||||
x:Name="_searchBox"
|
||||
Margin="8"
|
||||
Padding="18,8,8,8"
|
||||
IsSpellCheckEnabled="False"
|
||||
TextChanged="_filterTextChanged"
|
||||
PlaceholderText="{x:Bind SearchBoxPlaceholderText, Mode=OneWay}"
|
||||
Text="">
|
||||
</TextBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
x:Name="_prefixCharacter"
|
||||
Margin="16,16,0,-8"
|
||||
FontSize="14"
|
||||
Visibility="{x:Bind PrefixCharacter,
|
||||
Mode=OneWay,
|
||||
Converter={StaticResource ParentCommandVisibilityConverter}}"
|
||||
Text="{x:Bind PrefixCharacter, Mode=OneWay}"
|
||||
>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Padding="16, 0, 16, 4"
|
||||
x:Name="_parentCommandText"
|
||||
|
@ -194,17 +207,17 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
</TextBlock>
|
||||
|
||||
<ListView
|
||||
Grid.Row="2"
|
||||
x:Name="_filteredActionsView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
SelectionMode="Single"
|
||||
CanReorderItems="False"
|
||||
AllowDrop="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="_listItemClicked"
|
||||
PreviewKeyDown="_keyDownHandler"
|
||||
ItemsSource="{x:Bind FilteredActions}">
|
||||
Grid.Row="2"
|
||||
x:Name="_filteredActionsView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
SelectionMode="Single"
|
||||
CanReorderItems="False"
|
||||
AllowDrop="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="_listItemClicked"
|
||||
PreviewKeyDown="_keyDownHandler"
|
||||
ItemsSource="{x:Bind FilteredActions}">
|
||||
|
||||
<ItemsControl.ItemTemplate >
|
||||
<DataTemplate x:DataType="SettingsModel:Command">
|
||||
|
|
Loading…
Reference in a new issue