Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #3303. Makes Thickness a record struct #3585

Merged
merged 4 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 71 additions & 95 deletions Terminal.Gui/Drawing/Thickness.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using System.Numerics;
using System.Text.Json.Serialization;

namespace Terminal.Gui;

Expand All @@ -13,28 +14,18 @@
/// frame,
/// with the thickness widths subtracted.
/// </para>
/// <para>Use the helper API (<see cref="Draw(Rectangle, string)"/> to draw the frame with the specified thickness.</para>
/// <para>
/// Use the helper API (<see cref="Draw(Rectangle, string)"/> to draw the frame with the specified thickness.
/// </para>
/// <para>
/// Thickness uses <see langword="float"/> intenrally. As a result, there is a potential precision loss for very
/// large numbers. This is typically not an issue for UI dimensions but could be relevant in other contexts.
/// </para>
/// </remarks>
public class Thickness : IEquatable<Thickness>
public record struct Thickness
{
/// <summary>Gets or sets the width of the lower side of the rectangle.</summary>
[JsonInclude]
public int Bottom;

/// <summary>Gets or sets the width of the left side of the rectangle.</summary>
[JsonInclude]
public int Left;

/// <summary>Gets or sets the width of the right side of the rectangle.</summary>
[JsonInclude]
public int Right;

/// <summary>Gets or sets the width of the upper side of the rectangle.</summary>
[JsonInclude]
public int Top;

/// <summary>Initializes a new instance of the <see cref="Thickness"/> class with all widths set to 0.</summary>
public Thickness () { }
public Thickness () { _sides = Vector4.Zero; }

/// <summary>Initializes a new instance of the <see cref="Thickness"/> class with a uniform width to each side.</summary>
/// <param name="width"></param>
Expand All @@ -56,36 +47,24 @@
Bottom = bottom;
}

// TODO: add operator overloads
/// <summary>Gets an empty thickness.</summary>
public static Thickness Empty => new (0);
private Vector4 _sides;

/// <summary>
/// Gets the total width of the left and right sides of the rectangle. Sets the width of the left and rigth sides
/// of the rectangle to half the specified value.
/// Adds the thickness widths of another <see cref="Thickness"/> to the current <see cref="Thickness"/>, returning a
/// new <see cref="Thickness"/>.
/// </summary>
public int Horizontal
{
get => Left + Right;
set => Left = Right = value / 2;
}
/// <param name="other"></param>
/// <returns></returns>
public readonly Thickness Add (Thickness other) { return new (Left + other.Left, Top + other.Top, Right + other.Right, Bottom + other.Bottom); }

Check warning on line 58 in Terminal.Gui/Drawing/Thickness.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Call to non-readonly member 'Thickness.Left.get' from a 'readonly' member results in an implicit copy of 'this'.

Check warning on line 58 in Terminal.Gui/Drawing/Thickness.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Call to non-readonly member 'Thickness.Top.get' from a 'readonly' member results in an implicit copy of 'this'.

Check warning on line 58 in Terminal.Gui/Drawing/Thickness.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Call to non-readonly member 'Thickness.Right.get' from a 'readonly' member results in an implicit copy of 'this'.

Check warning on line 58 in Terminal.Gui/Drawing/Thickness.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Call to non-readonly member 'Thickness.Bottom.get' from a 'readonly' member results in an implicit copy of 'this'.

/// <summary>
/// Gets the total height of the top and bottom sides of the rectangle. Sets the height of the top and bottom
/// sides of the rectangle to half the specified value.
/// </summary>
public int Vertical
/// <summary>Gets or sets the width of the lower side of the rectangle.</summary>
[JsonInclude]
public int Bottom
{
get => Top + Bottom;
set => Top = Bottom = value / 2;
get => (int)_sides.W;
set => _sides.W = value;
}

// IEquitable
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other"></param>
/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
public bool Equals (Thickness other) { return other is { } && Left == other.Left && Right == other.Right && Top == other.Top && Bottom == other.Bottom; }

/// <summary>
/// Gets whether the specified coordinates lie within the thickness (inside the bounding rectangle but outside
/// the rectangle described by <see cref="GetInside(Rectangle)"/>.
Expand All @@ -100,22 +79,6 @@
return outside.Contains (location) && !inside.Contains (location);
}

/// <summary>
/// Adds the thickness widths of another <see cref="Thickness"/> to the current <see cref="Thickness"/>, returning a
/// new <see cref="Thickness"/>.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public Thickness Add (Thickness other) { return new (Left + other.Left, Top + other.Top, Right + other.Right, Bottom + other.Bottom); }

/// <summary>
/// Adds the thickness widths of another <see cref="Thickness"/> to another <see cref="Thickness"/>.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Thickness operator + (Thickness a, Thickness b) { return a.Add (b); }

/// <summary>Draws the <see cref="Thickness"/> rectangle with an optional diagnostics label.</summary>
/// <remarks>
/// If <see cref="ViewDiagnosticFlags"/> is set to
Expand Down Expand Up @@ -240,31 +203,8 @@
return GetInside (rect);
}

/// <summary>Determines whether the specified object is equal to the current object.</summary>
/// <param name="obj">The object to compare with the current object.</param>
/// <returns><c>true</c> if the specified object is equal to the current object; otherwise, <c>false</c>.</returns>
public override bool Equals (object obj)
{
//Check for null and compare run-time types.
if (obj is null || !GetType ().Equals (obj.GetType ()))
{
return false;
}

return Equals ((Thickness)obj);
}

/// <inheritdoc/>
public override int GetHashCode ()
{
var hashCode = 1380952125;
hashCode = hashCode * -1521134295 + Left.GetHashCode ();
hashCode = hashCode * -1521134295 + Right.GetHashCode ();
hashCode = hashCode * -1521134295 + Top.GetHashCode ();
hashCode = hashCode * -1521134295 + Bottom.GetHashCode ();

return hashCode;
}
/// <summary>Gets an empty thickness.</summary>
public static Thickness Empty => new (0);

/// <summary>
/// Returns a rectangle describing the location and size of the inside area of <paramref name="rect"/> with the
Expand All @@ -289,23 +229,59 @@
return new (x, y, width, height);
}

/// <inheritdoc/>
public static bool operator == (Thickness left, Thickness right) { return EqualityComparer<Thickness>.Default.Equals (left, right); }
/// <summary>
/// Gets the total width of the left and right sides of the rectangle. Sets the width of the left and rigth sides
/// of the rectangle to half the specified value.
/// </summary>
public int Horizontal
{
get => Left + Right;
set => Left = Right = value / 2;
}

/// <inheritdoc/>
public static bool operator != (Thickness left, Thickness right) { return !(left == right); }
/// <summary>Gets or sets the width of the left side of the rectangle.</summary>
[JsonInclude]
public int Left
{
get => (int)_sides.X;
set => _sides.X = value;
}

/// <summary>
/// Adds the thickness widths of another <see cref="Thickness"/> to another <see cref="Thickness"/>.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Thickness operator + (Thickness a, Thickness b) { return a.Add (b); }

/// <summary>Gets or sets the width of the right side of the rectangle.</summary>
[JsonInclude]
public int Right
{
get => (int)_sides.Z;
set => _sides.Z = value;
}

/// <summary>Gets or sets the width of the upper side of the rectangle.</summary>
[JsonInclude]
public int Top
{
get => (int)_sides.Y;
set => _sides.Y = value;
}

/// <summary>Returns the thickness widths of the Thickness formatted as a string.</summary>
/// <returns>The thickness widths as a string.</returns>
public override string ToString () { return $"(Left={Left},Top={Top},Right={Right},Bottom={Bottom})"; }

private int validate (int width)
/// <summary>
/// Gets the total height of the top and bottom sides of the rectangle. Sets the height of the top and bottom
/// sides of the rectangle to half the specified value.
/// </summary>
public int Vertical
{
if (width < 0)
{
throw new ArgumentException ("Thickness widths cannot be negative.");
}

return width;
get => Top + Bottom;
set => Top = Bottom = value / 2;
}
}
10 changes: 5 additions & 5 deletions UnitTests/View/Adornment/BorderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void Border_Parent_HasFocus_Title_Uses_FocusAttribute ()
view.Border.Thickness = new (0, 1, 0, 0);
view.Border.LineStyle = LineStyle.Single;

view.ColorScheme = new()
view.ColorScheme = new ()
{
Normal = new (Color.Red, Color.Green),
Focus = new (Color.Green, Color.Red)
Expand Down Expand Up @@ -53,7 +53,7 @@ public void Border_Uses_Parent_ColorScheme ()
view.Border.Thickness = new (0, 1, 0, 0);
view.Border.LineStyle = LineStyle.Single;

view.ColorScheme = new()
view.ColorScheme = new ()
{
Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red)
};
Expand Down Expand Up @@ -90,7 +90,7 @@ public void Border_With_Title_Border_Double_Thickness_Top_Four_Size_Width (int w
{
Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
};
win.Border.Thickness.Top = 4;
win.Border.Thickness = win.Border.Thickness with { Top = 4 };

RunState rs = Application.Begin (win);
var firstIteration = false;
Expand Down Expand Up @@ -224,7 +224,7 @@ public void Border_With_Title_Border_Double_Thickness_Top_Three_Size_Width (int
{
Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
};
win.Border.Thickness.Top = 3;
win.Border.Thickness = win.Border.Thickness with { Top = 3 };

RunState rs = Application.Begin (win);
var firstIteration = false;
Expand Down Expand Up @@ -358,7 +358,7 @@ public void Border_With_Title_Border_Double_Thickness_Top_Two_Size_Width (int wi
{
Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
};
win.Border.Thickness.Top = 2;
win.Border.Thickness = win.Border.Thickness with { Top = 2 };

RunState rs = Application.Begin (win);
var firstIteration = false;
Expand Down
Loading