Skip to content

Commit b5d950b

Browse files
katex: Render overline and underline and add widget tests
_findLineInfo() walks the vlist subtree (it's small) and collects spans with borderBottomWidthEm, recording each span’s line thickness, height, and vertical position. CustomPaint then draws these lines over the stacked spans. The +0.9 offset is a visual alignment tweak.
1 parent 445b1da commit b5d950b

File tree

2 files changed

+77
-5
lines changed

2 files changed

+77
-5
lines changed

lib/widgets/katex.dart

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:math' as math;
22

33
import 'package:flutter/foundation.dart';
4+
import 'package:flutter/material.dart';
45
import 'package:flutter/widgets.dart';
56
import 'package:flutter/rendering.dart';
67

@@ -231,12 +232,75 @@ class _KatexVlist extends StatelessWidget {
231232
@override
232233
Widget build(BuildContext context) {
233234
final em = DefaultTextStyle.of(context).style.fontSize!;
235+
final defaultColor = DefaultTextStyle.of(context).style.color ;
236+
237+
final lines = <_VlistLine>[];
238+
for (final row in node.rows) {
239+
final lineInfo = _findLineInfo(row.node);
240+
if (lineInfo != null) {
241+
final y = (row.verticalOffsetEm - (lineInfo.heightEm ?? 0) + 0.9) * em;
242+
final thickness = lineInfo.borderWidthEm * em;
243+
lines.add(_VlistLine(y, thickness, lineInfo.color ?? defaultColor));
244+
}}
245+
246+
return CustomPaint(
247+
foregroundPainter: _KatexBorderPainter(lines),
248+
child: Stack(
249+
children: List.unmodifiable(node.rows.map((row) {
250+
return Transform.translate(
251+
offset: Offset(0, row.verticalOffsetEm * em),
252+
child: _KatexSpan(row.node));
253+
}))));
254+
}
255+
({double? heightEm, double borderWidthEm, Color? color})? _findLineInfo(KatexSpanNode span) {
256+
final borderWidth = span.styles.borderBottomWidthEm;
257+
final color = span.styles.color != null
258+
? Color.fromARGB(span.styles.color!.a, span.styles.color!.r, span.styles.color!.g, span.styles.color!.b)
259+
: null;
260+
if (borderWidth != null) {return (heightEm: span.styles.heightEm, borderWidthEm: borderWidth, color: color);}
261+
262+
if (span.nodes != null) {
263+
for (final child in span.nodes!) {
264+
if (child is KatexSpanNode) {
265+
final info = _findLineInfo(child);
266+
if (info != null) return info;
267+
}}}
268+
return null;
269+
}
270+
}
271+
272+
class _VlistLine {
273+
const _VlistLine(this.y, this.thickness, this.color);
274+
final double y;
275+
final double thickness;
276+
final Color? color;
277+
}
278+
279+
class _KatexBorderPainter extends CustomPainter {
280+
const _KatexBorderPainter(this.lines);
234281

235-
return Stack(children: List.unmodifiable(node.rows.map((row) {
236-
return Transform.translate(
237-
offset: Offset(0, row.verticalOffsetEm * em),
238-
child: _KatexSpan(row.node));
239-
})));
282+
final List<_VlistLine> lines;
283+
284+
@override
285+
void paint(Canvas canvas, Size size) {
286+
if (lines.isEmpty) return;
287+
final paint = Paint()..style = PaintingStyle.fill;
288+
for (final line in lines) {
289+
paint.color = line.color ?? Colors.black;
290+
canvas.drawRect(Rect.fromLTWH(0, line.y, size.width, line.thickness), paint);
291+
}
292+
}
293+
294+
@override
295+
bool shouldRepaint(covariant _KatexBorderPainter oldDelegate) {
296+
if (oldDelegate.lines.length != lines.length) return true;
297+
for (var i = 0; i < lines.length; i++) {
298+
if (oldDelegate.lines[i].y != lines[i].y ||
299+
oldDelegate.lines[i].thickness != lines[i].thickness ||
300+
oldDelegate.lines[i].color != lines[i].color) {
301+
return true;
302+
}}
303+
return false;
240304
}
241305
}
242306

test/widgets/katex_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ void main() {
8181
('a', Offset(2.47, 3.36), Size(10.88, 25.00)),
8282
('b', Offset(15.81, 3.36), Size(8.82, 25.00)),
8383
]),
84+
(KatexExample.overline, skip: false, [
85+
('A', Offset(0.0, 5.61), Size(15.43, 25.0)),
86+
('B', Offset(15.43, 5.61), Size(15.61, 25.0)),
87+
]),
88+
(KatexExample.underline, skip: false, [
89+
('A', Offset(0.0, 5.61), Size(15.43, 25.0)),
90+
('B', Offset(15.43, 5.61), Size(15.61, 25.0)),
91+
]),
8492
];
8593

8694
for (final testCase in testCases) {

0 commit comments

Comments
 (0)