Skip to content

Commit f24d250

Browse files
committed
content: Include inline styles (italic etc.) in what we pass to WidgetSpans
Fixes #1813. Fixes #1819. This is NFC for inline math, since all the inline styles are overridden there. But see the previous commit for where we consolidated those overrides, making a natural place to add more if needed in the future, with TODOs for some existing ones we might not want to keep.
1 parent 6fde159 commit f24d250

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

lib/widgets/content.dart

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,10 +1081,21 @@ class _InlineContentBuilder {
10811081
_recognizer = _recognizerStack!.removeLast();
10821082
}
10831083

1084-
InlineSpan _buildNodes(List<InlineContentNode> nodes, {required TextStyle? style}) {
1084+
final List<TextStyle> _styleStack = [];
1085+
1086+
TextStyle _resolveStyleStack() {
1087+
assert(_styleStack.isNotEmpty); // first item is `widget.style`
1088+
return _styleStack.reduce((value, element) => value.merge(element));
1089+
}
1090+
1091+
InlineSpan _buildNodes(List<InlineContentNode> nodes, {required TextStyle style}) {
1092+
_styleStack.add(style);
1093+
final children = nodes.map(_buildNode).toList(growable: false);
1094+
_styleStack.removeLast();
1095+
10851096
return TextSpan(
10861097
style: style,
1087-
children: nodes.map(_buildNode).toList(growable: false));
1098+
children: children);
10881099
}
10891100

10901101
InlineSpan _buildNode(InlineContentNode node) {
@@ -1123,7 +1134,7 @@ class _InlineContentBuilder {
11231134

11241135
case UserMentionNode():
11251136
return WidgetSpan(alignment: PlaceholderAlignment.middle,
1126-
child: UserMention(ambientTextStyle: widget.style, node: node));
1137+
child: UserMention(ambientTextStyle: _resolveStyleStack(), node: node));
11271138

11281139
case UnicodeEmojiNode():
11291140
return TextSpan(text: node.emojiUnicode, recognizer: _recognizer,
@@ -1145,11 +1156,11 @@ class _InlineContentBuilder {
11451156
: WidgetSpan(
11461157
alignment: PlaceholderAlignment.baseline,
11471158
baseline: TextBaseline.alphabetic,
1148-
child: KatexWidget(ambientTextStyle: widget.style, nodes: nodes));
1159+
child: KatexWidget(ambientTextStyle: _resolveStyleStack(), nodes: nodes));
11491160

11501161
case GlobalTimeNode():
11511162
return WidgetSpan(alignment: PlaceholderAlignment.middle,
1152-
child: GlobalTime(node: node, ambientTextStyle: widget.style));
1163+
child: GlobalTime(node: node, ambientTextStyle: _resolveStyleStack()));
11531164

11541165
case UnimplementedInlineContentNode():
11551166
return _errorUnimplemented(node, context: _context!);
@@ -1341,7 +1352,7 @@ class GlobalTime extends StatelessWidget {
13411352
size: ambientTextStyle.fontSize!,
13421353
// (When GlobalTime appears in a link, it should be blue
13431354
// like the text.)
1344-
color: DefaultTextStyle.of(context).style.color!,
1355+
color: ambientTextStyle.color,
13451356
ZulipIcons.clock),
13461357
// Ad-hoc spacing adjustment per feedback:
13471358
// https://chat.zulip.org/#narrow/stream/101-design/topic/clock.20icons/near/1729345

test/widgets/content_test.dart

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,14 @@ void main() {
763763
});
764764
});
765765

766+
testWidgets('is italic in italic span', (tester) async {
767+
// Regression test for: https://github.com/zulip/zulip-flutter/issues/1813
768+
await prepareContent(tester,
769+
plainContent('<p><em><span class="user-mention" data-user-id="13313">@Chris Bobbe</span></em></p>'));
770+
final style = mergedStyleOf(tester, '@Chris Bobbe');
771+
check(style!.fontStyle).equals(FontStyle.italic);
772+
});
773+
766774
testFontWeight('silent or non-self mention in plain paragraph',
767775
expectedWght: 400,
768776
// @_**Greg Price**
@@ -1081,7 +1089,19 @@ void main() {
10811089
check(find.textContaining(renderedTextRegexpTwelveHour)).findsOne();
10821090
});
10831091

1084-
void testIconAndTextSameColor(String description, String html) {
1092+
testWidgets('is italic in italic span', (tester) async {
1093+
// Regression test for: https://github.com/zulip/zulip-flutter/issues/1813
1094+
await prepareContent(tester,
1095+
// We use the self-account's time-format setting.
1096+
wrapWithPerAccountStoreWidget: true,
1097+
initialSnapshot: eg.initialSnapshot(),
1098+
plainContent('<p><em>$timeSpanHtml</em></p>'));
1099+
final style = mergedStyleOf(tester,
1100+
findAncestor: find.byType(GlobalTime), renderedTextRegexp);
1101+
check(style!.fontStyle).equals(FontStyle.italic);
1102+
});
1103+
1104+
void testIconAndTextSameColor(String description, String html, {Color? expectedColor}) {
10851105
testWidgets('clock icon and text are the same color: $description', (tester) async {
10861106
await prepareContent(tester,
10871107
// We use the self-account's time-format setting.
@@ -1097,11 +1117,16 @@ void main() {
10971117
check(textColor).isNotNull();
10981118

10991119
check(icon).color.isNotNull().isSameColorAs(textColor!);
1120+
if (expectedColor != null) {
1121+
check(icon).color.equals(expectedColor);
1122+
}
11001123
});
11011124
}
11021125

11031126
testIconAndTextSameColor('common case', '<p>$timeSpanHtml</p>');
1104-
testIconAndTextSameColor('inside link', '<p><a href="https://example/">$timeSpanHtml</a></p>');
1127+
// Regression test for: https://github.com/zulip/zulip-flutter/issues/1819
1128+
testIconAndTextSameColor('inside link', '<p><a href="https://example/">$timeSpanHtml</a></p>',
1129+
expectedColor: const HSLColor.fromAHSL(1, 200, 1, 0.4).toColor());
11051130

11061131
group('maintains font-size ratio with surrounding text', () {
11071132
Future<void> doCheck(WidgetTester tester, double Function(GlobalTime widget) sizeFromWidget) async {

0 commit comments

Comments
 (0)