douglasg14b / BetterConsoleTables

Faster, colorable, more configurable, and more robust console colors & tables for C# console applications
GNU Lesser General Public License v3.0
92 stars 16 forks source link

header width not quite right #24

Closed zmira closed 2 years ago

zmira commented 2 years ago

Hi.

I'm currently using version 2.0.3-rc1 of the library and am getting a weird column width issue

image

The code that generates this is

    var decodingtable = new BetterConsoles.Tables.Table(decodingHeaders.ToArray());
    decodingtable.Config = new BetterConsoles.Tables.Configuration.TableConfig(BetterConsoles.Tables.Style.Unicode);
    foreach (var file in decodingValues.Keys)
    {
        var indexMin = decodingValues[file].IndexOf(decodingValues[file].Min());
        var benchmarkValues = decodingValues[file].ConvertAll(v => v.ToString(@"ss\.ffffff"));
        benchmarkValues[indexMin] = benchmarkValues[indexMin].ForegroundColor(System.Drawing.Color.DarkGreen);
        decodingtable.AddRow(
            Array.Empty<string>()
            .Append(file)
            .Concat(benchmarkValues).ToArray());
    }

    Console.Write(decodingtable.ToString());

I am setting the foreground color as DarkGreen for the cell that contains the minimum value in a list/row.

Any ideas as to why this is happening?

Thanks.

douglasg14b commented 2 years ago

This is expected as the color formatting is occurring outside of the table. This adds ANSI codes to the string that the console renders as formatting. This also means that the string essentially contains a bunch of invisible characters. The header will be sized to the expected column width, but the invisible characters are not actually rendered by the console/terminal. Resulting in the effects seen here.

If you wish for a column or a value be formatted you must use the table API for that, which will calculate the correct column widths and apply formatting after.

To apply column formatting you need to configure the columns in your table:

IColumn[] headers = new[]
{
    new Column(""),
    new Column("QoiCi"),
    new ColumnBuilder("QoiCore")
        .RowsFormat()
            .ForegroundColor(System.Drawing.Color.DarkGreen)
        .GetColumn(),
    new Column("QoiFileTypeNet"),
    new Column("QoiNetStandard"),
    new Column("QoiShark"),
};

var decodingtable = new BetterConsoles.Tables.Table(headers);

Unfortunately I cannot run your example as I don't have an example decodingHeaders or decodingValues to use. But the above should do what you are aiming for if my memory serves me right.

That said, you can find full examples of this and similar APIs in the examples project & file here: (Including the tables in the image on the repo's readme)


Admittedly this API isn't as good as it could be, it's high up on my list of things to make better when I have the time to invest in it.

douglasg14b commented 2 years ago

You could also use:

IColumn[] headers = new[]
{
    new Column(""),
    new Column("QoiCi"),
    new ColumnBuilder("QoiCore")
        .RowsFormat()
            .HasInnerFormatting()
        .GetColumn(),
    new Column("QoiFileTypeNet"),
    new Column("QoiNetStandard"),
    new Column("QoiShark"),
};

And continue coloring it as you currently are. I parse out the ANSI codes when this flag is active, but it has a performance cost (small).

zmira commented 2 years ago

Thanks for the above. Although I had to tweak the code a bit, it now displays correctly.

image

The new code

    var columnsWithMin = new List<int>();
    var decodingTableRows = new List<string[]>();
    foreach (var file in decodingValues.Keys)
    {
        var indexMin = decodingValues[file].IndexOf(decodingValues[file].Min());

        if (!columnsWithMin.Contains(indexMin))
        {
            columnsWithMin.Add(indexMin);
        }

        var benchmarkValues = decodingValues[file]
            .ConvertAll(v => v.ToString(@"ss\.ffffff"));
        benchmarkValues[indexMin] = benchmarkValues[indexMin]
            .ForegroundColor(System.Drawing.Color.DarkGreen);

        decodingTableRows.Add(Array.Empty<string>()
            .Append(file)
            .Concat(benchmarkValues).ToArray());
    }

    var decodingTableHeaders = new List<BetterConsoles.Tables.Models.IColumn>();
    decodingTableHeaders.Add(new BetterConsoles.Tables.Models.Column(string.Empty));

    for (var i = 0; i < decodingHeaders.Count; i++)
    {
        decodingTableHeaders.Add(columnsWithMin.Contains(i)
            ? new BetterConsoles.Tables.Builders.ColumnBuilder(decodingHeaders[i])
                .RowsFormat().HasInnerFormatting().GetColumn()
            : new BetterConsoles.Tables.Models.Column(decodingHeaders[i]));
    }

    var decodingtable = new BetterConsoles.Tables.Table(decodingTableHeaders.ToArray());
    decodingtable.Config = new BetterConsoles.Tables.Configuration.TableConfig(BetterConsoles.Tables.Style.Unicode);
    decodingtable.AddRows(decodingTableRows);

    Console.Write(decodingtable.ToString());