Implement cell size customizations (#14255)
Does what it says in the title. After this commit you can customize the height and width of the terminal's cells. This commit supports parts of CSS' `<length-percentage>` data type: Font-size relative sizes as multiples (`1.2`), percentage (`120%`), or advance-width relative (`1.2ch`), as well as absolute sizes in CSS pixels (`px`) or points (`pt`). This PR is neither bug free in DxEngine, nor in AtlasEngine. The former fails to implement glyph advance corrections (for instance #9381), as well as disallowing glyphs to overlap rows. The latter has the same overlap issue, but more severely as it tries to shrink glyphs to fit in. Closes #3498 Closes #14068 ## Validation Steps Performed * Setting `height` to `1` creates 12pt tall rows ✅ * Setting `height` to `1ch` creates square cells ✅ * Setting `width` to `1` creates square cells ✅ * Setting `width` or `height` to `Npx` or `Npt` works ✅ * Trailing zeroes are trimmed properly during serialization ✅ * Patching the PR to allow >100 line heights and entering "100.123456" displays 6 fractional digits ✅
This commit is contained in:
parent
e4bba3cd9a
commit
b6e6dd861d
|
@ -24,6 +24,13 @@
|
|||
"pattern": "^(-?\\d+)?(,\\s?(-?\\d+)?)?$",
|
||||
"type": "string"
|
||||
},
|
||||
"CSSLengthPercentage": {
|
||||
"pattern": "^[+-]?\\d+(?:\\.\\d+)?(?:%|ch|pt|px)?$",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"DynamicProfileSource": {
|
||||
"enum": [
|
||||
"Windows.Terminal.Wsl",
|
||||
|
@ -314,6 +321,14 @@
|
|||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"cellWidth": {
|
||||
"$ref": "#/$defs/CSSLengthPercentage",
|
||||
"description": "Override the width of the terminal's cells. The override works similar to CSS' letter-spacing. It defaults to the natural glyph advance width of the primary font rounded to the nearest pixel."
|
||||
},
|
||||
"cellHeight": {
|
||||
"$ref": "#/$defs/CSSLengthPercentage",
|
||||
"description": "Override the height of the terminal's cells. The override works similar to CSS' line-height. Defaults to the sum of the natural glyph ascend, descend and line-gap of the primary font rounded to the nearest pixel. The default is usually quite close to setting this to 1.2."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
|
@ -728,6 +728,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
|
||||
_cellWidth = CSSLengthPercentage::FromString(_settings->CellWidth().c_str());
|
||||
_cellHeight = CSSLengthPercentage::FromString(_settings->CellHeight().c_str());
|
||||
_runtimeOpacity = std::nullopt;
|
||||
|
||||
// Manually turn off acrylic if they turn off transparency.
|
||||
|
@ -880,6 +882,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_actualFont = { fontFace, 0, fontWeight.Weight, _desiredFont.GetEngineSize(), CP_UTF8, false };
|
||||
_actualFontFaceName = { fontFace };
|
||||
|
||||
_desiredFont.SetCellSize(_cellWidth, _cellHeight);
|
||||
|
||||
const auto before = _actualFont.GetSize();
|
||||
_updateFont();
|
||||
const auto after = _actualFont.GetSize();
|
||||
|
|
|
@ -258,6 +258,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
FontInfoDesired _desiredFont;
|
||||
FontInfo _actualFont;
|
||||
winrt::hstring _actualFontFaceName;
|
||||
CSSLengthPercentage _cellWidth;
|
||||
CSSLengthPercentage _cellHeight;
|
||||
|
||||
// storage location for the leading surrogate of a utf-16 surrogate pair
|
||||
std::optional<wchar_t> _leadingSurrogate{ std::nullopt };
|
||||
|
|
|
@ -42,6 +42,8 @@ namespace Microsoft.Terminal.Control
|
|||
String Padding { get; };
|
||||
Windows.Foundation.Collections.IMap<String, UInt32> FontFeatures { get; };
|
||||
Windows.Foundation.Collections.IMap<String, Single> FontAxes { get; };
|
||||
String CellWidth { get; };
|
||||
String CellHeight { get; };
|
||||
|
||||
Microsoft.Terminal.Control.IKeyBindings KeyBindings { get; };
|
||||
|
||||
|
|
|
@ -49,6 +49,78 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
}
|
||||
}
|
||||
|
||||
double AppearanceViewModel::LineHeight() const noexcept
|
||||
{
|
||||
const auto fontInfo = _appearance.SourceProfile().FontInfo();
|
||||
const auto cellHeight = fontInfo.CellHeight();
|
||||
const auto str = cellHeight.c_str();
|
||||
|
||||
auto& errnoRef = errno; // Nonzero cost, pay it once.
|
||||
errnoRef = 0;
|
||||
|
||||
wchar_t* end;
|
||||
const auto value = std::wcstod(str, &end);
|
||||
|
||||
return str == end || errnoRef == ERANGE ? NAN : value;
|
||||
}
|
||||
|
||||
void AppearanceViewModel::LineHeight(const double value)
|
||||
{
|
||||
std::wstring str;
|
||||
|
||||
if (value >= 0.1 && value <= 10.0)
|
||||
{
|
||||
str = fmt::format(FMT_STRING(L"{:.6g}"), value);
|
||||
}
|
||||
|
||||
const auto fontInfo = _appearance.SourceProfile().FontInfo();
|
||||
|
||||
if (fontInfo.CellHeight() != str)
|
||||
{
|
||||
if (str.empty())
|
||||
{
|
||||
fontInfo.ClearCellHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
fontInfo.CellHeight(str);
|
||||
}
|
||||
_NotifyChanges(L"HasLineHeight", L"LineHeight");
|
||||
}
|
||||
}
|
||||
|
||||
bool AppearanceViewModel::HasLineHeight() const
|
||||
{
|
||||
const auto fontInfo = _appearance.SourceProfile().FontInfo();
|
||||
return fontInfo.HasCellHeight();
|
||||
}
|
||||
|
||||
void AppearanceViewModel::ClearLineHeight()
|
||||
{
|
||||
LineHeight(NAN);
|
||||
}
|
||||
|
||||
Model::FontConfig AppearanceViewModel::LineHeightOverrideSource() const
|
||||
{
|
||||
const auto fontInfo = _appearance.SourceProfile().FontInfo();
|
||||
return fontInfo.CellHeightOverrideSource();
|
||||
}
|
||||
|
||||
void AppearanceViewModel::SetFontWeightFromDouble(double fontWeight)
|
||||
{
|
||||
FontWeight(Converters::DoubleToFontWeight(fontWeight));
|
||||
}
|
||||
|
||||
void AppearanceViewModel::SetBackgroundImageOpacityFromPercentageValue(double percentageValue)
|
||||
{
|
||||
BackgroundImageOpacity(Converters::PercentageValueToPercentage(percentageValue));
|
||||
}
|
||||
|
||||
void AppearanceViewModel::SetBackgroundImagePath(winrt::hstring path)
|
||||
{
|
||||
BackgroundImagePath(path);
|
||||
}
|
||||
|
||||
bool AppearanceViewModel::UseDesktopBGImage()
|
||||
{
|
||||
return BackgroundImagePath() == L"desktopWallpaper";
|
||||
|
@ -123,10 +195,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
// > .NET rounds to 12 significant digits when displaying doubles, so we will [...]
|
||||
// ...obviously not do that, because this is an UI element for humans. This prevents
|
||||
// issues when displaying 32-bit floats, because WinUI is unaware about their existence.
|
||||
SignificantDigitsNumberRounder rounder;
|
||||
rounder.SignificantDigits(6);
|
||||
// BODGY: Depends on WinUI internals.
|
||||
_fontSizeBox().NumberFormatter().as<DecimalFormatter>().NumberRounder(rounder);
|
||||
IncrementNumberRounder rounder;
|
||||
rounder.Increment(1e-6);
|
||||
|
||||
for (const auto& box : { _fontSizeBox(), _lineHeightBox() })
|
||||
{
|
||||
// BODGY: Depends on WinUI internals.
|
||||
box.NumberFormatter().as<DecimalFormatter>().NumberRounder(rounder);
|
||||
}
|
||||
}
|
||||
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(CursorShape, CursorStyle, winrt::Microsoft::Terminal::Core::CursorStyle, L"Profile_CursorShape", L"Content");
|
||||
|
|
|
@ -51,18 +51,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
public:
|
||||
AppearanceViewModel(const Model::AppearanceConfig& appearance);
|
||||
|
||||
void SetFontWeightFromDouble(double fontWeight)
|
||||
{
|
||||
FontWeight(winrt::Microsoft::Terminal::Settings::Editor::Converters::DoubleToFontWeight(fontWeight));
|
||||
}
|
||||
void SetBackgroundImageOpacityFromPercentageValue(double percentageValue)
|
||||
{
|
||||
BackgroundImageOpacity(winrt::Microsoft::Terminal::Settings::Editor::Converters::PercentageValueToPercentage(percentageValue));
|
||||
}
|
||||
void SetBackgroundImagePath(winrt::hstring path)
|
||||
{
|
||||
BackgroundImagePath(path);
|
||||
}
|
||||
double LineHeight() const noexcept;
|
||||
void LineHeight(const double value);
|
||||
bool HasLineHeight() const;
|
||||
void ClearLineHeight();
|
||||
Model::FontConfig LineHeightOverrideSource() const;
|
||||
void SetFontWeightFromDouble(double fontWeight);
|
||||
void SetBackgroundImageOpacityFromPercentageValue(double percentageValue);
|
||||
void SetBackgroundImagePath(winrt::hstring path);
|
||||
|
||||
// background image
|
||||
bool UseDesktopBGImage();
|
||||
|
@ -100,6 +96,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
private:
|
||||
Model::AppearanceConfig _appearance;
|
||||
winrt::hstring _lastBgImagePath;
|
||||
float _cachedLineHeight = 0;
|
||||
};
|
||||
|
||||
struct Appearances : AppearancesT<Appearances>
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
|||
|
||||
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, FontFace);
|
||||
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize);
|
||||
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Double, LineHeight);
|
||||
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.UI.Text.FontWeight, FontWeight);
|
||||
|
||||
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, DarkColorSchemeName);
|
||||
|
|
|
@ -219,7 +219,6 @@
|
|||
Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}">
|
||||
<muxc:NumberBox x:Name="_fontSizeBox"
|
||||
x:Uid="Profile_FontSizeBox"
|
||||
AcceptsExpression="False"
|
||||
LargeChange="10"
|
||||
Maximum="128"
|
||||
Minimum="1"
|
||||
|
@ -228,6 +227,22 @@
|
|||
Value="{x:Bind Appearance.FontSize, Mode=TwoWay}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Line Height -->
|
||||
<local:SettingContainer x:Uid="Profile_LineHeight"
|
||||
ClearSettingValue="{x:Bind Appearance.ClearLineHeight}"
|
||||
HasSettingValue="{x:Bind Appearance.HasLineHeight, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind Appearance.LineHeightOverrideSource, Mode=OneWay}"
|
||||
Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}">
|
||||
<muxc:NumberBox x:Name="_lineHeightBox"
|
||||
x:Uid="Profile_LineHeightBox"
|
||||
LargeChange="0.1"
|
||||
Maximum="10"
|
||||
Minimum="0.1"
|
||||
SmallChange="0.1"
|
||||
Style="{StaticResource NumberBoxSettingStyle}"
|
||||
Value="{x:Bind Appearance.LineHeight, Mode=TwoWay}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Font Weight -->
|
||||
<local:SettingContainer x:Name="FontWeightContainer"
|
||||
x:Uid="Profile_FontWeight"
|
||||
|
|
|
@ -874,6 +874,22 @@
|
|||
<value>Size of the font in points.</value>
|
||||
<comment>A description for what the "font size" setting does. Presented near "Profile_FontSize".</comment>
|
||||
</data>
|
||||
<data name="Profile_LineHeight.Header" xml:space="preserve">
|
||||
<value>Line height</value>
|
||||
<comment>Header for a control that sets the text line height.</comment>
|
||||
</data>
|
||||
<data name="Profile_LineHeightBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Line height</value>
|
||||
<comment>Header for a control that sets the text line height.</comment>
|
||||
</data>
|
||||
<data name="Profile_LineHeight.HelpText" xml:space="preserve">
|
||||
<value>Sets the height of each line in the terminal as a multiple of the font size. The default depends on your font and is usually around 1.2.</value>
|
||||
<comment>A description for what the "line height" setting does. Presented near "Profile_LineHeight".</comment>
|
||||
</data>
|
||||
<data name="Profile_LineHeightBox.PlaceholderText" xml:space="preserve">
|
||||
<value>1.2</value>
|
||||
<comment>"1.2" is a decimal number.</comment>
|
||||
</data>
|
||||
<data name="Profile_FontWeightComboBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Font weight</value>
|
||||
<comment>Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app.</comment>
|
||||
|
|
|
@ -295,7 +295,7 @@ Model::Profile CascadiaSettings::DuplicateProfile(const Model::Profile& source)
|
|||
MTSM_PROFILE_SETTINGS(DUPLICATE_PROFILE_SETTINGS)
|
||||
#undef DUPLICATE_PROFILE_SETTINGS
|
||||
|
||||
// These two aren't in MTSM_PROFILE_SETTINGS because they're special
|
||||
// These aren't in MTSM_PROFILE_SETTINGS because they're special
|
||||
DUPLICATE_SETTING_MACRO(TabColor);
|
||||
DUPLICATE_SETTING_MACRO(Padding);
|
||||
|
||||
|
|
|
@ -78,11 +78,6 @@ void FontConfig::LayerJson(const Json::Value& json)
|
|||
}
|
||||
}
|
||||
|
||||
bool FontConfig::HasAnyOptionSet() const
|
||||
{
|
||||
return HasFontFace() || HasFontSize() || HasFontWeight();
|
||||
}
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile FontConfig::SourceProfile()
|
||||
{
|
||||
return _sourceProfile.get();
|
||||
|
|
|
@ -35,7 +35,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
static winrt::com_ptr<FontConfig> CopyFontInfo(const FontConfig* source, winrt::weak_ref<Profile> sourceProfile);
|
||||
Json::Value ToJson() const;
|
||||
void LayerJson(const Json::Value& json);
|
||||
bool HasAnyOptionSet() const;
|
||||
|
||||
Model::Profile SourceProfile();
|
||||
|
||||
|
|
|
@ -18,8 +18,9 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
INHERITABLE_FONT_SETTING(String, FontFace);
|
||||
INHERITABLE_FONT_SETTING(Single, FontSize);
|
||||
INHERITABLE_FONT_SETTING(Windows.UI.Text.FontWeight, FontWeight);
|
||||
|
||||
INHERITABLE_FONT_SETTING(Windows.Foundation.Collections.IMap<String COMMA UInt32>, FontFeatures);
|
||||
INHERITABLE_FONT_SETTING(Windows.Foundation.Collections.IMap<String COMMA Single>, FontAxes);
|
||||
INHERITABLE_FONT_SETTING(String, CellWidth);
|
||||
INHERITABLE_FONT_SETTING(String, CellHeight);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,9 @@ Author(s):
|
|||
X(float, FontSize, "size", DEFAULT_FONT_SIZE) \
|
||||
X(winrt::Windows::UI::Text::FontWeight, FontWeight, "weight", DEFAULT_FONT_WEIGHT) \
|
||||
X(IFontAxesMap, FontAxes, "axes") \
|
||||
X(IFontFeatureMap, FontFeatures, "features")
|
||||
X(IFontFeatureMap, FontFeatures, "features") \
|
||||
X(winrt::hstring, CellWidth, "cellWidth") \
|
||||
X(winrt::hstring, CellHeight, "cellHeight")
|
||||
|
||||
#define MTSM_APPEARANCE_SETTINGS(X) \
|
||||
X(Core::CursorStyle, CursorShape, "cursorShape", Core::CursorStyle::Bar) \
|
||||
|
|
|
@ -324,11 +324,9 @@ Json::Value Profile::ToJson() const
|
|||
MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_TO_JSON)
|
||||
#undef PROFILE_SETTINGS_TO_JSON
|
||||
|
||||
// Font settings
|
||||
const auto fontInfoImpl = winrt::get_self<FontConfig>(_FontInfo);
|
||||
if (fontInfoImpl->HasAnyOptionSet())
|
||||
if (auto fontJSON = winrt::get_self<FontConfig>(_FontInfo)->ToJson(); !fontJSON.empty())
|
||||
{
|
||||
json[JsonKey(FontInfoKey)] = winrt::get_self<FontConfig>(_FontInfo)->ToJson();
|
||||
json[JsonKey(FontInfoKey)] = std::move(fontJSON);
|
||||
}
|
||||
|
||||
if (_UnfocusedAppearance)
|
||||
|
|
|
@ -271,11 +271,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
_ProfileSource = profile.Source();
|
||||
_UseAcrylic = profile.UseAcrylic();
|
||||
|
||||
_FontFace = profile.FontInfo().FontFace();
|
||||
_FontSize = profile.FontInfo().FontSize();
|
||||
_FontWeight = profile.FontInfo().FontWeight();
|
||||
_FontFeatures = profile.FontInfo().FontFeatures();
|
||||
_FontAxes = profile.FontInfo().FontAxes();
|
||||
const auto fontInfo = profile.FontInfo();
|
||||
_FontFace = fontInfo.FontFace();
|
||||
_FontSize = fontInfo.FontSize();
|
||||
_FontWeight = fontInfo.FontWeight();
|
||||
_FontFeatures = fontInfo.FontFeatures();
|
||||
_FontAxes = fontInfo.FontAxes();
|
||||
_CellWidth = fontInfo.CellWidth();
|
||||
_CellHeight = fontInfo.CellHeight();
|
||||
_Padding = profile.Padding();
|
||||
|
||||
_Commandline = profile.Commandline();
|
||||
|
|
|
@ -126,6 +126,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, IFontAxesMap, FontAxes);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, IFontFeatureMap, FontFeatures);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, hstring, CellWidth);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, hstring, CellHeight);
|
||||
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, Model::ColorScheme, AppliedColorScheme);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage);
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
X(winrt::Windows::UI::Text::FontWeight, FontWeight) \
|
||||
X(IFontFeatureMap, FontFeatures) \
|
||||
X(IFontAxesMap, FontAxes) \
|
||||
X(winrt::hstring, CellWidth) \
|
||||
X(winrt::hstring, CellHeight) \
|
||||
X(winrt::Microsoft::Terminal::Control::IKeyBindings, KeyBindings, nullptr) \
|
||||
X(winrt::hstring, Commandline) \
|
||||
X(winrt::hstring, StartingDirectory) \
|
||||
|
|
|
@ -627,20 +627,23 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
|
|||
const auto designUnitsPerPx = fontSizeInPx / static_cast<float>(metrics.designUnitsPerEm);
|
||||
const auto ascent = static_cast<float>(metrics.ascent) * designUnitsPerPx;
|
||||
const auto descent = static_cast<float>(metrics.descent) * designUnitsPerPx;
|
||||
const auto lineGap = static_cast<float>(metrics.lineGap) * designUnitsPerPx;
|
||||
const auto underlinePosition = static_cast<float>(-metrics.underlinePosition) * designUnitsPerPx;
|
||||
const auto underlineThickness = static_cast<float>(metrics.underlineThickness) * designUnitsPerPx;
|
||||
const auto strikethroughPosition = static_cast<float>(-metrics.strikethroughPosition) * designUnitsPerPx;
|
||||
const auto strikethroughThickness = static_cast<float>(metrics.strikethroughThickness) * designUnitsPerPx;
|
||||
|
||||
const auto advanceWidth = static_cast<float>(glyphMetrics.advanceWidth) * designUnitsPerPx;
|
||||
const auto advanceHeight = ascent + descent + lineGap;
|
||||
|
||||
// NOTE: Line-gaps shouldn't be taken into account for lineHeight calculations.
|
||||
// Terminals don't really have "gaps" between lines and instead the expectation
|
||||
// is that two full block characters above each other don't leave any gaps
|
||||
// between the lines. "Terminus TTF" for instance sets a line-gap of 90 units
|
||||
// even though its font bitmap only covers the ascend/descend height.
|
||||
const auto baseline = std::roundf(ascent);
|
||||
const auto lineHeight = std::roundf(baseline + descent);
|
||||
auto adjustedWidth = std::roundf(fontInfoDesired.GetCellWidth().Resolve(advanceWidth, _api.dpi, fontSizeInPx, advanceWidth));
|
||||
auto adjustedHeight = std::roundf(fontInfoDesired.GetCellHeight().Resolve(advanceHeight, _api.dpi, fontSizeInPx, advanceWidth));
|
||||
|
||||
// Protection against bad user values in GetCellWidth/Y.
|
||||
// AtlasEngine fails hard with 0 cell sizes.
|
||||
adjustedWidth = std::max(1.0f, adjustedWidth);
|
||||
adjustedHeight = std::max(1.0f, adjustedHeight);
|
||||
|
||||
const auto baseline = std::roundf(ascent + (lineGap + adjustedHeight - advanceHeight) / 2.0f);
|
||||
const auto underlinePos = std::roundf(baseline + underlinePosition);
|
||||
const auto underlineWidth = std::max(1.0f, std::roundf(underlineThickness));
|
||||
const auto strikethroughPos = std::roundf(baseline + strikethroughPosition);
|
||||
|
@ -667,10 +670,10 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
|
|||
const auto doubleUnderlineGap = std::max(1.0f, std::roundf(1.2f / 72.0f * _api.dpi));
|
||||
doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + thinLineWidth);
|
||||
// Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries.
|
||||
doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, lineHeight - thinLineWidth);
|
||||
doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, adjustedHeight - thinLineWidth);
|
||||
|
||||
const auto cellWidth = gsl::narrow<u16>(std::lroundf(advanceWidth));
|
||||
const auto cellHeight = gsl::narrow<u16>(lineHeight);
|
||||
const auto cellWidth = gsl::narrow<u16>(std::lroundf(adjustedWidth));
|
||||
const auto cellHeight = gsl::narrow<u16>(std::lroundf(adjustedHeight));
|
||||
|
||||
{
|
||||
til::size coordSize;
|
||||
|
@ -708,7 +711,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
|
|||
fontMetrics->fontName = std::move(fontName);
|
||||
fontMetrics->fontSizeInDIP = fontSizeInDIP;
|
||||
fontMetrics->baselineInDIP = baseline / static_cast<float>(_api.dpi) * 96.0f;
|
||||
fontMetrics->advanceScale = cellWidth / advanceWidth;
|
||||
fontMetrics->advanceScale = cellWidth / adjustedWidth;
|
||||
fontMetrics->cellSize = { cellWidth, cellHeight };
|
||||
fontMetrics->fontWeight = fontWeightU16;
|
||||
fontMetrics->underlinePos = underlinePosU16;
|
||||
|
|
|
@ -1148,6 +1148,14 @@ void AtlasEngine::_recreateFontDependentResources()
|
|||
// fonts making them look fairly unslightly. With no option to easily disable this
|
||||
// feature in Windows Terminal, it's better left disabled by default.
|
||||
|
||||
const DWRITE_LINE_SPACING lineSpacing{
|
||||
.method = DWRITE_LINE_SPACING_METHOD_UNIFORM,
|
||||
.height = _r.cellSizeDIP.y,
|
||||
.baseline = _api.fontMetrics.baselineInDIP,
|
||||
.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_ENABLED,
|
||||
};
|
||||
THROW_IF_FAILED(textFormat.query<IDWriteTextFormat2>()->SetLineSpacing(&lineSpacing));
|
||||
|
||||
if (!_api.fontAxisValues.empty())
|
||||
{
|
||||
if (const auto textFormat3 = textFormat.try_query<IDWriteTextFormat3>())
|
||||
|
|
|
@ -412,8 +412,8 @@ namespace Microsoft::Console::Render
|
|||
wil::com_ptr<IDWriteFontCollection> fontCollection;
|
||||
wil::com_ptr<IDWriteFontFamily> fontFamily;
|
||||
std::wstring fontName;
|
||||
float baselineInDIP = 0.0f;
|
||||
float fontSizeInDIP = 0.0f;
|
||||
f32 baselineInDIP = 0.0f;
|
||||
f32 fontSizeInDIP = 0.0f;
|
||||
f32 advanceScale = 0;
|
||||
u16x2 cellSize;
|
||||
u16 fontWeight = 0;
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "../inc/CSSLengthPercentage.h"
|
||||
|
||||
CSSLengthPercentage CSSLengthPercentage::FromString(const wchar_t* str)
|
||||
{
|
||||
auto& errnoRef = errno; // Nonzero cost, pay it once.
|
||||
errnoRef = 0;
|
||||
|
||||
wchar_t* end;
|
||||
auto value = std::wcstof(str, &end);
|
||||
|
||||
if (str == end || errnoRef == ERANGE)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto referenceFrame = ReferenceFrame::FontSize;
|
||||
|
||||
if (*end)
|
||||
{
|
||||
if (wcscmp(end, L"%") == 0)
|
||||
{
|
||||
value /= 100.0f;
|
||||
}
|
||||
else if (wcscmp(end, L"px") == 0)
|
||||
{
|
||||
referenceFrame = ReferenceFrame::Absolute;
|
||||
value /= 96.0f;
|
||||
}
|
||||
else if (wcscmp(end, L"pt") == 0)
|
||||
{
|
||||
referenceFrame = ReferenceFrame::Absolute;
|
||||
value /= 72.0f;
|
||||
}
|
||||
else if (wcscmp(end, L"ch") == 0)
|
||||
{
|
||||
referenceFrame = ReferenceFrame::AdvanceWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
CSSLengthPercentage obj;
|
||||
obj._value = value;
|
||||
obj._referenceFrame = referenceFrame;
|
||||
return obj;
|
||||
}
|
||||
|
||||
CSSLengthPercentage::ReferenceFrame CSSLengthPercentage::GetReferenceFrame() const noexcept
|
||||
{
|
||||
return _referenceFrame;
|
||||
}
|
||||
|
||||
float CSSLengthPercentage::Resolve(float factor) const noexcept
|
||||
{
|
||||
return _value * factor;
|
||||
}
|
||||
|
||||
float CSSLengthPercentage::Resolve(float fallback, float dpi, float fontSize, float advanceWidth) const noexcept
|
||||
{
|
||||
switch (_referenceFrame)
|
||||
{
|
||||
case ReferenceFrame::Absolute:
|
||||
return _value * dpi;
|
||||
case ReferenceFrame::FontSize:
|
||||
return _value * fontSize;
|
||||
case ReferenceFrame::AdvanceWidth:
|
||||
return _value * advanceWidth;
|
||||
default:
|
||||
return fallback;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,22 @@ FontInfoDesired::FontInfoDesired(const FontInfo& fiFont) noexcept :
|
|||
{
|
||||
}
|
||||
|
||||
void FontInfoDesired::SetCellSize(const CSSLengthPercentage& cellWidth, const CSSLengthPercentage& cellHeight) noexcept
|
||||
{
|
||||
_cellWidth = cellWidth;
|
||||
_cellHeight = cellHeight;
|
||||
}
|
||||
|
||||
const CSSLengthPercentage& FontInfoDesired::GetCellWidth() const noexcept
|
||||
{
|
||||
return _cellWidth;
|
||||
}
|
||||
|
||||
const CSSLengthPercentage& FontInfoDesired::GetCellHeight() const noexcept
|
||||
{
|
||||
return _cellHeight;
|
||||
}
|
||||
|
||||
float FontInfoDesired::GetFontSize() const noexcept
|
||||
{
|
||||
return _fontSize;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<Import Project="$(SolutionDir)src\common.nugetversions.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\CSSLengthPercentage.cpp" />
|
||||
<ClCompile Include="..\FontInfo.cpp" />
|
||||
<ClCompile Include="..\FontInfoBase.cpp" />
|
||||
<ClCompile Include="..\FontInfoDesired.cpp" />
|
||||
|
@ -25,6 +26,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\inc\Cluster.hpp" />
|
||||
<ClInclude Include="..\..\inc\CSSLengthPercentage.h" />
|
||||
<ClInclude Include="..\..\inc\FontInfo.hpp" />
|
||||
<ClInclude Include="..\..\inc\FontInfoBase.hpp" />
|
||||
<ClInclude Include="..\..\inc\FontInfoDesired.hpp" />
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
<ClCompile Include="..\RenderSettings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\CSSLengthPercentage.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h">
|
||||
|
@ -89,8 +92,11 @@
|
|||
<ClInclude Include="..\FontCache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\inc\CSSLengthPercentage.h">
|
||||
<Filter>Header Files\inc</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -706,6 +706,7 @@ std::vector<DWRITE_FONT_AXIS_VALUE> DxFontRenderData::GetAxisVector(const DWRITE
|
|||
// - None
|
||||
void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, FontInfo& actual, const int dpi)
|
||||
{
|
||||
const auto dpiF = static_cast<float>(dpi);
|
||||
auto fontLocaleName = UserLocaleName();
|
||||
// This is the first attempt to resolve font face after `UpdateFont`.
|
||||
// Note that the following line may cause property changes _inside_ `_defaultFontInfo` because the desired font may not exist.
|
||||
|
@ -737,27 +738,20 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
|
|||
// - 12 ppi font * (96 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 16 pixels tall font for 100% display (96 dpi is 100%)
|
||||
// - 12 ppi font * (144 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 24 pixels tall font for 150% display (144 dpi is 150%)
|
||||
// - 12 ppi font * (192 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 32 pixels tall font for 200% display (192 dpi is 200%)
|
||||
auto heightDesired = desired.GetEngineSize().height * USER_DEFAULT_SCREEN_DPI / POINTS_PER_INCH;
|
||||
const auto heightDesired = desired.GetEngineSize().height / POINTS_PER_INCH * dpiF;
|
||||
|
||||
// The advance is the number of pixels left-to-right (X dimension) for the given font.
|
||||
// We're finding a proportional factor here with the design units in "ems", not an actual pixel measurement.
|
||||
|
||||
// Now we play trickery with the font size. Scale by the DPI to get the height we expect.
|
||||
heightDesired *= (static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI));
|
||||
|
||||
const auto widthAdvance = static_cast<float>(advanceInDesignUnits) / fontMetrics.designUnitsPerEm;
|
||||
|
||||
// Use the real pixel height desired by the "em" factor for the width to get the number of pixels
|
||||
// we will need per character in width. This will almost certainly result in fractional X-dimension pixels.
|
||||
const auto widthApprox = heightDesired * widthAdvance;
|
||||
|
||||
// Since we can't deal with columns of the presentation grid being fractional pixels in width, round to the nearest whole pixel.
|
||||
const auto widthExact = round(widthApprox);
|
||||
const auto widthAdvanceInPx = heightDesired * widthAdvance;
|
||||
|
||||
// Now reverse the "em" factor from above to turn the exact pixel width into a (probably) fractional
|
||||
// height in pixels of each character. It's easier for us to pad out height and align vertically
|
||||
// than it is horizontally.
|
||||
const auto fontSize = widthExact / widthAdvance;
|
||||
const auto fontSize = roundf(widthAdvanceInPx) / widthAdvance;
|
||||
_fontSize = fontSize;
|
||||
|
||||
// Now figure out the basic properties of the character height which include ascent and descent
|
||||
|
@ -809,8 +803,12 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
|
|||
|
||||
const auto fullPixelAscent = ceil(ascent + halfGap);
|
||||
const auto fullPixelDescent = ceil(descent + halfGap);
|
||||
lineSpacing.height = fullPixelAscent + fullPixelDescent;
|
||||
lineSpacing.baseline = fullPixelAscent;
|
||||
const auto defaultHeight = fullPixelAscent + fullPixelDescent;
|
||||
const auto lineHeight = desired.GetCellHeight().Resolve(defaultHeight, dpiF, heightDesired, widthAdvanceInPx);
|
||||
const auto baseline = fullPixelAscent + (lineHeight - defaultHeight) / 2.0f;
|
||||
|
||||
lineSpacing.height = roundf(lineHeight);
|
||||
lineSpacing.baseline = roundf(baseline);
|
||||
|
||||
// According to MSDN (https://docs.microsoft.com/en-us/windows/win32/api/dwrite_3/ne-dwrite_3-dwrite_font_line_gap_usage)
|
||||
// Setting "ENABLED" means we've included the line gapping in the spacing numbers given.
|
||||
|
@ -818,6 +816,9 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
|
|||
|
||||
_lineSpacing = lineSpacing;
|
||||
|
||||
const auto widthApprox = desired.GetCellWidth().Resolve(widthAdvanceInPx, dpiF, heightDesired, widthAdvanceInPx);
|
||||
const auto widthExact = roundf(widthApprox);
|
||||
|
||||
// The scaled size needs to represent the pixel box that each character will fit within for the purposes
|
||||
// of hit testing math and other such multiplication/division.
|
||||
til::size coordSize;
|
||||
|
@ -861,8 +862,8 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
|
|||
|
||||
// Offsets are relative to the base line of the font, so we subtract
|
||||
// from the ascent to get an offset relative to the top of the cell.
|
||||
lineMetrics.underlineOffset = fullPixelAscent - lineMetrics.underlineOffset;
|
||||
lineMetrics.strikethroughOffset = fullPixelAscent - lineMetrics.strikethroughOffset;
|
||||
lineMetrics.underlineOffset = lineSpacing.baseline - lineMetrics.underlineOffset;
|
||||
lineMetrics.strikethroughOffset = lineSpacing.baseline - lineMetrics.strikethroughOffset;
|
||||
|
||||
// For double underlines we need a second offset, just below the first,
|
||||
// but with a bit of a gap (about double the grid line width).
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
struct CSSLengthPercentage
|
||||
{
|
||||
enum class ReferenceFrame : uint8_t
|
||||
{
|
||||
// This indicates the object is empty/unset.
|
||||
// No need to call Resolve().
|
||||
None,
|
||||
// Call Resolve() with factor set to the target DPI (e.g. 96 for DIP).
|
||||
// Returns an absolute length value scaled by that DPI.
|
||||
Absolute,
|
||||
// Call Resolve() with factor set to the font size
|
||||
// in an arbitrary DPI. Returns a value relative to it.
|
||||
FontSize,
|
||||
// Call Resolve() with factor set to the "0" glyph advance width
|
||||
// in an arbitrary DPI. Returns a value relative to it.
|
||||
AdvanceWidth,
|
||||
};
|
||||
|
||||
static CSSLengthPercentage FromString(const wchar_t* str);
|
||||
|
||||
ReferenceFrame GetReferenceFrame() const noexcept;
|
||||
float Resolve(float factor) const noexcept;
|
||||
float Resolve(float fallback, float dpi, float fontSize, float advanceWidth) const noexcept;
|
||||
|
||||
private:
|
||||
float _value = 0;
|
||||
ReferenceFrame _referenceFrame = ReferenceFrame::None;
|
||||
};
|
|
@ -20,6 +20,7 @@ Author(s):
|
|||
|
||||
#include "FontInfoBase.hpp"
|
||||
#include "FontInfo.hpp"
|
||||
#include "CSSLengthPercentage.h"
|
||||
|
||||
class FontInfoDesired : public FontInfoBase
|
||||
{
|
||||
|
@ -33,6 +34,10 @@ public:
|
|||
|
||||
bool operator==(const FontInfoDesired& other) = delete;
|
||||
|
||||
void SetCellSize(const CSSLengthPercentage& cellWidth, const CSSLengthPercentage& cellHeight) noexcept;
|
||||
|
||||
const CSSLengthPercentage& GetCellWidth() const noexcept;
|
||||
const CSSLengthPercentage& GetCellHeight() const noexcept;
|
||||
float GetFontSize() const noexcept;
|
||||
til::size GetEngineSize() const noexcept;
|
||||
bool IsDefaultRasterFont() const noexcept;
|
||||
|
@ -40,4 +45,6 @@ public:
|
|||
private:
|
||||
til::size _coordSizeDesired;
|
||||
float _fontSize;
|
||||
CSSLengthPercentage _cellWidth;
|
||||
CSSLengthPercentage _cellHeight;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue