Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 133 additions & 50 deletions lib/ui/text.dart

Large diffs are not rendered by default.

43 changes: 29 additions & 14 deletions lib/ui/text/paragraph_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,18 @@ const int tsTextDecorationStyleIndex = 4;
const int tsFontWeightIndex = 5;
const int tsFontStyleIndex = 6;
const int tsTextBaselineIndex = 7;
const int tsTextDecorationThicknessIndex = 8;
const int tsFontFamilyIndex = 9;
const int tsFontSizeIndex = 10;
const int tsLetterSpacingIndex = 11;
const int tsWordSpacingIndex = 12;
const int tsHeightIndex = 13;
const int tsLocaleIndex = 14;
const int tsBackgroundIndex = 15;
const int tsForegroundIndex = 16;
const int tsTextShadowsIndex = 17;
const int tsFontFeaturesIndex = 18;
const int tsLeadingDistributionIndex = 8;
const int tsTextDecorationThicknessIndex = 9;
const int tsFontFamilyIndex = 10;
const int tsFontSizeIndex = 11;
const int tsLetterSpacingIndex = 12;
const int tsWordSpacingIndex = 13;
const int tsHeightIndex = 14;
const int tsLocaleIndex = 15;
const int tsBackgroundIndex = 16;
const int tsForegroundIndex = 17;
const int tsTextShadowsIndex = 18;
const int tsFontFeaturesIndex = 19;

const int tsColorMask = 1 << tsColorIndex;
const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
Expand All @@ -59,6 +60,7 @@ const int tsTextDecorationThicknessMask = 1 << tsTextDecorationThicknessIndex;
const int tsFontWeightMask = 1 << tsFontWeightIndex;
const int tsFontStyleMask = 1 << tsFontStyleIndex;
const int tsTextBaselineMask = 1 << tsTextBaselineIndex;
const int tsLeadingDistributionMask = 1 << tsLeadingDistributionIndex;
const int tsFontFamilyMask = 1 << tsFontFamilyIndex;
const int tsFontSizeMask = 1 << tsFontSizeIndex;
const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
Expand Down Expand Up @@ -118,14 +120,16 @@ const int sFontStyleIndex = 1;
const int sFontFamilyIndex = 2;
const int sFontSizeIndex = 3;
const int sHeightIndex = 4;
const int sLeadingIndex = 5;
const int sForceStrutHeightIndex = 6;
const int sLeadingDistributionIndex = 5;
const int sLeadingIndex = 6;
const int sForceStrutHeightIndex = 7;

const int sFontWeightMask = 1 << sFontWeightIndex;
const int sFontStyleMask = 1 << sFontStyleIndex;
const int sFontFamilyMask = 1 << sFontFamilyIndex;
const int sFontSizeMask = 1 << sFontSizeIndex;
const int sHeightMask = 1 << sHeightIndex;
const int sLeadingDistributionMask = 1 << sLeadingDistributionIndex;
const int sLeadingMask = 1 << sLeadingIndex;
const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex;

Expand Down Expand Up @@ -210,6 +214,12 @@ void decodeStrut(Dart_Handle strut_data,
if (mask & sHeightMask) {
paragraph_style.strut_height = float_data[float_count++];
paragraph_style.strut_has_height_override = true;

// LeadingDistribution does not affect layout if height is not set.
if (mask & sLeadingDistributionMask) {
paragraph_style.strut_half_leading = uint8_data[byte_count];
paragraph_style.strut_has_leading_distribution_override = true;
}
}
if (mask & sLeadingMask) {
paragraph_style.strut_leading = float_data[float_count++];
Expand Down Expand Up @@ -373,7 +383,7 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
Dart_Handle foreground_data,
Dart_Handle shadows_data,
Dart_Handle font_features_data) {
FML_DCHECK(encoded.num_elements() == 8);
FML_DCHECK(encoded.num_elements() == 9);

int32_t mask = encoded[0];

Expand Down Expand Up @@ -410,6 +420,11 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
// property wasn't wired up either.
}

style.has_leading_distribution_override = mask & tsLeadingDistributionMask;
if (mask & tsLeadingDistributionMask) {
style.half_leading = encoded[tsLeadingDistributionIndex];
}

if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontSizeMask |
tsLetterSpacingMask | tsWordSpacingMask)) {
if (mask & tsFontWeightMask) {
Expand Down
6 changes: 6 additions & 0 deletions lib/web_ui/lib/src/engine/text/paragraph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,8 @@ class EngineStrutStyle implements ui.StrutStyle {
List<String>? fontFamilyFallback,
double? fontSize,
double? height,
//TODO(LongCatIsLooong): implement leadingDistribution.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this TODO meant to be here? It looks like this is implemented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by that I meant currently setting leadingDistribution doesn't do anything on web and it needs to be implemented.

ui.TextLeadingDistribution? leadingDistribution,
double? leading,
ui.FontWeight? fontWeight,
ui.FontStyle? fontStyle,
Expand All @@ -1219,6 +1221,7 @@ class EngineStrutStyle implements ui.StrutStyle {
_fontFamilyFallback = fontFamilyFallback,
_fontSize = fontSize,
_height = height,
_leadingDistribution = leadingDistribution,
_leading = leading,
_fontWeight = fontWeight,
_fontStyle = fontStyle,
Expand All @@ -1232,6 +1235,7 @@ class EngineStrutStyle implements ui.StrutStyle {
final ui.FontWeight? _fontWeight;
final ui.FontStyle? _fontStyle;
final bool? _forceStrutHeight;
final ui.TextLeadingDistribution? _leadingDistribution;

@override
bool operator ==(Object other) {
Expand All @@ -1246,6 +1250,7 @@ class EngineStrutStyle implements ui.StrutStyle {
&& other._fontSize == _fontSize
&& other._height == _height
&& other._leading == _leading
&& other._leadingDistribution == _leadingDistribution
&& other._fontWeight == _fontWeight
&& other._fontStyle == _fontStyle
&& other._forceStrutHeight == _forceStrutHeight
Expand All @@ -1259,6 +1264,7 @@ class EngineStrutStyle implements ui.StrutStyle {
_fontSize,
_height,
_leading,
_leadingDistribution,
_fontWeight,
_fontStyle,
_forceStrutHeight,
Expand Down
27 changes: 21 additions & 6 deletions lib/web_ui/lib/src/ui/text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -204,18 +204,29 @@ enum TextDecorationStyle {
wavy
}

enum TextLeadingDistribution {
proportional,
even,
}

class TextHeightBehavior {
const TextHeightBehavior({
this.applyHeightToFirstAscent = true,
this.applyHeightToLastDescent = true,
this.leadingDistribution = TextLeadingDistribution.proportional,
});
const TextHeightBehavior.fromEncoded(int encoded)
: applyHeightToFirstAscent = (encoded & 0x1) == 0,
applyHeightToLastDescent = (encoded & 0x2) == 0;
TextHeightBehavior.fromEncoded(int encoded)
: applyHeightToFirstAscent = (encoded & 0x1) == 0,
applyHeightToLastDescent = (encoded & 0x2) == 0,
leadingDistribution = TextLeadingDistribution.values[encoded >> 2];
final bool applyHeightToFirstAscent;
final bool applyHeightToLastDescent;
final TextLeadingDistribution leadingDistribution;

int encode() {
return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1);
return (applyHeightToFirstAscent ? 0 : 1 << 0)
| (applyHeightToLastDescent ? 0 : 1 << 1)
| (leadingDistribution.index << 2);
}

@override
Expand All @@ -224,7 +235,8 @@ class TextHeightBehavior {
return false;
return other is TextHeightBehavior
&& other.applyHeightToFirstAscent == applyHeightToFirstAscent
&& other.applyHeightToLastDescent == applyHeightToLastDescent;
&& other.applyHeightToLastDescent == applyHeightToLastDescent
&& other.leadingDistribution == leadingDistribution;
}

@override
Expand All @@ -239,7 +251,8 @@ class TextHeightBehavior {
String toString() {
return 'TextHeightBehavior('
'applyHeightToFirstAscent: $applyHeightToFirstAscent, '
'applyHeightToLastDescent: $applyHeightToLastDescent'
'applyHeightToLastDescent: $applyHeightToLastDescent, '
'leadingDistribution: $leadingDistribution'
')';
}
}
Expand All @@ -260,6 +273,7 @@ abstract class TextStyle {
double? letterSpacing,
double? wordSpacing,
double? height,
TextLeadingDistribution? leadingDistribution,
Locale? locale,
Paint? background,
Paint? foreground,
Expand Down Expand Up @@ -370,6 +384,7 @@ abstract class StrutStyle {
List<String>? fontFamilyFallback,
double? fontSize,
double? height,
TextLeadingDistribution? leadingDistribution,
double? leading,
FontWeight? fontWeight,
FontStyle? fontStyle,
Expand Down
57 changes: 49 additions & 8 deletions testing/dart/text_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,37 @@ void main() {
});
});

group('TextStyle', () {
final TextStyle ts0 = TextStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0);
final TextStyle ts1 = TextStyle(color: const Color(0xFF00FF00), fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0);
final TextStyle ts2 = TextStyle(fontFamily: 'test');
final TextStyle ts3 = TextStyle(fontFamily: 'foo', fontFamilyFallback: <String>['Roboto', 'test']);
final TextStyle ts4 = TextStyle(leadingDistribution: TextLeadingDistribution.even);

test('toString works', () {
expect(
ts0.toString(),
equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'),
);
expect(
ts1.toString(),
equals('TextStyle(color: Color(0xff00ff00), decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'),
);
expect(
ts2.toString(),
equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'),
);
expect(
ts3.toString(),
equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: foo, fontFamilyFallback: [Roboto, test], fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'),
);
expect(
ts4.toString(),
equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: TextLeadingDistribution.even, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'),
);
});
});

group('TextHeightBehavior', () {
const TextHeightBehavior behavior0 = TextHeightBehavior();
const TextHeightBehavior behavior1 = TextHeightBehavior(
Expand All @@ -54,6 +85,10 @@ void main() {
const TextHeightBehavior behavior3 = TextHeightBehavior(
applyHeightToLastDescent: false
);
const TextHeightBehavior behavior4 = TextHeightBehavior(
applyHeightToLastDescent: false,
leadingDistribution: TextLeadingDistribution.even,
);

test('default constructor works', () {
expect(behavior0.applyHeightToFirstAscent, equals(true));
Expand All @@ -67,27 +102,33 @@ void main() {

expect(behavior3.applyHeightToFirstAscent, equals(true));
expect(behavior3.applyHeightToLastDescent, equals(false));

expect(behavior4.applyHeightToLastDescent, equals(false));
expect(behavior4.leadingDistribution, equals(TextLeadingDistribution.even));
});

test('encode works', () {
expect(behavior0.encode(), equals(0));
expect(behavior1.encode(), equals(3));
expect(behavior2.encode(), equals(1));
expect(behavior3.encode(), equals(2));
expect(behavior4.encode(), equals(6));
});

test('decode works', () {
expect(const TextHeightBehavior.fromEncoded(0), equals(behavior0));
expect(const TextHeightBehavior.fromEncoded(3), equals(behavior1));
expect(const TextHeightBehavior.fromEncoded(1), equals(behavior2));
expect(const TextHeightBehavior.fromEncoded(2), equals(behavior3));
expect(TextHeightBehavior.fromEncoded(0), equals(behavior0));
expect(TextHeightBehavior.fromEncoded(3), equals(behavior1));
expect(TextHeightBehavior.fromEncoded(1), equals(behavior2));
expect(TextHeightBehavior.fromEncoded(2), equals(behavior3));
expect(TextHeightBehavior.fromEncoded(6), equals(behavior4));
});

test('toString works', () {
expect(behavior0.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true)'));
expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false)'));
expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true)'));
expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false)'));
expect(behavior0.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)'));
expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)'));
expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)'));
expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)'));
expect(behavior4.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.even)'));
});
});

Expand Down
21 changes: 16 additions & 5 deletions third_party/txt/src/txt/paragraph_style.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,18 @@ enum class TextDirection {
ltr,
};

// Allows disabling height adjustments to first line's ascent and the
// last line's descent. If disabled, the line will use the default font
// metric provided ascent/descent and ParagraphStyle.height will not take
// effect.
// Adjusts the leading over and under text.
//
// kDisableFirstAscent and kDisableLastDescent allow disabling height
// adjustments to first line's ascent and the last line's descent. If disabled,
// the line will use the default font metric provided ascent/descent and
// ParagraphStyle.height or TextStyle.height will not take effect.
//
// kEvenLeading determines how the leading is distributed over and under the
// text. When true, half of the leading is added to the top of the text and the
// other half is added to the bottom of the text. Otherwise, instead of
// distributing the space evenly, it's distributed proportionally to the font's
// ascent/descent ratio.
//
// The default behavior is kAll where height adjustments are enabled for all
// lines.
Expand All @@ -58,6 +66,7 @@ enum TextHeightBehavior {
kDisableFirstAscent = 0x1,
kDisableLastDescent = 0x2,
kDisableAll = 0x1 | 0x2,
kEvenLeading = 0x1 << 2,
};

class ParagraphStyle {
Expand All @@ -69,8 +78,8 @@ class ParagraphStyle {
std::string font_family = "";
double font_size = 14;
double height = 1;
size_t text_height_behavior = TextHeightBehavior::kAll;
bool has_height_override = false;
size_t text_height_behavior = TextHeightBehavior::kAll;

// Strut properties. strut_enabled must be set to true for the rest of the
// properties to take effect.
Expand All @@ -82,6 +91,8 @@ class ParagraphStyle {
double strut_font_size = 14;
double strut_height = 1;
bool strut_has_height_override = false;
bool strut_half_leading = false;
bool strut_has_leading_distribution_override = false;
double strut_leading = -1; // Negative to use font's default leading. [0,inf)
// to use custom leading as a ratio of font size.
bool force_strut_height = false;
Expand Down
Loading