diff --git a/lib/widgets/content.dart b/lib/widgets/content.dart index 1c5d23de87..b723806ceb 100644 --- a/lib/widgets/content.dart +++ b/lib/widgets/content.dart @@ -19,7 +19,7 @@ import 'store.dart'; import 'text.dart'; /// The font size for message content in a plain unstyled paragraph. -const double kBaseFontSize = 14; +const double kBaseFontSize = 17; /// The entire content of a message, aka its body. /// @@ -112,7 +112,7 @@ class Paragraph extends StatelessWidget { static const textStyle = TextStyle( fontSize: kBaseFontSize, - height: (17 / kBaseFontSize), + height: (22 / kBaseFontSize), ); @override @@ -159,8 +159,9 @@ class Heading extends StatelessWidget { child: _buildBlockInlineContainer( style: TextStyle( fontSize: kBaseFontSize * emHeight, - fontWeight: FontWeight.w600, - height: 1.4), + height: 1.4, + ) + .merge(weightVariableTextStyle(context, wght: 600)), node: node)); } } @@ -477,7 +478,7 @@ class MathBlock extends StatelessWidget { // Widget _buildBlockInlineContainer({ - required TextStyle? style, + required TextStyle style, required BlockInlineContainerNode node, }) { if (node.links == null) { @@ -493,7 +494,7 @@ class _BlockInlineContainer extends StatefulWidget { {required this.links, required this.style, required this.nodes}); final List links; - final TextStyle? style; + final TextStyle style; final List nodes; @override @@ -556,7 +557,7 @@ class InlineContent extends StatelessWidget { final GestureRecognizer? recognizer; final Map? linkRecognizers; - final TextStyle? style; + final TextStyle style; final List nodes; late final _InlineContentBuilder _builder; @@ -605,13 +606,15 @@ class _InlineContentBuilder { _recognizer = _recognizerStack!.removeLast(); } - InlineSpan _buildNodes(List nodes, {required TextStyle? style}) { + InlineSpan _buildNodes(List nodes, {required TextStyle style}) { return TextSpan( style: style, - children: nodes.map(_buildNode).toList(growable: false)); + children: nodes + .map((node) => _buildNode(node, surroundingSpanStyle: style)) + .toList(growable: false)); } - InlineSpan _buildNode(InlineContentNode node) { + InlineSpan _buildNode(InlineContentNode node, {required TextStyle surroundingSpanStyle}) { if (node is TextNode) { return TextSpan(text: node.text, recognizer: _recognizer); } else if (node is LineBreakInlineNode) { @@ -625,21 +628,24 @@ class _InlineContentBuilder { } else if (node is LinkNode) { return _buildLink(node); } else if (node is InlineCodeNode) { - return _buildInlineCode(node); + return _buildInlineCode(node, surroundingSpanStyle: surroundingSpanStyle); } else if (node is UserMentionNode) { return WidgetSpan(alignment: PlaceholderAlignment.middle, - child: UserMention(node: node)); + child: UserMention(node: node, surroundingSpanStyle: surroundingSpanStyle)); } else if (node is UnicodeEmojiNode) { return TextSpan(text: node.emojiUnicode, recognizer: _recognizer); } else if (node is ImageEmojiNode) { return WidgetSpan(alignment: PlaceholderAlignment.middle, child: MessageImageEmoji(node: node)); } else if (node is MathInlineNode) { - return TextSpan(style: _kInlineMathStyle, + return TextSpan( + style: surroundingSpanStyle + .merge(_kInlineMathStyle) + .apply(fontSizeFactor: _kInlineCodeFontSizeFactor), children: [TextSpan(text: node.texSource)]); } else if (node is GlobalTimeNode) { return WidgetSpan(alignment: PlaceholderAlignment.middle, - child: GlobalTime(node: node)); + child: GlobalTime(node: node, surroundingSpanStyle: surroundingSpanStyle)); } else if (node is UnimplementedInlineContentNode) { return _errorUnimplemented(node); } else { @@ -664,7 +670,7 @@ class _InlineContentBuilder { return result; } - InlineSpan _buildInlineCode(InlineCodeNode node) { + InlineSpan _buildInlineCode(InlineCodeNode node, {required TextStyle surroundingSpanStyle}) { // TODO `code` elements: border, padding -- seems hard // // Hard because this is an inline span, which we want to be able to break @@ -689,7 +695,11 @@ class _InlineContentBuilder { // TODO `code`: find equivalent of web's `unicode-bidi: embed; direction: ltr` // Use a light gray background, instead of a border. - return _buildNodes(style: _kInlineCodeStyle, node.nodes); + return _buildNodes( + style: surroundingSpanStyle + .merge(_kInlineCodeStyle) + .apply(fontSizeFactor: _kInlineCodeFontSizeFactor), + node.nodes); // Another fun solution -- we can in fact have a border! Like so: // TextStyle( @@ -709,17 +719,27 @@ class _InlineContentBuilder { } } +/// The [TextStyle] for inline math, excluding font-size adjustment. +/// +/// Inline math should use this and also apply [_kInlineCodeFontSizeFactor] +/// to the font size of the surrounding span +/// (which might be a Paragraph, a Heading, etc.) final _kInlineMathStyle = _kInlineCodeStyle.merge(TextStyle( backgroundColor: const HSLColor.fromAHSL(1, 240, 0.4, 0.93).toColor())); +const _kInlineCodeFontSizeFactor = 0.825; + +/// The [TextStyle] for inline code, excluding font-size adjustment. +/// +/// Inline code should use this and also apply [_kInlineCodeFontSizeFactor] +/// to the font size of the surrounding span +/// (which might be a Paragraph, a Heading, etc.) // Even though [kMonospaceTextStyle] is a variable-weight font, // it's acceptable to skip [weightVariableTextStyle] here, // assuming the text gets the effect of [weightVariableTextStyle] // through inheritance, e.g., from a [DefaultTextStyle]. final _kInlineCodeStyle = kMonospaceTextStyle - .merge(const TextStyle( - backgroundColor: Color(0xffeeeeee), - fontSize: 0.825 * kBaseFontSize)); + .merge(const TextStyle(backgroundColor: Color(0xffeeeeee))); final _kCodeBlockStyle = kMonospaceTextStyle .merge(const TextStyle( @@ -750,9 +770,10 @@ final _kCodeBlockStyle = kMonospaceTextStyle // const _kInlineCodeRightBracket = '⟩'; class UserMention extends StatelessWidget { - const UserMention({super.key, required this.node}); + const UserMention({super.key, required this.node, required this.surroundingSpanStyle}); final UserMentionNode node; + final TextStyle surroundingSpanStyle; @override Widget build(BuildContext context) { @@ -765,7 +786,7 @@ class UserMention extends StatelessWidget { // One hopes an @-mention can't contain an embedded link. // (The parser on creating a UserMentionNode has a TODO to check that.) linkRecognizers: null, - style: Paragraph.textStyle, + style: surroundingSpanStyle, nodes: node.nodes)); } @@ -832,9 +853,10 @@ class MessageImageEmoji extends StatelessWidget { } class GlobalTime extends StatelessWidget { - const GlobalTime({super.key, required this.node}); + const GlobalTime({super.key, required this.node, required this.surroundingSpanStyle}); final GlobalTimeNode node; + final TextStyle surroundingSpanStyle; static final _backgroundColor = const HSLColor.fromAHSL(1, 0, 0, 0.93).toColor(); static final _borderColor = const HSLColor.fromAHSL(1, 0, 0, 0.8).toColor(); @@ -861,7 +883,7 @@ class GlobalTime extends StatelessWidget { // Ad-hoc spacing adjustment per feedback: // https://chat.zulip.org/#narrow/stream/101-design/topic/clock.20icons/near/1729345 const SizedBox(width: 1), - Text(text, style: Paragraph.textStyle), + Text(text, style: surroundingSpanStyle), ])))); } } @@ -1116,8 +1138,9 @@ InlineSpan _errorUnimplemented(UnimplementedNode node) { } } -const errorStyle = TextStyle(fontWeight: FontWeight.bold, color: Colors.red); +const errorStyle = TextStyle( + fontSize: kBaseFontSize, fontWeight: FontWeight.bold, color: Colors.red); final errorCodeStyle = kMonospaceTextStyle - .merge(const TextStyle(color: Colors.red)) + .merge(const TextStyle(fontSize: kBaseFontSize, color: Colors.red)) .merge(weightVariableTextStyle(null)); // TODO(a11y) pass a BuildContext