templates tested
This commit is contained in:
@@ -109,6 +109,59 @@ public class LayoutEngine
|
||||
return string.Concat(parts);
|
||||
}
|
||||
|
||||
public List<string> FormatMultiColumnWithWrap(
|
||||
List<(string text, int width, string align, bool wrap)> columns, int lineWidth)
|
||||
{
|
||||
// Calculate widths
|
||||
int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
|
||||
int unspecifiedCount = columns.Count(c => c.width <= 0);
|
||||
int remaining = lineWidth - totalExplicit;
|
||||
int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;
|
||||
|
||||
var colWidths = columns.Select(c => c.width > 0 ? c.width : Math.Max(defaultWidth, 1)).ToList();
|
||||
|
||||
// Wrap or truncate each column's text into lines
|
||||
var allColLines = new List<List<string>>();
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
var (text, _, _, wrap) = columns[i];
|
||||
text ??= "";
|
||||
int w = colWidths[i];
|
||||
|
||||
if (wrap)
|
||||
allColLines.Add(WrapText(text, w));
|
||||
else
|
||||
allColLines.Add(new List<string> { text.Length > w ? text[..w] : text });
|
||||
}
|
||||
|
||||
// Find the tallest column
|
||||
int maxLines = allColLines.Max(l => l.Count);
|
||||
|
||||
// Build each output line
|
||||
var result = new List<string>();
|
||||
for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
for (int colIdx = 0; colIdx < columns.Count; colIdx++)
|
||||
{
|
||||
string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
|
||||
int w = colWidths[colIdx];
|
||||
string formatted = columns[colIdx].align.ToLowerInvariant() switch
|
||||
{
|
||||
"center" => CenterInWidth(cellText, w),
|
||||
"right" => cellText.PadLeft(w),
|
||||
_ => cellText.PadRight(w)
|
||||
};
|
||||
if (formatted.Length > w)
|
||||
formatted = formatted[..w];
|
||||
parts.Add(formatted);
|
||||
}
|
||||
result.Add(string.Concat(parts));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<string> WrapText(string text, int maxWidth, int indent = 0)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
@@ -177,20 +230,26 @@ public class LayoutEngine
|
||||
List<(string text, int width, string align)> columns,
|
||||
List<List<string>> headerRows,
|
||||
List<List<string>> dataRows,
|
||||
int lineWidth)
|
||||
int lineWidth,
|
||||
List<bool>? wrapFlags = null)
|
||||
{
|
||||
var result = new List<string>();
|
||||
|
||||
// Calculate column widths
|
||||
var colWidths = CalculateTableColumnWidths(columns, lineWidth);
|
||||
|
||||
bool hasWrap = wrapFlags != null && wrapFlags.Any(w => w);
|
||||
|
||||
// Top border
|
||||
result.Add(FormatTableBorder(colWidths));
|
||||
|
||||
// Header rows
|
||||
foreach (var row in headerRows)
|
||||
{
|
||||
result.Add(FormatTableRow(row, colWidths, columns));
|
||||
if (hasWrap)
|
||||
result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
|
||||
else
|
||||
result.Add(FormatTableRow(row, colWidths, columns));
|
||||
}
|
||||
|
||||
// Separator between header and data
|
||||
@@ -202,7 +261,10 @@ public class LayoutEngine
|
||||
// Data rows
|
||||
foreach (var row in dataRows)
|
||||
{
|
||||
result.Add(FormatTableRow(row, colWidths, columns));
|
||||
if (hasWrap)
|
||||
result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
|
||||
else
|
||||
result.Add(FormatTableRow(row, colWidths, columns));
|
||||
}
|
||||
|
||||
// Bottom border
|
||||
@@ -245,6 +307,40 @@ public class LayoutEngine
|
||||
return "|" + string.Join("|", parts) + "|";
|
||||
}
|
||||
|
||||
private List<string> FormatTableRows(List<string> cells, List<int> colWidths,
|
||||
List<(string text, int width, string align)> columns, List<bool> wrapFlags)
|
||||
{
|
||||
// Wrap each cell that has wrap enabled
|
||||
var allColLines = new List<List<string>>();
|
||||
for (int i = 0; i < colWidths.Count; i++)
|
||||
{
|
||||
string cell = i < cells.Count ? cells[i] : "";
|
||||
bool wrap = i < wrapFlags.Count && wrapFlags[i];
|
||||
|
||||
if (wrap)
|
||||
allColLines.Add(WrapText(cell, colWidths[i]));
|
||||
else
|
||||
allColLines.Add(new List<string> { cell.Length > colWidths[i] ? cell[..colWidths[i]] : cell });
|
||||
}
|
||||
|
||||
int maxLines = allColLines.Max(l => l.Count);
|
||||
|
||||
var result = new List<string>();
|
||||
for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
for (int colIdx = 0; colIdx < colWidths.Count; colIdx++)
|
||||
{
|
||||
string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
|
||||
string align = colIdx < columns.Count ? columns[colIdx].align : "left";
|
||||
parts.Add(FormatCellContent(cellText, colWidths[colIdx], align));
|
||||
}
|
||||
result.Add("|" + string.Join("|", parts) + "|");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string FormatCellContent(string text, int width, string align)
|
||||
{
|
||||
if (text.Length > width)
|
||||
|
||||
@@ -145,15 +145,36 @@ public class TemplateRenderer
|
||||
var evaluator = new ExpressionEvaluator(context);
|
||||
int effectiveWidth = row.Big ? _bigFontLineWidth : _lineWidth;
|
||||
|
||||
var columnData = row.Columns.Select(c => (
|
||||
text: evaluator.Evaluate(c.Text ?? ""),
|
||||
width: c.Width ?? 0,
|
||||
align: c.Align
|
||||
)).ToList();
|
||||
bool anyWrap = row.Columns.Any(c => c.Wrap);
|
||||
|
||||
string text = _layout.FormatMultiColumn(columnData, effectiveWidth);
|
||||
var cmd = CreateCommand(text, row.Bold, row.Big, row.Tall, row.Red, row.LineSpacing);
|
||||
commands.Add(cmd);
|
||||
if (anyWrap)
|
||||
{
|
||||
var columnData = row.Columns.Select(c => (
|
||||
text: evaluator.Evaluate(c.Text ?? ""),
|
||||
width: ResolveColumnWidth(c, effectiveWidth),
|
||||
align: c.Align,
|
||||
wrap: c.Wrap
|
||||
)).ToList();
|
||||
|
||||
var lines = _layout.FormatMultiColumnWithWrap(columnData, effectiveWidth);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var cmd = CreateCommand(line, row.Bold, row.Big, row.Tall, row.Red, row.LineSpacing);
|
||||
commands.Add(cmd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var columnData = row.Columns.Select(c => (
|
||||
text: evaluator.Evaluate(c.Text ?? ""),
|
||||
width: ResolveColumnWidth(c, effectiveWidth),
|
||||
align: c.Align
|
||||
)).ToList();
|
||||
|
||||
string text = _layout.FormatMultiColumn(columnData, effectiveWidth);
|
||||
var cmd = CreateCommand(text, row.Bold, row.Big, row.Tall, row.Red, row.LineSpacing);
|
||||
commands.Add(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderSeparator(SeparatorNode separator, List<PrintCommand> commands)
|
||||
@@ -199,9 +220,10 @@ public class TemplateRenderer
|
||||
{
|
||||
var evaluator = new ExpressionEvaluator(context);
|
||||
|
||||
int tableContentWidth = _lineWidth - table.Columns.Count - 1;
|
||||
var columnDefs = table.Columns.Select(c => (
|
||||
text: evaluator.Evaluate(c.Text ?? ""),
|
||||
width: c.Width ?? 0,
|
||||
width: ResolveColumnWidth(c, tableContentWidth),
|
||||
align: c.Align
|
||||
)).ToList();
|
||||
|
||||
@@ -267,13 +289,21 @@ public class TemplateRenderer
|
||||
}
|
||||
}
|
||||
|
||||
var lines = _layout.FormatTable(columnDefs, headerRows, dataRows, _lineWidth);
|
||||
var wrapFlags = table.Columns.Select(c => c.Wrap).ToList();
|
||||
var lines = _layout.FormatTable(columnDefs, headerRows, dataRows, _lineWidth, wrapFlags);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
commands.Add(new PrintCommand(line));
|
||||
}
|
||||
}
|
||||
|
||||
private static int ResolveColumnWidth(ColumnDef col, int availableWidth)
|
||||
{
|
||||
if (col.Width.HasValue) return col.Width.Value;
|
||||
if (col.WidthPercent.HasValue) return (int)(availableWidth * col.WidthPercent.Value / 100.0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static PrintCommand CreateCommand(string text, bool bold, bool big, bool tall, bool red, int? lineSpacing)
|
||||
{
|
||||
return new PrintCommand(text, isBig: big, isBold: bold)
|
||||
|
||||
Reference in New Issue
Block a user