From 6e451a2d4b6574aed846385b8223894472366805 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Thu, 29 Feb 2024 12:08:52 -0800 Subject: [PATCH] Allow editing font features in the Settings UI (#16678) ## Summary of the Pull Request **Targets #16104** Same as #16104, but for font features ## References and Relevant Issues #10000 ## Validation Steps Performed Font features are detected correctly and can be set in the settings UI ![image](https://github.com/microsoft/terminal/assets/26824113/054c30fa-c584-4b71-872d-d956526c373b) ![image](https://github.com/microsoft/terminal/assets/26824113/484a20eb-abe9-478c-99cf-f63939ab4c5b) ## PR Checklist - [ ] Closes #xxx - [ ] Tests added/passed - [ ] Documentation updated - If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx - [ ] Schema updated (if necessary) --- .github/actions/spelling/allow/apis.txt | 8 + .../TerminalSettingsEditor/Appearances.cpp | 344 +++++++++++++++++- .../TerminalSettingsEditor/Appearances.h | 47 ++- .../TerminalSettingsEditor/Appearances.idl | 17 + .../TerminalSettingsEditor/Appearances.xaml | 63 ++++ .../Resources/en-US/Resources.resw | 120 ++++++ .../TerminalSettingsModel/FontConfig.cpp | 10 + 7 files changed, 605 insertions(+), 4 deletions(-) diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 5ae7e5753df..839fcbe76b2 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -1,3 +1,5 @@ +aalt +abvm ACCEPTFILES ACCESSDENIED acl @@ -36,6 +38,7 @@ delayimp DERR dlldata DNE +dnom DONTADDTORECENT DWMSBT DWMWA @@ -49,6 +52,7 @@ EXPCMDFLAGS EXPCMDSTATE filetime FILTERSPEC +fina FORCEFILESYSTEM FORCEMINIMIZE frac @@ -120,6 +124,7 @@ LSHIFT LTGRAY MAINWINDOW MAXIMIZEBOX +medi memchr memicmp MENUCOMMAND @@ -150,6 +155,7 @@ NOTIFYBYPOS NOTIFYICON NOTIFYICONDATA ntprivapi +numr oaidl ocidl ODR @@ -175,9 +181,11 @@ REGCLS RETURNCMD rfind RLO +rnrn ROOTOWNER roundf RSHIFT +rvrn SACL schandle SEH diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index 848cb99349f..0ba4f344e23 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -5,6 +5,7 @@ #include "Appearances.h" #include "Appearances.g.cpp" #include "AxisKeyValuePair.g.cpp" +#include "FeatureKeyValuePair.g.cpp" #include "EnumEntry.h" #include @@ -19,6 +20,20 @@ using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Microsoft::Terminal::Settings::Model; +static constexpr std::array DefaultFeatures{ + L"rlig", + L"locl", + L"ccmp", + L"calt", + L"liga", + L"clig", + L"rnrn", + L"kern", + L"mark", + L"mkmk", + L"dist" +}; + namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { bool Font::HasPowerlineCharacters() @@ -86,7 +101,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::impl::hstring_builder builder{ length }; if (SUCCEEDED(names->GetString(localeIndex, builder.data(), length + 1))) { - fontAxesTagsAndNames.insert(std::pair(_axisTagToString(axesVector[i].axisTag), builder.to_hstring())); + fontAxesTagsAndNames.insert(std::pair(_tagToString(axesVector[i].axisTag), builder.to_hstring())); continue; } } @@ -100,7 +115,47 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _fontAxesTagsAndNames; } - winrt::hstring Font::_axisTagToString(DWRITE_FONT_AXIS_TAG tag) + IMap Font::FontFeaturesTagsAndNames() + { + if (!_fontFeaturesTagsAndNames) + { + wil::com_ptr font; + THROW_IF_FAILED(_family->GetFont(0, font.put())); + wil::com_ptr fontFace; + THROW_IF_FAILED(font->CreateFontFace(fontFace.put())); + + wil::com_ptr factory; + THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<::IUnknown**>(factory.addressof()))); + wil::com_ptr textAnalyzer; + factory->CreateTextAnalyzer(textAnalyzer.addressof()); + wil::com_ptr textAnalyzer2 = textAnalyzer.query(); + + DWRITE_SCRIPT_ANALYSIS scriptAnalysis{}; + UINT32 tagCount; + // we have to call GetTypographicFeatures twice, first to get the actual count then to get the features + std::ignore = textAnalyzer2->GetTypographicFeatures(fontFace.get(), scriptAnalysis, L"en-us", 0, &tagCount, nullptr); + std::vector tags{ tagCount }; + textAnalyzer2->GetTypographicFeatures(fontFace.get(), scriptAnalysis, L"en-us", tagCount, &tagCount, tags.data()); + + std::unordered_map fontFeaturesTagsAndNames; + for (auto tag : tags) + { + const auto tagString = _tagToString(tag); + hstring formattedResourceString{ fmt::format(L"Profile_FontFeature_{}", tagString) }; + hstring localizedName{ tagString }; + // we have resource strings for common font features, see if one for this feature exists + if (HasLibraryResourceWithName(formattedResourceString)) + { + localizedName = GetLibraryResourceString(formattedResourceString); + } + fontFeaturesTagsAndNames.insert(std::pair(tagString, localizedName)); + } + _fontFeaturesTagsAndNames = winrt::single_threaded_map(std::move(fontFeaturesTagsAndNames)); + } + return _fontFeaturesTagsAndNames; + } + + winrt::hstring Font::_tagToString(DWRITE_FONT_AXIS_TAG tag) { std::wstring result; for (int i = 0; i < 4; ++i) @@ -110,6 +165,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return winrt::hstring{ result }; } + hstring Font::_tagToString(DWRITE_FONT_FEATURE_TAG tag) + { + std::wstring result; + for (int i = 0; i < 4; ++i) + { + result.push_back((tag >> (i * 8)) & 0xFF); + } + return hstring{ result }; + } + AxisKeyValuePair::AxisKeyValuePair(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap) : _AxisKey{ axisKey }, _AxisValue{ axisValue }, @@ -191,6 +256,87 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } + FeatureKeyValuePair::FeatureKeyValuePair(hstring featureKey, uint32_t featureValue, const IMap& baseMap, const IMap& tagToNameMap) : + _FeatureKey{ featureKey }, + _FeatureValue{ featureValue }, + _baseMap{ baseMap }, + _tagToNameMap{ tagToNameMap } + { + if (_tagToNameMap.HasKey(_FeatureKey)) + { + int32_t i{ 0 }; + // this loop assumes that every time we iterate through the map + // we get the same ordering + for (const auto tagAndName : _tagToNameMap) + { + if (tagAndName.Key() == _FeatureKey) + { + _FeatureIndex = i; + break; + } + ++i; + } + } + } + + hstring FeatureKeyValuePair::FeatureKey() + { + return _FeatureKey; + } + + uint32_t FeatureKeyValuePair::FeatureValue() + { + return _FeatureValue; + } + + int32_t FeatureKeyValuePair::FeatureIndex() + { + return _FeatureIndex; + } + + void FeatureKeyValuePair::FeatureValue(uint32_t featureValue) + { + if (featureValue != _FeatureValue) + { + _baseMap.Remove(_FeatureKey); + _FeatureValue = featureValue; + _baseMap.Insert(_FeatureKey, _FeatureValue); + _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"FeatureValue" }); + } + } + + void FeatureKeyValuePair::FeatureKey(hstring featureKey) + { + if (featureKey != _FeatureKey) + { + _baseMap.Remove(_FeatureKey); + _FeatureKey = featureKey; + _baseMap.Insert(_FeatureKey, _FeatureValue); + _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"FeatureKey" }); + } + } + + void FeatureKeyValuePair::FeatureIndex(int32_t featureIndex) + { + if (featureIndex != _FeatureIndex) + { + _FeatureIndex = featureIndex; + + int32_t i{ 0 }; + // same as in the constructor, this assumes that iterating through the map + // gives us the same order every time + for (const auto tagAndName : _tagToNameMap) + { + if (i == _FeatureIndex) + { + FeatureKey(tagAndName.Key()); + break; + } + ++i; + } + } + } + AppearanceViewModel::AppearanceViewModel(const Model::AppearanceConfig& appearance) : _appearance{ appearance } { @@ -217,9 +363,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // so when the FontAxes change (say from the reset button), reinitialize the observable vector InitializeFontAxesVector(); } + else if (viewModelProperty == L"FontFeatures") + { + // same as the FontAxes one + InitializeFontFeaturesVector(); + } }); InitializeFontAxesVector(); + InitializeFontFeaturesVector(); // Cache the original BG image path. If the user clicks "Use desktop // wallpaper", then un-checks it, this is the string we'll restore to @@ -477,6 +629,150 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return axisKeyValuePair; } + void AppearanceViewModel::AddNewFeatureKeyValuePair() + { + const auto fontInfo = _appearance.SourceProfile().FontInfo(); + auto fontFeaturesMap = fontInfo.FontFeatures(); + if (!fontFeaturesMap) + { + fontFeaturesMap = winrt::single_threaded_map(); + fontInfo.FontFeatures(fontFeaturesMap); + } + + // find one feature that does not already exist, and add that + // if there are no more possible features to add, the button is disabled so there shouldn't be a way to get here + const auto possibleFeaturesTagsAndNames = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); + for (const auto tagAndName : possibleFeaturesTagsAndNames) + { + const auto featureKey = tagAndName.Key(); + if (!fontFeaturesMap.HasKey(featureKey)) + { + const auto featureDefaultValue = _IsDefaultFeature(featureKey) ? 1 : 0; + fontFeaturesMap.Insert(featureKey, featureDefaultValue); + FontFeaturesVector().Append(_CreateFeatureKeyValuePairHelper(featureKey, featureDefaultValue, fontFeaturesMap, possibleFeaturesTagsAndNames)); + break; + } + } + _NotifyChanges(L"CanFontFeaturesBeAdded"); + } + + void AppearanceViewModel::DeleteFeatureKeyValuePair(hstring key) + { + for (uint32_t i = 0; i < _FontFeaturesVector.Size(); i++) + { + if (_FontFeaturesVector.GetAt(i).FeatureKey() == key) + { + FontFeaturesVector().RemoveAt(i); + _appearance.SourceProfile().FontInfo().FontFeatures().Remove(key); + if (_FontFeaturesVector.Size() == 0) + { + _appearance.SourceProfile().FontInfo().ClearFontFeatures(); + } + break; + } + } + _NotifyChanges(L"CanFontAxesBeAdded"); + } + + void AppearanceViewModel::InitializeFontFeaturesVector() + { + if (!_FontFeaturesVector) + { + _FontFeaturesVector = single_threaded_observable_vector(); + } + + _FontFeaturesVector.Clear(); + if (const auto fontFeaturesMap = _appearance.SourceProfile().FontInfo().FontFeatures()) + { + const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); + for (const auto feature : fontFeaturesMap) + { + const auto featureKey = feature.Key(); + // only show the features that the font supports + // any features that the font doesn't support continue to be stored in the json, we just don't show them in the UI + if (fontFeaturesTagToNameMap.HasKey(featureKey)) + { + _FontFeaturesVector.Append(_CreateFeatureKeyValuePairHelper(featureKey, feature.Value(), fontFeaturesMap, fontFeaturesTagToNameMap)); + } + } + } + _NotifyChanges(L"AreFontFeaturesAvailable", L"CanFontFeaturesBeAdded"); + } + + // Method Description: + // - Determines whether the currently selected font has any font features + bool AppearanceViewModel::AreFontFeaturesAvailable() + { + return ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames().Size() > 0; + } + + // Method Description: + // - Determines whether the currently selected font has any font features that have not already been set + bool AppearanceViewModel::CanFontFeaturesBeAdded() + { + if (const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); fontFeaturesTagToNameMap.Size() > 0) + { + if (const auto fontFeaturesMap = _appearance.SourceProfile().FontInfo().FontFeatures()) + { + for (const auto tagAndName : fontFeaturesTagToNameMap) + { + if (!fontFeaturesMap.HasKey(tagAndName.Key())) + { + // we found a feature that has not been set + return true; + } + } + // all possible features have been set already + return false; + } + // the font supports font features but the profile has none set + return true; + } + // the font does not support any font features + return false; + } + + // Method Description: + // - Creates a FeatureKeyValuePair and sets up an event handler for it + Editor::FeatureKeyValuePair AppearanceViewModel::_CreateFeatureKeyValuePairHelper(hstring featureKey, uint32_t featureValue, const IMap& baseMap, const IMap& tagToNameMap) + { + const auto featureKeyValuePair = winrt::make(featureKey, featureValue, baseMap, tagToNameMap); + // when either the key or the value changes, send an event for the preview control to catch + featureKeyValuePair.PropertyChanged([weakThis = get_weak()](auto& sender, const PropertyChangedEventArgs& args) { + if (auto appVM{ weakThis.get() }) + { + appVM->_NotifyChanges(L"FeatureKeyValuePair"); + const auto settingName{ args.PropertyName() }; + if (settingName == L"FeatureKey") + { + const auto senderPair = sender.as(); + const auto senderKey = senderPair->FeatureKey(); + if (appVM->_IsDefaultFeature(senderKey)) + { + senderPair->FeatureValue(1); + } + else + { + senderPair->FeatureValue(0); + } + } + } + }); + return featureKeyValuePair; + } + + bool AppearanceViewModel::_IsDefaultFeature(winrt::hstring featureKey) + { + for (const auto defaultFeature : DefaultFeatures) + { + if (defaultFeature == featureKey) + { + return true; + } + } + return false; + } + DependencyProperty Appearances::_AppearanceProperty{ nullptr }; Appearances::Appearances() : @@ -546,6 +842,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _FontAxesNames = winrt::single_threaded_observable_vector(); FontAxesNamesCVS().Source(_FontAxesNames); + _FontFeaturesNames = winrt::single_threaded_observable_vector(); + FontFeaturesNamesCVS().Source(_FontFeaturesNames); + INITIALIZE_BINDABLE_ENUM_SETTING(IntenseTextStyle, IntenseTextStyle, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle, L"Appearance_IntenseTextStyle", L"Content"); } @@ -613,7 +912,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _FontAxesNames.Append(tagAndName.Value()); } - // when the font face changes, we have to tell the view model to update the font axes vector + _FontFeaturesNames.Clear(); + const auto featuresTagsAndNames = newFontFace.FontFeaturesTagsAndNames(); + for (const auto tagAndName : featuresTagsAndNames) + { + _FontFeaturesNames.Append(tagAndName.Value()); + } + + // when the font face changes, we have to tell the view model to update the font axes/features vectors // since the new font may not have the same possible axes as the previous one Appearance().InitializeFontAxesVector(); if (!Appearance().AreFontAxesAvailable()) @@ -627,6 +933,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { FontAxesContainer().HelpText(RS_(L"Profile_FontAxesAvailable/Text")); } + + Appearance().InitializeFontFeaturesVector(); + if (!Appearance().AreFontFeaturesAvailable()) + { + // if the previous font had available font features and the expander was expanded, + // at this point the expander would be set to disabled so manually collapse it + FontFeaturesContainer().SetExpanded(false); + FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesUnavailable/Text")); + } + else + { + FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesAvailable/Text")); + } } void Appearances::_ViewModelChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*args*/) @@ -648,6 +967,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation FontAxesCVS().Source(Appearance().FontAxesVector()); Appearance().AreFontAxesAvailable() ? FontAxesContainer().HelpText(RS_(L"Profile_FontAxesAvailable/Text")) : FontAxesContainer().HelpText(RS_(L"Profile_FontAxesUnavailable/Text")); + FontFeaturesCVS().Source(Appearance().FontFeaturesVector()); + Appearance().AreFontFeaturesAvailable() ? FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesAvailable/Text")) : FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesUnavailable/Text")); + _ViewModelChangedRevoker = Appearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) { const auto settingName{ args.PropertyName() }; if (settingName == L"CursorShape") @@ -786,6 +1108,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Appearance().AddNewAxisKeyValuePair(); } + void Appearances::DeleteFeatureKeyValuePair_Click(const IInspectable& sender, const RoutedEventArgs& /*e*/) + { + if (const auto& button{ sender.try_as() }) + { + if (const auto& tag{ button.Tag().try_as() }) + { + Appearance().DeleteFeatureKeyValuePair(tag.value()); + } + } + } + + void Appearances::AddNewFeatureKeyValuePair_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/) + { + Appearance().AddNewFeatureKeyValuePair(); + } + bool Appearances::IsVintageCursor() const { return Appearance().CursorShape() == Core::CursorStyle::Vintage; diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index 53a244ab0d9..d72bb418693 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -18,11 +18,13 @@ Author(s): #include "Font.g.h" #include "AxisKeyValuePair.g.h" +#include "FeatureKeyValuePair.g.h" #include "Appearances.g.h" #include "AppearanceViewModel.g.h" #include "Utils.h" #include "ViewModelHelpers.h" #include "SettingContainer.h" +#include namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { @@ -39,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation hstring ToString() { return _LocalizedName; } bool HasPowerlineCharacters(); Windows::Foundation::Collections::IMap FontAxesTagsAndNames(); + Windows::Foundation::Collections::IMap FontFeaturesTagsAndNames(); WINRT_PROPERTY(hstring, Name); WINRT_PROPERTY(hstring, LocalizedName); @@ -46,8 +49,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation private: winrt::com_ptr _family; std::optional _hasPowerlineCharacters; - winrt::hstring _axisTagToString(DWRITE_FONT_AXIS_TAG tag); + + winrt::hstring _tagToString(DWRITE_FONT_AXIS_TAG tag); + winrt::hstring _tagToString(DWRITE_FONT_FEATURE_TAG tag); + Windows::Foundation::Collections::IMap _fontAxesTagsAndNames; + Windows::Foundation::Collections::IMap _fontFeaturesTagsAndNames; }; struct AxisKeyValuePair : AxisKeyValuePairT, ViewModelHelper @@ -73,6 +80,29 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Windows::Foundation::Collections::IMap _tagToNameMap{ nullptr }; }; + struct FeatureKeyValuePair : FeatureKeyValuePairT, ViewModelHelper + { + FeatureKeyValuePair(winrt::hstring featureKey, uint32_t featureValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); + + winrt::hstring FeatureKey(); + void FeatureKey(winrt::hstring featureKey); + + uint32_t FeatureValue(); + void FeatureValue(uint32_t featureValue); + + int32_t FeatureIndex(); + void FeatureIndex(int32_t featureIndex); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + + private: + winrt::hstring _FeatureKey; + uint32_t _FeatureValue; + int32_t _FeatureIndex; + Windows::Foundation::Collections::IMap _baseMap{ nullptr }; + Windows::Foundation::Collections::IMap _tagToNameMap{ nullptr }; + }; + struct AppearanceViewModel : AppearanceViewModelT, ViewModelHelper { public: @@ -102,6 +132,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool AreFontAxesAvailable(); bool CanFontAxesBeAdded(); + void AddNewFeatureKeyValuePair(); + void DeleteFeatureKeyValuePair(winrt::hstring key); + void InitializeFontFeaturesVector(); + bool AreFontFeaturesAvailable(); + bool CanFontFeaturesBeAdded(); + WINRT_PROPERTY(bool, IsDefault, false); // These settings are not defined in AppearanceConfig, so we grab them @@ -113,6 +149,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontSize); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontWeight); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontAxes); + OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontFeatures); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), EnableBuiltinGlyphs); OBSERVABLE_PROJECTED_SETTING(_appearance, RetroTerminalEffect); @@ -128,12 +165,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance, AdjustIndistinguishableColors); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, SchemesList, _propertyChangedHandlers, nullptr); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontAxesVector, _propertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontFeaturesVector, _propertyChangedHandlers, nullptr); private: Model::AppearanceConfig _appearance; winrt::hstring _lastBgImagePath; Editor::AxisKeyValuePair _CreateAxisKeyValuePairHelper(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); + Editor::FeatureKeyValuePair _CreateFeatureKeyValuePairHelper(winrt::hstring axisKey, uint32_t axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); + + bool _IsDefaultFeature(winrt::hstring featureTag); }; struct Appearances : AppearancesT @@ -156,6 +197,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void FontFace_SelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e); void DeleteAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void AddNewAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + void DeleteFeatureKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + void AddNewFeatureKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); // manually bind FontWeight Windows::Foundation::IInspectable CurrentFontWeight() const; @@ -184,6 +227,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Editor::EnumEntry _CustomFontWeight{ nullptr }; Windows::Foundation::Collections::IObservableVector _FontAxesNames; + Windows::Foundation::Collections::IObservableVector _FontFeaturesNames; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; static void _ViewModelChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); @@ -195,4 +239,5 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation { BASIC_FACTORY(Appearances); BASIC_FACTORY(AxisKeyValuePair); + BASIC_FACTORY(FeatureKeyValuePair); } diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.idl b/src/cascadia/TerminalSettingsEditor/Appearances.idl index 68bd887d217..17ef8a1867a 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.idl +++ b/src/cascadia/TerminalSettingsEditor/Appearances.idl @@ -23,6 +23,7 @@ namespace Microsoft.Terminal.Settings.Editor String LocalizedName { get; }; Boolean HasPowerlineCharacters { get; }; Windows.Foundation.Collections.IMap FontAxesTagsAndNames { get; }; + Windows.Foundation.Collections.IMap FontFeaturesTagsAndNames { get; }; } // We have to make this because we cannot bind an IObservableMap to a ListView in XAML (in c++) @@ -35,6 +36,14 @@ namespace Microsoft.Terminal.Settings.Editor Int32 AxisIndex; } + runtimeclass FeatureKeyValuePair : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + FeatureKeyValuePair(String featureKey, UInt32 featureValue, Windows.Foundation.Collections.IMap baseMap, Windows.Foundation.Collections.IMap tagToNameMap); + String FeatureKey; + UInt32 FeatureValue; + Int32 FeatureIndex; + } + runtimeclass AppearanceViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged { Boolean IsDefault; @@ -58,6 +67,14 @@ namespace Microsoft.Terminal.Settings.Editor Windows.Foundation.Collections.IObservableVector FontAxesVector; OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.Collections.IMap, FontAxes); + void AddNewFeatureKeyValuePair(); + void DeleteFeatureKeyValuePair(String key); + void InitializeFontFeaturesVector(); + Boolean AreFontFeaturesAvailable { get; }; + Boolean CanFontFeaturesBeAdded { get; }; + Windows.Foundation.Collections.IObservableVector FontFeaturesVector; + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.Collections.IMap, FontFeatures); + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, FontFace); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Double, LineHeight); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index 0b5a9c2abfa..47c01df1fc9 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -39,6 +39,8 @@ + @@ -349,6 +351,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Add new Button label that adds a new font axis for the current font. + + Add new + Button label that adds a new font feature for the current font. + Background image opacity Name for a control to choose the opacity of the image presented on the background of the app. @@ -938,6 +942,18 @@ The selected font has no variable font axes. A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". + + Font features + Header for a control to allow editing the font features. + + + Add or remove font features for the given font. + A description for what the "font features" setting does. Presented near "Profile_FontFeatures". + + + The selected font has no font features. + A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". + General Header for a sub-page of profile settings focused on more general scenarios. @@ -1254,6 +1270,110 @@ Thin This is the formal name for a font weight. + + Required ligatures + This is the formal name for a font feature. + + + Localized forms + This is the formal name for a font feature. + + + Composition/decomposition + This is the formal name for a font feature. + + + Contextual alternates + This is the formal name for a font feature. + + + Standard ligatures + This is the formal name for a font feature. + + + Contextual ligatures + This is the formal name for a font feature. + + + Required variation alternates + This is the formal name for a font feature. + + + Kerning + This is the formal name for a font feature. + + + Mark positioning + This is the formal name for a font feature. + + + Mark to mark positioning + This is the formal name for a font feature. + + + Distance + This is the formal name for a font feature. + + + Access all alternates + This is the formal name for a font feature. + + + Case sensitive forms + This is the formal name for a font feature. + + + Denominator + This is the formal name for a font feature. + + + Terminal forms + This is the formal name for a font feature. + + + Fractions + This is the formal name for a font feature. + + + Initial forms + This is the formal name for a font feature. + + + Medial forms + This is the formal name for a font feature. + + + Numerator + This is the formal name for a font feature. + + + Ordinals + This is the formal name for a font feature. + + + Required contextual alternates + This is the formal name for a font feature. + + + Scientific inferiors + This is the formal name for a font feature. + + + Subscript + This is the formal name for a font feature. + + + Superscript + This is the formal name for a font feature. + + + Slashed zero + This is the formal name for a font feature. + + + Above-base mark positioning + This is the formal name for a font feature. + Launch size Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". diff --git a/src/cascadia/TerminalSettingsModel/FontConfig.cpp b/src/cascadia/TerminalSettingsModel/FontConfig.cpp index b01ec960658..fd9a181ae30 100644 --- a/src/cascadia/TerminalSettingsModel/FontConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/FontConfig.cpp @@ -42,6 +42,16 @@ winrt::com_ptr FontConfig::CopyFontInfo(const FontConfig* source, wi fontInfo->_FontAxes = winrt::single_threaded_map(std::move(fontAxes)); } + if (source->_FontFeatures) + { + std::map fontFeatures; + for (const auto keyValuePair : source->_FontFeatures.value()) + { + fontFeatures.insert(std::pair(keyValuePair.Key(), keyValuePair.Value())); + } + fontInfo->_FontFeatures = winrt::single_threaded_map(std::move(fontFeatures)); + } + return fontInfo; }