From bb74104a0d0766242bb5bfc398ea680128fabcd8 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Thu, 2 Jul 2026 17:19:02 -0700 Subject: [PATCH] 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 --- .../ActionsViewModel.cpp | 2 + .../TerminalSettingsEditor/EditAction.cpp | 78 +++++++++++++++---- .../TerminalSettingsEditor/EditAction.h | 3 + .../TerminalSettingsEditor/EditAction.xaml | 7 +- 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp index 32912178cd..d678745fa9 100644 --- a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp @@ -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"); } } diff --git a/src/cascadia/TerminalSettingsEditor/EditAction.cpp b/src/cascadia/TerminalSettingsEditor/EditAction.cpp index 67986e0491..e5324266ef 100644 --- a/src/cascadia/TerminalSettingsEditor/EditAction.cpp +++ b/src/cascadia/TerminalSettingsEditor/EditAction.cpp @@ -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()) + { + 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(); _ViewModel = args.ViewModel().as(); + + // 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())) { + // Entry focus went to a key chord row, not the box; entry is over. + _isPageEntryFocus = false; listener.FocusInput(); return; } @@ -163,23 +192,44 @@ 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&) { - // Only rebuild the list if we don't have a cached list or if the cached list is filtered - if (!_filteredActions || !_currentActionFilter.empty()) - { - // Open the suggestions list with all available actions - std::vector allActions; - for (const auto& action : _ViewModel.AvailableShortcutActions()) - { - allActions.push_back(action); - } + const auto box = sender.as(); - _filteredActions = winrt::single_threaded_observable_vector(std::move(allActions)); - _currentActionFilter = L""; - sender.as().ItemsSource(_filteredActions); + // 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()) + { + // Open the suggestions list with all available actions + std::vector allActions; + for (const auto& action : _ViewModel.AvailableShortcutActions()) + { + allActions.push_back(action); + } + + _filteredActions = winrt::single_threaded_observable_vector(std::move(allActions)); + _currentActionFilter = L""; + 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())) + { + textBox.SelectAll(); } - sender.as().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(); diff --git a/src/cascadia/TerminalSettingsEditor/EditAction.h b/src/cascadia/TerminalSettingsEditor/EditAction.h index 531661a5eb..a461cb4fe1 100644 --- a/src/cascadia/TerminalSettingsEditor/EditAction.h +++ b/src/cascadia/TerminalSettingsEditor/EditAction.h @@ -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 _filteredActions{ nullptr }; winrt::hstring _lastValidAction; winrt::hstring _currentActionFilter; + bool _openSuggestionsOnFocus{ false }; + bool _isPageEntryFocus{ false }; }; } diff --git a/src/cascadia/TerminalSettingsEditor/EditAction.xaml b/src/cascadia/TerminalSettingsEditor/EditAction.xaml index 1984186967..3bc8829fad 100644 --- a/src/cascadia/TerminalSettingsEditor/EditAction.xaml +++ b/src/cascadia/TerminalSettingsEditor/EditAction.xaml @@ -430,6 +430,7 @@ + Spacing="4"> @@ -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}">