diff --git a/build/pipelines/ob-nightly.yml b/build/pipelines/ob-nightly.yml index 63eba379d8..603593dca5 100644 --- a/build/pipelines/ob-nightly.yml +++ b/build/pipelines/ob-nightly.yml @@ -28,7 +28,7 @@ extends: official: true branding: Canary buildTerminal: true - pgoBuildMode: None # BODGY - OneBranch is on VS 17.10, which is known to be the worst + pgoBuildMode: Optimize codeSign: true signingIdentity: serviceName: $(SigningServiceName) diff --git a/src/cascadia/ShellExtension/OpenTerminalHere.h b/src/cascadia/ShellExtension/OpenTerminalHere.h index de5d088643..af35469738 100644 --- a/src/cascadia/ShellExtension/OpenTerminalHere.h +++ b/src/cascadia/ShellExtension/OpenTerminalHere.h @@ -34,7 +34,7 @@ struct #else // DEV __declspec(uuid("52065414-e077-47ec-a3ac-1cc5455e1b54")) #endif - OpenTerminalHere : public RuntimeClass, IExplorerCommand, IObjectWithSite> + OpenTerminalHere : public RuntimeClass, IExplorerCommand, IObjectWithSite> { #pragma region IExplorerCommand STDMETHODIMP Invoke(IShellItemArray* psiItemArray, diff --git a/src/cascadia/TerminalApp/DebugTapConnection.h b/src/cascadia/TerminalApp/DebugTapConnection.h index 57dfbe4572..191b56eb6f 100644 --- a/src/cascadia/TerminalApp/DebugTapConnection.h +++ b/src/cascadia/TerminalApp/DebugTapConnection.h @@ -13,7 +13,7 @@ namespace winrt::Microsoft::TerminalApp::implementation { public: explicit DebugTapConnection(Microsoft::Terminal::TerminalConnection::ITerminalConnection wrappedConnection); - void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/){}; + void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) {}; ~DebugTapConnection(); void Start(); void WriteInput(const winrt::array_view data); diff --git a/src/cascadia/TerminalApp/MarkdownPaneContent.h b/src/cascadia/TerminalApp/MarkdownPaneContent.h index e8fa4cb0ce..5f91b982f1 100644 --- a/src/cascadia/TerminalApp/MarkdownPaneContent.h +++ b/src/cascadia/TerminalApp/MarkdownPaneContent.h @@ -25,7 +25,7 @@ namespace winrt::TerminalApp::implementation #pragma region IPaneContent winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); - void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings&){}; + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings&) {}; winrt::Windows::Foundation::Size MinimumSize() { return { 1, 1 }; }; void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic) { reason; }; diff --git a/src/cascadia/TerminalApp/TaskbarState.cpp b/src/cascadia/TerminalApp/TaskbarState.cpp index 183460556c..ef9c34a75d 100644 --- a/src/cascadia/TerminalApp/TaskbarState.cpp +++ b/src/cascadia/TerminalApp/TaskbarState.cpp @@ -9,7 +9,7 @@ namespace winrt::TerminalApp::implementation { // Default to unset, 0%. TaskbarState::TaskbarState() : - TaskbarState(0, 0){}; + TaskbarState(0, 0) {}; TaskbarState::TaskbarState(const uint64_t dispatchTypesState, const uint64_t progressParam) : _State{ dispatchTypesState }, diff --git a/src/cascadia/TerminalConnection/CTerminalHandoff.h b/src/cascadia/TerminalConnection/CTerminalHandoff.h index 004b3f5274..beb957b66e 100644 --- a/src/cascadia/TerminalConnection/CTerminalHandoff.h +++ b/src/cascadia/TerminalConnection/CTerminalHandoff.h @@ -31,7 +31,7 @@ Author(s): using NewHandoffFunction = HRESULT (*)(HANDLE* in, HANDLE* out, HANDLE signal, HANDLE reference, HANDLE server, HANDLE client, const TERMINAL_STARTUP_INFO* startupInfo); struct __declspec(uuid(__CLSID_CTerminalHandoff)) - CTerminalHandoff : public Microsoft::WRL::RuntimeClass, ITerminalHandoff3> +CTerminalHandoff : public Microsoft::WRL::RuntimeClass, ITerminalHandoff3> { #pragma region ITerminalHandoff STDMETHODIMP EstablishPtyHandoff(HANDLE* in, HANDLE* out, HANDLE signal, HANDLE reference, HANDLE server, HANDLE client, const TERMINAL_STARTUP_INFO* startupInfo) override; diff --git a/src/cascadia/TerminalControl/HwndTerminal.cpp b/src/cascadia/TerminalControl/HwndTerminal.cpp index f4cfb967ae..2c792e81c7 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.cpp +++ b/src/cascadia/TerminalControl/HwndTerminal.cpp @@ -958,7 +958,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font for (size_t tableIndex = 0; tableIndex < 16; tableIndex++) { // It's using gsl::at to check the index is in bounds, but the analyzer still calls this array-to-pointer-decay - GSL_SUPPRESS(bounds .3) + GSL_SUPPRESS(bounds.3) renderSettings.SetColorTableEntry(tableIndex, gsl::at(theme.ColorTable, tableIndex)); } diff --git a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp index fe1b5ed652..b2506a365f 100644 --- a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp @@ -502,7 +502,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation auto lifetime = get_strong(); static constexpr winrt::guid clientGuidFiles{ 0xbd00ae34, 0x839b, 0x43f6, { 0x8b, 0x94, 0x12, 0x37, 0x1a, 0xfe, 0xea, 0xb5 } }; - const auto parentHwnd{ reinterpret_cast(_WindowRoot.GetHostingWindow()) }; + + const auto windowRoot = WindowRoot(); + if (!windowRoot) + { + co_return; + } + const auto parentHwnd{ reinterpret_cast(windowRoot.GetHostingWindow()) }; auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { THROW_IF_FAILED(dialog->SetClientGuid(clientGuidFiles)); try @@ -525,8 +531,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation auto lifetime = get_strong(); static constexpr winrt::guid clientGuidFolders{ 0xa611027, 0x42be, 0x4665, { 0xaf, 0xf1, 0x3f, 0x22, 0x26, 0xe9, 0xf7, 0x4d } }; - ; - const auto parentHwnd{ reinterpret_cast(_WindowRoot.GetHostingWindow()) }; + + const auto windowRoot = WindowRoot(); + if (!windowRoot) + { + co_return; + } + const auto parentHwnd{ reinterpret_cast(windowRoot.GetHostingWindow()) }; auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { THROW_IF_FAILED(dialog->SetClientGuid(clientGuidFolders)); try diff --git a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h index e48825962b..67a811d0e5 100644 --- a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h @@ -141,6 +141,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::hstring Type() const noexcept { return _descriptor.Type; }; Model::ArgTypeHint TypeHint() const noexcept { return _descriptor.TypeHint; }; bool Required() const noexcept { return _descriptor.Required; }; + Editor::IHostedInWindow WindowRoot() const noexcept { return _WeakWindowRoot.get(); } + void WindowRoot(const Editor::IHostedInWindow& value) { _WeakWindowRoot = value; } // We cannot use the macro here because we need to implement additional logic for the setter Windows::Foundation::IInspectable EnumValue() const noexcept { return _EnumValue; }; @@ -186,10 +188,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::ColorSchemeViewModel, DefaultColorScheme, nullptr); VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::IInspectable, Value, nullptr); WINRT_PROPERTY(Windows::Foundation::Collections::IVector, ColorSchemeNamesList, nullptr); - WINRT_PROPERTY(Editor::IHostedInWindow, WindowRoot, nullptr); private: Model::ArgDescriptor _descriptor; + winrt::weak_ref _WeakWindowRoot{ nullptr }; Windows::Foundation::IInspectable _EnumValue{ nullptr }; Windows::Foundation::Collections::IObservableVector _EnumList; Windows::Foundation::Collections::IObservableVector _FlagList; diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index b0de55a85d..c7548e636b 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -1444,10 +1444,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation safe_void_coroutine Appearances::BackgroundImage_Click(const IInspectable&, const RoutedEventArgs&) { - auto lifetime = get_strong(); + const auto lifetime = get_strong(); - const auto parentHwnd{ reinterpret_cast(WindowRoot().GetHostingWindow()) }; - auto file = co_await OpenImagePicker(parentHwnd); + const auto windowRoot = WindowRoot(); + if (!windowRoot) + { + co_return; + } + const auto parentHwnd{ reinterpret_cast(windowRoot.GetHostingWindow()) }; + const auto file = co_await OpenImagePicker(parentHwnd); if (!file.empty()) { Appearance().SetBackgroundImagePath(file); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index 11422fce08..e0e8248db9 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -193,6 +193,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // CursorShape visibility logic bool IsVintageCursor() const; + Editor::IHostedInWindow WindowRoot() const noexcept { return _WeakWindowRoot.get(); } + void WindowRoot(const Editor::IHostedInWindow& value) noexcept { _WeakWindowRoot = value; } + Windows::Foundation::Collections::IObservableVector FilteredFontList(); bool ShowAllFonts() const noexcept; void ShowAllFonts(bool value); @@ -220,12 +223,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation DEPENDENCY_PROPERTY(Editor::AppearanceViewModel, Appearance); WINRT_PROPERTY(Editor::ProfileViewModel, SourceProfile, nullptr); - WINRT_PROPERTY(IHostedInWindow, WindowRoot, nullptr); GETSET_BINDABLE_ENUM_SETTING(BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch, Appearance().BackgroundImageStretchMode); GETSET_BINDABLE_ENUM_SETTING(IntenseTextStyle, Microsoft::Terminal::Settings::Model::IntenseStyle, Appearance().IntenseTextStyle); private: + winrt::weak_ref _WeakWindowRoot{ nullptr }; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; std::array _BIAlignmentButtons; Windows::Foundation::Collections::IMap _FontWeightMap; diff --git a/src/cascadia/TerminalSettingsEditor/IconPicker.cpp b/src/cascadia/TerminalSettingsEditor/IconPicker.cpp new file mode 100644 index 0000000000..1b26eb66c1 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/IconPicker.cpp @@ -0,0 +1,320 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "IconPicker.h" +#include "IconPicker.g.cpp" + +#include +#include "SegoeFluentIconList.h" +#include "../../types/inc/utils.hpp" +#include "../WinRTUtils/inc/Utils.h" + +using namespace winrt; +using namespace winrt::Windows::UI; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Navigation; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Xaml::Media; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Microsoft::UI::Xaml::Controls; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + namespace WUX = Windows::UI::Xaml; +} + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + static constexpr std::wstring_view HideIconValue{ L"none" }; + + Windows::Foundation::Collections::IObservableVector IconPicker::_BuiltInIcons{ nullptr }; + Windows::Foundation::Collections::IObservableVector IconPicker::_IconTypes{ nullptr }; + DependencyProperty IconPicker::_CurrentIconPathProperty{ nullptr }; + + Windows::Foundation::Collections::IObservableVector IconPicker::BuiltInIcons() noexcept + { + if (!_BuiltInIcons) + { + // lazy load the built-in icons + std::vector builtInIcons; + for (auto& [val, name] : s_SegoeFluentIcons) + { + builtInIcons.emplace_back(make(hstring{ name }, box_value(val))); + } + _BuiltInIcons = single_threaded_observable_vector(std::move(builtInIcons)); + } + return _BuiltInIcons; + } + + Windows::Foundation::Collections::IObservableVector IconPicker::IconTypes() noexcept + { + if (!_IconTypes) + { + // lazy load the icon types + std::vector iconTypes; + iconTypes.reserve(4); + iconTypes.emplace_back(make(RS_(L"IconPicker_IconTypeNone"), box_value(IconType::None))); + iconTypes.emplace_back(make(RS_(L"IconPicker_IconTypeFontIcon"), box_value(IconType::FontIcon))); + iconTypes.emplace_back(make(RS_(L"IconPicker_IconTypeEmoji"), box_value(IconType::Emoji))); + iconTypes.emplace_back(make(RS_(L"IconPicker_IconTypeImage"), box_value(IconType::Image))); + _IconTypes = winrt::single_threaded_observable_vector(std::move(iconTypes)); + } + return _IconTypes; + } + + IconPicker::IconPicker() + { + _InitializeProperties(); + InitializeComponent(); + + _DeduceCurrentIconType(); + PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) { + const auto propertyName{ args.PropertyName() }; + // "CurrentIconPath" changes are handled by _OnCurrentIconPathChanged() + if (propertyName == L"CurrentIconType") + { + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingNoIcon" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingBuiltInIcon" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingEmojiIcon" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingImageIcon" }); + } + else if (propertyName == L"CurrentBuiltInIcon") + { + CurrentIconPath(unbox_value(_CurrentBuiltInIcon.EnumValue())); + } + else if (propertyName == L"CurrentEmojiIcon") + { + CurrentIconPath(CurrentEmojiIcon()); + } + }); + } + + void IconPicker::_InitializeProperties() + { + // Initialize any dependency properties here. + // This performs a lazy load on these properties, instead of + // initializing them when the DLL loads. + if (!_CurrentIconPathProperty) + { + _CurrentIconPathProperty = + DependencyProperty::Register( + L"CurrentIconPath", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ nullptr, PropertyChangedCallback{ &IconPicker::_OnCurrentIconPathChanged } }); + } + } + + void IconPicker::_OnCurrentIconPathChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/) + { + d.as()->_DeduceCurrentIconType(); + } + + safe_void_coroutine IconPicker::Icon_Click(const IInspectable&, const RoutedEventArgs&) + { + auto lifetime = get_strong(); + + const auto parentHwnd{ reinterpret_cast(WindowRoot().GetHostingWindow()) }; + auto file = co_await OpenImagePicker(parentHwnd); + if (!file.empty()) + { + CurrentIconPath(file); + } + } + + void IconPicker::BuiltInIconPicker_GotFocus(const IInspectable& sender, const RoutedEventArgs& /*e*/) + { + _updateIconFilter({}); + sender.as().IsSuggestionListOpen(true); + } + + void IconPicker::BuiltInIconPicker_QuerySubmitted(const AutoSuggestBox& /*sender*/, const AutoSuggestBoxQuerySubmittedEventArgs& e) + { + const auto iconEntry = unbox_value_or(e.ChosenSuggestion(), nullptr); + if (!iconEntry) + { + return; + } + CurrentBuiltInIcon(iconEntry); + } + + void IconPicker::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 IconPicker::_updateIconFilter(std::wstring_view filter) + { + if (_iconFilter != filter) + { + _filteredBuiltInIcons = nullptr; + _iconFilter = filter; + _updateFilteredIconList(); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FilteredBuiltInIconList" }); + } + } + + Windows::Foundation::Collections::IObservableVector IconPicker::FilteredBuiltInIconList() + { + if (!_filteredBuiltInIcons) + { + _updateFilteredIconList(); + } + return _filteredBuiltInIcons; + } + + void IconPicker::_updateFilteredIconList() + { + _filteredBuiltInIcons = BuiltInIcons(); + if (_iconFilter.empty()) + { + return; + } + + // Find matching icons and populate the filtered list + std::vector 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)); + } + + void IconPicker::CurrentIconType(const Windows::Foundation::IInspectable& value) + { + if (_currentIconType != value) + { + // Switching from... + if (_currentIconType && unbox_value(_currentIconType.as().EnumValue()) == IconType::Image) + { + // Stash the current value of Icon. If the user + // switches out of then back to IconType::Image, we want + // the path that we display in the text box to remain unchanged. + _lastIconPath = CurrentIconPath(); + } + + // Set the member here instead of after setting Icon() below! + // We have an Icon property changed handler defined for when we discard changes. + // Inadvertently, that means that we call this setter again. + // Setting the member here means that we early exit at the beginning of the function + // because _currentIconType == value. + _currentIconType = value; + + // Switched to... + switch (unbox_value(value.as().EnumValue())) + { + case IconType::None: + { + CurrentIconPath(winrt::hstring{ HideIconValue }); + break; + } + case IconType::Image: + { + if (!_lastIconPath.empty()) + { + // Conversely, if we switch to Image, + // retrieve that saved value and apply it + CurrentIconPath(_lastIconPath); + } + break; + } + case IconType::FontIcon: + { + if (_CurrentBuiltInIcon) + { + CurrentIconPath(unbox_value(_CurrentBuiltInIcon.EnumValue())); + } + break; + } + case IconType::Emoji: + { + // Don't set Icon here! + // Clear out the text box so we direct the user to use the emoji picker. + CurrentEmojiIcon({}); + } + } + // We're not using the VM's Icon() setter above, + // so notify HasIcon changed manually + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"CurrentIconType" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"HasIcon" }); + } + } + + bool IconPicker::UsingNoIcon() const + { + return _currentIconType == IconTypes().GetAt(0); + } + + bool IconPicker::UsingBuiltInIcon() const + { + return _currentIconType == IconTypes().GetAt(1); + } + + bool IconPicker::UsingEmojiIcon() const + { + return _currentIconType == IconTypes().GetAt(2); + } + + bool IconPicker::UsingImageIcon() const + { + return _currentIconType == IconTypes().GetAt(3); + } + + void IconPicker::_DeduceCurrentIconType() + { + const auto icon = CurrentIconPath(); + if (icon.empty() || icon == HideIconValue) + { + _currentIconType = IconTypes().GetAt(0); + } + else if (icon.size() == 1 && (L'\uE700' <= til::at(icon, 0) && til::at(icon, 0) <= L'\uF8B3')) + { + _currentIconType = IconTypes().GetAt(1); + _DeduceCurrentBuiltInIcon(); + } + else if (::Microsoft::Console::Utils::IsLikelyToBeEmojiOrSymbolIcon(icon)) + { + // We already did a range check for MDL2 Assets in the previous one, + // so if we're out of that range but still short, assume we're an emoji + _currentIconType = IconTypes().GetAt(2); + } + else + { + _currentIconType = IconTypes().GetAt(3); + } + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"CurrentIconType" }); + } + + void IconPicker::_DeduceCurrentBuiltInIcon() + { + const auto icon = CurrentIconPath(); + for (uint32_t i = 0; i < BuiltInIcons().Size(); i++) + { + const auto& builtIn = BuiltInIcons().GetAt(i); + if (icon == unbox_value(builtIn.EnumValue())) + { + CurrentBuiltInIcon(builtIn); + return; + } + } + CurrentBuiltInIcon(BuiltInIcons().GetAt(0)); + } + + WUX::Controls::IconSource IconPicker::BuiltInIconConverter(const IInspectable& iconVal) + { + return Microsoft::Terminal::UI::IconPathConverter::IconSourceWUX(unbox_value(iconVal)); + } +} diff --git a/src/cascadia/TerminalSettingsEditor/IconPicker.h b/src/cascadia/TerminalSettingsEditor/IconPicker.h new file mode 100644 index 0000000000..b73c567981 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/IconPicker.h @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "IconPicker.g.h" +#include "EnumEntry.h" +#include "Utils.h" +#include "cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct IconPicker : public HasScrollViewer, IconPickerT + { + public: + IconPicker(); + + static constexpr std::wstring_view HideIconValue{ L"none" }; + static Windows::UI::Xaml::Controls::IconSource BuiltInIconConverter(const Windows::Foundation::IInspectable& iconVal); + static Windows::Foundation::Collections::IObservableVector BuiltInIcons() noexcept; + static Windows::Foundation::Collections::IObservableVector IconTypes() noexcept; + + Windows::Foundation::Collections::IObservableVector FilteredBuiltInIconList(); + safe_void_coroutine Icon_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + 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); + + Windows::Foundation::IInspectable CurrentIconType() const noexcept { return _currentIconType; } + void CurrentIconType(const Windows::Foundation::IInspectable& value); + + Editor::IHostedInWindow WindowRoot() const noexcept { return _weakWindowRoot.get(); } + void WindowRoot(const Editor::IHostedInWindow& value) noexcept { _weakWindowRoot = value; } + + bool UsingNoIcon() const; + bool UsingBuiltInIcon() const; + bool UsingEmojiIcon() const; + bool UsingImageIcon() const; + + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(Editor::EnumEntry, CurrentBuiltInIcon, PropertyChanged.raise, nullptr); + + DEPENDENCY_PROPERTY(hstring, CurrentIconPath); + + private: + static Windows::Foundation::Collections::IObservableVector _BuiltInIcons; + static Windows::Foundation::Collections::IObservableVector _IconTypes; + Windows::Foundation::Collections::IObservableVector _filteredBuiltInIcons; + std::wstring _iconFilter; + Windows::Foundation::IInspectable _currentIconType{}; + winrt::hstring _lastIconPath; + winrt::weak_ref _weakWindowRoot; + + static void _InitializeProperties(); + static void _OnCurrentIconPathChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); + + void _DeduceCurrentIconType(); + void _DeduceCurrentBuiltInIcon(); + void _updateIconFilter(std::wstring_view filter); + void _updateFilteredIconList(); + }; +} + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(IconPicker); +} diff --git a/src/cascadia/TerminalSettingsEditor/IconPicker.idl b/src/cascadia/TerminalSettingsEditor/IconPicker.idl new file mode 100644 index 0000000000..0ff9915962 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/IconPicker.idl @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "EnumEntry.idl"; +import "MainPage.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + enum IconType + { + None = 0, + FontIcon, + Image, + Emoji + }; + + [default_interface] runtimeclass IconPicker : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + IconPicker(); + + IInspectable CurrentIconType; + Windows.Foundation.Collections.IObservableVector IconTypes { get; }; + + Boolean UsingBuiltInIcon { get; }; + Boolean UsingEmojiIcon { get; }; + Boolean UsingImageIcon { get; }; + + String CurrentEmojiIcon; + + EnumEntry CurrentBuiltInIcon; + Windows.Foundation.Collections.IObservableVector BuiltInIcons { get; }; + Windows.Foundation.Collections.IObservableVector FilteredBuiltInIconList { get; }; + + static Windows.UI.Xaml.Controls.IconSource BuiltInIconConverter(IInspectable iconVal); + + String CurrentIconPath; + static Windows.UI.Xaml.DependencyProperty CurrentIconPathProperty { get; }; + + IHostedInWindow WindowRoot; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/IconPicker.xaml b/src/cascadia/TerminalSettingsEditor/IconPicker.xaml new file mode 100644 index 0000000000..1e585cd823 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/IconPicker.xaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +