From 77afd3481fe6ec29a232bf710f272c940c7aa9ca Mon Sep 17 00:00:00 2001 From: BobSilent Date: Tue, 1 Feb 2022 00:51:04 +0100 Subject: [PATCH] Added new ColumnWidth class according PR discussion --- docs/input/widgets/table.md | 4 +- examples/Console/Showcase/Program.cs | 2 +- src/Spectre.Console/ColumnWidth.cs | 123 ++++++++++++++++++ .../Extensions/ColumnExtensions.cs | 71 ++++++---- src/Spectre.Console/IColumn.cs | 10 +- src/Spectre.Console/SizeMode.cs | 4 +- src/Spectre.Console/Widgets/GridColumn.cs | 36 +---- .../Widgets/Table/TableColumn.cs | 31 +---- .../Widgets/Table/TableMeasurer.cs | 10 +- 9 files changed, 185 insertions(+), 106 deletions(-) create mode 100644 src/Spectre.Console/ColumnWidth.cs diff --git a/docs/input/widgets/table.md b/docs/input/widgets/table.md index 9b56ad3bd..60fd6de62 100644 --- a/docs/input/widgets/table.md +++ b/docs/input/widgets/table.md @@ -82,7 +82,7 @@ table.HideHeaders(); ```csharp // Sets the table width to 50 cells -table.FixWidth(50); +table.Width(50); ``` ### Alignment @@ -135,5 +135,5 @@ table.Columns[0].NoWrap(); ```csharp // Set the column width -table.Columns[0].FixWidth(15); +table.Columns[0].Width(15); ``` \ No newline at end of file diff --git a/examples/Console/Showcase/Program.cs b/examples/Console/Showcase/Program.cs index 81634e160..e2c971361 100644 --- a/examples/Console/Showcase/Program.cs +++ b/examples/Console/Showcase/Program.cs @@ -8,7 +8,7 @@ public static void Main() { var table = new Table().HideHeaders().NoBorder(); table.Title("[u][yellow]Spectre.Console[/] [b]Features[/][/]"); - table.AddColumn("Feature", c => c.NoWrap().RightAligned().FixWidth(10).PadRight(3)); + table.AddColumn("Feature", c => c.NoWrap().RightAligned().Width(10).PadRight(3)); table.AddColumn("Demonstration", c => c.PadRight(0)); table.AddEmptyRow(); diff --git a/src/Spectre.Console/ColumnWidth.cs b/src/Spectre.Console/ColumnWidth.cs new file mode 100644 index 000000000..947d325fb --- /dev/null +++ b/src/Spectre.Console/ColumnWidth.cs @@ -0,0 +1,123 @@ +namespace Spectre.Console; + +/// +/// Column width definition. +/// The size mode defines how the column width will be interpreted: +/// +/// +/// SizeToContent (Auto) +/// value is ignored and width will auto-size to content. +/// +/// +/// Fixed +/// value is interpreted as integer, fixed size. +/// +/// +/// Star (*) +/// value is interpreted as double and means proportional sizing. If the width value is 1 is implied +/// +/// +/// If mixed and widths with (proportional) widths: +/// The columns are apportioned to the remainder after the and +/// widths have been calculated. +/// +public class ColumnWidth : IEquatable +{ + /// + /// Gets the size mode, to define + /// how the width is interpreted. + /// + public SizeMode SizeMode + { + get; + } + + /// + /// Gets the width value. + /// + public double Value + { + get; + } + + private ColumnWidth(SizeMode sizeMode, double value) + { + SizeMode = sizeMode; + Value = value; + } + + /// + /// Implictly converts an to a fixed column width. + /// + /// The column fixed width. + public static implicit operator ColumnWidth(int? size) => size == null ? SizeToContent() : Fixed(size.Value); + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// if the specified objects are equal; otherwise, . + public static bool operator ==(ColumnWidth? left, ColumnWidth? right) => EqualityComparer.Default.Equals(left, right); + + /// + /// Determines whether the specified objects are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// if the specified objects are not equal; otherwise, . + public static bool operator !=(ColumnWidth? left, ColumnWidth? right) => !(left == right); + + /// + public override bool Equals(object? obj) => Equals(obj as ColumnWidth); + + /// + public bool Equals(ColumnWidth? other) + => other != null && + SizeMode == other.SizeMode && + Value == other.Value; + + /// + public override int GetHashCode() +#if NETSTANDARD2_0 + => new { SizeMode, Value }.GetHashCode(); +#else + => HashCode.Combine(SizeMode, Value); +#endif + + /// + /// Creates a with a fix size. + /// + /// The column fixed width. + /// A fix size width. + public static ColumnWidth Fixed(int size) + { + if (size < 0.0) + { + throw new ArgumentException("Fixed size cannot be negative", nameof(size)); + } + + return new ColumnWidth(SizeMode.Fixed, size); + } + + /// + /// Creates a proportional weighted . + /// + /// The column weighted width. + /// A proportional weighted width. + public static ColumnWidth Proportional(double weight = 1.0) + { + if (weight < 0.0) + { + throw new ArgumentException("Weight cannot be negative", nameof(weight)); + } + + return new ColumnWidth(SizeMode.Proportional, weight); + } + + /// + /// Creates a to auto size to content. + /// + /// An auto size width. + public static ColumnWidth SizeToContent() => new ColumnWidth(SizeMode.SizeToContent, 0.0); +} \ No newline at end of file diff --git a/src/Spectre.Console/Extensions/ColumnExtensions.cs b/src/Spectre.Console/Extensions/ColumnExtensions.cs index cf106c1f0..28667a817 100644 --- a/src/Spectre.Console/Extensions/ColumnExtensions.cs +++ b/src/Spectre.Console/Extensions/ColumnExtensions.cs @@ -24,13 +24,15 @@ public static T NoWrap(this T obj) } /// - /// Sets the width of the column to a fix size. + /// Sets the width of the column. /// /// An object implementing . /// The column. - /// The column fixed width. + /// The column width. /// The same instance so that multiple calls can be chained. - public static T FixWidth(this T obj, int size) + /// if is null. + /// if is negative. + public static T Width(this T obj, int? size) where T : class, IColumn { if (obj is null) @@ -43,19 +45,20 @@ public static T FixWidth(this T obj, int size) throw new ArgumentException("Fixed width cannot be negative", nameof(size)); } + // Implicitly convert int? to ColumnWidth obj.Width = size; - obj.SizeMode = SizeMode.Fixed; return obj; } /// - /// Sets the width of the column to a proportional weight. + /// Sets the width of the column. /// /// An object implementing . /// The column. - /// The column weighted width. + /// The column width. /// The same instance so that multiple calls can be chained. - public static T StarWidth(this T obj, double weight = 1.0) + /// if is null. + public static T Width(this T obj, ColumnWidth width) where T : class, IColumn { if (obj is null) @@ -63,32 +66,46 @@ public static T StarWidth(this T obj, double weight = 1.0) throw new ArgumentNullException(nameof(obj)); } - if (weight < 0.0) - { - throw new ArgumentException("Weight cannot be negative", nameof(weight)); - } - - obj.Width = weight; - obj.SizeMode = SizeMode.Star; + obj.Width = width; return obj; } /// - /// Sets the column to auto size to content. + /// Checks column width size mode. /// - /// An object implementing . /// The column. - /// The same instance so that multiple calls can be chained. - public static T SizeToContent(this T obj) - where T : class, IColumn - { - if (obj is null) + /// if column width is proportional. + /// If is null. + public static bool IsProportionalWidth(this IColumn obj) + => obj switch { - throw new ArgumentNullException(nameof(obj)); - } + null => throw new ArgumentNullException(nameof(obj)), + _ => obj.Width.SizeMode == SizeMode.Proportional, + }; - obj.Width = null; - obj.SizeMode = SizeMode.SizeToContent; - return obj; - } + /// + /// Checks column width size mode. + /// + /// The column. + /// if column width is fix. + /// If is null. + public static bool IsFixedWidth(this IColumn obj) + => obj switch + { + null => throw new ArgumentNullException(nameof(obj)), + _ => obj.Width.SizeMode == SizeMode.Fixed, + }; + + /// + /// Checks column width size mode. + /// + /// The column. + /// if column width is auto-size. + /// If is null. + public static bool IsAutoWidth(this IColumn obj) + => obj switch + { + null => throw new ArgumentNullException(nameof(obj)), + _ => obj.Width.SizeMode == SizeMode.SizeToContent, + }; } \ No newline at end of file diff --git a/src/Spectre.Console/IColumn.cs b/src/Spectre.Console/IColumn.cs index a121a6663..6dc0a84b4 100644 --- a/src/Spectre.Console/IColumn.cs +++ b/src/Spectre.Console/IColumn.cs @@ -14,11 +14,5 @@ public interface IColumn : IAlignable, IPaddable /// /// Gets or sets the width of the column. /// - double? Width { get; set; } - - /// - /// Gets or sets the size mode, to define - /// how the value is interpreted. - /// - SizeMode SizeMode { get; set; } -} \ No newline at end of file + ColumnWidth Width { get; set; } +} diff --git a/src/Spectre.Console/SizeMode.cs b/src/Spectre.Console/SizeMode.cs index 8b27a90b3..70899e93b 100644 --- a/src/Spectre.Console/SizeMode.cs +++ b/src/Spectre.Console/SizeMode.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console; +namespace Spectre.Console; /// /// Defines how the width value will be interpreted. @@ -9,7 +9,7 @@ public enum SizeMode Fixed = 1, /// Proportional sizing. - Star = 2, + Proportional = 2, /// Auto-size width to content. SizeToContent = 0, diff --git a/src/Spectre.Console/Widgets/GridColumn.cs b/src/Spectre.Console/Widgets/GridColumn.cs index c84b3979e..96ec94a9d 100644 --- a/src/Spectre.Console/Widgets/GridColumn.cs +++ b/src/Spectre.Console/Widgets/GridColumn.cs @@ -6,8 +6,7 @@ namespace Spectre.Console; public sealed class GridColumn : IColumn, IHasDirtyState { private bool _isDirty; - private double? _width; - private SizeMode _sizeMode; + private ColumnWidth _width = ColumnWidth.SizeToContent(); private bool _noWrap; private Padding? _padding; private Justify? _alignment; @@ -17,43 +16,14 @@ public sealed class GridColumn : IColumn, IHasDirtyState /// /// Gets or sets the width of the column. - /// The interpretation of width depends on the value of . - ///
- /// By default it is set to , and the column will size to its contents - /// is set to . + /// By default it is set to . ///
- public double? Width + public ColumnWidth Width { get => _width; set => MarkAsDirty(() => _width = value); } - /// - /// Gets or sets the size mode which defines how the column width will be interpreted: - /// - /// - /// SizeToContent (Auto) - /// value is ignored and width will auto-size to content. - /// - /// - /// Fixed - /// value is interpreted as integer, fixed size. - /// - /// - /// Star (*) - /// value is interpreted as double and means proportional sizing. If the width value is 1 is implied - /// - /// - /// If mixed and widths with (proportional) widths: - /// The columns are apportioned to the remainder after the and - /// widths have been calculated. - /// - public SizeMode SizeMode - { - get => _sizeMode; - set => MarkAsDirty(() => _sizeMode = value); - } - /// /// Gets or sets a value indicating whether wrapping of /// text within the column should be prevented. diff --git a/src/Spectre.Console/Widgets/Table/TableColumn.cs b/src/Spectre.Console/Widgets/Table/TableColumn.cs index bba5a8415..3bb649e0b 100644 --- a/src/Spectre.Console/Widgets/Table/TableColumn.cs +++ b/src/Spectre.Console/Widgets/Table/TableColumn.cs @@ -17,34 +17,9 @@ public sealed class TableColumn : IColumn /// /// Gets or sets the width of the column. - /// The interpretation of width depends on the value of . - ///
- /// By default it is set to , and the column will size to its contents - /// is set to . + /// By default it is set to . ///
- public double? Width { get; set; } - - /// - /// Gets or sets the size mode which defines how the column width will be interpreted: - /// - /// - /// SizeToContent (Auto) - /// value is ignored and width will auto-size to content. - /// - /// - /// Fixed - /// value is interpreted as integer, fixed size. - /// - /// - /// Star (*) - /// value is interpreted as double and means proportional sizing. If the width value is 1 is implied - /// - /// - /// If mixed and widths with (proportional) widths: - /// The columns are apportioned to the remainder after the and - /// widths have been calculated. - /// - public SizeMode SizeMode { get; set; } + public ColumnWidth Width { get; set; } /// /// Gets or sets the padding of the column. @@ -79,7 +54,7 @@ public TableColumn(string header) public TableColumn(IRenderable header) { Header = header ?? throw new ArgumentNullException(nameof(header)); - Width = null; + Width = ColumnWidth.SizeToContent(); Padding = new Padding(1, 0, 1, 0); NoWrap = false; Alignment = null; diff --git a/src/Spectre.Console/Widgets/Table/TableMeasurer.cs b/src/Spectre.Console/Widgets/Table/TableMeasurer.cs index 5e36c7461..6aaffc32d 100644 --- a/src/Spectre.Console/Widgets/Table/TableMeasurer.cs +++ b/src/Spectre.Console/Widgets/Table/TableMeasurer.cs @@ -86,11 +86,11 @@ internal Measurement[] MeasureColumns(int maxWidth) { IEnumerable<(int Index, Measurement Measurement)> MeasureStarColumns(IEnumerable<(TableColumn Column, int Index)> starColumns, int totalWidthForStar) { - var sumStarWeight = starColumns.Sum(x => x.Column.Width ?? 1); + var sumStarWeight = starColumns.Sum(x => x.Column.Width.Value); var ratio = totalWidthForStar / sumStarWeight; foreach (var x in starColumns) { - var starWidth = (int)Math.Round((x.Column.Width ?? 1) * ratio); + var starWidth = (int)Math.Round(x.Column.Width.Value * ratio); yield return (x.Index, new Measurement(starWidth, starWidth)); } @@ -98,8 +98,8 @@ internal Measurement[] MeasureColumns(int maxWidth) // Index and separate columns var indexColumns = Columns.Select((column, index) => (column, index)).ToList(); - var fixedColumns = indexColumns.Where(x => x.column.SizeMode != SizeMode.Star); - var starColumns = indexColumns.Where(x => x.column.SizeMode == SizeMode.Star); + var fixedColumns = indexColumns.Where(x => !x.column.IsProportionalWidth()); + var starColumns = indexColumns.Where(x => x.column.IsProportionalWidth()); // First calculate fixed cells var fixedWidth_ranges = fixedColumns.Select(x => (x.index, measurement: MeasureColumn(x.column, maxWidth))).ToList(); @@ -118,7 +118,7 @@ internal Measurement[] MeasureColumns(int maxWidth) private Measurement MeasureColumn(TableColumn column, int maxWidth) { // Predetermined width? - if (column.Width != null && column.SizeMode == SizeMode.Fixed) + if (column.IsFixedWidth()) { return new Measurement((int)column.Width.Value, (int)column.Width.Value); }