Polish Edit Action page

- Fix key bindings section being hidden when no keys are present
- Fix action name not updating when type changes
- Polish interactions with action type suggestions list
  - select all text when control gets focus
  - do NOT open suggestions when the page gets navigated to or
      light dismiss (aka programmatic focus)
  - DO open suggestions when user tabs to control
This commit is contained in:
Carlos Zamora
2026-07-02 17:19:02 -07:00
parent e5d95d71e0
commit bb74104a0d
4 changed files with 74 additions and 16 deletions

View File

@@ -444,6 +444,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
ActionArgsVM(*actionArgsVM);
if (!_command.HasName())
{
// Invalidate the cache to make the getter recompute the display name for the "action name" field
_cachedDisplayName.clear();
_NotifyChanges(L"DisplayName");
}
}

View File

@@ -65,6 +65,30 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
return nullptr;
}
// Depth-first search of the visual tree under 'root' for the first TextBox (the AutoSuggestBox's
// inner editable TextBox), so we can select its text on focus.
static Controls::TextBox _findChildTextBox(const Windows::UI::Xaml::DependencyObject& root)
{
if (!root)
{
return nullptr;
}
if (const auto textBox = root.try_as<Controls::TextBox>())
{
return textBox;
}
const auto count = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(root);
for (int32_t i = 0; i < count; ++i)
{
const auto child = Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(root, i);
if (const auto found = _findChildTextBox(child))
{
return found;
}
}
return nullptr;
}
EditAction::EditAction()
{
}
@@ -77,6 +101,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::CommandViewModel>();
// Suppress opening the suggestion list for the whole page-entry window; see LostFocus.
_isPageEntryFocus = true;
_propagateWindowRootRevoker = _ViewModel.PropagateWindowRootRequested(
winrt::auto_revoke,
[windowRoot = args.WindowRoot()](const IInspectable&, const Editor::ArgWrapper& wrapper) {
@@ -144,6 +171,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (const auto listener = _findKeyChordListener(container.try_as<DependencyObject>()))
{
// Entry focus went to a key chord row, not the box; entry is over.
_isPageEntryFocus = false;
listener.FocusInput();
return;
}
@@ -163,7 +192,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
void EditAction::ShortcutActionBox_GettingFocus(const IInspectable& /*sender*/, const Windows::UI::Xaml::Input::GettingFocusEventArgs& args)
{
// Open on Tab, but not on page entry.
// FocusState is unreliable, so use InputDevice: "Keyboard" means we tabbed to focus.
_openSuggestionsOnFocus = args.InputDevice() == Windows::UI::Xaml::Input::FocusInputDeviceKind::Keyboard && !_isPageEntryFocus;
}
void EditAction::ShortcutActionBox_GotFocus(const IInspectable& sender, const RoutedEventArgs&)
{
const auto box = sender.as<AutoSuggestBox>();
// Seeding ItemsSource inside this branch is intentional: assigning it on a focused
// AutoSuggestBox opens the popup on its own. Typing filters via ShortcutActionBox_TextChanged.
if (_openSuggestionsOnFocus)
{
// Only rebuild the list if we don't have a cached list or if the cached list is filtered
if (!_filteredActions || !_currentActionFilter.empty())
@@ -177,9 +219,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_filteredActions = winrt::single_threaded_observable_vector(std::move(allActions));
_currentActionFilter = L"";
sender.as<AutoSuggestBox>().ItemsSource(_filteredActions);
box.ItemsSource(_filteredActions);
}
box.IsSuggestionListOpen(true);
}
// Select all current text so the user can immediately overwrite it. AutoSuggestBox has no
// SelectAll, so use the inner TextBox.
if (const auto textBox = _findChildTextBox(box.as<Windows::UI::Xaml::DependencyObject>()))
{
textBox.SelectAll();
}
sender.as<AutoSuggestBox>().IsSuggestionListOpen(true);
}
void EditAction::ShortcutActionBox_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& args)
@@ -225,6 +275,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void EditAction::ShortcutActionBox_LostFocus(const IInspectable& sender, const RoutedEventArgs&)
{
_isPageEntryFocus = false;
// The auto suggest box does a weird thing where it reverts to the last query text when you
// keyboard navigate out of it. Intercept it here and keep the correct text.
const auto box = sender.as<AutoSuggestBox>();

View File

@@ -21,6 +21,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_OBSERVABLE_PROPERTY(Editor::CommandViewModel, ViewModel, PropertyChanged.raise, nullptr);
void ShortcutActionBox_GotFocus(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void ShortcutActionBox_GettingFocus(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Input::GettingFocusEventArgs& args);
void ShortcutActionBox_TextChanged(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const winrt::Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs& args);
void ShortcutActionBox_QuerySubmitted(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const winrt::Windows::UI::Xaml::Controls::AutoSuggestBoxQuerySubmittedEventArgs& args);
void ShortcutActionBox_LostFocus(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
@@ -33,6 +34,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _filteredActions{ nullptr };
winrt::hstring _lastValidAction;
winrt::hstring _currentActionFilter;
bool _openSuggestionsOnFocus{ false };
bool _isPageEntryFocus{ false };
};
}

View File

@@ -430,6 +430,7 @@
<AutoSuggestBox x:Name="ShortcutActionBox"
MinWidth="248"
AutomationProperties.Name="{x:Bind ViewModel.ShortcutActionComboBoxAutomationPropName}"
GettingFocus="ShortcutActionBox_GettingFocus"
GotFocus="ShortcutActionBox_GotFocus"
LostFocus="ShortcutActionBox_LostFocus"
QuerySubmitted="ShortcutActionBox_QuerySubmitted"
@@ -438,8 +439,7 @@
<!-- Key bindings -->
<StackPanel x:Name="KeyBindingsContainer"
Spacing="4"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.HasNoKeyChords), Mode=OneWay}">
Spacing="4">
<TextBlock x:Uid="EditAction_KeyBindings"
Style="{StaticResource TextBlockSubHeaderStyle}" />
@@ -462,7 +462,8 @@
HorizontalContentAlignment="Stretch"
IsTabStop="False"
ItemTemplate="{StaticResource KeyChordTemplate}"
ItemsSource="{x:Bind ViewModel.KeyChordList, Mode=OneWay}">
ItemsSource="{x:Bind ViewModel.KeyChordList, Mode=OneWay}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.HasNoKeyChords), Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"