paintBackground method
Implementation
void paintBackground(Canvas canvas, List<LineMetricsHelper> lineInfo) {
if (lineInfo.isEmpty) return;
if (lineInfo.length == 1) {
final info = lineInfo.first;
if (!info.isEmpty) {
canvas.drawRRect(
RRect.fromLTRBR(
info.x,
info.y,
info.fullWidth,
info.fullHeight,
Radius.circular(info.outerRadius(outerRadius)),
),
Paint()..color = backgroundColor,
);
}
return;
}
// This ensures the normalization will be done for all lines in the paragraph
// and not only for the next one
for (final info in lineInfo) {
normalize(lineInfo.elementAtOrNull(lineInfo.indexOf(info) + 1), info);
}
final path = Path();
final firstInfo = lineInfo.elementAt(0);
final lastInfo = lineInfo.elementAt(lineInfo.length - 1);
path.moveTo(firstInfo.x + firstInfo.outerRadius(outerRadius), firstInfo.y);
LineMetricsHelper previous = firstInfo;
for (final info in lineInfo) {
final next = lineInfo.elementAtOrNull(lineInfo.indexOf(info) + 1);
final outerRadius = info.outerRadius(this.outerRadius);
final innerRadius = info.innerRadius(this.innerRadius);
void drawTopLeftCorner(LineMetricsHelper info) {
final localOuterRadius = previous == info
? outerRadius
: (previous.x - info.x).clamp(0, outerRadius);
final controlPoint = Offset(info.x, info.y);
final endPoint = Offset(info.x, info.y + localOuterRadius);
path.lineTo(info.x + localOuterRadius, info.y);
path.quadraticBezierTo(
controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy);
}
void drawBottomLeftCorner(LineMetricsHelper info) {
path.lineTo(info.x, info.fullHeight - outerRadius);
final iControlPoint = Offset(info.x, info.fullHeight);
final iEndPoint = Offset(info.x + outerRadius, info.fullHeight);
path.quadraticBezierTo(
iControlPoint.dx, iControlPoint.dy, iEndPoint.dx, iEndPoint.dy);
}
void drawInnerCorner(LineMetricsHelper info, [bool toLeft = true]) {
if (toLeft) {
final formattedHeight =
info.fullHeight - info._innerLinePadding.bottom;
final localInnerRadius = (info.x - next!.x).clamp(0, innerRadius);
path.lineTo(info.x, info.fullHeight - localInnerRadius);
final iControlPoint = Offset(info.x, formattedHeight);
final iEndPoint = Offset(info.x - localInnerRadius, formattedHeight);
path.quadraticBezierTo(
iControlPoint.dx, iControlPoint.dy, iEndPoint.dx, iEndPoint.dy);
} else {
final formattedY = next!.y + info._innerLinePadding.bottom;
final localInnerRadius = (next.x - info.x).clamp(0, innerRadius);
path.lineTo(next.x - localInnerRadius, formattedY);
final iControlPoint = Offset(next.x, formattedY);
final iEndPoint = Offset(next.x, formattedY + localInnerRadius);
path.quadraticBezierTo(
iControlPoint.dx, iControlPoint.dy, iEndPoint.dx, iEndPoint.dy);
}
}
if (next != null) {
// If it's the first line OR the previous line is bigger than the current
// one, draw the top left corner
if (info == firstInfo || previous.x > info.x) {
drawTopLeftCorner(info);
}
if (info.x > next.x) {
// If the current one is less than the next, draw the inner corner
drawInnerCorner(info);
// drawBottomLeftCorner(info);
} else
// If the next one is more to the right, draw the bottom left
if (info.x < next.x) {
// Draw bottom right corner
drawBottomLeftCorner(info);
// Otherwise draw the inverse inner corner
drawInnerCorner(info, false);
}
} else {
// If it's in the last one, draw the top and bottom corners
drawTopLeftCorner(info);
drawBottomLeftCorner(info);
}
previous = info;
}
// Draw the last line only to the half of it
path.lineTo(lastInfo.fullWidth / 2, lastInfo.fullHeight);
final reversedInfo = lineInfo.reversed.toList(growable: false);
previous = reversedInfo.first;
// !Goes horizontal and up
for (final info in reversedInfo) {
final next = reversedInfo.elementAtOrNull(reversedInfo.indexOf(info) + 1);
final outerRadius = info.outerRadius(this.outerRadius);
final innerRadius = info.innerRadius(this.innerRadius);
void drawTopRightCorner(
LineMetricsHelper info, [
double? factor,
]) {
factor ??= outerRadius;
final controlPoint = Offset(info.fullWidth, info.y);
final endPoint = Offset(info.fullWidth - factor, info.y);
path.lineTo(info.fullWidth, info.y + factor);
path.quadraticBezierTo(
controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy);
}
void drawBottomRightCorner(LineMetricsHelper info) {
path.lineTo(info.fullWidth - outerRadius, info.fullHeight);
final iControlPoint = Offset(info.fullWidth, info.fullHeight);
final iEndPoint = Offset(info.fullWidth, info.fullHeight - outerRadius);
path.quadraticBezierTo(
iControlPoint.dx, iControlPoint.dy, iEndPoint.dx, iEndPoint.dy);
}
void drawInnerCorner(LineMetricsHelper info, [bool toRight = true]) {
// To left
if (!toRight) {
final formattedHeight =
info.fullHeight - info._innerLinePadding.bottom;
path.lineTo(info.fullWidth + innerRadius, formattedHeight);
final controlPoint = Offset(info.fullWidth, formattedHeight);
final endPoint =
Offset(info.fullWidth, formattedHeight - innerRadius);
path.quadraticBezierTo(
controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy);
} else {
final formattedY = info.y + info._innerLinePadding.bottom;
path.lineTo(info.fullWidth, formattedY + innerRadius);
final controlPoint = Offset(info.fullWidth, formattedY);
final endPoint = Offset(info.fullWidth + innerRadius, formattedY);
path.quadraticBezierTo(
controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy);
}
}
if (next != null) {
if (info == previous) {
// if it's the last line
drawBottomRightCorner(info);
} else if (info.fullWidth < previous.fullWidth) {
// if the current one is less than the previous one
drawTopRightCorner(previous);
drawInnerCorner(info, false);
// drawInnerCorner(info);
} else if (info.fullWidth > previous.fullWidth) {
// if the current one is bigger than the previous one
drawInnerCorner(previous, true);
drawBottomRightCorner(info);
} else {
// if the current one is equal to the previous one, ignore it
}
} else {
// if it's the first line
if (previous.fullWidth < info.fullWidth) {
// if the current one is bigger than the previous one
drawInnerCorner(previous);
drawBottomRightCorner(info);
} else if (previous.fullWidth > info.fullWidth) {
drawTopRightCorner(previous);
drawInnerCorner(info, false);
}
drawTopRightCorner(info);
}
previous = info;
}
// First line horizontal
path
..lineTo(firstInfo.fullWidth / 2, firstInfo.y)
..close();
canvas.drawPath(path, Paint()..color = backgroundColor);
}