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)
This commit is contained in:
PankajBhojwani
2024-02-29 12:08:52 -08:00
committed by GitHub
parent 99042d2f0c
commit 6e451a2d4b
7 changed files with 605 additions and 4 deletions

View File

@@ -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

View File

@@ -5,6 +5,7 @@
#include "Appearances.h"
#include "Appearances.g.cpp"
#include "AxisKeyValuePair.g.cpp"
#include "FeatureKeyValuePair.g.cpp"
#include "EnumEntry.h"
#include <LibraryResources.h>
@@ -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<std::wstring_view, 11> 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<winrt::hstring, winrt::hstring>(_axisTagToString(axesVector[i].axisTag), builder.to_hstring()));
fontAxesTagsAndNames.insert(std::pair<winrt::hstring, winrt::hstring>(_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<hstring, hstring> Font::FontFeaturesTagsAndNames()
{
if (!_fontFeaturesTagsAndNames)
{
wil::com_ptr<IDWriteFont> font;
THROW_IF_FAILED(_family->GetFont(0, font.put()));
wil::com_ptr<IDWriteFontFace> fontFace;
THROW_IF_FAILED(font->CreateFontFace(fontFace.put()));
wil::com_ptr<IDWriteFactory2> factory;
THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<::IUnknown**>(factory.addressof())));
wil::com_ptr<IDWriteTextAnalyzer> textAnalyzer;
factory->CreateTextAnalyzer(textAnalyzer.addressof());
wil::com_ptr<IDWriteTextAnalyzer2> textAnalyzer2 = textAnalyzer.query<IDWriteTextAnalyzer2>();
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<DWRITE_FONT_FEATURE_TAG> tags{ tagCount };
textAnalyzer2->GetTypographicFeatures(fontFace.get(), scriptAnalysis, L"en-us", tagCount, &tagCount, tags.data());
std::unordered_map<winrt::hstring, winrt::hstring> 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<winrt::hstring, winrt::hstring>(tagString, localizedName));
}
_fontFeaturesTagsAndNames = winrt::single_threaded_map<winrt::hstring, winrt::hstring>(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<winrt::hstring, float>& baseMap, const Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring>& 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<hstring, uint32_t>& baseMap, const IMap<hstring, hstring>& 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<hstring, uint32_t>();
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<Editor::FeatureKeyValuePair>();
}
_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<hstring, uint32_t>& baseMap, const IMap<hstring, hstring>& tagToNameMap)
{
const auto featureKeyValuePair = winrt::make<winrt::Microsoft::Terminal::Settings::Editor::implementation::FeatureKeyValuePair>(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<FeatureKeyValuePair>();
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<winrt::hstring>();
FontAxesNamesCVS().Source(_FontAxesNames);
_FontFeaturesNames = winrt::single_threaded_observable_vector<hstring>();
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<Controls::Button>() })
{
if (const auto& tag{ button.Tag().try_as<hstring>() })
{
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;

View File

@@ -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 <LibraryResources.h>
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<winrt::hstring, winrt::hstring> FontAxesTagsAndNames();
Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring> FontFeaturesTagsAndNames();
WINRT_PROPERTY(hstring, Name);
WINRT_PROPERTY(hstring, LocalizedName);
@@ -46,8 +49,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
private:
winrt::com_ptr<IDWriteFontFamily> _family;
std::optional<bool> _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<winrt::hstring, winrt::hstring> _fontAxesTagsAndNames;
Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring> _fontFeaturesTagsAndNames;
};
struct AxisKeyValuePair : AxisKeyValuePairT<AxisKeyValuePair>, ViewModelHelper<AxisKeyValuePair>
@@ -73,6 +80,29 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring> _tagToNameMap{ nullptr };
};
struct FeatureKeyValuePair : FeatureKeyValuePairT<FeatureKeyValuePair>, ViewModelHelper<FeatureKeyValuePair>
{
FeatureKeyValuePair(winrt::hstring featureKey, uint32_t featureValue, const Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>& baseMap, const Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring>& 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<winrt::hstring, uint32_t> _baseMap{ nullptr };
Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring> _tagToNameMap{ nullptr };
};
struct AppearanceViewModel : AppearanceViewModelT<AppearanceViewModel>, ViewModelHelper<AppearanceViewModel>
{
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<Editor::ColorSchemeViewModel>, SchemesList, _propertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::AxisKeyValuePair>, FontAxesVector, _propertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::FeatureKeyValuePair>, FontFeaturesVector, _propertyChangedHandlers, nullptr);
private:
Model::AppearanceConfig _appearance;
winrt::hstring _lastBgImagePath;
Editor::AxisKeyValuePair _CreateAxisKeyValuePairHelper(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap<winrt::hstring, float>& baseMap, const Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring>& tagToNameMap);
Editor::FeatureKeyValuePair _CreateFeatureKeyValuePairHelper(winrt::hstring axisKey, uint32_t axisValue, const Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>& baseMap, const Windows::Foundation::Collections::IMap<winrt::hstring, winrt::hstring>& tagToNameMap);
bool _IsDefaultFeature(winrt::hstring featureTag);
};
struct Appearances : AppearancesT<Appearances>
@@ -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<winrt::hstring> _FontAxesNames;
Windows::Foundation::Collections::IObservableVector<winrt::hstring> _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);
}

View File

@@ -23,6 +23,7 @@ namespace Microsoft.Terminal.Settings.Editor
String LocalizedName { get; };
Boolean HasPowerlineCharacters { get; };
Windows.Foundation.Collections.IMap<String, String> FontAxesTagsAndNames { get; };
Windows.Foundation.Collections.IMap<String, String> 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<String, UInt32> baseMap, Windows.Foundation.Collections.IMap<String, String> 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<AxisKeyValuePair> FontAxesVector;
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.Collections.IMap<String COMMA Single>, FontAxes);
void AddNewFeatureKeyValuePair();
void DeleteFeatureKeyValuePair(String key);
void InitializeFontFeaturesVector();
Boolean AreFontFeaturesAvailable { get; };
Boolean CanFontFeaturesBeAdded { get; };
Windows.Foundation.Collections.IObservableVector<FeatureKeyValuePair> FontFeaturesVector;
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.Collections.IMap<String COMMA UInt32>, FontFeatures);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, FontFace);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Double, LineHeight);

View File

@@ -39,6 +39,8 @@
</DataTemplate>
<CollectionViewSource x:Key="FontAxesCVS"
x:Name="FontAxesCVS" />
<CollectionViewSource x:Key="FontFeaturesCVS"
x:Name="FontFeaturesCVS" />
</ResourceDictionary>
</UserControl.Resources>
@@ -349,6 +351,67 @@
</Button>
</StackPanel>
</local:SettingContainer>
<local:SettingContainer x:Name="FontFeaturesContainer"
x:Uid="Profile_FontFeatures"
ClearSettingValue="{x:Bind Appearance.ClearFontFeatures}"
HasSettingValue="{x:Bind Appearance.HasFontFeatures, Mode=OneWay}"
IsEnabled="{x:Bind Appearance.AreFontFeaturesAvailable, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.FontFeaturesOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}">
<StackPanel Spacing="16">
<ListView IsItemClickEnabled="False"
ItemsSource="{Binding Source={StaticResource FontFeaturesCVS}}"
SelectionMode="None">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<CollectionViewSource x:Key="FontFeaturesNamesCVS"
x:Name="FontFeaturesNamesCVS" />
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:FeatureKeyValuePair">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0"
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource FontFeaturesNamesCVS}}"
SelectedIndex="{x:Bind FeatureIndex, Mode=TwoWay}" />
<muxc:NumberBox Grid.Column="1"
Value="{x:Bind FeatureValue, Mode=TwoWay}" />
<Button Grid.Column="2"
HorizontalAlignment="Right"
Click="DeleteFeatureKeyValuePair_Click"
Style="{StaticResource DeleteButtonStyle}"
Tag="{x:Bind FeatureKey}">
<FontIcon FontSize="{StaticResource StandardIconSize}"
Glyph="&#xE74D;" />
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Click="AddNewFeatureKeyValuePair_Click"
IsEnabled="{x:Bind Appearance.CanFontFeaturesBeAdded, Mode=OneWay}">
<Button.Content>
<StackPanel Orientation="Horizontal">
<FontIcon VerticalAlignment="Bottom"
FontSize="{StaticResource StandardIconSize}"
Glyph="&#xE710;" />
<TextBlock x:Uid="Profile_AddNewFontFeature"
Style="{StaticResource IconButtonTextBlockStyle}" />
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
</local:SettingContainer>
<!-- Builtin Glyphs -->
<local:SettingContainer x:Uid="Profile_EnableBuiltinGlyphs"

View File

@@ -726,6 +726,10 @@
<value>Add new</value>
<comment>Button label that adds a new font axis for the current font.</comment>
</data>
<data name="Profile_AddNewFontFeature.Text" xml:space="preserve">
<value>Add new</value>
<comment>Button label that adds a new font feature for the current font.</comment>
</data>
<data name="Profile_BackgroundImageOpacitySlider.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Background image opacity</value>
<comment>Name for a control to choose the opacity of the image presented on the background of the app.</comment>
@@ -938,6 +942,18 @@
<value>The selected font has no variable font axes.</value>
<comment>A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes".</comment>
</data>
<data name="Profile_FontFeatures.Header" xml:space="preserve">
<value>Font features</value>
<comment>Header for a control to allow editing the font features.</comment>
</data>
<data name="Profile_FontFeaturesAvailable.Text" xml:space="preserve">
<value>Add or remove font features for the given font.</value>
<comment>A description for what the "font features" setting does. Presented near "Profile_FontFeatures".</comment>
</data>
<data name="Profile_FontFeaturesUnavailable.Text" xml:space="preserve">
<value>The selected font has no font features.</value>
<comment>A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures".</comment>
</data>
<data name="Profile_General.Header" xml:space="preserve">
<value>General</value>
<comment>Header for a sub-page of profile settings focused on more general scenarios.</comment>
@@ -1254,6 +1270,110 @@
<value>Thin</value>
<comment>This is the formal name for a font weight.</comment>
</data>
<data name="Profile_FontFeature_rlig" xml:space="preserve">
<value>Required ligatures</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_locl" xml:space="preserve">
<value>Localized forms</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_ccmp" xml:space="preserve">
<value>Composition/decomposition</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_calt" xml:space="preserve">
<value>Contextual alternates</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_liga" xml:space="preserve">
<value>Standard ligatures</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_clig" xml:space="preserve">
<value>Contextual ligatures</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_rvrn" xml:space="preserve">
<value>Required variation alternates</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_kern" xml:space="preserve">
<value>Kerning</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_mark" xml:space="preserve">
<value>Mark positioning</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_mkmk" xml:space="preserve">
<value>Mark to mark positioning</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_dist" xml:space="preserve">
<value>Distance</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_aalt" xml:space="preserve">
<value>Access all alternates</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_case" xml:space="preserve">
<value>Case sensitive forms</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_dnom" xml:space="preserve">
<value>Denominator</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_fina" xml:space="preserve">
<value>Terminal forms</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_frac" xml:space="preserve">
<value>Fractions</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_init" xml:space="preserve">
<value>Initial forms</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_medi" xml:space="preserve">
<value>Medial forms</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_numr" xml:space="preserve">
<value>Numerator</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_ordn" xml:space="preserve">
<value>Ordinals</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_rclt" xml:space="preserve">
<value>Required contextual alternates</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_sinf" xml:space="preserve">
<value>Scientific inferiors</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_subs" xml:space="preserve">
<value>Subscript</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_sups" xml:space="preserve">
<value>Superscript</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_zero" xml:space="preserve">
<value>Slashed zero</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Profile_FontFeature_abvm" xml:space="preserve">
<value>Above-base mark positioning</value>
<comment>This is the formal name for a font feature.</comment>
</data>
<data name="Globals_LaunchSize.Header" xml:space="preserve">
<value>Launch size</value>
<comment>Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows".</comment>

View File

@@ -42,6 +42,16 @@ winrt::com_ptr<FontConfig> FontConfig::CopyFontInfo(const FontConfig* source, wi
fontInfo->_FontAxes = winrt::single_threaded_map(std::move(fontAxes));
}
if (source->_FontFeatures)
{
std::map<winrt::hstring, uint32_t> fontFeatures;
for (const auto keyValuePair : source->_FontFeatures.value())
{
fontFeatures.insert(std::pair<winrt::hstring, uint32_t>(keyValuePair.Key(), keyValuePair.Value()));
}
fontInfo->_FontFeatures = winrt::single_threaded_map(std::move(fontFeatures));
}
return fontInfo;
}