Skip to content

Commit

Permalink
Added new ColumnWidth class according PR discussion
Browse files Browse the repository at this point in the history
  • Loading branch information
BobSilent committed Jan 31, 2022
1 parent d5a32d5 commit 1739e1d
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 104 deletions.
2 changes: 1 addition & 1 deletion examples/Console/Showcase/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
123 changes: 123 additions & 0 deletions src/Spectre.Console/ColumnWidth.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
namespace Spectre.Console;

/// <summary>
/// Column width definition.
/// The size mode defines how the column width will be interpreted:
/// <list type="bullet">
/// <item>
/// <term><see cref="SizeMode.SizeToContent">SizeToContent (Auto)</see></term>
/// <description><see cref="Value" /> value is ignored and width will auto-size to content.</description>
/// </item>
/// <item>
/// <term><see cref="SizeMode.Fixed">Fixed</see></term>
/// <description><see cref="Value" /> value is interpreted as integer, fixed size.</description>
/// </item>
/// <item>
/// <term><see cref="SizeMode.Proportional">Star (*)</see></term>
/// <description><see cref="Value" /> value is interpreted as double and means proportional sizing. If the width value is <see langword="null"/> 1 is implied</description>
/// </item>
/// </list>
/// If mixed <see cref="SizeMode.SizeToContent" /> and <see cref="SizeMode.Fixed" /> widths with <see cref="SizeMode.Proportional" /> (proportional) widths:
/// The <see cref="SizeMode.Proportional" /> columns are apportioned to the remainder after the <see cref="SizeMode.SizeToContent" /> and
/// <see cref="SizeMode.Fixed" /> widths have been calculated.
/// </summary>
public class ColumnWidth : IEquatable<ColumnWidth?>
{
/// <summary>
/// Gets the size mode, to define
/// how the width <see cref="Value"/> is interpreted.
/// </summary>
public SizeMode SizeMode
{
get;
}

/// <summary>
/// Gets the width value.
/// </summary>
public double Value
{
get;
}

private ColumnWidth(SizeMode sizeMode, double value)
{
SizeMode = sizeMode;
Value = value;
}

/// <summary>
/// Implictly converts an <see cref="int"/> to a fixed column width.
/// </summary>
/// <param name="size">The column fixed width.</param>
public static implicit operator ColumnWidth(int? size) => size == null ? SizeToContent() : Fixed(size.Value);

/// <summary>
/// Determines whether the specified objects are equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><see langword="true"/> if the specified objects are equal; otherwise, <see langword="false"/>.</returns>
public static bool operator ==(ColumnWidth? left, ColumnWidth? right) => EqualityComparer<ColumnWidth>.Default.Equals(left, right);

/// <summary>
/// Determines whether the specified objects are not equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><see langword="true"/> if the specified objects are not equal; otherwise, <see langword="false"/>.</returns>
public static bool operator !=(ColumnWidth? left, ColumnWidth? right) => !(left == right);

/// <inheritdoc/>
public override bool Equals(object? obj) => Equals(obj as ColumnWidth);

/// <inheritdoc/>
public bool Equals(ColumnWidth? other)
=> other != null &&
SizeMode == other.SizeMode &&
Value == other.Value;

/// <inheritdoc/>
public override int GetHashCode()
#if NETSTANDARD2_0
=> new { SizeMode, Value }.GetHashCode();
#else
=> HashCode.Combine(SizeMode, Value);
#endif

/// <summary>
/// Creates a <see cref="ColumnWidth"/> with a fix size.
/// </summary>
/// <param name="size">The column fixed width.</param>
/// <returns>A fix size width.</returns>
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);
}

/// <summary>
/// Creates a proportional weighted <see cref="ColumnWidth"/>.
/// </summary>
/// <param name="weight">The column weighted width.</param>
/// <returns>A proportional weighted width.</returns>
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);
}

/// <summary>
/// Creates a <see cref="ColumnWidth"/> to auto size to content.
/// </summary>
/// <returns>An auto size width.</returns>
public static ColumnWidth SizeToContent() => new ColumnWidth(SizeMode.SizeToContent, 0.0);
}
71 changes: 44 additions & 27 deletions src/Spectre.Console/Extensions/ColumnExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ public static T NoWrap<T>(this T obj)
}

/// <summary>
/// Sets the width of the column to a fix size.
/// Sets the width of the column.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IColumn"/>.</typeparam>
/// <param name="obj">The column.</param>
/// <param name="size">The column fixed width.</param>
/// <param name="size">The column width.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T FixWidth<T>(this T obj, int size)
/// <exception cref="ArgumentNullException">if <paramref name="obj"/> is null.</exception>
/// <exception cref="ArgumentException">if <paramref name="size"/> is negative.</exception>
public static T Width<T>(this T obj, int? size)
where T : class, IColumn
{
if (obj is null)
Expand All @@ -43,52 +45,67 @@ public static T FixWidth<T>(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;
}

/// <summary>
/// Sets the width of the column to a proportional weight.
/// Sets the width of the column.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IColumn"/>.</typeparam>
/// <param name="obj">The column.</param>
/// <param name="weight">The column weighted width.</param>
/// <param name="width">The column width.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T StarWidth<T>(this T obj, double weight = 1.0)
/// <exception cref="ArgumentNullException">if <paramref name="obj"/> is null.</exception>
public static T Width<T>(this T obj, ColumnWidth width)
where T : class, IColumn
{
if (obj is null)
{
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;
}

/// <summary>
/// Sets the column to auto size to content.
/// Checks column width size mode.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IColumn"/>.</typeparam>
/// <param name="obj">The column.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SizeToContent<T>(this T obj)
where T : class, IColumn
{
if (obj is null)
/// <returns><see langword="true"/> if column width is proportional.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="obj"/> is null.</exception>
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;
}
/// <summary>
/// Checks column width size mode.
/// </summary>
/// <param name="obj">The column.</param>
/// <returns><see langword="true"/> if column width is fix.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="obj"/> is null.</exception>
public static bool IsFixedWidth(this IColumn obj)
=> obj switch
{
null => throw new ArgumentNullException(nameof(obj)),
_ => obj.Width.SizeMode == SizeMode.Fixed,
};

/// <summary>
/// Checks column width size mode.
/// </summary>
/// <param name="obj">The column.</param>
/// <returns><see langword="true"/> if column width is auto-size.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="obj"/> is null.</exception>
public static bool IsAutoWidth(this IColumn obj)
=> obj switch
{
null => throw new ArgumentNullException(nameof(obj)),
_ => obj.Width.SizeMode == SizeMode.SizeToContent,
};
}
10 changes: 2 additions & 8 deletions src/Spectre.Console/IColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,5 @@ public interface IColumn : IAlignable, IPaddable
/// <summary>
/// Gets or sets the width of the column.
/// </summary>
double? Width { get; set; }

/// <summary>
/// Gets or sets the size mode, to define
/// how the <see cref="Width"/> value is interpreted.
/// </summary>
SizeMode SizeMode { get; set; }
}
ColumnWidth Width { get; set; }
}
4 changes: 2 additions & 2 deletions src/Spectre.Console/SizeMode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Spectre.Console;
namespace Spectre.Console;

/// <summary>
/// Defines how the width value will be interpreted.
Expand All @@ -9,7 +9,7 @@ public enum SizeMode
Fixed = 1,

/// <summary>Proportional sizing.</summary>
Star = 2,
Proportional = 2,

/// <summary>Auto-size width to content.</summary>
SizeToContent = 0,
Expand Down
36 changes: 3 additions & 33 deletions src/Spectre.Console/Widgets/GridColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,43 +16,14 @@ public sealed class GridColumn : IColumn, IHasDirtyState

/// <summary>
/// Gets or sets the width of the column.
/// The interpretation of width depends on the value of <see cref="SizeMode"/>.
/// <br/>
/// By default it is set to <see langword="null"/>, and the column will size to its contents <see cref="SizeMode.SizeToContent"/>
/// is set to <see cref="SizeMode.SizeToContent" />.
/// By default it is set to <see cref="ColumnWidth.SizeToContent"/>.
/// </summary>
public double? Width
public ColumnWidth Width
{
get => _width;
set => MarkAsDirty(() => _width = value);
}

/// <summary>
/// Gets or sets the size mode which defines how the column width will be interpreted:
/// <list type="bullet">
/// <item>
/// <term><see cref="SizeMode.SizeToContent">SizeToContent (Auto)</see></term>
/// <description><see cref="Width" /> value is ignored and width will auto-size to content.</description>
/// </item>
/// <item>
/// <term><see cref="SizeMode.Fixed">Fixed</see></term>
/// <description><see cref="Width" /> value is interpreted as integer, fixed size.</description>
/// </item>
/// <item>
/// <term><see cref="SizeMode.Star">Star (*)</see></term>
/// <description><see cref="Width" /> value is interpreted as double and means proportional sizing. If the width value is <see langword="null"/> 1 is implied</description>
/// </item>
/// </list>
/// If mixed <see cref="SizeMode.SizeToContent" /> and <see cref="SizeMode.Fixed" /> widths with <see cref="SizeMode.Star" /> (proportional) widths:
/// The <see cref="SizeMode.Star" /> columns are apportioned to the remainder after the <see cref="SizeMode.SizeToContent" /> and
/// <see cref="SizeMode.Fixed" /> widths have been calculated.
/// </summary>
public SizeMode SizeMode
{
get => _sizeMode;
set => MarkAsDirty(() => _sizeMode = value);
}

/// <summary>
/// Gets or sets a value indicating whether wrapping of
/// text within the column should be prevented.
Expand Down
31 changes: 3 additions & 28 deletions src/Spectre.Console/Widgets/Table/TableColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,9 @@ public sealed class TableColumn : IColumn

/// <summary>
/// Gets or sets the width of the column.
/// The interpretation of width depends on the value of <see cref="SizeMode"/>.
/// <br/>
/// By default it is set to <see langword="null"/>, and the column will size to its contents <see cref="SizeMode.SizeToContent"/>
/// is set to <see cref="SizeMode.SizeToContent" />.
/// By default it is set to <see cref="ColumnWidth.SizeToContent" />.
/// </summary>
public double? Width { get; set; }

/// <summary>
/// Gets or sets the size mode which defines how the column width will be interpreted:
/// <list type="bullet">
/// <item>
/// <term><see cref="SizeMode.SizeToContent">SizeToContent (Auto)</see></term>
/// <description><see cref="Width" /> value is ignored and width will auto-size to content.</description>
/// </item>
/// <item>
/// <term><see cref="SizeMode.Fixed">Fixed</see></term>
/// <description><see cref="Width" /> value is interpreted as integer, fixed size.</description>
/// </item>
/// <item>
/// <term><see cref="SizeMode.Star">Star (*)</see></term>
/// <description><see cref="Width" /> value is interpreted as double and means proportional sizing. If the width value is <see langword="null"/> 1 is implied</description>
/// </item>
/// </list>
/// If mixed <see cref="SizeMode.SizeToContent" /> and <see cref="SizeMode.Fixed" /> widths with <see cref="SizeMode.Star" /> (proportional) widths:
/// The <see cref="SizeMode.Star" /> columns are apportioned to the remainder after the <see cref="SizeMode.SizeToContent" /> and
/// <see cref="SizeMode.Fixed" /> widths have been calculated.
/// </summary>
public SizeMode SizeMode { get; set; }
public ColumnWidth Width { get; set; }

/// <summary>
/// Gets or sets the padding of the column.
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 1739e1d

Please sign in to comment.