Replace BuiltInIcon ComboBox with AutoSuggestBox (#19569)

## Summary of the Pull Request
Replaces the `ComboBox` used for built-in profile icons with an
`AutoSuggestBox` to allow for searching.

## References and Relevant Issues
Practically plagiarizes #16821

## Validation Steps Performed
 It completes
 It filters

## PR Checklist
Closes #19457
This commit is contained in:
Carlos Zamora
2025-11-20 11:02:30 -08:00
committed by GitHub
parent c28610d016
commit 2537ea7df8
8 changed files with 103 additions and 21 deletions

View File

@@ -233,7 +233,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IObservableVector<winrt::hstring> _FontFeaturesNames; Windows::Foundation::Collections::IObservableVector<winrt::hstring> _FontFeaturesNames;
std::wstring _fontNameFilter; std::wstring _fontNameFilter;
bool _ShowAllFonts = false; bool _ShowAllFonts = false;
bool _suppressFontFaceBoxList = false;
static void _ViewModelChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); static void _ViewModelChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);

View File

@@ -29,7 +29,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr }; Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_FontList{ nullptr }; Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_FontList{ nullptr };
Windows::Foundation::Collections::IVector<IInspectable> ProfileViewModel::_BuiltInIcons{ nullptr }; Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> ProfileViewModel::_BuiltInIcons{ nullptr };
static constexpr std::wstring_view HideIconValue{ L"none" }; static constexpr std::wstring_view HideIconValue{ L"none" };
@@ -114,7 +114,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
} }
else if (viewModelProperty == L"CurrentBuiltInIcon") else if (viewModelProperty == L"CurrentBuiltInIcon")
{ {
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.as<Editor::EnumEntry>().EnumValue())); IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
} }
else if (viewModelProperty == L"CurrentEmojiIcon") else if (viewModelProperty == L"CurrentEmojiIcon")
{ {
@@ -193,12 +193,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void ProfileViewModel::_UpdateBuiltInIcons() void ProfileViewModel::_UpdateBuiltInIcons()
{ {
std::vector<IInspectable> builtInIcons; std::vector<Editor::EnumEntry> builtInIcons;
for (auto& [val, name] : s_SegoeFluentIcons) for (auto& [val, name] : s_SegoeFluentIcons)
{ {
builtInIcons.emplace_back(make<EnumEntry>(hstring{ name }, box_value(val))); builtInIcons.emplace_back(make<EnumEntry>(hstring{ name }, box_value(val)));
} }
_BuiltInIcons = single_threaded_vector<IInspectable>(std::move(builtInIcons)); _BuiltInIcons = single_threaded_observable_vector<Editor::EnumEntry>(std::move(builtInIcons));
} }
void ProfileViewModel::_DeduceCurrentIconType() void ProfileViewModel::_DeduceCurrentIconType()
@@ -236,7 +236,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
for (uint32_t i = 0; i < _BuiltInIcons.Size(); i++) for (uint32_t i = 0; i < _BuiltInIcons.Size(); i++)
{ {
const auto& builtIn = _BuiltInIcons.GetAt(i); const auto& builtIn = _BuiltInIcons.GetAt(i);
if (profileIcon == unbox_value<hstring>(builtIn.as<Editor::EnumEntry>().EnumValue())) if (profileIcon == unbox_value<hstring>(builtIn.EnumValue()))
{ {
_CurrentBuiltInIcon = builtIn; _CurrentBuiltInIcon = builtIn;
return; return;
@@ -695,7 +695,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{ {
if (_CurrentBuiltInIcon) if (_CurrentBuiltInIcon)
{ {
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.as<Editor::EnumEntry>().EnumValue())); IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
} }
break; break;
} }

View File

@@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static void UpdateFontList() noexcept; static void UpdateFontList() noexcept;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> CompleteFontList() noexcept { return _FontList; }; static Windows::Foundation::Collections::IObservableVector<Editor::Font> CompleteFontList() noexcept { return _FontList; };
static Windows::Foundation::Collections::IObservableVector<Editor::Font> MonospaceFontList() noexcept { return _MonospaceFontList; }; static Windows::Foundation::Collections::IObservableVector<Editor::Font> MonospaceFontList() noexcept { return _MonospaceFontList; };
static Windows::Foundation::Collections::IVector<IInspectable> BuiltInIcons() noexcept { return _BuiltInIcons; }; static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> BuiltInIcons() noexcept { return _BuiltInIcons; };
ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings, const Windows::UI::Core::CoreDispatcher& dispatcher); ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings, const Windows::UI::Core::CoreDispatcher& dispatcher);
Control::IControlSettings TermSettings() const; Control::IControlSettings TermSettings() const;
@@ -134,7 +134,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage); VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::BellSoundViewModel>, CurrentBellSounds); VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::BellSoundViewModel>, CurrentBellSounds);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::IInspectable, CurrentBuiltInIcon); VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::EnumEntry, CurrentBuiltInIcon, nullptr);
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon); VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid);
@@ -197,7 +197,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void _MarkDuplicateBellSoundDirectories(); void _MarkDuplicateBellSoundDirectories();
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList; static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList; static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList;
static Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> _BuiltInIcons; static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _BuiltInIcons;
Model::CascadiaSettings _appSettings; Model::CascadiaSettings _appSettings;
Editor::AppearanceViewModel _unfocusedAppearanceViewModel; Editor::AppearanceViewModel _unfocusedAppearanceViewModel;

View File

@@ -116,8 +116,8 @@ namespace Microsoft.Terminal.Settings.Editor
Boolean UsingImageIcon { get; }; Boolean UsingImageIcon { get; };
String IconPath; String IconPath;
IInspectable CurrentBuiltInIcon; EnumEntry CurrentBuiltInIcon;
Windows.Foundation.Collections.IVector<IInspectable> BuiltInIcons { get; }; Windows.Foundation.Collections.IObservableVector<EnumEntry> BuiltInIcons { get; };
String TabTitlePreview { get; }; String TabTitlePreview { get; };
String AnswerbackMessagePreview { get; }; String AnswerbackMessagePreview { get; };

View File

@@ -171,8 +171,76 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
} }
} }
Windows::UI::Xaml::Controls::IconSource Profiles_Base::BuiltInIconConverter(const IInspectable& iconVal) IconSource Profiles_Base::BuiltInIconConverter(const IInspectable& iconVal)
{ {
return Microsoft::Terminal::UI::IconPathConverter::IconSourceWUX(unbox_value<hstring>(iconVal)); return Microsoft::Terminal::UI::IconPathConverter::IconSourceWUX(unbox_value<hstring>(iconVal));
} }
void Profiles_Base::BuiltInIconPicker_GotFocus(const IInspectable& sender, const RoutedEventArgs& /*e*/)
{
_updateIconFilter({});
sender.as<AutoSuggestBox>().IsSuggestionListOpen(true);
}
void Profiles_Base::BuiltInIconPicker_QuerySubmitted(const AutoSuggestBox& /*sender*/, const AutoSuggestBoxQuerySubmittedEventArgs& e)
{
const auto iconEntry = unbox_value_or<EnumEntry>(e.ChosenSuggestion(), nullptr);
if (!iconEntry)
{
return;
}
_Profile.CurrentBuiltInIcon(iconEntry);
}
void Profiles_Base::BuiltInIconPicker_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& e)
{
if (e.Reason() != AutoSuggestionBoxTextChangeReason::UserInput)
{
return;
}
std::wstring_view filter{ sender.Text() };
filter = til::trim(filter, L' ');
_updateIconFilter(filter);
}
void Profiles_Base::_updateIconFilter(std::wstring_view filter)
{
if (_iconFilter != filter)
{
_filteredBuiltInIcons = nullptr;
_iconFilter = filter;
_updateFilteredIconList();
PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FilteredBuiltInIconList" });
}
}
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> Profiles_Base::FilteredBuiltInIconList()
{
if (!_filteredBuiltInIcons)
{
_updateFilteredIconList();
}
return _filteredBuiltInIcons;
}
void Profiles_Base::_updateFilteredIconList()
{
_filteredBuiltInIcons = ProfileViewModel::BuiltInIcons();
if (_iconFilter.empty())
{
return;
}
// Find matching icons and populate the filtered list
std::vector<Editor::EnumEntry> filtered;
filtered.reserve(_filteredBuiltInIcons.Size());
for (const auto& icon : _filteredBuiltInIcons)
{
if (til::contains_linguistic_insensitive(icon.EnumName(), _iconFilter))
{
filtered.emplace_back(icon);
}
}
_filteredBuiltInIcons = winrt::single_threaded_observable_vector(std::move(filtered));
}
} }

View File

@@ -25,6 +25,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Advanced_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void Advanced_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void DeleteConfirmation_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void DeleteConfirmation_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> FilteredBuiltInIconList();
void BuiltInIconPicker_GotFocus(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void BuiltInIconPicker_TextChanged(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs& e);
void BuiltInIconPicker_QuerySubmitted(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxQuerySubmittedEventArgs& e);
static Windows::UI::Xaml::Controls::IconSource BuiltInIconConverter(const Windows::Foundation::IInspectable& iconVal); static Windows::UI::Xaml::Controls::IconSource BuiltInIconConverter(const Windows::Foundation::IInspectable& iconVal);
til::property_changed_event PropertyChanged; til::property_changed_event PropertyChanged;
@@ -34,6 +39,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker; winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
Editor::IHostedInWindow _windowRoot; Editor::IHostedInWindow _windowRoot;
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _filteredBuiltInIcons;
std::wstring _iconFilter;
void _updateIconFilter(std::wstring_view filter);
void _updateFilteredIconList();
}; };
}; };

View File

@@ -9,6 +9,7 @@ namespace Microsoft.Terminal.Settings.Editor
{ {
Profiles_Base(); Profiles_Base();
ProfileViewModel Profile { get; }; ProfileViewModel Profile { get; };
Windows.Foundation.Collections.IObservableVector<EnumEntry> FilteredBuiltInIconList { get; };
static Windows.UI.Xaml.Controls.IconSource BuiltInIconConverter(IInspectable iconVal); static Windows.UI.Xaml.Controls.IconSource BuiltInIconConverter(IInspectable iconVal);
} }

View File

@@ -142,12 +142,16 @@
</ComboBox> </ComboBox>
<!-- Built-In Icon --> <!-- Built-In Icon -->
<ComboBox x:Uid="Profile_BuiltInIcon" <AutoSuggestBox x:Uid="Profile_BuiltInIcon"
Grid.Column="1" Grid.Column="1"
ItemsSource="{x:Bind Profile.BuiltInIcons}" GotFocus="BuiltInIconPicker_GotFocus"
SelectedItem="{x:Bind Profile.CurrentBuiltInIcon, Mode=TwoWay}" ItemsSource="{x:Bind FilteredBuiltInIconList, Mode=OneWay}"
Visibility="{x:Bind Profile.UsingBuiltInIcon, Mode=OneWay}"> QuerySubmitted="BuiltInIconPicker_QuerySubmitted"
<ComboBox.ItemTemplate> Text="{x:Bind Profile.CurrentBuiltInIcon.EnumName, Mode=OneWay}"
TextBoxStyle="{StaticResource TextBoxSettingStyle}"
TextChanged="BuiltInIconPicker_TextChanged"
Visibility="{x:Bind Profile.UsingBuiltInIcon, Mode=OneWay}">
<AutoSuggestBox.ItemTemplate>
<DataTemplate x:DataType="local:EnumEntry"> <DataTemplate x:DataType="local:EnumEntry">
<Grid ColumnSpacing="12"> <Grid ColumnSpacing="12">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -163,8 +167,8 @@
Text="{x:Bind EnumName}" /> Text="{x:Bind EnumName}" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ComboBox.ItemTemplate> </AutoSuggestBox.ItemTemplate>
</ComboBox> </AutoSuggestBox>
<!-- Image (File) Icon --> <!-- Image (File) Icon -->
<TextBox x:Uid="Profile_IconBox" <TextBox x:Uid="Profile_IconBox"