diff --git a/NativeAot/Program.cs b/NativeAot/Program.cs
index 5ef38db05..4aae3c099 100644
--- a/NativeAot/Program.cs
+++ b/NativeAot/Program.cs
@@ -17,14 +17,14 @@ public static class Program
#region The code in this region is not intended for use in a native Aot self-contained. It's just here to make sure there is no functionality break with localization in Terminal.Gui using self-contained
- if (Equals(Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures.Count == 0)
+ if (Equals(Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures!.Count == 0)
{
// Only happens if the project has true
Debug.Assert (Application.SupportedCultures.Count == 0);
}
else
{
- Debug.Assert (Application.SupportedCultures.Count > 0);
+ Debug.Assert (Application.SupportedCultures!.Count > 0);
Debug.Assert (Equals (CultureInfo.CurrentCulture, Thread.CurrentThread.CurrentUICulture));
}
diff --git a/Terminal.Gui/Application/Application.Keyboard.cs b/Terminal.Gui/Application/Application.Keyboard.cs
index 675d78c6f..4653f5419 100644
--- a/Terminal.Gui/Application/Application.Keyboard.cs
+++ b/Terminal.Gui/Application/Application.Keyboard.cs
@@ -190,7 +190,7 @@ public static partial class Application // Keyboard handling
foreach (Command command in appBinding.Commands)
{
- if (!CommandImplementations.ContainsKey (command))
+ if (!CommandImplementations!.ContainsKey (command))
{
throw new NotSupportedException (
@$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application."
@@ -274,7 +274,7 @@ public static partial class Application // Keyboard handling
///
/// Commands for Application.
///
- private static Dictionary> CommandImplementations { get; set; }
+ private static Dictionary>? CommandImplementations { get; set; }
///
///
@@ -292,7 +292,7 @@ public static partial class Application // Keyboard handling
///
/// The command.
/// The function.
- private static void AddCommand (Command command, Func f) { CommandImplementations [command] = ctx => f (); }
+ private static void AddCommand (Command command, Func f) { CommandImplementations! [command] = ctx => f (); }
static Application () { AddApplicationKeyBindings (); }
@@ -451,31 +451,4 @@ public static partial class Application // Keyboard handling
.Distinct ()
.ToList ();
}
-
- /////
- ///// Gets the list of Views that have key bindings for the specified key.
- /////
- /////
- ///// This is an internal method used by the class to add Application key bindings.
- /////
- ///// The key to check.
- ///// Outputs the list of views bound to
- ///// if successful.
- //internal static bool TryGetKeyBindings (Key key, out List views) { return _keyBindings.TryGetValue (key, out views); }
-
- ///
- /// Removes all scoped key bindings for the specified view.
- ///
- ///
- /// This is an internal method used by the class to remove Application key bindings.
- ///
- /// The view that is bound to the key.
- internal static void RemoveKeyBindings (View view)
- {
- List list = KeyBindings.Bindings
- .Where (kv => kv.Value.Scope != KeyBindingScope.Application)
- .Select (kv => kv.Value)
- .Distinct ()
- .ToList ();
- }
}
diff --git a/Terminal.Gui/Application/Application.Mouse.cs b/Terminal.Gui/Application/Application.Mouse.cs
index 2c497b761..3af3808b2 100644
--- a/Terminal.Gui/Application/Application.Mouse.cs
+++ b/Terminal.Gui/Application/Application.Mouse.cs
@@ -160,13 +160,13 @@ public static partial class Application // Mouse handling
Position = frameLoc,
Flags = mouseEvent.Flags,
ScreenPosition = mouseEvent.Position,
- View = MouseGrabView
+ View = view ?? MouseGrabView
};
if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
{
// The mouse has moved outside the bounds of the view that grabbed the mouse
- MouseEnteredView?.NewMouseLeaveEvent (mouseEvent);
+ MouseGrabView?.NewMouseLeaveEvent (mouseEvent);
}
//System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
diff --git a/Terminal.Gui/Application/Application.cs b/Terminal.Gui/Application/Application.cs
index 0ef0facba..d37ba3513 100644
--- a/Terminal.Gui/Application/Application.cs
+++ b/Terminal.Gui/Application/Application.cs
@@ -32,7 +32,7 @@ public static partial class Application
/// A string representation of the Application
public new static string ToString ()
{
- ConsoleDriver driver = Driver;
+ ConsoleDriver? driver = Driver;
if (driver is null)
{
@@ -47,13 +47,17 @@ public static partial class Application
///
/// The driver to use to render the contents.
/// A string representation of the Application
- public static string ToString (ConsoleDriver driver)
+ public static string ToString (ConsoleDriver? driver)
{
+ if (driver is null)
+ {
+ return string.Empty;
+ }
var sb = new StringBuilder ();
- Cell [,] contents = driver.Contents;
+ Cell [,] contents = driver?.Contents!;
- for (var r = 0; r < driver.Rows; r++)
+ for (var r = 0; r < driver!.Rows; r++)
{
for (var c = 0; c < driver.Cols; c++)
{
diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs
index 8585ccb38..d96ab73ea 100644
--- a/Terminal.Gui/Drawing/Thickness.cs
+++ b/Terminal.Gui/Drawing/Thickness.cs
@@ -61,7 +61,7 @@ public record struct Thickness
[JsonInclude]
public int Bottom
{
- get => (int)_sides.W;
+ readonly get => (int)_sides.W;
set => _sides.W = value;
}
@@ -249,7 +249,7 @@ public record struct Thickness
[JsonInclude]
public int Left
{
- get => (int)_sides.X;
+ readonly get => (int)_sides.X;
set => _sides.X = value;
}
@@ -265,7 +265,7 @@ public record struct Thickness
[JsonInclude]
public int Right
{
- get => (int)_sides.Z;
+ readonly get => (int)_sides.Z;
set => _sides.Z = value;
}
@@ -273,7 +273,7 @@ public record struct Thickness
[JsonInclude]
public int Top
{
- get => (int)_sides.Y;
+ readonly get => (int)_sides.Y;
set => _sides.Y = value;
}
diff --git a/Terminal.Gui/View/Layout/Dim.cs b/Terminal.Gui/View/Layout/Dim.cs
index 3db2be5f9..cfb286b6c 100644
--- a/Terminal.Gui/View/Layout/Dim.cs
+++ b/Terminal.Gui/View/Layout/Dim.cs
@@ -1,8 +1,8 @@
#nullable enable
-using System.Diagnostics;
-
namespace Terminal.Gui;
+using System.Numerics;
+
///
///
/// A Dim object describes the dimensions of a . Dim is the type of the
@@ -78,7 +78,7 @@ namespace Terminal.Gui;
///
///
///
-public abstract class Dim
+public abstract record Dim : IEqualityOperators
{
#region static Dim creation methods
@@ -113,12 +113,10 @@ public abstract class Dim
/// The maximum dimension the View's ContentSize will be fit to.
public static Dim? Auto (DimAutoStyle style = DimAutoStyle.Auto, Dim? minimumContentDim = null, Dim? maximumContentDim = null)
{
- return new DimAuto ()
- {
- MinimumContentDim = minimumContentDim,
- MaximumContentDim = maximumContentDim,
- Style = style
- };
+ return new DimAuto (
+ MinimumContentDim: minimumContentDim,
+ MaximumContentDim: maximumContentDim,
+ Style: style);
}
///
@@ -172,28 +170,22 @@ public abstract class Dim
#endregion static Dim creation methods
+
///
- /// Indicates whether the specified type is in the hierarchy of this Dim object.
+ /// Indicates whether the specified type is in the hierarchy of this Dim object.
///
- ///
- ///
+ /// A reference to this instance.
///
- public bool Has (Type type, out Dim dim)
+ public bool Has (out Dim dim) where T : Dim
{
dim = this;
- if (type == GetType ())
- {
- return true;
- }
- // If we are a PosCombine, we have to check the left and right
- // to see if they are of the type we are looking for.
- if (this is DimCombine { } combine && (combine.Left.Has (type, out dim) || combine.Right.Has (type, out dim)))
- {
- return true;
- }
-
- return false;
+ return this switch
+ {
+ DimCombine combine => combine.Left.Has (out dim) || combine.Right.Has (out dim),
+ T => true,
+ _ => false
+ };
}
#region virtual methods
@@ -208,7 +200,7 @@ public abstract class Dim
/// subclass of Dim that is used. For example, DimAbsolute returns a fixed dimension, DimFactor returns a
/// dimension that is a certain percentage of the super view's size, and so on.
///
- internal virtual int GetAnchor (int size) { return 0; }
+ internal abstract int GetAnchor (int size);
///
/// Calculates and returns the dimension of a object. It takes into account the location of the
@@ -228,7 +220,7 @@ public abstract class Dim
///
internal virtual int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
{
- return Math.Max (GetAnchor (superviewContentSize - location), 0);
+ return Math.Clamp (GetAnchor (superviewContentSize - location), 0, short.MaxValue);
}
///
diff --git a/Terminal.Gui/View/Layout/DimAbsolute.cs b/Terminal.Gui/View/Layout/DimAbsolute.cs
index 72d4e12f7..cb078a121 100644
--- a/Terminal.Gui/View/Layout/DimAbsolute.cs
+++ b/Terminal.Gui/View/Layout/DimAbsolute.cs
@@ -10,19 +10,13 @@ namespace Terminal.Gui;
/// methods on the class to create objects instead.
///
///
-///
-public class DimAbsolute (int size) : Dim
+///
+public record DimAbsolute (int Size) : Dim
{
- ///
- public override bool Equals (object? other) { return other is DimAbsolute abs && abs.Size == Size; }
-
- ///
- public override int GetHashCode () { return Size.GetHashCode (); }
-
///
/// Gets the size of the dimension.
///
- public int Size { get; } = size;
+ public int Size { get; } = Size;
///
public override string ToString () { return $"Absolute({Size})"; }
diff --git a/Terminal.Gui/View/Layout/DimAuto.cs b/Terminal.Gui/View/Layout/DimAuto.cs
index 6bd5f4d0f..e552d1028 100644
--- a/Terminal.Gui/View/Layout/DimAuto.cs
+++ b/Terminal.Gui/View/Layout/DimAuto.cs
@@ -15,64 +15,17 @@ namespace Terminal.Gui;
/// methods on the class to create objects instead.
///
///
-public class DimAuto : Dim
+/// The maximum dimension the View's ContentSize will be fit to.
+/// The minimum dimension the View's ContentSize will be constrained to.
+/// The of the .
+public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoStyle Style) : Dim
{
- private readonly Dim? _maximumContentDim;
-
- private readonly Dim? _minimumContentDim;
-
- private readonly DimAutoStyle _style;
-
- ///
- public override bool Equals (object? other)
- {
- if (other is not DimAuto auto)
- {
- return false;
- }
-
- return auto.MinimumContentDim == MinimumContentDim && auto.MaximumContentDim == MaximumContentDim && auto.Style == Style;
- }
-
- ///
- public override int GetHashCode () { return HashCode.Combine (MinimumContentDim, MaximumContentDim, Style); }
-
- ///
- /// Gets the maximum dimension the View's ContentSize will be fit to. NOT CURRENTLY SUPPORTED.
- ///
-
- // ReSharper disable once ConvertToAutoProperty
- public required Dim? MaximumContentDim
- {
- get => _maximumContentDim;
- init => _maximumContentDim = value;
- }
-
- ///
- /// Gets the minimum dimension the View's ContentSize will be constrained to.
- ///
-
- // ReSharper disable once ConvertToAutoProperty
- public required Dim? MinimumContentDim
- {
- get => _minimumContentDim;
- init => _minimumContentDim = value;
- }
-
- ///
- /// Gets the style of the DimAuto.
- ///
-
- // ReSharper disable once ConvertToAutoProperty
- public required DimAutoStyle Style
- {
- get => _style;
- init => _style = value;
- }
-
///
public override string ToString () { return $"Auto({Style},{MinimumContentDim},{MaximumContentDim})"; }
+ ///
+ internal override int GetAnchor (int size) => 0;
+
internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
{
var textSize = 0;
@@ -181,8 +134,8 @@ public class DimAuto : Dim
&& !v.X.Has (typeof (PosAnchorEnd), out _)
&& !v.X.Has (typeof (PosAlign), out _)
&& !v.X.Has (typeof (PosCenter), out _)
- && !v.Width.Has (typeof (DimFill), out _)
- && !v.Width.Has (typeof (DimPercent), out _)
+ && !v.Width.Has (out _)
+ && !v.Width.Has (out _)
)
.ToList ();
}
@@ -194,8 +147,8 @@ public class DimAuto : Dim
&& !v.Y.Has (typeof (PosAnchorEnd), out _)
&& !v.Y.Has (typeof (PosAlign), out _)
&& !v.Y.Has (typeof (PosCenter), out _)
- && !v.Height.Has (typeof (DimFill), out _)
- && !v.Height.Has (typeof (DimPercent), out _)
+ && !v.Height.Has (out _)
+ && !v.Height.Has (out _)
)
.ToList ();
}
@@ -419,11 +372,11 @@ public class DimAuto : Dim
if (dimension == Dimension.Width)
{
- dimViewSubViews = includedSubviews.Where (v => v.Width is { } && v.Width.Has (typeof (DimView), out _)).ToList ();
+ dimViewSubViews = includedSubviews.Where (v => v.Width is { } && v.Width.Has (out _)).ToList ();
}
else
{
- dimViewSubViews = includedSubviews.Where (v => v.Height is { } && v.Height.Has (typeof (DimView), out _)).ToList ();
+ dimViewSubViews = includedSubviews.Where (v => v.Height is { } && v.Height.Has (out _)).ToList ();
}
for (var i = 0; i < dimViewSubViews.Count; i++)
diff --git a/Terminal.Gui/View/Layout/DimCombine.cs b/Terminal.Gui/View/Layout/DimCombine.cs
index f8b5d141c..7a5d23614 100644
--- a/Terminal.Gui/View/Layout/DimCombine.cs
+++ b/Terminal.Gui/View/Layout/DimCombine.cs
@@ -4,31 +4,31 @@ namespace Terminal.Gui;
///
/// Represents a dimension that is a combination of two other dimensions.
///
-///
+///
/// Indicates whether the two dimensions are added or subtracted.
///
///
/// This is a low-level API that is typically used internally by the layout system. Use the various static
/// methods on the class to create objects instead.
///
-/// The left dimension.
-/// The right dimension.
-public class DimCombine (AddOrSubtract add, Dim left, Dim right) : Dim
+/// The left dimension.
+/// The right dimension.
+public record DimCombine (AddOrSubtract Add, Dim Left, Dim Right) : Dim
{
///
/// Gets whether the two dimensions are added or subtracted.
///
- public AddOrSubtract Add { get; } = add;
+ public AddOrSubtract Add { get; } = Add;
///
/// Gets the left dimension.
///
- public Dim Left { get; } = left;
+ public Dim Left { get; } = Left;
///
/// Gets the right dimension.
///
- public Dim Right { get; } = right;
+ public Dim Right { get; } = Right;
///
public override string ToString () { return $"Combine({Left}{(Add == AddOrSubtract.Add ? '+' : '-')}{Right})"; }
diff --git a/Terminal.Gui/View/Layout/DimFill.cs b/Terminal.Gui/View/Layout/DimFill.cs
index 03cf6f3d2..7550b87db 100644
--- a/Terminal.Gui/View/Layout/DimFill.cs
+++ b/Terminal.Gui/View/Layout/DimFill.cs
@@ -8,20 +8,9 @@ namespace Terminal.Gui;
/// This is a low-level API that is typically used internally by the layout system. Use the various static
/// methods on the class to create objects instead.
///
-/// The margin to not fill.
-public class DimFill (int margin) : Dim
+/// The margin to not fill.
+public record DimFill (int Margin) : Dim
{
- ///
- public override bool Equals (object? other) { return other is DimFill fill && fill.Margin == Margin; }
-
- ///
- public override int GetHashCode () { return Margin.GetHashCode (); }
-
- ///
- /// Gets the margin to not fill.
- ///
- public int Margin { get; } = margin;
-
///
public override string ToString () { return $"Fill({Margin})"; }
diff --git a/Terminal.Gui/View/Layout/DimFunc.cs b/Terminal.Gui/View/Layout/DimFunc.cs
index c15e9fc8c..c51406f40 100644
--- a/Terminal.Gui/View/Layout/DimFunc.cs
+++ b/Terminal.Gui/View/Layout/DimFunc.cs
@@ -2,28 +2,22 @@
namespace Terminal.Gui;
///
-/// Represents a function object that computes the dimension by executing the provided function.
+/// Represents a function object that computes the dimension by executing the provided function.
///
///
/// This is a low-level API that is typically used internally by the layout system. Use the various static
-/// methods on the class to create objects instead.
+/// methods on the class to create objects instead.
///
-///
-public class DimFunc (Func dim) : Dim
+/// The function that computes the dimension.
+public record DimFunc (Func Fn) : Dim
{
- ///
- public override bool Equals (object? other) { return other is DimFunc f && f.Func () == Func (); }
-
///
/// Gets the function that computes the dimension.
///
- public new Func Func { get; } = dim;
+ public Func Fn { get; } = Fn;
///
- public override int GetHashCode () { return Func.GetHashCode (); }
+ public override string ToString () { return $"DimFunc({Fn ()})"; }
- ///
- public override string ToString () { return $"DimFunc({Func ()})"; }
-
- internal override int GetAnchor (int size) { return Func (); }
+ internal override int GetAnchor (int size) { return Fn (); }
}
\ No newline at end of file
diff --git a/Terminal.Gui/View/Layout/DimPercent.cs b/Terminal.Gui/View/Layout/DimPercent.cs
index 2ae81302a..e94be76a7 100644
--- a/Terminal.Gui/View/Layout/DimPercent.cs
+++ b/Terminal.Gui/View/Layout/DimPercent.cs
@@ -8,35 +8,24 @@ namespace Terminal.Gui;
/// This is a low-level API that is typically used internally by the layout system. Use the various static
/// methods on the class to create objects instead.
///
-/// The percentage.
-///
+/// The percentage.
+///
/// If the dimension is computed using the View's position ( or
/// ); otherwise, the dimension is computed using the View's .
///
-public class DimPercent (int percent, DimPercentMode mode = DimPercentMode.ContentSize) : Dim
+public record DimPercent (int Percentage, DimPercentMode Mode = DimPercentMode.ContentSize) : Dim
{
- ///
- public override bool Equals (object? other) { return other is DimPercent f && f.Percent == Percent && f.Mode == Mode; }
-
- ///
- public override int GetHashCode () { return Percent.GetHashCode (); }
-
- ///
- /// Gets the percentage.
- ///
- public new int Percent { get; } = percent;
-
///
///
///
- public override string ToString () { return $"Percent({Percent},{Mode})"; }
+ public override string ToString () { return $"Percent({Percentage},{Mode})"; }
///
/// Gets whether the dimension is computed using the View's position or GetContentSize ().
///
- public DimPercentMode Mode { get; } = mode;
+ public DimPercentMode Mode { get; } = Mode;
- internal override int GetAnchor (int size) { return (int)(size * (Percent / 100f)); }
+ internal override int GetAnchor (int size) { return (int)(size * (Percentage / 100f)); }
internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
{
diff --git a/Terminal.Gui/View/Layout/DimView.cs b/Terminal.Gui/View/Layout/DimView.cs
index 7a7568a95..b37f9bb7e 100644
--- a/Terminal.Gui/View/Layout/DimView.cs
+++ b/Terminal.Gui/View/Layout/DimView.cs
@@ -8,7 +8,7 @@ namespace Terminal.Gui;
/// This is a low-level API that is typically used internally by the layout system. Use the various static
/// methods on the class to create objects instead.
///
-public class DimView : Dim
+public record DimView : Dim
{
///
/// Initializes a new instance of the class.
@@ -26,12 +26,6 @@ public class DimView : Dim
///
public Dimension Dimension { get; }
- ///
- public override bool Equals (object? other) { return other is DimView abs && abs.Target == Target && abs.Dimension == Dimension; }
-
- ///
- public override int GetHashCode () { return Target!.GetHashCode (); }
-
///
/// Gets the View the dimension is anchored to.
///
diff --git a/Terminal.Gui/View/Layout/Pos.cs b/Terminal.Gui/View/Layout/Pos.cs
index ba377fcdc..638b13b9e 100644
--- a/Terminal.Gui/View/Layout/Pos.cs
+++ b/Terminal.Gui/View/Layout/Pos.cs
@@ -131,7 +131,7 @@ namespace Terminal.Gui;
///
///
///
-public abstract class Pos
+public abstract record Pos
{
#region static Pos creation methods
diff --git a/Terminal.Gui/View/Layout/PosAbsolute.cs b/Terminal.Gui/View/Layout/PosAbsolute.cs
index 44afdac97..b71da5013 100644
--- a/Terminal.Gui/View/Layout/PosAbsolute.cs
+++ b/Terminal.Gui/View/Layout/PosAbsolute.cs
@@ -10,19 +10,13 @@ namespace Terminal.Gui;
/// methods on the class to create objects instead.
///
///
-///
-public class PosAbsolute (int position) : Pos
+///
+public record PosAbsolute (int Position) : Pos
{
///
/// The position of the in the layout.
///
- public int Position { get; } = position;
-
- ///
- public override bool Equals (object? other) { return other is PosAbsolute abs && abs.Position == Position; }
-
- ///
- public override int GetHashCode () { return Position.GetHashCode (); }
+ public int Position { get; } = Position;
///
public override string ToString () { return $"Absolute({Position})"; }
diff --git a/Terminal.Gui/View/Layout/PosAlign.cs b/Terminal.Gui/View/Layout/PosAlign.cs
index 79e22d410..d3873a4c0 100644
--- a/Terminal.Gui/View/Layout/PosAlign.cs
+++ b/Terminal.Gui/View/Layout/PosAlign.cs
@@ -1,7 +1,6 @@
#nullable enable
using System.ComponentModel;
-using System.Drawing;
namespace Terminal.Gui;
@@ -24,19 +23,13 @@ namespace Terminal.Gui;
/// The alignment is applied to all views with the same .
///
///
-public class PosAlign : Pos
+public record PosAlign : Pos
{
///
/// The cached location. Used to store the calculated location to minimize recalculating it.
///
public int? _cachedLocation;
- ///
- /// Gets the identifier of a set of views that should be aligned together. When only a single
- /// set of views in a SuperView is aligned, setting is not needed because it defaults to 0.
- ///
- public int GroupId { get; init; }
-
private readonly Aligner? _aligner;
///
@@ -57,6 +50,88 @@ public class PosAlign : Pos
}
}
+ // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
+ ///
+ /// Returns the minimum size a group of views with the same can be.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static int CalculateMinDimension (int groupId, IList views, Dimension dimension)
+ {
+ List dimensionsList = new ();
+
+ // PERF: If this proves a perf issue, consider caching a ref to this list in each item
+ List viewsInGroup = views.Where (
+ v =>
+ {
+ return dimension switch
+ {
+ Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
+ Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
+ _ => false
+ };
+ })
+ .ToList ();
+
+ if (viewsInGroup.Count == 0)
+ {
+ return 0;
+ }
+
+ // PERF: We iterate over viewsInGroup multiple times here.
+
+ // Update the dimensionList with the sizes of the views
+ for (var index = 0; index < viewsInGroup.Count; index++)
+ {
+ View view = viewsInGroup [index];
+
+ PosAlign? posAlign = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
+
+ if (posAlign is { })
+ {
+ dimensionsList.Add (dimension == Dimension.Width ? view.Frame.Width : view.Frame.Height);
+ }
+ }
+
+ // Align
+ return dimensionsList.Sum ();
+ }
+
+ ///
+ /// Gets the identifier of a set of views that should be aligned together. When only a single
+ /// set of views in a SuperView is aligned, setting is not needed because it defaults to 0.
+ ///
+ public int GroupId { get; init; }
+
+ ///
+ public override string ToString () { return $"Align(alignment={Aligner.Alignment},modes={Aligner.AlignmentModes},groupId={GroupId})"; }
+
+ internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
+ {
+ if (_cachedLocation.HasValue && Aligner.ContainerSize == superviewDimension)
+ {
+ return _cachedLocation.Value;
+ }
+
+ if (us?.SuperView is null)
+ {
+ return 0;
+ }
+
+ AlignAndUpdateGroup (GroupId, us.SuperView.Subviews, dimension, superviewDimension);
+
+ if (_cachedLocation.HasValue)
+ {
+ return _cachedLocation.Value;
+ }
+
+ return 0;
+ }
+
+ internal override int GetAnchor (int width) { return _cachedLocation ?? 0 - width; }
+
///
/// Aligns the views in that have the same group ID as .
/// Updates each view's cached _location.
@@ -71,30 +146,30 @@ public class PosAlign : Pos
// PERF: If this proves a perf issue, consider caching a ref to this list in each item
List posAligns = views.Select (
- v =>
- {
- switch (dimension)
- {
- case Dimension.Width when v.X.Has (typeof (PosAlign), out var pos):
+ v =>
+ {
+ switch (dimension)
+ {
+ case Dimension.Width when v.X.Has (typeof (PosAlign), out Pos pos):
- if (pos is PosAlign posAlignX && posAlignX.GroupId == groupId)
- {
- return posAlignX;
- }
+ if (pos is PosAlign posAlignX && posAlignX.GroupId == groupId)
+ {
+ return posAlignX;
+ }
- break;
- case Dimension.Height when v.Y.Has (typeof (PosAlign), out var pos):
- if (pos is PosAlign posAlignY && posAlignY.GroupId == groupId)
- {
- return posAlignY;
- }
+ break;
+ case Dimension.Height when v.Y.Has (typeof (PosAlign), out Pos pos):
+ if (pos is PosAlign posAlignY && posAlignY.GroupId == groupId)
+ {
+ return posAlignY;
+ }
- break;
- }
+ break;
+ }
- return null;
- })
- .ToList ();
+ return null;
+ })
+ .ToList ();
// PERF: We iterate over viewsInGroup multiple times here.
@@ -136,92 +211,4 @@ public class PosAlign : Pos
}
private void Aligner_PropertyChanged (object? sender, PropertyChangedEventArgs e) { _cachedLocation = null; }
-
- ///
- public override bool Equals (object? other)
- {
- return other is PosAlign align
- && GroupId == align.GroupId
- && align.Aligner.Alignment == Aligner.Alignment
- && align.Aligner.AlignmentModes == Aligner.AlignmentModes;
- }
-
- ///
- public override int GetHashCode () { return HashCode.Combine (Aligner, GroupId); }
-
- ///
- public override string ToString () { return $"Align(alignment={Aligner.Alignment},modes={Aligner.AlignmentModes},groupId={GroupId})"; }
-
- internal override int GetAnchor (int width) { return _cachedLocation ?? 0 - width; }
-
- internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
- {
- if (_cachedLocation.HasValue && Aligner.ContainerSize == superviewDimension)
- {
- return _cachedLocation.Value;
- }
-
- if (us?.SuperView is null)
- {
- return 0;
- }
-
- AlignAndUpdateGroup (GroupId, us.SuperView.Subviews, dimension, superviewDimension);
-
- if (_cachedLocation.HasValue)
- {
- return _cachedLocation.Value;
- }
-
- return 0;
- }
-
- // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
- ///
- /// Returns the minimum size a group of views with the same can be.
- ///
- ///
- ///
- ///
- ///
- public static int CalculateMinDimension (int groupId, IList views, Dimension dimension)
- {
- List dimensionsList = new ();
-
- // PERF: If this proves a perf issue, consider caching a ref to this list in each item
- List viewsInGroup = views.Where (
- v =>
- {
- return dimension switch
- {
- Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
- Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
- _ => false
- };
- })
- .ToList ();
-
- if (viewsInGroup.Count == 0)
- {
- return 0;
- }
-
- // PERF: We iterate over viewsInGroup multiple times here.
-
- // Update the dimensionList with the sizes of the views
- for (var index = 0; index < viewsInGroup.Count; index++)
- {
- View view = viewsInGroup [index];
-
- PosAlign? posAlign = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
-
- if (posAlign is { })
- {
- dimensionsList.Add (dimension == Dimension.Width ? view.Frame.Width : view.Frame.Height);
- }
- }
-
- // Align
- return dimensionsList.Sum ();
- }
}
diff --git a/Terminal.Gui/View/Layout/PosAnchorEnd.cs b/Terminal.Gui/View/Layout/PosAnchorEnd.cs
index e4641c2b5..9156e2230 100644
--- a/Terminal.Gui/View/Layout/PosAnchorEnd.cs
+++ b/Terminal.Gui/View/Layout/PosAnchorEnd.cs
@@ -10,7 +10,7 @@ namespace Terminal.Gui;
/// methods on the class to create objects instead.
///
///
-public class PosAnchorEnd : Pos
+public record PosAnchorEnd : Pos
{
///
/// Gets the offset of the position from the right/bottom.
@@ -30,12 +30,6 @@ public class PosAnchorEnd : Pos
///
public PosAnchorEnd (int offset) { Offset = offset; }
- ///
- public override bool Equals (object? other) { return other is PosAnchorEnd anchorEnd && anchorEnd.Offset == Offset; }
-
- ///
- public override int GetHashCode () { return Offset.GetHashCode (); }
-
///
/// If true, the offset is the width of the view, if false, the offset is the offset value.
///
diff --git a/Terminal.Gui/View/Layout/PosCenter.cs b/Terminal.Gui/View/Layout/PosCenter.cs
index 8f3d700b5..1d7cdc1e1 100644
--- a/Terminal.Gui/View/Layout/PosCenter.cs
+++ b/Terminal.Gui/View/Layout/PosCenter.cs
@@ -4,7 +4,7 @@ namespace Terminal.Gui;
///
/// Represents a position that is centered.
///
-public class PosCenter : Pos
+public record PosCenter : Pos
{
///
public override string ToString () { return "Center"; }
diff --git a/Terminal.Gui/View/Layout/PosCombine.cs b/Terminal.Gui/View/Layout/PosCombine.cs
index 63ff1814f..884bb2704 100644
--- a/Terminal.Gui/View/Layout/PosCombine.cs
+++ b/Terminal.Gui/View/Layout/PosCombine.cs
@@ -10,27 +10,27 @@ namespace Terminal.Gui;
/// methods on the class to create objects instead.
///
///
-///
+///
/// Indicates whether the two positions are added or subtracted.
///
-/// The left position.
-/// The right position.
-public class PosCombine (AddOrSubtract add, Pos left, Pos right) : Pos
+/// The left position.
+/// The right position.
+public record PosCombine (AddOrSubtract Add, Pos Left, Pos Right) : Pos
{
///
/// Gets whether the two positions are added or subtracted.
///
- public AddOrSubtract Add { get; } = add;
+ public AddOrSubtract Add { get; } = Add;
///
/// Gets the left position.
///
- public new Pos Left { get; } = left;
+ public new Pos Left { get; } = Left;
///
/// Gets the right position.
///
- public new Pos Right { get; } = right;
+ public new Pos Right { get; } = Right;
///
public override string ToString () { return $"Combine({Left}{(Add == AddOrSubtract.Add ? '+' : '-')}{Right})"; }
diff --git a/Terminal.Gui/View/Layout/PosFunc.cs b/Terminal.Gui/View/Layout/PosFunc.cs
index 24344f9a8..0bd819ad5 100644
--- a/Terminal.Gui/View/Layout/PosFunc.cs
+++ b/Terminal.Gui/View/Layout/PosFunc.cs
@@ -4,28 +4,11 @@ namespace Terminal.Gui;
///
/// Represents a position that is computed by executing a function that returns an integer position.
///
-///
-///
-/// This is a low-level API that is typically used internally by the layout system. Use the various static
-/// methods on the class to create objects instead.
-///
-///
-/// The position.
-public class PosFunc (Func pos) : Pos
+/// The function that computes the position.
+public record PosFunc (Func Fn) : Pos
{
- ///
- /// Gets the function that computes the position.
- ///
- public new Func Func { get; } = pos;
-
///
- public override bool Equals (object? other) { return other is PosFunc f && f.Func () == Func (); }
+ public override string ToString () { return $"PosFunc({Fn ()})"; }
- ///
- public override int GetHashCode () { return Func.GetHashCode (); }
-
- ///
- public override string ToString () { return $"PosFunc({Func ()})"; }
-
- internal override int GetAnchor (int size) { return Func (); }
+ internal override int GetAnchor (int size) { return Fn (); }
}
\ No newline at end of file
diff --git a/Terminal.Gui/View/Layout/PosPercent.cs b/Terminal.Gui/View/Layout/PosPercent.cs
index 384ba1fda..17b99cb69 100644
--- a/Terminal.Gui/View/Layout/PosPercent.cs
+++ b/Terminal.Gui/View/Layout/PosPercent.cs
@@ -10,19 +10,13 @@ namespace Terminal.Gui;
/// methods on the class to create objects instead.
///
///
-///
-public class PosPercent (int percent) : Pos
+///
+public record PosPercent (int Percent) : Pos
{
///
/// Gets the percentage of the width or height of the SuperView.
///
- public new int Percent { get; } = percent;
-
- ///
- public override bool Equals (object? other) { return other is PosPercent i && i.Percent == Percent; }
-
- ///
- public override int GetHashCode () { return Percent.GetHashCode (); }
+ public new int Percent { get; } = Percent;
///
public override string ToString () { return $"Percent({Percent})"; }
diff --git a/Terminal.Gui/View/Layout/PosView.cs b/Terminal.Gui/View/Layout/PosView.cs
index a46f6898a..92748d88b 100644
--- a/Terminal.Gui/View/Layout/PosView.cs
+++ b/Terminal.Gui/View/Layout/PosView.cs
@@ -10,25 +10,19 @@ namespace Terminal.Gui;
/// methods on the class to create objects instead.
///
///
-/// The View the position is anchored to.
-/// The side of the View the position is anchored to.
-public class PosView (View? view, Side side) : Pos
+/// The View the position is anchored to.
+/// The side of the View the position is anchored to.
+public record PosView (View? View, Side Side) : Pos
{
///
/// Gets the View the position is anchored to.
///
- public View? Target { get; } = view;
+ public View? Target { get; } = View;
///
/// Gets the side of the View the position is anchored to.
///
- public Side Side { get; } = side;
-
- ///
- public override bool Equals (object? other) { return other is PosView abs && abs.Target == Target && abs.Side == Side; }
-
- ///
- public override int GetHashCode () { return Target!.GetHashCode (); }
+ public Side Side { get; } = Side;
///
public override string ToString ()
diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs
index 73625417a..7a7fd35c5 100644
--- a/Terminal.Gui/View/View.Keyboard.cs
+++ b/Terminal.Gui/View/View.Keyboard.cs
@@ -27,7 +27,6 @@ public partial class View // Keyboard APIs
private void DisposeKeyboard ()
{
TitleTextFormatter.HotKeyChanged -= TitleTextFormatter_HotKeyChanged;
- Application.RemoveKeyBindings (this);
}
#region HotKey Support
diff --git a/Terminal.Gui/View/View.Layout.cs b/Terminal.Gui/View/View.Layout.cs
index 419f2b56c..c13367df3 100644
--- a/Terminal.Gui/View/View.Layout.cs
+++ b/Terminal.Gui/View/View.Layout.cs
@@ -474,7 +474,7 @@ public partial class View // Layout APIs
return;
}
- if (_height is { } && _height.Has (typeof (DimAuto), out _))
+ if (_height is { } && _height.Has (out _))
{
// Reset ContentSize to Viewport
_contentSize = null;
@@ -523,7 +523,7 @@ public partial class View // Layout APIs
return;
}
- if (_width is { } && _width.Has (typeof (DimAuto), out _))
+ if (_width is { } && _width.Has (out _))
{
// Reset ContentSize to Viewport
_contentSize = null;
diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs
index b05593fda..55f174c34 100644
--- a/Terminal.Gui/Views/Label.cs
+++ b/Terminal.Gui/Views/Label.cs
@@ -51,9 +51,9 @@ public class Label : View
set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
}
- private new bool? FocusNext ()
+ private bool? FocusNext ()
{
- var me = SuperView?.Subviews.IndexOf (this) ?? -1;
+ int me = SuperView?.Subviews.IndexOf (this) ?? -1;
if (me != -1 && me < SuperView?.Subviews.Count - 1)
{
SuperView?.Subviews [me + 1].SetFocus ();
diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs
index a900d060e..570bf7847 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menu/MenuBar.cs
@@ -1504,8 +1504,7 @@ public class MenuBar : View, IDesignable
return false;
}
}
- else if (!_isContextMenuLoading
- && !(me.View is MenuBar || me.View is Menu)
+ else if (!(me.View is MenuBar || me.View is Menu)
&& me.Flags != MouseFlags.ReportMousePosition
&& me.Flags != 0)
{
diff --git a/Terminal.Gui/Views/NumericUpDown.cs b/Terminal.Gui/Views/NumericUpDown.cs
index df57bd6b0..d250b0390 100644
--- a/Terminal.Gui/Views/NumericUpDown.cs
+++ b/Terminal.Gui/Views/NumericUpDown.cs
@@ -100,7 +100,7 @@ public class NumericUpDown : View where T : notnull
return false;
}
- if (Value is { })
+ if (Value is { } && Increment is { })
{
Value = (dynamic)Value + (dynamic)Increment;
}
@@ -117,11 +117,12 @@ public class NumericUpDown : View where T : notnull
return false;
}
- if (Value is { })
+ if (Value is { } && Increment is { })
{
Value = (dynamic)Value - (dynamic)Increment;
}
+
return true;
});
@@ -218,23 +219,23 @@ public class NumericUpDown : View where T : notnull
Text = _number.Text;
}
- private T _increment;
+ private T? _increment;
///
///
- public T Increment
+ public T? Increment
{
get => _increment;
set
{
- if ((dynamic)_increment == (dynamic)value)
+ if (_increment is { } && value is { } && (dynamic)_increment == (dynamic)value)
{
return;
}
_increment = value;
- IncrementChanged?.Invoke (this, new (value));
+ IncrementChanged?.Invoke (this, new (value!));
}
}
diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs
index 7256b5bfd..7dc85171f 100644
--- a/Terminal.Gui/Views/Toplevel.cs
+++ b/Terminal.Gui/Views/Toplevel.cs
@@ -69,74 +69,19 @@ public partial class Toplevel : View
#region Subviews
// TODO: Deprecate - Any view can host a menubar in v2
- /// Gets or sets the menu for this Toplevel.
- public MenuBar? MenuBar { get; set; }
+ /// Gets the latest added into this Toplevel.
+ public MenuBar? MenuBar => (MenuBar?)Subviews?.LastOrDefault (s => s is MenuBar);
// TODO: Deprecate - Any view can host a statusbar in v2
- /// Gets or sets the status bar for this Toplevel.
- public StatusBar? StatusBar { get; set; }
+ /// Gets the latest added into this Toplevel.
+ public StatusBar? StatusBar => (StatusBar?)Subviews?.LastOrDefault (s => s is StatusBar);
///
public override View Add (View view)
{
- AddMenuStatusBar (view);
-
return base.Add (view);
}
- ///
- public override View Remove (View view)
- {
- if (this is Toplevel { MenuBar: { } })
- {
- RemoveMenuStatusBar (view);
- }
-
- return base.Remove (view);
- }
-
- ///
- public override void RemoveAll ()
- {
- if (this == Application.Top)
- {
- MenuBar?.Dispose ();
- MenuBar = null;
- StatusBar?.Dispose ();
- StatusBar = null;
- }
-
- base.RemoveAll ();
- }
-
- internal void AddMenuStatusBar (View view)
- {
- if (view is MenuBar)
- {
- MenuBar = view as MenuBar;
- }
-
- if (view is StatusBar)
- {
- StatusBar = view as StatusBar;
- }
- }
-
- internal void RemoveMenuStatusBar (View view)
- {
- if (view is MenuBar)
- {
- MenuBar?.Dispose ();
- MenuBar = null;
- }
-
- if (view is StatusBar)
- {
- StatusBar?.Dispose ();
- StatusBar = null;
- }
- }
-
// TODO: Overlapped - Rename to AllSubviewsClosed - Move to View?
///
/// Invoked when the last child of the Toplevel is closed from by
diff --git a/UICatalog/Scenarios/ListViewWithSelection.cs b/UICatalog/Scenarios/ListViewWithSelection.cs
index f9bb7ea03..e22b2e2ec 100644
--- a/UICatalog/Scenarios/ListViewWithSelection.cs
+++ b/UICatalog/Scenarios/ListViewWithSelection.cs
@@ -202,9 +202,11 @@ public class ListViewWithSelection : Scenario
return false;
}
-
+#pragma warning disable CS0067
///
public event NotifyCollectionChangedEventHandler CollectionChanged;
+#pragma warning restore CS0067
+
public int Count => Scenarios?.Count ?? 0;
public int Length { get; private set; }
public bool SuspendCollectionChangedEvent { get => throw new System.NotImplementedException (); set => throw new System.NotImplementedException (); }
diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs
index 60353f59f..14b06f6e5 100644
--- a/UICatalog/UICatalog.cs
+++ b/UICatalog/UICatalog.cs
@@ -408,7 +408,7 @@ public class UICatalogApp
_themeMenuItems = CreateThemeMenuItems ();
_themeMenuBarItem = new ("_Themes", _themeMenuItems);
- MenuBar = new ()
+ MenuBar menuBar = new ()
{
Menus =
[
@@ -462,13 +462,15 @@ public class UICatalogApp
)
]
};
+ Add (menuBar);
- StatusBar = new ()
+ StatusBar statusBar = new ()
{
Visible = ShowStatusBar,
AlignmentModes = AlignmentModes.IgnoreFirstOrLast,
CanFocus = false
};
+ Add (statusBar);
if (StatusBar is { })
{
@@ -484,7 +486,11 @@ public class UICatalogApp
Title = "Show/Hide Status Bar",
CanFocus = false,
};
- statusBarShortcut.Accept += (sender, args) => { StatusBar.Visible = !StatusBar.Visible; };
+ statusBarShortcut.Accept += (sender, args) =>
+ {
+ StatusBar.Visible = !StatusBar.Visible;
+ args.Handled = true;
+ };
ShForce16Colors = new ()
{
@@ -637,7 +643,7 @@ public class UICatalogApp
Add (CategoryList);
Add (ScenarioList);
- Add (MenuBar);
+ Add (MenuBar!);
if (StatusBar is { })
{
diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs
index 44b681d28..fbca323b5 100644
--- a/UnitTests/Configuration/ConfigurationMangerTests.cs
+++ b/UnitTests/Configuration/ConfigurationMangerTests.cs
@@ -389,6 +389,7 @@ public class ConfigurationManagerTests
)
);
+#pragma warning disable xUnit2029
Assert.Empty (
Settings.Where (
cp => cp.Value.PropertyInfo!.GetCustomAttribute (
@@ -397,6 +398,7 @@ public class ConfigurationManagerTests
== null
)
);
+#pragma warning restore xUnit2029
// Application is a static class
PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");
diff --git a/UnitTests/Text/TextFormatterTests.cs b/UnitTests/Text/TextFormatterTests.cs
index 31d2f59f0..109065302 100644
--- a/UnitTests/Text/TextFormatterTests.cs
+++ b/UnitTests/Text/TextFormatterTests.cs
@@ -1,4 +1,5 @@
using System.Text;
+using UICatalog;
using Xunit.Abstractions;
// Alias Console to MockConsole so we don't accidentally use Console
@@ -7,37 +8,8 @@ namespace Terminal.Gui.TextTests;
public class TextFormatterTests
{
- private readonly ITestOutputHelper _output;
public TextFormatterTests (ITestOutputHelper output) { _output = output; }
-
- public static IEnumerable CMGlyphs =>
- new List { new object [] { $"{CM.Glyphs.LeftBracket} Say Hello 你 {CM.Glyphs.RightBracket}", 16, 15 } };
-
- public static IEnumerable FormatEnvironmentNewLine =>
- new List
- {
- new object []
- {
- $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3{Environment.NewLine}",
- 60,
- new [] { "Line1", "Line2", "Line3" }
- }
- };
-
- public static IEnumerable SplitEnvironmentNewLine =>
- new List
- {
- new object []
- {
- $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界",
- new [] { "First Line 界", "Second Line 界", "Third Line 界" }
- },
- new object []
- {
- $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界{Environment.NewLine}",
- new [] { "First Line 界", "Second Line 界", "Third Line 界", "" }
- }
- };
+ private readonly ITestOutputHelper _output;
[Theory]
[InlineData ("")]
@@ -262,2760 +234,8 @@ public class TextFormatterTests
);
}
- [Theory]
- [InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misęrables")]
- [InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę\nr\na\nb\nl\ne\ns")]
- [InlineData (
- 4,
- 4,
- TextDirection.TopBottom_LeftRight,
- @"
-LMre
-eias
-ssb
- ęl "
- )]
- public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected)
- {
- var driver = new FakeDriver ();
- driver.Init ();
-
- var text = "Les Mise\u0328\u0301rables";
-
- var tf = new TextFormatter ();
- tf.Direction = textDirection;
- tf.Text = text;
-
- Assert.True (tf.WordWrap);
-
- tf.ConstrainToSize = new (width, height);
-
- tf.Draw (
- new (0, 0, width, height),
- new (ColorName.White, ColorName.Black),
- new (ColorName.Blue, ColorName.Black),
- default (Rectangle),
- driver
- );
- TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
-
- driver.End ();
- }
-
- [Fact]
- [SetupFakeDriver]
- public void FillRemaining_True_False ()
- {
- ((FakeDriver)Application.Driver!).SetBufferSize (22, 5);
-
- Attribute [] attrs =
- {
- Attribute.Default, new (ColorName.Green, ColorName.BrightMagenta),
- new (ColorName.Blue, ColorName.Cyan)
- };
- var tf = new TextFormatter { ConstrainToSize = new (14, 3), Text = "Test\nTest long\nTest long long\n", MultiLine = true };
-
- tf.Draw (
- new (1, 1, 19, 3),
- attrs [1],
- attrs [2]);
-
- Assert.False (tf.FillRemaining);
-
- TestHelpers.AssertDriverContentsWithFrameAre (
- @"
- Test
- Test long
- Test long long",
- _output);
-
- TestHelpers.AssertDriverAttributesAre (
- @"
-000000000000000000000
-011110000000000000000
-011111111100000000000
-011111111111111000000
-000000000000000000000",
- null,
- attrs);
-
- tf.FillRemaining = true;
-
- tf.Draw (
- new (1, 1, 19, 3),
- attrs [1],
- attrs [2]);
-
- TestHelpers.AssertDriverAttributesAre (
- @"
-000000000000000000000
-011111111111111111110
-011111111111111111110
-011111111111111111110
-000000000000000000000",
- null,
- attrs);
- }
-
- [Theory]
- [InlineData ("_k Before", true, 0, (KeyCode)'K')] // lower case should return uppercase Hotkey
- [InlineData ("a_k Second", true, 1, (KeyCode)'K')]
- [InlineData ("Last _k", true, 5, (KeyCode)'K')]
- [InlineData ("After k_", false, -1, KeyCode.Null)]
- [InlineData ("Multiple _k and _R", true, 9, (KeyCode)'K')]
- [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к')] // Lower case Cryllic K (к)
- [InlineData ("_k Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results
- [InlineData ("a_k Second", true, 1, (KeyCode)'K', true)]
- [InlineData ("Last _k", true, 5, (KeyCode)'K', true)]
- [InlineData ("After k_", false, -1, KeyCode.Null, true)]
- [InlineData ("Multiple _k and _r", true, 9, (KeyCode)'K', true)]
- [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к', true)] // Cryllic K (К)
- public void FindHotKey_AlphaLowerCase_Succeeds (
- string text,
- bool expectedResult,
- int expectedHotPos,
- KeyCode expectedKey,
- bool supportFirstUpperCase = false
- )
- {
- var hotKeySpecifier = (Rune)'_';
-
- bool result = TextFormatter.FindHotKey (
- text,
- hotKeySpecifier,
- out int hotPos,
- out Key hotKey,
- supportFirstUpperCase
- );
-
- if (expectedResult)
- {
- Assert.True (result);
- }
- else
- {
- Assert.False (result);
- }
-
- Assert.Equal (expectedResult, result);
- Assert.Equal (expectedHotPos, hotPos);
- Assert.Equal (expectedKey, hotKey);
- }
-
- [Theory]
- [InlineData ("_K Before", true, 0, (KeyCode)'K')]
- [InlineData ("a_K Second", true, 1, (KeyCode)'K')]
- [InlineData ("Last _K", true, 5, (KeyCode)'K')]
- [InlineData ("After K_", false, -1, KeyCode.Null)]
- [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K')]
- [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
- [InlineData ("_K Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results
- [InlineData ("a_K Second", true, 1, (KeyCode)'K', true)]
- [InlineData ("Last _K", true, 5, (KeyCode)'K', true)]
- [InlineData ("After K_", false, -1, KeyCode.Null, true)]
- [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K', true)]
- [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К', true)] // Cryllic K (К)
- public void FindHotKey_AlphaUpperCase_Succeeds (
- string text,
- bool expectedResult,
- int expectedHotPos,
- KeyCode expectedKey,
- bool supportFirstUpperCase = false
- )
- {
- var hotKeySpecifier = (Rune)'_';
-
- bool result = TextFormatter.FindHotKey (
- text,
- hotKeySpecifier,
- out int hotPos,
- out Key hotKey,
- supportFirstUpperCase
- );
-
- if (expectedResult)
- {
- Assert.True (result);
- }
- else
- {
- Assert.False (result);
- }
-
- Assert.Equal (expectedResult, result);
- Assert.Equal (expectedHotPos, hotPos);
- Assert.Equal (expectedKey, hotKey);
- }
-
- [Theory]
- [InlineData (null)]
- [InlineData ("")]
- [InlineData ("no hotkey")]
- [InlineData ("No hotkey, Upper Case")]
- [InlineData ("Non-english: Сохранить")]
- public void FindHotKey_Invalid_ReturnsFalse (string text)
- {
- var hotKeySpecifier = (Rune)'_';
- var supportFirstUpperCase = false;
- var hotPos = 0;
- Key hotKey = KeyCode.Null;
- var result = false;
-
- result = TextFormatter.FindHotKey (
- text,
- hotKeySpecifier,
- out hotPos,
- out hotKey,
- supportFirstUpperCase
- );
- Assert.False (result);
- Assert.Equal (-1, hotPos);
- Assert.Equal (KeyCode.Null, hotKey);
- }
-
- [Theory]
- [InlineData ("\"k before")]
- [InlineData ("ak second")]
- [InlineData ("last k")]
- [InlineData ("multiple k and r")]
- [InlineData ("12345")]
- [InlineData ("`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?")] // punctuation
- [InlineData (" ~ s gui.cs master ↑10")] // ~IsLetterOrDigit + Unicode
- [InlineData ("non-english: кдать")] // Lower case Cryllic K (к)
- public void FindHotKey_Legacy_FirstUpperCase_NotFound_Returns_False (string text)
- {
- var supportFirstUpperCase = true;
-
- var hotKeySpecifier = (Rune)0;
-
- bool result = TextFormatter.FindHotKey (
- text,
- hotKeySpecifier,
- out int hotPos,
- out Key hotKey,
- supportFirstUpperCase
- );
- Assert.False (result);
- Assert.Equal (-1, hotPos);
- Assert.Equal (KeyCode.Null, hotKey);
- }
-
- [Theory]
- [InlineData ("K Before", true, 0, (KeyCode)'K')]
- [InlineData ("aK Second", true, 1, (KeyCode)'K')]
- [InlineData ("last K", true, 5, (KeyCode)'K')]
- [InlineData ("multiple K and R", true, 9, (KeyCode)'K')]
- [InlineData ("non-english: Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
- public void FindHotKey_Legacy_FirstUpperCase_Succeeds (
- string text,
- bool expectedResult,
- int expectedHotPos,
- KeyCode expectedKey
- )
- {
- var supportFirstUpperCase = true;
-
- var hotKeySpecifier = (Rune)0;
-
- bool result = TextFormatter.FindHotKey (
- text,
- hotKeySpecifier,
- out int hotPos,
- out Key hotKey,
- supportFirstUpperCase
- );
-
- if (expectedResult)
- {
- Assert.True (result);
- }
- else
- {
- Assert.False (result);
- }
-
- Assert.Equal (expectedResult, result);
- Assert.Equal (expectedHotPos, hotPos);
- Assert.Equal (expectedKey, hotKey);
- }
-
- [Theory]
- [InlineData ("_1 Before", true, 0, (KeyCode)'1')] // Digits
- [InlineData ("a_1 Second", true, 1, (KeyCode)'1')]
- [InlineData ("Last _1", true, 5, (KeyCode)'1')]
- [InlineData ("After 1_", false, -1, KeyCode.Null)]
- [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1')]
- [InlineData ("_1 Before", true, 0, (KeyCode)'1', true)] // Turn on FirstUpperCase and verify same results
- [InlineData ("a_1 Second", true, 1, (KeyCode)'1', true)]
- [InlineData ("Last _1", true, 5, (KeyCode)'1', true)]
- [InlineData ("After 1_", false, -1, KeyCode.Null, true)]
- [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1', true)]
- public void FindHotKey_Numeric_Succeeds (
- string text,
- bool expectedResult,
- int expectedHotPos,
- KeyCode expectedKey,
- bool supportFirstUpperCase = false
- )
- {
- var hotKeySpecifier = (Rune)'_';
-
- bool result = TextFormatter.FindHotKey (
- text,
- hotKeySpecifier,
- out int hotPos,
- out Key hotKey,
- supportFirstUpperCase
- );
-
- if (expectedResult)
- {
- Assert.True (result);
- }
- else
- {
- Assert.False (result);
- }
-
- Assert.Equal (expectedResult, result);
- Assert.Equal (expectedHotPos, hotPos);
- Assert.Equal (expectedKey, hotKey);
- }
-
- [Theory]
- [InlineData ("_\"k before", true, (KeyCode)'"')] // BUGBUG: Not sure why this fails. " is a normal char
- [InlineData ("\"_k before", true, KeyCode.K)]
- [InlineData ("_`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'`')]
- [InlineData ("`_~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'~')]
- [InlineData (
- "`~!@#$%^&*()-__=+[{]}\\|;:'\",<.>/?",
- true,
- (KeyCode)'='
- )] // BUGBUG: Not sure why this fails. Ignore the first and consider the second
- [InlineData ("_ ~ s gui.cs master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
- [InlineData (" ~ s gui.cs _ master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
- [InlineData ("non-english: _кдать", true, (KeyCode)'к')] // Lower case Cryllic K (к)
- public void FindHotKey_Symbols_Returns_Symbol (string text, bool found, KeyCode expected)
- {
- var hotKeySpecifier = (Rune)'_';
-
- bool result = TextFormatter.FindHotKey (text, hotKeySpecifier, out int _, out Key hotKey);
- Assert.Equal (found, result);
- Assert.Equal (expected, hotKey);
- }
-
- [Fact]
- public void Format_Dont_Throw_ArgumentException_With_WordWrap_As_False_And_Keep_End_Spaces_As_True ()
- {
- Exception exception = Record.Exception (
- () =>
- TextFormatter.Format (
- "Some text",
- 4,
- Alignment.Start,
- false,
- true
- )
- );
- Assert.Null (exception);
- }
-
- [Theory]
- [InlineData (
- "Hello world, how are you today? Pretty neat!",
- 44,
- 80,
- "Hello world, how are you today? Pretty neat!"
- )]
- public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Horizontal (
- string text,
- int runeCount,
- int maxWidth,
- string justifiedText
- )
- {
- Assert.Equal (runeCount, text.GetRuneCount ());
-
- var fmtText = string.Empty;
-
- for (int i = text.GetRuneCount (); i < maxWidth; i++)
- {
- fmtText = TextFormatter.Format (text, i, Alignment.Fill, true) [0];
- Assert.Equal (i, fmtText.GetRuneCount ());
- char c = fmtText [^1];
- Assert.True (text.EndsWith (c));
- }
-
- Assert.Equal (justifiedText, fmtText);
- }
-
- [Theory]
- [InlineData (
- "Hello world, how are you today? Pretty neat!",
- 44,
- 80,
- "Hello world, how are you today? Pretty neat!"
- )]
- public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Vertical (
- string text,
- int runeCount,
- int maxWidth,
- string justifiedText
- )
- {
- Assert.Equal (runeCount, text.GetRuneCount ());
-
- var fmtText = string.Empty;
-
- for (int i = text.GetRuneCount (); i < maxWidth; i++)
- {
- fmtText = TextFormatter.Format (
- text,
- i,
- Alignment.Fill,
- false,
- true,
- 0,
- TextDirection.TopBottom_LeftRight
- ) [0];
- Assert.Equal (i, fmtText.GetRuneCount ());
- char c = fmtText [^1];
- Assert.True (text.EndsWith (c));
- }
-
- Assert.Equal (justifiedText, fmtText);
- }
-
- [Theory]
- [InlineData ("Truncate", 3, "Tru")]
- [InlineData ("デモエムポンズ", 3, "デ")]
- public void Format_Truncate_Simple_And_Wide_Runes (string text, int width, string expected)
- {
- List list = TextFormatter.Format (text, width, false, false);
- Assert.Equal (expected, list [^1]);
- }
-
- [Theory]
- [MemberData (nameof (FormatEnvironmentNewLine))]
- public void Format_With_PreserveTrailingSpaces_And_Without_PreserveTrailingSpaces (
- string text,
- int width,
- IEnumerable expected
- )
- {
- var preserveTrailingSpaces = false;
- List formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces);
- Assert.Equal (expected, formated);
-
- preserveTrailingSpaces = true;
- formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces);
- Assert.Equal (expected, formated);
- }
-
- [Theory]
- [InlineData (
- " A sentence has words. \n This is the second Line - 2. ",
- 4,
- -50,
- Alignment.Start,
- true,
- false,
- new [] { " A", "sent", "ence", "has", "word", "s. ", " Thi", "s is", "the", "seco", "nd", "Line", "- 2." },
- " Asentencehaswords. This isthesecondLine- 2."
- )]
- [InlineData (
- " A sentence has words. \n This is the second Line - 2. ",
- 4,
- -50,
- Alignment.Start,
- true,
- true,
- new []
- {
- " A ",
- "sent",
- "ence",
- " ",
- "has ",
- "word",
- "s. ",
- " ",
- "This",
- " is ",
- "the ",
- "seco",
- "nd ",
- "Line",
- " - ",
- "2. "
- },
- " A sentence has words. This is the second Line - 2. "
- )]
- public void Format_WordWrap_PreserveTrailingSpaces (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- bool preserveTrailingSpaces,
- IEnumerable resultLines,
- string expectedWrappedText
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
- Assert.Equal (list.Count, resultLines.Count ());
- Assert.Equal (resultLines, list);
- var wrappedText = string.Empty;
-
- foreach (string txt in list)
- {
- wrappedText += txt;
- }
-
- Assert.Equal (expectedWrappedText, wrappedText);
- }
-
- [Theory]
- [InlineData ("Hello World", 11)]
- [InlineData ("こんにちは世界", 14)]
- public void GetColumns_Simple_And_Wide_Runes (string text, int width) { Assert.Equal (width, text.GetColumns ()); }
-
- [Theory]
- [InlineData ("Hello World", 6, 6)]
- [InlineData ("こんにちは 世界", 6, 3)]
- [MemberData (nameof (CMGlyphs))]
- public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
- {
- List runes = text.ToRuneList ();
- Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
- }
-
- [Theory]
- [InlineData ("test", 3, 3)]
- [InlineData ("test", 4, 4)]
- [InlineData ("test", 10, 4)]
- public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength)
- {
- List runes = text.ToRuneList ();
-
- Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
- }
-
- [Theory]
- [InlineData ("Hello World", 6, 6)]
- [InlineData ("こんにちは 世界", 6, 3)]
- public void GetLengthThatFits_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
- {
- Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
- }
-
- [Theory]
- [InlineData ("test", 3, 3)]
- [InlineData ("test", 4, 4)]
- [InlineData ("test", 10, 4)]
- [InlineData ("test", 1, 1)]
- [InlineData ("test", 0, 0)]
- [InlineData ("test", -1, 0)]
- [InlineData (null, -1, 0)]
- [InlineData ("", -1, 0)]
- public void GetLengthThatFits_String (string text, int columns, int expectedLength)
- {
- Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
- }
-
- [Fact]
- public void GetLengthThatFits_With_Combining_Runes ()
- {
- var text = "Les Mise\u0328\u0301rables";
- Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14));
- }
-
- [Fact]
- public void GetMaxColsForWidth_With_Combining_Runes ()
- {
- List text = new () { "Les Mis", "e\u0328\u0301", "rables" };
- Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1));
- }
-
- [Theory]
- [InlineData (new [] { "0123456789" }, 1)]
- [InlineData (new [] { "Hello World" }, 1)]
- [InlineData (new [] { "Hello", "World" }, 2)]
- [InlineData (new [] { "こんにちは", "世界" }, 4)]
- public void GetColumnsRequiredForVerticalText_List_GetsWidth (IEnumerable text, int expectedWidth)
- {
- Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
- }
-
- [Theory]
- [InlineData (new [] { "Hello World" }, 1, 0, 1, 1)]
- [InlineData (new [] { "Hello", "World" }, 2, 1, 1, 1)]
- [InlineData (new [] { "こんにちは", "世界" }, 4, 1, 1, 2)]
- public void GetColumnsRequiredForVerticalText_List_Simple_And_Wide_Runes (
- IEnumerable text,
- int expectedWidth,
- int index,
- int length,
- int expectedIndexWidth
- )
- {
- Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
- Assert.Equal (expectedIndexWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList (), index, length));
- }
-
- [Fact]
- public void GetColumnsRequiredForVerticalText_List_With_Combining_Runes ()
- {
- List text = new () { "Les Mis", "e\u0328\u0301", "rables" };
- Assert.Equal (1, TextFormatter.GetColumnsRequiredForVerticalText (text, 1, 1));
- }
-
- //[Fact]
- //public void GetWidestLineLength_With_Combining_Runes ()
- //{
- // var text = "Les Mise\u0328\u0301rables";
- // Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1));
- //}
-
- [Fact]
- public void Internal_Tests ()
- {
- var tf = new TextFormatter ();
- Assert.Equal (KeyCode.Null, tf.HotKey);
- tf.HotKey = KeyCode.CtrlMask | KeyCode.Q;
- Assert.Equal (KeyCode.CtrlMask | KeyCode.Q, tf.HotKey);
- }
-
- [Theory]
- [InlineData ("")]
- [InlineData (null)]
- [InlineData ("test")]
- public void Justify_Invalid (string text)
- {
- Assert.Equal (text, TextFormatter.Justify (text, 0));
- Assert.Equal (text, TextFormatter.Justify (text, 0));
- Assert.Throws (() => TextFormatter.Justify (text, -1));
- }
-
- [Theory]
-
- // Even # of spaces
- // 0123456789
- [InlineData ("012 456 89", "012 456 89", 10, 0, "+", true)]
- [InlineData ("012 456 89", "012++456+89", 11, 1)]
- [InlineData ("012 456 89", "012 456 89", 12, 2, "++", true)]
- [InlineData ("012 456 89", "012+++456++89", 13, 3)]
- [InlineData ("012 456 89", "012 456 89", 14, 4, "+++", true)]
- [InlineData ("012 456 89", "012++++456+++89", 15, 5)]
- [InlineData ("012 456 89", "012 456 89", 16, 6, "++++", true)]
- [InlineData ("012 456 89", "012 456 89", 30, 20, "+++++++++++", true)]
- [InlineData ("012 456 89", "012+++++++++++++456++++++++++++89", 33, 23)]
-
- // Odd # of spaces
- // 01234567890123
- [InlineData ("012 456 89 end", "012 456 89 end", 14, 0, "+", true)]
- [InlineData ("012 456 89 end", "012++456+89+end", 15, 1)]
- [InlineData ("012 456 89 end", "012++456++89+end", 16, 2)]
- [InlineData ("012 456 89 end", "012 456 89 end", 17, 3, "++", true)]
- [InlineData ("012 456 89 end", "012+++456++89++end", 18, 4)]
- [InlineData ("012 456 89 end", "012+++456+++89++end", 19, 5)]
- [InlineData ("012 456 89 end", "012 456 89 end", 20, 6, "+++", true)]
- [InlineData ("012 456 89 end", "012++++++++456++++++++89+++++++end", 34, 20)]
- [InlineData ("012 456 89 end", "012+++++++++456+++++++++89++++++++end", 37, 23)]
-
- // Unicode
- // Even # of chars
- // 0123456789
- [InlineData ("пÑРвРÑ", "пÑРвРÑ", 10, 0, "+", true)]
- [InlineData ("пÑРвРÑ", "пÑÐ++вÐ+Ñ", 11, 1)]
- [InlineData ("пÑРвРÑ", "пÑРвРÑ", 12, 2, "++", true)]
- [InlineData ("пÑРвРÑ", "пÑÐ+++вÐ++Ñ", 13, 3)]
- [InlineData ("пÑРвРÑ", "пÑРвРÑ", 14, 4, "+++", true)]
- [InlineData ("пÑРвРÑ", "пÑÐ++++вÐ+++Ñ", 15, 5)]
- [InlineData ("пÑРвРÑ", "пÑРвРÑ", 16, 6, "++++", true)]
- [InlineData ("пÑРвРÑ", "пÑРвРÑ", 30, 20, "+++++++++++", true)]
- [InlineData ("пÑРвРÑ", "пÑÐ+++++++++++++вÐ++++++++++++Ñ", 33, 23)]
-
- // Unicode
- // Odd # of chars
- // 0123456789
- [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 10, 0, "+", true)]
- [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ+вÐ+Ñ", 11, 1)]
- [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ++вÐ+Ñ", 12, 2)]
- [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 13, 3, "++", true)]
- [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ++вÐ++Ñ", 14, 4)]
- [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ+++вÐ++Ñ", 15, 5)]
- [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 16, 6, "+++", true)]
- [InlineData ("Ð ÑРвРÑ", "Ð++++++++ÑÐ++++++++вÐ+++++++Ñ", 30, 20)]
- [InlineData ("Ð ÑРвРÑ", "Ð+++++++++ÑÐ+++++++++вÐ++++++++Ñ", 33, 23)]
- public void Justify_Sentence (
- string text,
- string justifiedText,
- int forceToWidth,
- int widthOffset,
- string replaceWith = null,
- bool replace = false
- )
- {
- var fillChar = '+';
-
- Assert.Equal (forceToWidth, text.GetRuneCount () + widthOffset);
-
- if (replace)
- {
- justifiedText = text.Replace (" ", replaceWith);
- }
-
- Assert.Equal (justifiedText, TextFormatter.Justify (text, forceToWidth, fillChar));
- Assert.True (Math.Abs (forceToWidth - justifiedText.GetRuneCount ()) < text.Count (s => s == ' '));
- Assert.True (Math.Abs (forceToWidth - justifiedText.GetColumns ()) < text.Count (s => s == ' '));
- }
-
- [Theory]
- [InlineData ("word")] // Even # of chars
- [InlineData ("word.")] // Odd # of chars
- [InlineData ("пÑивеÑ")] // Unicode (even #)
- [InlineData ("пÑивеÑ.")] // Unicode (odd # of chars)
- public void Justify_SingleWord (string text)
- {
- string justifiedText = text;
- var fillChar = '+';
-
- int width = text.GetRuneCount ();
- Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
- width = text.GetRuneCount () + 1;
- Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
- width = text.GetRuneCount () + 2;
- Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
- width = text.GetRuneCount () + 10;
- Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
- width = text.GetRuneCount () + 11;
- Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
- }
-
- [Theory]
- [InlineData ("Single Line 界", 14)]
- [InlineData ("First Line 界\nSecond Line 界\nThird Line 界\n", 14)]
- public void MaxWidthLine_With_And_Without_Newlines (string text, int expected) { Assert.Equal (expected, TextFormatter.GetWidestLineLength (text)); }
-
- [Theory]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 0,
- false,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 1,
- false,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 1,
- 0,
- false,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 0,
- true,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 1,
- true,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 1,
- 0,
- true,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 6,
- 5,
- false,
- new [] { "First " }
- )]
- [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3 " })]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 6,
- 5,
- true,
- new [] { "First ", "Second", "Third ", "Forty ", "Fiftee" }
- )]
- [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一" })]
- [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一", "第二", "第三", "四十", "第十" })]
- public void MultiLine_WordWrap_False_Horizontal_Direction (
- string text,
- int maxWidth,
- int maxHeight,
- bool multiLine,
- IEnumerable resultLines
- )
- {
- var tf = new TextFormatter
- {
- Text = text, ConstrainToSize = new (maxWidth, maxHeight), WordWrap = false, MultiLine = multiLine
- };
-
- Assert.False (tf.WordWrap);
- Assert.True (tf.MultiLine == multiLine);
- Assert.Equal (TextDirection.LeftRight_TopBottom, tf.Direction);
-
- List splitLines = tf.GetLines ();
- Assert.Equal (splitLines.Count, resultLines.Count ());
- Assert.Equal (splitLines, resultLines);
- }
-
- [Theory]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 0,
- false,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 1,
- false,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 1,
- 0,
- false,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 0,
- true,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 0,
- 1,
- true,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 1,
- 0,
- true,
- new [] { "" }
- )]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 6,
- 5,
- false,
- new [] { "First" }
- )]
- [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3" })]
- [InlineData (
- "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
- 6,
- 5,
- true,
- new [] { "First", "Secon", "Third", "Forty", "Fifte", "Seven" }
- )]
- [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一行 第" })]
- [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一行", "第二行" })]
- public void MultiLine_WordWrap_False_Vertical_Direction (
- string text,
- int maxWidth,
- int maxHeight,
- bool multiLine,
- IEnumerable resultLines
- )
- {
- var tf = new TextFormatter
- {
- Text = text,
- ConstrainToSize = new (maxWidth, maxHeight),
- WordWrap = false,
- MultiLine = multiLine,
- Direction = TextDirection.TopBottom_LeftRight
- };
-
- Assert.False (tf.WordWrap);
- Assert.True (tf.MultiLine == multiLine);
- Assert.Equal (TextDirection.TopBottom_LeftRight, tf.Direction);
-
- List splitLines = tf.GetLines ();
- Assert.Equal (splitLines.Count, resultLines.Count ());
- Assert.Equal (splitLines, resultLines);
- }
-
- [Fact]
- public void NeedsFormat_Sets ()
- {
- var testText = "test";
- var testBounds = new Rectangle (0, 0, 100, 1);
- var tf = new TextFormatter ();
-
- tf.Text = "test";
- Assert.True (tf.NeedsFormat); // get_Lines causes a Format
- Assert.NotEmpty (tf.GetLines ());
- Assert.False (tf.NeedsFormat); // get_Lines causes a Format
- Assert.Equal (testText, tf.Text);
- tf.Draw (testBounds, new (), new ());
- Assert.False (tf.NeedsFormat);
-
- tf.ConstrainToSize = new (1, 1);
- Assert.True (tf.NeedsFormat);
- Assert.NotEmpty (tf.GetLines ());
- Assert.False (tf.NeedsFormat); // get_Lines causes a Format
-
- tf.Alignment = Alignment.Center;
- Assert.True (tf.NeedsFormat);
- Assert.NotEmpty (tf.GetLines ());
- Assert.False (tf.NeedsFormat); // get_Lines causes a Format
- }
-
- [Theory]
- [InlineData ("", -1, Alignment.Start, false, 0)]
- [InlineData (null, 0, Alignment.Start, false, 1)]
- [InlineData (null, 0, Alignment.Start, true, 1)]
- [InlineData ("", 0, Alignment.Start, false, 1)]
- [InlineData ("", 0, Alignment.Start, true, 1)]
- public void Reformat_Invalid (string text, int maxWidth, Alignment alignment, bool wrap, int linesCount)
- {
- if (maxWidth < 0)
- {
- Assert.Throws (
- () =>
- TextFormatter.Format (text, maxWidth, alignment, wrap)
- );
- }
- else
- {
- List list = TextFormatter.Format (text, maxWidth, alignment, wrap);
- Assert.NotEmpty (list);
- Assert.True (list.Count == linesCount);
- Assert.Equal (string.Empty, list [0]);
- }
- }
-
- [Theory]
- [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true)]
- [InlineData ("A sentence has words.\nLine 2.", 1, -28, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.\nLine 2.", 5, -24, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.\nLine 2.", 28, -1, Alignment.Start, false, 1, false)]
-
- // no clip
- [InlineData ("A sentence has words.\nLine 2.", 29, 0, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.\nLine 2.", 30, 1, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true)]
- [InlineData ("A sentence has words.\r\nLine 2.", 1, -29, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.\r\nLine 2.", 5, -25, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.\r\nLine 2.", 29, -1, Alignment.Start, false, 1, false, 1)]
- [InlineData ("A sentence has words.\r\nLine 2.", 30, 0, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.\r\nLine 2.", 31, 1, Alignment.Start, false, 1, false)]
- public void Reformat_NoWordrap_NewLines_MultiLine_False (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- int linesCount,
- bool stringEmpty,
- int clipWidthOffset = 0
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth) + clipWidthOffset;
- List list = TextFormatter.Format (text, maxWidth, alignment, wrap);
- Assert.NotEmpty (list);
- Assert.True (list.Count == linesCount);
-
- if (stringEmpty)
- {
- Assert.Equal (string.Empty, list [0]);
- }
- else
- {
- Assert.NotEqual (string.Empty, list [0]);
- }
-
- if (text.Contains ("\r\n") && maxWidth > 0)
- {
- Assert.Equal (
- StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth])
- .Replace ("\r\n", " "),
- list [0]
- );
- }
- else if (text.Contains ('\n') && maxWidth > 0)
- {
- Assert.Equal (
- StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth])
- .Replace ("\n", " "),
- list [0]
- );
- }
- else
- {
- Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]);
- }
- }
-
- [Theory]
- [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 1,
- -28,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A", "L" }
- )]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 5,
- -24,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sen", "Line " }
- )]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 28,
- -1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- //// no clip
- [InlineData (
- "A sentence has words.\nLine 2.",
- 29,
- 0,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 30,
- 1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 1,
- -29,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A", "L" }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 5,
- -25,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sen", "Line " }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 29,
- -1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 30,
- 0,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 31,
- 1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- public void Reformat_NoWordrap_NewLines_MultiLine_True (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- int linesCount,
- bool stringEmpty,
- IEnumerable resultLines
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
-
- List list = TextFormatter.Format (
- text,
- maxWidth,
- alignment,
- wrap,
- false,
- 0,
- TextDirection.LeftRight_TopBottom,
- true
- );
- Assert.NotEmpty (list);
- Assert.True (list.Count == linesCount);
-
- if (stringEmpty)
- {
- Assert.Equal (string.Empty, list [0]);
- }
- else
- {
- Assert.NotEqual (string.Empty, list [0]);
- }
-
- Assert.Equal (list, resultLines);
- }
-
- [Theory]
- [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 1,
- -28,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A", "L" }
- )]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 5,
- -24,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sen", "Line " }
- )]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 28,
- -1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- //// no clip
- [InlineData (
- "A sentence has words.\nLine 2.",
- 29,
- 0,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData (
- "A sentence has words.\nLine 2.",
- 30,
- 1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 1,
- -29,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A", "L" }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 5,
- -25,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sen", "Line " }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 29,
- -1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 30,
- 0,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- [InlineData (
- "A sentence has words.\r\nLine 2.",
- 31,
- 1,
- Alignment.Start,
- false,
- 2,
- false,
- new [] { "A sentence has words.", "Line 2." }
- )]
- public void Reformat_NoWordrap_NewLines_MultiLine_True_Vertical (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- int linesCount,
- bool stringEmpty,
- IEnumerable resultLines
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
-
- List list = TextFormatter.Format (
- text,
- maxWidth,
- alignment,
- wrap,
- false,
- 0,
- TextDirection.TopBottom_LeftRight,
- true
- );
- Assert.NotEmpty (list);
- Assert.True (list.Count == linesCount);
-
- if (stringEmpty)
- {
- Assert.Equal (string.Empty, list [0]);
- }
- else
- {
- Assert.NotEqual (string.Empty, list [0]);
- }
-
- Assert.Equal (list, resultLines);
- }
-
- [Theory]
- [InlineData ("", 0, 0, Alignment.Start, false, 1, true)]
- [InlineData ("", 1, 1, Alignment.Start, false, 1, true)]
- [InlineData ("A sentence has words.", 0, -21, Alignment.Start, false, 1, true)]
- [InlineData ("A sentence has words.", 1, -20, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.", 5, -16, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.", 20, -1, Alignment.Start, false, 1, false)]
-
- // no clip
- [InlineData ("A sentence has words.", 21, 0, Alignment.Start, false, 1, false)]
- [InlineData ("A sentence has words.", 22, 1, Alignment.Start, false, 1, false)]
- public void Reformat_NoWordrap_SingleLine (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- int linesCount,
- bool stringEmpty
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- List list = TextFormatter.Format (text, maxWidth, alignment, wrap);
- Assert.NotEmpty (list);
- Assert.True (list.Count == linesCount);
-
- if (stringEmpty)
- {
- Assert.Equal (string.Empty, list [0]);
- }
- else
- {
- Assert.NotEqual (string.Empty, list [0]);
- }
-
- Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]);
- }
-
- [Theory]
-
- // Unicode
- [InlineData (
- "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
- 8,
- -1,
- Alignment.Start,
- true,
- false,
- new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
- )]
-
- // no clip
- [InlineData (
- "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
- 9,
- 0,
- Alignment.Start,
- true,
- false,
- new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
- )]
- [InlineData (
- "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
- 10,
- 1,
- Alignment.Start,
- true,
- false,
- new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
- )]
- public void Reformat_Unicode_Wrap_Spaces_NewLines (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- bool preserveTrailingSpaces,
- IEnumerable resultLines
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
- Assert.Equal (list.Count, resultLines.Count ());
- Assert.Equal (resultLines, list);
- }
-
- [Theory]
-
- // Unicode
- // Even # of chars
- // 0123456789
- [InlineData ("\u2660пÑРвРÑ", 10, -1, Alignment.Start, true, false, new [] { "\u2660пÑРвÐ", "Ñ" })]
-
- // no clip
- [InlineData ("\u2660пÑРвРÑ", 11, 0, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })]
- [InlineData ("\u2660пÑРвРÑ", 12, 1, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })]
-
- // Unicode
- // Odd # of chars
- // 0123456789
- [InlineData ("\u2660 ÑРвРÑ", 9, -1, Alignment.Start, true, false, new [] { "\u2660 ÑРвÐ", "Ñ" })]
-
- // no clip
- [InlineData ("\u2660 ÑРвРÑ", 10, 0, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })]
- [InlineData ("\u2660 ÑРвРÑ", 11, 1, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })]
- public void Reformat_Unicode_Wrap_Spaces_No_NewLines (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- bool preserveTrailingSpaces,
- IEnumerable resultLines
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
- Assert.Equal (list.Count, resultLines.Count ());
- Assert.Equal (resultLines, list);
- }
-
- [Theory]
-
- // Even # of spaces
- // 0123456789
- [InlineData ("012 456 89", 0, -10, Alignment.Start, true, true, true, new [] { "" })]
- [InlineData (
- "012 456 89",
- 1,
- -9,
- Alignment.Start,
- true,
- true,
- false,
- new [] { "0", "1", "2", " ", "4", "5", "6", " ", "8", "9" },
- "01245689"
- )]
- [InlineData ("012 456 89", 5, -5, Alignment.Start, true, true, false, new [] { "012 ", "456 ", "89" })]
- [InlineData ("012 456 89", 9, -1, Alignment.Start, true, true, false, new [] { "012 456 ", "89" })]
-
- // no clip
- [InlineData ("012 456 89", 10, 0, Alignment.Start, true, true, false, new [] { "012 456 89" })]
- [InlineData ("012 456 89", 11, 1, Alignment.Start, true, true, false, new [] { "012 456 89" })]
-
- // Odd # of spaces
- // 01234567890123
- [InlineData ("012 456 89 end", 13, -1, Alignment.Start, true, true, false, new [] { "012 456 89 ", "end" })]
-
- // no clip
- [InlineData ("012 456 89 end", 14, 0, Alignment.Start, true, true, false, new [] { "012 456 89 end" })]
- [InlineData ("012 456 89 end", 15, 1, Alignment.Start, true, true, false, new [] { "012 456 89 end" })]
- public void Reformat_Wrap_Spaces_No_NewLines (
- string text,
- int maxWidth,
- int widthOffset,
- Alignment alignment,
- bool wrap,
- bool preserveTrailingSpaces,
- bool stringEmpty,
- IEnumerable resultLines,
- string noSpaceText = ""
- )
- {
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
- Assert.NotEmpty (list);
- Assert.True (list.Count == resultLines.Count ());
-
- if (stringEmpty)
- {
- Assert.Equal (string.Empty, list [0]);
- }
- else
- {
- Assert.NotEqual (string.Empty, list [0]);
- }
-
- Assert.Equal (resultLines, list);
-
- if (maxWidth > 0)
- {
- // remove whitespace chars
- if (maxWidth < 5)
- {
- expectedClippedWidth = text.GetRuneCount () - text.Sum (r => r == ' ' ? 1 : 0);
- }
- else
- {
- expectedClippedWidth = Math.Min (
- text.GetRuneCount (),
- maxWidth - text.Sum (r => r == ' ' ? 1 : 0)
- );
- }
-
- list = TextFormatter.Format (text, maxWidth, Alignment.Start, wrap);
-
- if (maxWidth == 1)
- {
- Assert.Equal (expectedClippedWidth, list.Count);
- Assert.Equal (noSpaceText, string.Concat (list.ToArray ()));
- }
-
- if (maxWidth > 1 && maxWidth < 10)
- {
- Assert.Equal (
- StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]),
- list [0]
- );
- }
- }
- }
-
- [Theory]
- [InlineData (null)]
- [InlineData ("")]
- [InlineData ("a")]
- public void RemoveHotKeySpecifier_InValid_ReturnsOriginal (string text)
- {
- var hotKeySpecifier = (Rune)'_';
-
- if (text == null)
- {
- Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier));
- Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier));
- Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier));
- }
- else
- {
- Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier));
- Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier));
- Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier));
- }
- }
-
- [Theory]
- [InlineData ("all lower case", 0)]
- [InlineData ("K Before", 0)]
- [InlineData ("aK Second", 1)]
- [InlineData ("Last K", 5)]
- [InlineData ("fter K", 7)]
- [InlineData ("Multiple K and R", 9)]
- [InlineData ("Non-english: Кдать", 13)]
- public void RemoveHotKeySpecifier_Valid_Legacy_ReturnsOriginal (string text, int hotPos)
- {
- var hotKeySpecifier = (Rune)'_';
-
- Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier));
- }
-
- [Theory]
- [InlineData ("_K Before", 0, "K Before")]
- [InlineData ("a_K Second", 1, "aK Second")]
- [InlineData ("Last _K", 5, "Last K")]
- [InlineData ("After K_", 7, "After K")]
- [InlineData ("Multiple _K and _R", 9, "Multiple K and _R")]
- [InlineData ("Non-english: _Кдать", 13, "Non-english: Кдать")]
- public void RemoveHotKeySpecifier_Valid_ReturnsStripped (string text, int hotPos, string expectedText)
- {
- var hotKeySpecifier = (Rune)'_';
-
- Assert.Equal (expectedText, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier));
- }
-
- [Theory]
- [InlineData ("test", 0, 't', "test")]
- [InlineData ("test", 1, 'e', "test")]
- [InlineData ("Ok", 0, 'O', "Ok")]
- [InlineData ("[◦ Ok ◦]", 3, 'O', "[◦ Ok ◦]")]
- [InlineData ("^k", 0, '^', "^k")]
- public void ReplaceHotKeyWithTag (string text, int hotPos, uint tag, string expected)
- {
- var tf = new TextFormatter ();
- List runes = text.ToRuneList ();
- Rune rune;
-
- if (Rune.TryGetRuneAt (text, hotPos, out rune))
- {
- Assert.Equal (rune, (Rune)tag);
- }
-
- string result = TextFormatter.ReplaceHotKeyWithTag (text, hotPos);
- Assert.Equal (result, expected);
- Assert.Equal ((Rune)tag, result.ToRunes () [hotPos]);
- Assert.Equal (text.GetRuneCount (), runes.Count);
- Assert.Equal (text, StringExtensions.ToString (runes));
- }
-
- [Theory]
- [MemberData (nameof (SplitEnvironmentNewLine))]
- public void SplitNewLine_Ending__With_Or_Without_NewLine_Probably_CRLF (
- string text,
- IEnumerable expected
- )
- {
- List splited = TextFormatter.SplitNewLine (text);
- Assert.Equal (expected, splited);
- }
-
- [Theory]
- [InlineData (
- "First Line 界\nSecond Line 界\nThird Line 界\n",
- new [] { "First Line 界", "Second Line 界", "Third Line 界", "" }
- )]
- public void SplitNewLine_Ending_With_NewLine_Only_LF (string text, IEnumerable expected)
- {
- List splited = TextFormatter.SplitNewLine (text);
- Assert.Equal (expected, splited);
- }
-
- [Theory]
- [InlineData (
- "First Line 界\nSecond Line 界\nThird Line 界",
- new [] { "First Line 界", "Second Line 界", "Third Line 界" }
- )]
- public void SplitNewLine_Ending_Without_NewLine_Only_LF (string text, IEnumerable expected)
- {
- List splited = TextFormatter.SplitNewLine (text);
- Assert.Equal (expected, splited);
- }
-
- [Theory]
- [InlineData ("New Test 你", 10, 10, 20320, 20320, 9, "你")]
- [InlineData ("New Test \U0001d539", 10, 11, 120121, 55349, 9, "𝔹")]
- public void String_Array_Is_Not_Always_Equal_ToRunes_Array (
- string text,
- int runesLength,
- int stringLength,
- int runeValue,
- int stringValue,
- int index,
- string expected
- )
- {
- Rune [] usToRunes = text.ToRunes ();
- Assert.Equal (runesLength, usToRunes.Length);
- Assert.Equal (stringLength, text.Length);
- Assert.Equal (runeValue, usToRunes [index].Value);
- Assert.Equal (stringValue, text [index]);
- Assert.Equal (expected, usToRunes [index].ToString ());
-
- if (char.IsHighSurrogate (text [index]))
- {
- // Rune array length isn't equal to string array
- Assert.Equal (expected, new (new [] { text [index], text [index + 1] }));
- }
- else
- {
- // Rune array length is equal to string array
- Assert.Equal (expected, text [index].ToString ());
- }
- }
-
- [Theory]
- [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
- [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
- [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
- [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
- public void TabWith_PreserveTrailingSpaces_False (
- int width,
- int height,
- TextDirection textDirection,
- int tabWidth,
- string expected
- )
- {
- var driver = new FakeDriver ();
- driver.Init ();
-
- var text = "This is a \tTab";
- var tf = new TextFormatter ();
- tf.Direction = textDirection;
- tf.TabWidth = tabWidth;
- tf.Text = text;
- tf.ConstrainToWidth = 20;
- tf.ConstrainToHeight = 20;
-
- Assert.True (tf.WordWrap);
- Assert.False (tf.PreserveTrailingSpaces);
-
- tf.Draw (
- new (0, 0, width, height),
- new (ColorName.White, ColorName.Black),
- new (ColorName.Blue, ColorName.Black),
- default (Rectangle),
- driver
- );
- TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
-
- driver.End ();
- }
-
- [Theory]
- [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
- [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
- [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
- [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
- public void TabWith_PreserveTrailingSpaces_True (
- int width,
- int height,
- TextDirection textDirection,
- int tabWidth,
- string expected
- )
- {
- var driver = new FakeDriver ();
- driver.Init ();
-
- var text = "This is a \tTab";
- var tf = new TextFormatter ();
-
- tf.Direction = textDirection;
- tf.TabWidth = tabWidth;
- tf.PreserveTrailingSpaces = true;
- tf.Text = text;
- tf.ConstrainToWidth = 20;
- tf.ConstrainToHeight = 20;
-
- Assert.True (tf.WordWrap);
-
- tf.Draw (
- new (0, 0, width, height),
- new (ColorName.White, ColorName.Black),
- new (ColorName.Blue, ColorName.Black),
- default (Rectangle),
- driver
- );
- TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
-
- driver.End ();
- }
-
- [Theory]
- [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
- [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
- [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
- [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
- public void TabWith_WordWrap_True (
- int width,
- int height,
- TextDirection textDirection,
- int tabWidth,
- string expected
- )
- {
- var driver = new FakeDriver ();
- driver.Init ();
-
- var text = "This is a \tTab";
- var tf = new TextFormatter ();
-
- tf.Direction = textDirection;
- tf.TabWidth = tabWidth;
- tf.WordWrap = true;
- tf.Text = text;
- tf.ConstrainToWidth = 20;
- tf.ConstrainToHeight = 20;
-
- Assert.False (tf.PreserveTrailingSpaces);
-
- tf.Draw (
- new (0, 0, width, height),
- new (ColorName.White, ColorName.Black),
- new (ColorName.Blue, ColorName.Black),
- default (Rectangle),
- driver
- );
- TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
-
- driver.End ();
- }
-
- [Theory]
- [InlineData ("123456789", 3, "123")]
- [InlineData ("Hello World", 8, "Hello Wo")]
- public void TestClipOrPad_LongWord (string text, int fillPad, string expectedText)
- {
- // word is long but we want it to fill # space only
- Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad));
- }
-
- [Theory]
- [InlineData ("fff", 6, "fff ")]
- [InlineData ("Hello World", 16, "Hello World ")]
- public void TestClipOrPad_ShortWord (string text, int fillPad, string expectedText)
- {
- // word is short but we want it to fill # so it should be padded
- Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad));
- }
-
- [Theory]
- [InlineData ("你", TextDirection.LeftRight_TopBottom, 2, 1)]
- [InlineData ("你", TextDirection.TopBottom_LeftRight, 2, 1)]
- [InlineData ("你你", TextDirection.LeftRight_TopBottom, 4, 1)]
- [InlineData ("你你", TextDirection.TopBottom_LeftRight, 2, 2)]
- public void Text_Set_SizeIsCorrect (string text, TextDirection textDirection, int expectedWidth, int expectedHeight)
- {
- var tf = new TextFormatter { Direction = textDirection, Text = text };
- tf.ConstrainToWidth = 10;
- tf.ConstrainToHeight = 10;
-
- Assert.Equal (new (expectedWidth, expectedHeight), tf.FormatAndGetSize ());
- }
-
- [Fact]
- public void WordWrap_BigWidth ()
- {
- List wrappedLines;
-
- var text = "Constantinople";
- wrappedLines = TextFormatter.WordWrapText (text, 100);
- Assert.True (wrappedLines.Count == 1);
- Assert.Equal ("Constantinople", wrappedLines [0]);
- }
-
- [Fact]
- public void WordWrap_Invalid ()
- {
- var text = string.Empty;
- var width = 0;
-
- Assert.Empty (TextFormatter.WordWrapText (null, width));
- Assert.Empty (TextFormatter.WordWrapText (text, width));
- Assert.Throws (() => TextFormatter.WordWrapText (text, -1));
- }
-
- [Theory]
- [InlineData ("A sentence has words.", 3, -18, new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." })]
- [InlineData (
- "A sentence has words.",
- 2,
- -19,
- new [] { "A", "se", "nt", "en", "ce", "ha", "s", "wo", "rd", "s." }
- )]
- [InlineData (
- "A sentence has words.",
- 1,
- -20,
- new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." }
- )]
- public void WordWrap_Narrow_Default (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("A sentence has words.", 21, 0, new [] { "A sentence has words." })]
- [InlineData ("A sentence has words.", 20, -1, new [] { "A sentence has", "words." })]
- [InlineData ("A sentence has words.", 15, -6, new [] { "A sentence has", "words." })]
- [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence has", "words." })]
- [InlineData ("A sentence has words.", 13, -8, new [] { "A sentence", "has words." })]
-
- // Unicode
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.",
- 42,
- 0,
- new [] { "A Unicode sentence (пÑивеÑ) has words." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.",
- 41,
- -1,
- new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.",
- 36,
- -6,
- new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.",
- 35,
- -7,
- new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.",
- 34,
- -8,
- new [] { "A Unicode sentence (пÑивеÑ)", "has words." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.",
- 25,
- -17,
- new [] { "A Unicode sentence", "(пÑивеÑ) has words." }
- )]
- public void WordWrap_NoNewLines_Default (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("これが最初の行です。 こんにちは世界。 これが2行目です。", 29, 0, new [] { "これが最初の行です。", "こんにちは世界。", "これが2行目です。" })]
- public void WordWrap_PreserveTrailingSpaces_False_Unicode_Wide_Runes (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉", "があり ます。" })]
- [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
- [InlineData ("文に は言葉 があり ます。", 2, -12, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
- [InlineData (
- "文に は言葉 があり ます。",
- 1,
- -13,
- new [] { " ", " ", " " }
- )] // Just Spaces; should result in a single space for each line
- public void WordWrap_PreserveTrailingSpaces_False_Wide_Runes (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData (null, 1, new string [] { })] // null input
- [InlineData ("", 1, new string [] { })] // Empty input
- [InlineData ("1 34", 1, new [] { "1", "3", "4" })] // Single Spaces
- [InlineData ("1", 1, new [] { "1" })] // Short input
- [InlineData ("12", 1, new [] { "1", "2" })]
- [InlineData ("123", 1, new [] { "1", "2", "3" })]
- [InlineData ("123456", 1, new [] { "1", "2", "3", "4", "5", "6" })] // No spaces
- [InlineData (" ", 1, new [] { " " })] // Just Spaces; should result in a single space
- [InlineData (" ", 1, new [] { " " })]
- [InlineData (" ", 1, new [] { " ", " " })]
- [InlineData (" ", 1, new [] { " ", " " })]
- [InlineData ("12 456", 1, new [] { "1", "2", "4", "5", "6" })] // Single Spaces
- [InlineData (" 2 456", 1, new [] { " ", "2", "4", "5", "6" })] // Leading spaces should be preserved.
- [InlineData (" 2 456 8", 1, new [] { " ", "2", "4", "5", "6", "8" })]
- [InlineData (
- "A sentence has words. ",
- 1,
- new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." }
- )] // Complex example
- [InlineData ("12 567", 1, new [] { "1", "2", " ", "5", "6", "7" })] // Double Spaces
- [InlineData (" 3 567", 1, new [] { " ", "3", "5", "6", "7" })] // Double Leading spaces should be preserved.
- [InlineData (" 3 678 1", 1, new [] { " ", "3", " ", "6", "7", "8", " ", "1" })]
- [InlineData ("1 456", 1, new [] { "1", " ", "4", "5", "6" })]
- [InlineData (
- "A sentence has words. ",
- 1,
- new []
- {
- "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", "w", "o", "r", "d", "s", ".", " "
- }
- )] // Double space Complex example
- public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_1 (
- string text,
- int width,
- IEnumerable resultLines
- )
- {
- List wrappedLines = TextFormatter.WordWrapText (text, width);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
- Assert.Equal (resultLines, wrappedLines);
- var breakLines = "";
-
- foreach (string line in wrappedLines)
- {
- breakLines += $"{line}{Environment.NewLine}";
- }
-
- var expected = string.Empty;
-
- foreach (string line in resultLines)
- {
- expected += $"{line}{Environment.NewLine}";
- }
-
- Assert.Equal (expected, breakLines);
- }
-
- [Theory]
- [InlineData (null, 3, new string [] { })] // null input
- [InlineData ("", 3, new string [] { })] // Empty input
- [InlineData ("1", 3, new [] { "1" })] // Short input
- [InlineData ("12", 3, new [] { "12" })]
- [InlineData ("123", 3, new [] { "123" })]
- [InlineData ("123456", 3, new [] { "123", "456" })] // No spaces
- [InlineData ("1234567", 3, new [] { "123", "456", "7" })] // No spaces
- [InlineData (" ", 3, new [] { " " })] // Just Spaces; should result in a single space
- [InlineData (" ", 3, new [] { " " })]
- [InlineData (" ", 3, new [] { " " })]
- [InlineData (" ", 3, new [] { " " })]
- [InlineData ("12 456", 3, new [] { "12", "456" })] // Single Spaces
- [InlineData (" 2 456", 3, new [] { " 2", "456" })] // Leading spaces should be preserved.
- [InlineData (" 2 456 8", 3, new [] { " 2", "456", "8" })]
- [InlineData (
- "A sentence has words. ",
- 3,
- new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." }
- )] // Complex example
- [InlineData ("12 567", 3, new [] { "12 ", "567" })] // Double Spaces
- [InlineData (" 3 567", 3, new [] { " 3", "567" })] // Double Leading spaces should be preserved.
- [InlineData (" 3 678 1", 3, new [] { " 3", " 67", "8 ", "1" })]
- [InlineData ("1 456", 3, new [] { "1 ", "456" })]
- [InlineData (
- "A sentence has words. ",
- 3,
- new [] { "A ", "sen", "ten", "ce ", " ", "has", "wor", "ds.", " " }
- )] // Double space Complex example
- public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_3 (
- string text,
- int width,
- IEnumerable resultLines
- )
- {
- List wrappedLines = TextFormatter.WordWrapText (text, width);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
- Assert.Equal (resultLines, wrappedLines);
- var breakLines = "";
-
- foreach (string line in wrappedLines)
- {
- breakLines += $"{line}{Environment.NewLine}";
- }
-
- var expected = string.Empty;
-
- foreach (string line in resultLines)
- {
- expected += $"{line}{Environment.NewLine}";
- }
-
- Assert.Equal (expected, breakLines);
- }
-
- [Theory]
- [InlineData (null, 50, new string [] { })] // null input
- [InlineData ("", 50, new string [] { })] // Empty input
- [InlineData ("1", 50, new [] { "1" })] // Short input
- [InlineData ("12", 50, new [] { "12" })]
- [InlineData ("123", 50, new [] { "123" })]
- [InlineData ("123456", 50, new [] { "123456" })] // No spaces
- [InlineData ("1234567", 50, new [] { "1234567" })] // No spaces
- [InlineData (" ", 50, new [] { " " })] // Just Spaces; should result in a single space
- [InlineData (" ", 50, new [] { " " })]
- [InlineData (" ", 50, new [] { " " })]
- [InlineData ("12 456", 50, new [] { "12 456" })] // Single Spaces
- [InlineData (" 2 456", 50, new [] { " 2 456" })] // Leading spaces should be preserved.
- [InlineData (" 2 456 8", 50, new [] { " 2 456 8" })]
- [InlineData ("A sentence has words. ", 50, new [] { "A sentence has words. " })] // Complex example
- [InlineData ("12 567", 50, new [] { "12 567" })] // Double Spaces
- [InlineData (" 3 567", 50, new [] { " 3 567" })] // Double Leading spaces should be preserved.
- [InlineData (" 3 678 1", 50, new [] { " 3 678 1" })]
- [InlineData ("1 456", 50, new [] { "1 456" })]
- [InlineData (
- "A sentence has words. ",
- 50,
- new [] { "A sentence has words. " }
- )] // Double space Complex example
- public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_50 (
- string text,
- int width,
- IEnumerable resultLines
- )
- {
- List wrappedLines = TextFormatter.WordWrapText (text, width);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
- Assert.Equal (resultLines, wrappedLines);
- var breakLines = "";
-
- foreach (string line in wrappedLines)
- {
- breakLines += $"{line}{Environment.NewLine}";
- }
-
- var expected = string.Empty;
-
- foreach (string line in resultLines)
- {
- expected += $"{line}{Environment.NewLine}";
- }
-
- Assert.Equal (expected, breakLines);
- }
-
- [Theory]
- [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence ", "has words." })]
- [InlineData ("A sentence has words.", 8, -13, new [] { "A ", "sentence", " has ", "words." })]
- [InlineData ("A sentence has words.", 6, -15, new [] { "A ", "senten", "ce ", "has ", "words." })]
- [InlineData ("A sentence has words.", 3, -18, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds." })]
- [InlineData (
- "A sentence has words.",
- 2,
- -19,
- new [] { "A ", "se", "nt", "en", "ce", " ", "ha", "s ", "wo", "rd", "s." }
- )]
- [InlineData (
- "A sentence has words.",
- 1,
- -20,
- new []
- {
- "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", " ", "w", "o", "r", "d", "s", "."
- }
- )]
- public void WordWrap_PreserveTrailingSpaces_True (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉 ", "があり ます。" })]
- [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に ", "は", "言", "葉 ", "が", "あ", "り ", "ま", "す", "。" })]
- [InlineData (
- "文に は言葉 があり ます。",
- 2,
- -12,
- new [] { "文", "に", " ", "は", "言", "葉", " ", "が", "あ", "り", " ", "ま", "す", "。" }
- )]
- [InlineData ("文に は言葉 があり ます。", 1, -13, new string [] { })]
- public void WordWrap_PreserveTrailingSpaces_True_Wide_Runes (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("A sentence has words. ", 3, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds.", " " })]
- [InlineData (
- "A sentence has words. ",
- 3,
- new [] { "A ", " ", "sen", "ten", "ce ", " ", " ", " ", "has", " ", "wor", "ds.", " " }
- )]
- public void WordWrap_PreserveTrailingSpaces_True_With_Simple_Runes_Width_3 (
- string text,
- int width,
- IEnumerable resultLines
- )
- {
- List wrappedLines = TextFormatter.WordWrapText (text, width, true);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
- Assert.Equal (resultLines, wrappedLines);
- var breakLines = "";
-
- foreach (string line in wrappedLines)
- {
- breakLines += $"{line}{Environment.NewLine}";
- }
-
- var expected = string.Empty;
-
- foreach (string line in resultLines)
- {
- expected += $"{line}{Environment.NewLine}";
- }
-
- Assert.Equal (expected, breakLines);
-
- // Double space Complex example - this is how VS 2022 does it
- //text = "A sentence has words. ";
- //breakLines = "";
- //wrappedLines = TextFormatter.WordWrapText (text, width, preserveTrailingSpaces: true);
- //foreach (var line in wrappedLines) {
- // breakLines += $"{line}{Environment.NewLine}";
- //}
- //expected = "A " + Environment.NewLine +
- // " se" + Environment.NewLine +
- // " nt" + Environment.NewLine +
- // " en" + Environment.NewLine +
- // " ce" + Environment.NewLine +
- // " " + Environment.NewLine +
- // " " + Environment.NewLine +
- // " " + Environment.NewLine +
- // " ha" + Environment.NewLine +
- // " s " + Environment.NewLine +
- // " wo" + Environment.NewLine +
- // " rd" + Environment.NewLine +
- // " s." + Environment.NewLine;
- //Assert.Equal (expected, breakLines);
- }
-
- [Theory]
- [InlineData ("A sentence\t\t\t has words.", 14, -10, new [] { "A sentence\t", "\t\t has ", "words." })]
- [InlineData (
- "A sentence\t\t\t has words.",
- 8,
- -16,
- new [] { "A ", "sentence", "\t\t", "\t ", "has ", "words." }
- )]
- [InlineData (
- "A sentence\t\t\t has words.",
- 3,
- -21,
- new [] { "A ", "sen", "ten", "ce", "\t", "\t", "\t", " ", "has", " ", "wor", "ds." }
- )]
- [InlineData (
- "A sentence\t\t\t has words.",
- 2,
- -22,
- new [] { "A ", "se", "nt", "en", "ce", "\t", "\t", "\t", " ", "ha", "s ", "wo", "rd", "s." }
- )]
- [InlineData (
- "A sentence\t\t\t has words.",
- 1,
- -23,
- new []
- {
- "A",
- " ",
- "s",
- "e",
- "n",
- "t",
- "e",
- "n",
- "c",
- "e",
- "\t",
- "\t",
- "\t",
- " ",
- "h",
- "a",
- "s",
- " ",
- "w",
- "o",
- "r",
- "d",
- "s",
- "."
- }
- )]
- public void WordWrap_PreserveTrailingSpaces_True_With_Tab (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines,
- int tabWidth = 4
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true, tabWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("Constantinople", 14, 0, new [] { "Constantinople" })]
- [InlineData ("Constantinople", 12, -2, new [] { "Constantinop", "le" })]
- [InlineData ("Constantinople", 9, -5, new [] { "Constanti", "nople" })]
- [InlineData ("Constantinople", 7, -7, new [] { "Constan", "tinople" })]
- [InlineData ("Constantinople", 5, -9, new [] { "Const", "antin", "ople" })]
- [InlineData ("Constantinople", 4, -10, new [] { "Cons", "tant", "inop", "le" })]
- [InlineData (
- "Constantinople",
- 1,
- -13,
- new [] { "C", "o", "n", "s", "t", "a", "n", "t", "i", "n", "o", "p", "l", "e" }
- )]
- public void WordWrap_SingleWordLine (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 20, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
- [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 19, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
- [InlineData (
- "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence.",
- 19,
- 0,
- new [] { "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence." }
- )]
- public void WordWrap_Unicode_2LinesWithNonBreakingSpace (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 19, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
- [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 18, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence", "." })]
- [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 17, -2, new [] { "This\u00A0is\u00A0a\u00A0sentenc", "e." })]
- [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 14, -5, new [] { "This\u00A0is\u00A0a\u00A0sent", "ence." })]
- [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 10, -9, new [] { "This\u00A0is\u00A0a\u00A0", "sentence." })]
- [InlineData (
- "This\u00A0is\u00A0a\u00A0sentence.",
- 7,
- -12,
- new [] { "This\u00A0is", "\u00A0a\u00A0sent", "ence." }
- )]
- [InlineData (
- "This\u00A0is\u00A0a\u00A0sentence.",
- 5,
- -14,
- new [] { "This\u00A0", "is\u00A0a\u00A0", "sente", "nce." }
- )]
- [InlineData (
- "This\u00A0is\u00A0a\u00A0sentence.",
- 1,
- -18,
- new []
- {
- "T", "h", "i", "s", "\u00A0", "i", "s", "\u00A0", "a", "\u00A0", "s", "e", "n", "t", "e", "n", "c", "e", "."
- }
- )]
- public void WordWrap_Unicode_LineWithNonBreakingSpace (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [Theory]
- [InlineData (
- "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
- 51,
- 0,
- new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
- )]
- [InlineData (
- "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
- 50,
- -1,
- new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
- )]
- [InlineData (
- "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
- 46,
- -5,
- new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" }
- )]
- [InlineData (
- "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
- 26,
- -25,
- new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
- )]
- [InlineData (
- "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
- 17,
- -34,
- new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑ", "ฒณดตถทธนบปผฝพฟภมย", "รฤลฦวศษสหฬอฮฯะัาำ" }
- )]
- [InlineData (
- "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
- 13,
- -38,
- new [] { "กขฃคฅฆงจฉชซฌญ", "ฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦว", "ศษสหฬอฮฯะัาำ" }
- )]
- [InlineData (
- "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
- 1,
- -50,
- new []
- {
- "ก",
- "ข",
- "ฃ",
- "ค",
- "ฅ",
- "ฆ",
- "ง",
- "จ",
- "ฉ",
- "ช",
- "ซ",
- "ฌ",
- "ญ",
- "ฎ",
- "ฏ",
- "ฐ",
- "ฑ",
- "ฒ",
- "ณ",
- "ด",
- "ต",
- "ถ",
- "ท",
- "ธ",
- "น",
- "บ",
- "ป",
- "ผ",
- "ฝ",
- "พ",
- "ฟ",
- "ภ",
- "ม",
- "ย",
- "ร",
- "ฤ",
- "ล",
- "ฦ",
- "ว",
- "ศ",
- "ษ",
- "ส",
- "ห",
- "ฬ",
- "อ",
- "ฮ",
- "ฯ",
- "ะั",
- "า",
- "ำ"
- }
- )]
- public void WordWrap_Unicode_SingleWordLine (
- string text,
- int maxWidth,
- int widthOffset,
- IEnumerable resultLines
- )
- {
- List wrappedLines;
-
- IEnumerable zeroWidth = text.EnumerateRunes ().Where (r => r.GetColumns () == 0);
- Assert.Single (zeroWidth);
- Assert.Equal ('ั', zeroWidth.ElementAt (0).Value);
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth
- >= (wrappedLines.Count > 0
- ? wrappedLines.Max (
- l => l.GetRuneCount ()
- + zeroWidth.Count ()
- - 1
- + widthOffset
- )
- : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- /// WordWrap strips CRLF
- [Theory]
- [InlineData (
- "A sentence has words.\nA paragraph has lines.",
- 44,
- 0,
- new [] { "A sentence has words.A paragraph has lines." }
- )]
- [InlineData (
- "A sentence has words.\nA paragraph has lines.",
- 43,
- -1,
- new [] { "A sentence has words.A paragraph has lines." }
- )]
- [InlineData (
- "A sentence has words.\nA paragraph has lines.",
- 38,
- -6,
- new [] { "A sentence has words.A paragraph has", "lines." }
- )]
- [InlineData (
- "A sentence has words.\nA paragraph has lines.",
- 34,
- -10,
- new [] { "A sentence has words.A paragraph", "has lines." }
- )]
- [InlineData (
- "A sentence has words.\nA paragraph has lines.",
- 27,
- -17,
- new [] { "A sentence has words.A", "paragraph has lines." }
- )]
-
- // Unicode
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
- 69,
- 0,
- new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
- 68,
- -1,
- new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
- 63,
- -6,
- new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has", "Линии." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
- 59,
- -10,
- new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт", "has Линии." }
- )]
- [InlineData (
- "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
- 52,
- -17,
- new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode", "Пункт has Линии." }
- )]
- public void WordWrap_WithNewLines (string text, int maxWidth, int widthOffset, IEnumerable resultLines)
- {
- List wrappedLines;
-
- Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
- int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
- wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
- Assert.Equal (wrappedLines.Count, resultLines.Count ());
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
- );
-
- Assert.True (
- expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
- );
- Assert.Equal (resultLines, wrappedLines);
- }
-
- [SetupFakeDriver]
- [Theory]
- [InlineData ("A", 0, "")]
- [InlineData ("A", 1, "A")]
- [InlineData ("A", 2, "A")]
- [InlineData ("AB", 1, "A")]
- [InlineData ("AB", 2, "AB")]
- [InlineData ("ABC", 3, "ABC")]
- [InlineData ("ABC", 4, "ABC")]
- [InlineData ("ABC", 6, "ABC")]
- public void Draw_Horizontal_Left (string text, int width, string expectedText)
-
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Alignment = Alignment.Start
- };
-
- tf.ConstrainToWidth = width;
- tf.ConstrainToHeight = 1;
- tf.Draw (new (0, 0, width, 1), Attribute.Default, Attribute.Default);
-
- TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- }
-
- [SetupFakeDriver]
- [Theory]
- [InlineData ("A", 0, "")]
- [InlineData ("A", 1, "A")]
- [InlineData ("A", 2, " A")]
- [InlineData ("AB", 1, "B")]
- [InlineData ("AB", 2, "AB")]
- [InlineData ("ABC", 3, "ABC")]
- [InlineData ("ABC", 4, " ABC")]
- [InlineData ("ABC", 6, " ABC")]
- public void Draw_Horizontal_Right (string text, int width, string expectedText)
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Alignment = Alignment.End
- };
-
- tf.ConstrainToWidth = width;
- tf.ConstrainToHeight = 1;
-
- tf.Draw (new (Point.Empty, new (width, 1)), Attribute.Default, Attribute.Default);
- TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- }
+ public static IEnumerable CMGlyphs =>
+ new List { new object [] { $"{CM.Glyphs.LeftBracket} Say Hello 你 {CM.Glyphs.RightBracket}", 16, 15 } };
[SetupFakeDriver]
[Theory]
@@ -3075,340 +295,52 @@ ssb
[SetupFakeDriver]
[Theory]
- [InlineData ("A", 5, 5, "A")]
- [InlineData (
- "AB12",
- 5,
- 5,
- @"
-A
-B
-1
-2")]
- [InlineData (
- "AB\n12",
- 5,
- 5,
- @"
-A1
-B2")]
- [InlineData ("", 5, 1, "")]
- [InlineData (
- "Hello Worlds",
- 1,
- 12,
- @"
-H
-e
-l
-l
-o
-
-W
-o
-r
-l
-d
-s")]
- [InlineData (
- "Hello Worlds",
- 1,
- 12,
- @"
-H
-e
-l
-l
-o
-
-W
-o
-r
-l
-d
-s")]
- [InlineData ("Hello Worlds", 12, 1, @"HelloWorlds")]
- public void Draw_Vertical_TopBottom_LeftRight (string text, int width, int height, string expectedText)
+ [InlineData ("A", 0, "")]
+ [InlineData ("A", 1, "A")]
+ [InlineData ("A", 2, "A")]
+ [InlineData ("AB", 1, "A")]
+ [InlineData ("AB", 2, "AB")]
+ [InlineData ("ABC", 3, "ABC")]
+ [InlineData ("ABC", 4, "ABC")]
+ [InlineData ("ABC", 6, "ABC")]
+ public void Draw_Horizontal_Left (string text, int width, string expectedText)
+
{
TextFormatter tf = new ()
{
Text = text,
- Direction = TextDirection.TopBottom_LeftRight
+ Alignment = Alignment.Start
};
tf.ConstrainToWidth = width;
- tf.ConstrainToHeight = height;
- tf.Draw (new (0, 0, 20, 20), Attribute.Default, Attribute.Default);
+ tf.ConstrainToHeight = 1;
+ tf.Draw (new (0, 0, width, 1), Attribute.Default, Attribute.Default);
TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
}
[SetupFakeDriver]
[Theory]
- [InlineData ("Hello World", 15, 1, "Hello World")]
- [InlineData (
- "Well Done\nNice Work",
- 15,
- 2,
- @"
-Well Done
-Nice Work")]
- [InlineData ("你好 世界", 15, 1, "你好 世界")]
- [InlineData (
- "做 得好\n幹 得好",
- 15,
- 2,
- @"
-做 得好
-幹 得好")]
- public void Justify_Horizontal (string text, int width, int height, string expectedText)
+ [InlineData ("A", 0, "")]
+ [InlineData ("A", 1, "A")]
+ [InlineData ("A", 2, " A")]
+ [InlineData ("AB", 1, "B")]
+ [InlineData ("AB", 2, "AB")]
+ [InlineData ("ABC", 3, "ABC")]
+ [InlineData ("ABC", 4, " ABC")]
+ [InlineData ("ABC", 6, " ABC")]
+ public void Draw_Horizontal_Right (string text, int width, string expectedText)
{
TextFormatter tf = new ()
{
Text = text,
- Alignment = Alignment.Fill,
- ConstrainToSize = new Size (width, height),
- MultiLine = true
- };
-
- tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
-
- TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- }
-
- [SetupFakeDriver]
- [Theory]
- [InlineData ("Hello World", 1, 15, "H\ne\nl\nl\no\n \n \n \n \n \nW\no\nr\nl\nd")]
- [InlineData (
- "Well Done\nNice Work",
- 2,
- 15,
- @"
-WN
-ei
-lc
-le
-
-
-
-
-
-
-
-DW
-oo
-nr
-ek")]
- [InlineData ("你好 世界", 2, 15, "你\n好\n \n \n \n \n \n \n \n \n \n \n \n世\n界")]
- [InlineData (
- "做 得好\n幹 得好",
- 4,
- 15,
- @"
-做幹
-
-
-
-
-
-
-
-
-
-
-
-
-得得
-好好")]
- public void Justify_Vertical (string text, int width, int height, string expectedText)
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Direction = TextDirection.TopBottom_LeftRight,
- VerticalAlignment = Alignment.Fill,
- ConstrainToSize = new Size (width, height),
- MultiLine = true
- };
-
- tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
-
- TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- }
-
- [SetupFakeDriver]
- [Theory]
- [InlineData ("A", 0, 1, "", 0)]
- [InlineData ("A", 1, 1, "A", 0)]
- [InlineData ("A", 2, 2, " A", 1)]
- [InlineData ("AB", 1, 1, "B", 0)]
- [InlineData ("AB", 2, 2, " A\n B", 0)]
- [InlineData ("ABC", 3, 2, " B\n C", 0)]
- [InlineData ("ABC", 4, 2, " B\n C", 0)]
- [InlineData ("ABC", 6, 2, " B\n C", 0)]
- [InlineData ("こんにちは", 0, 1, "", 0)]
- [InlineData ("こんにちは", 1, 0, "", 0)]
- [InlineData ("こんにちは", 1, 1, "", 0)]
- [InlineData ("こんにちは", 2, 1, "は", 0)]
- [InlineData ("こんにちは", 2, 2, "ち\nは", 0)]
- [InlineData ("こんにちは", 2, 3, "に\nち\nは", 0)]
- [InlineData ("こんにちは", 2, 4, "ん\nに\nち\nは", 0)]
- [InlineData ("こんにちは", 2, 5, "こ\nん\nに\nち\nは", 0)]
- [InlineData ("こんにちは", 2, 6, "こ\nん\nに\nち\nは", 1)]
- [InlineData ("ABCD\nこんにちは", 4, 7, " こ\n Aん\n Bに\n Cち\n Dは", 2)]
- [InlineData ("こんにちは\nABCD", 3, 7, "こ \nんA\nにB\nちC\nはD", 2)]
- public void Draw_Vertical_Bottom_Horizontal_Right (string text, int width, int height, string expectedText, int expectedY)
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Alignment = Alignment.End,
- Direction = TextDirection.TopBottom_LeftRight,
- VerticalAlignment = Alignment.End
+ Alignment = Alignment.End
};
tf.ConstrainToWidth = width;
- tf.ConstrainToHeight = height;
-
- tf.Draw (new (Point.Empty, new (width, height)), Attribute.Default, Attribute.Default);
- Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- Assert.Equal (expectedY, rect.Y);
- }
-
- [SetupFakeDriver]
- [Theory]
- [InlineData ("A", 5, "A")]
- [InlineData (
- "AB12",
- 5,
- @"
-A
-B
-1
-2")]
- [InlineData (
- "AB\n12",
- 5,
- @"
-A1
-B2")]
- [InlineData ("", 1, "")]
- [InlineData (
- "AB1 2",
- 2,
- @"
-A12
-B ")]
- [InlineData (
- "こんにちは",
- 1,
- @"
-こん")]
- [InlineData (
- "こんにちは",
- 2,
- @"
-こに
-んち")]
- [InlineData (
- "こんにちは",
- 5,
- @"
-こ
-ん
-に
-ち
-は")]
- public void Draw_Vertical_TopBottom_LeftRight_Top (string text, int height, string expectedText)
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Direction = TextDirection.TopBottom_LeftRight
- };
-
- tf.ConstrainToWidth = 5;
- tf.ConstrainToHeight = height;
- tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default);
-
- TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- }
-
- [SetupFakeDriver]
- [Theory]
-
- // The expectedY param is to probe that the expectedText param start at that Y coordinate
- [InlineData ("A", 0, "", 0)]
- [InlineData ("A", 1, "A", 0)]
- [InlineData ("A", 2, "A", 0)]
- [InlineData ("A", 3, "A", 1)]
- [InlineData ("AB", 1, "A", 0)]
- [InlineData ("AB", 2, "A\nB", 0)]
- [InlineData ("ABC", 2, "A\nB", 0)]
- [InlineData ("ABC", 3, "A\nB\nC", 0)]
- [InlineData ("ABC", 4, "A\nB\nC", 0)]
- [InlineData ("ABC", 5, "A\nB\nC", 1)]
- [InlineData ("ABC", 6, "A\nB\nC", 1)]
- [InlineData ("ABC", 9, "A\nB\nC", 3)]
- [InlineData ("ABCD", 2, "B\nC", 0)]
- [InlineData ("こんにちは", 0, "", 0)]
- [InlineData ("こんにちは", 1, "に", 0)]
- [InlineData ("こんにちは", 2, "ん\nに", 0)]
- [InlineData ("こんにちは", 3, "ん\nに\nち", 0)]
- [InlineData ("こんにちは", 4, "こ\nん\nに\nち", 0)]
- [InlineData ("こんにちは", 5, "こ\nん\nに\nち\nは", 0)]
- [InlineData ("こんにちは", 6, "こ\nん\nに\nち\nは", 0)]
- [InlineData ("ABCD\nこんにちは", 7, "Aこ\nBん\nCに\nDち\n は", 1)]
- [InlineData ("こんにちは\nABCD", 7, "こA\nんB\nにC\nちD\nは ", 1)]
- public void Draw_Vertical_TopBottom_LeftRight_Middle (string text, int height, string expectedText, int expectedY)
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Direction = TextDirection.TopBottom_LeftRight,
- VerticalAlignment = Alignment.Center
- };
-
- int width = text.ToRunes ().Max (r => r.GetColumns ());
-
- if (text.Contains ("\n"))
- {
- width++;
- }
-
- tf.ConstrainToWidth = width;
- tf.ConstrainToHeight = height;
- tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default);
-
- Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- Assert.Equal (expectedY, rect.Y);
- }
-
- [SetupFakeDriver]
- [Theory]
- [InlineData ("A", 1, 0, "")]
- [InlineData ("A", 0, 1, "")]
- [InlineData ("AB1 2", 2, 1, "2")]
- [InlineData ("AB12", 5, 1, "21BA")]
- [InlineData ("AB\n12", 5, 2, "BA\n21")]
- [InlineData ("ABC 123 456", 7, 2, "654 321\nCBA ")]
- [InlineData ("こんにちは", 1, 1, "")]
- [InlineData ("こんにちは", 2, 1, "は")]
- [InlineData ("こんにちは", 5, 1, "はち")]
- [InlineData ("こんにちは", 10, 1, "はちにんこ")]
- [InlineData ("こんにちは\nAB\n12", 10, 3, "はちにんこ\nBA \n21 ")]
- public void Draw_Horizontal_RightLeft_TopBottom (string text, int width, int height, string expectedText)
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Direction = TextDirection.RightLeft_TopBottom
- };
-
- tf.ConstrainToWidth = width;
- tf.ConstrainToHeight = height;
- tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
+ tf.ConstrainToHeight = 1;
+ tf.Draw (new (Point.Empty, new (width, 1)), Attribute.Default, Attribute.Default);
TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
}
@@ -3444,21 +376,21 @@ B ")]
[Theory]
[InlineData ("A", 1, 0, "")]
[InlineData ("A", 0, 1, "")]
- [InlineData ("AB1 2", 1, 2, "2")]
- [InlineData ("AB12", 1, 5, "2\n1\nB\nA")]
- [InlineData ("AB\n12", 2, 5, "B2\nA1")]
- [InlineData ("ABC 123 456", 2, 7, "6C\n5B\n4A\n \n3 \n2 \n1 ")]
+ [InlineData ("AB1 2", 2, 1, "2")]
+ [InlineData ("AB12", 5, 1, "21BA")]
+ [InlineData ("AB\n12", 5, 2, "BA\n21")]
+ [InlineData ("ABC 123 456", 7, 2, "654 321\nCBA ")]
[InlineData ("こんにちは", 1, 1, "")]
[InlineData ("こんにちは", 2, 1, "は")]
- [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")]
- [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")]
- [InlineData ("こんにちは\nAB\n12", 4, 10, "はB2\nちA1\nに \nん \nこ ")]
- public void Draw_Vertical_BottomTop_LeftRight (string text, int width, int height, string expectedText)
+ [InlineData ("こんにちは", 5, 1, "はち")]
+ [InlineData ("こんにちは", 10, 1, "はちにんこ")]
+ [InlineData ("こんにちは\nAB\n12", 10, 3, "はちにんこ\nBA \n21 ")]
+ public void Draw_Horizontal_RightLeft_TopBottom (string text, int width, int height, string expectedText)
{
TextFormatter tf = new ()
{
Text = text,
- Direction = TextDirection.BottomTop_LeftRight
+ Direction = TextDirection.RightLeft_TopBottom
};
tf.ConstrainToWidth = width;
@@ -3468,70 +400,6 @@ B ")]
TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
}
- [SetupFakeDriver]
- [Theory]
- [InlineData ("A", 1, 0, "")]
- [InlineData ("A", 0, 1, "")]
- [InlineData ("AB1 2", 1, 2, "2")]
- [InlineData ("AB12", 1, 5, "2\n1\nB\nA")]
- [InlineData ("AB\n12", 2, 5, "2B\n1A")]
- [InlineData ("ABC 123 456", 2, 7, "C6\nB5\nA4\n \n 3\n 2\n 1")]
- [InlineData ("こんにちは", 1, 1, "")]
- [InlineData ("こんにちは", 2, 1, "は")]
- [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")]
- [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")]
- [InlineData ("こんにちは\nAB\n12", 4, 10, "2Bは\n1Aち\n に\n ん\n こ")]
- public void Draw_Vertical_BottomTop_RightLeft (string text, int width, int height, string expectedText)
- {
- TextFormatter tf = new ()
- {
- Text = text,
- Direction = TextDirection.BottomTop_RightLeft
- };
-
- tf.ConstrainToWidth = width;
- tf.ConstrainToHeight = height;
- tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
-
- TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
- }
-
- // Draw tests - Note that these depend on View
-
- [Fact]
- [TestRespondersDisposed]
- public void Draw_Vertical_Throws_IndexOutOfRangeException_With_Negative_Bounds ()
- {
- Application.Init (new FakeDriver ());
-
- Toplevel top = new ();
-
- var view = new View { Y = -2, Height = 10, TextDirection = TextDirection.TopBottom_LeftRight, Text = "view" };
- top.Add (view);
-
- Application.Iteration += (s, a) =>
- {
- Assert.Equal (-2, view.Y);
-
- Application.RequestStop ();
- };
-
- try
- {
- Application.Run (top);
- }
- catch (IndexOutOfRangeException ex)
- {
- // After the fix this exception will not be caught.
- Assert.IsType (ex);
- }
-
- top.Dispose ();
-
- // Shutdown must be called to safely clean up Application if Init has been called
- Application.Shutdown ();
- }
-
[SetupFakeDriver]
[Theory]
@@ -6950,6 +3818,1334 @@ B ")]
TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
}
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData ("A", 0, 1, "", 0)]
+ [InlineData ("A", 1, 1, "A", 0)]
+ [InlineData ("A", 2, 2, " A", 1)]
+ [InlineData ("AB", 1, 1, "B", 0)]
+ [InlineData ("AB", 2, 2, " A\n B", 0)]
+ [InlineData ("ABC", 3, 2, " B\n C", 0)]
+ [InlineData ("ABC", 4, 2, " B\n C", 0)]
+ [InlineData ("ABC", 6, 2, " B\n C", 0)]
+ [InlineData ("こんにちは", 0, 1, "", 0)]
+ [InlineData ("こんにちは", 1, 0, "", 0)]
+ [InlineData ("こんにちは", 1, 1, "", 0)]
+ [InlineData ("こんにちは", 2, 1, "は", 0)]
+ [InlineData ("こんにちは", 2, 2, "ち\nは", 0)]
+ [InlineData ("こんにちは", 2, 3, "に\nち\nは", 0)]
+ [InlineData ("こんにちは", 2, 4, "ん\nに\nち\nは", 0)]
+ [InlineData ("こんにちは", 2, 5, "こ\nん\nに\nち\nは", 0)]
+ [InlineData ("こんにちは", 2, 6, "こ\nん\nに\nち\nは", 1)]
+ [InlineData ("ABCD\nこんにちは", 4, 7, " こ\n Aん\n Bに\n Cち\n Dは", 2)]
+ [InlineData ("こんにちは\nABCD", 3, 7, "こ \nんA\nにB\nちC\nはD", 2)]
+ public void Draw_Vertical_Bottom_Horizontal_Right (string text, int width, int height, string expectedText, int expectedY)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Alignment = Alignment.End,
+ Direction = TextDirection.TopBottom_LeftRight,
+ VerticalAlignment = Alignment.End
+ };
+
+ tf.ConstrainToWidth = width;
+ tf.ConstrainToHeight = height;
+
+ tf.Draw (new (Point.Empty, new (width, height)), Attribute.Default, Attribute.Default);
+ Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ Assert.Equal (expectedY, rect.Y);
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData ("A", 1, 0, "")]
+ [InlineData ("A", 0, 1, "")]
+ [InlineData ("AB1 2", 1, 2, "2")]
+ [InlineData ("AB12", 1, 5, "2\n1\nB\nA")]
+ [InlineData ("AB\n12", 2, 5, "B2\nA1")]
+ [InlineData ("ABC 123 456", 2, 7, "6C\n5B\n4A\n \n3 \n2 \n1 ")]
+ [InlineData ("こんにちは", 1, 1, "")]
+ [InlineData ("こんにちは", 2, 1, "は")]
+ [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")]
+ [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")]
+ [InlineData ("こんにちは\nAB\n12", 4, 10, "はB2\nちA1\nに \nん \nこ ")]
+ public void Draw_Vertical_BottomTop_LeftRight (string text, int width, int height, string expectedText)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Direction = TextDirection.BottomTop_LeftRight
+ };
+
+ tf.ConstrainToWidth = width;
+ tf.ConstrainToHeight = height;
+ tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+ TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData ("A", 1, 0, "")]
+ [InlineData ("A", 0, 1, "")]
+ [InlineData ("AB1 2", 1, 2, "2")]
+ [InlineData ("AB12", 1, 5, "2\n1\nB\nA")]
+ [InlineData ("AB\n12", 2, 5, "2B\n1A")]
+ [InlineData ("ABC 123 456", 2, 7, "C6\nB5\nA4\n \n 3\n 2\n 1")]
+ [InlineData ("こんにちは", 1, 1, "")]
+ [InlineData ("こんにちは", 2, 1, "は")]
+ [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")]
+ [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")]
+ [InlineData ("こんにちは\nAB\n12", 4, 10, "2Bは\n1Aち\n に\n ん\n こ")]
+ public void Draw_Vertical_BottomTop_RightLeft (string text, int width, int height, string expectedText)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Direction = TextDirection.BottomTop_RightLeft
+ };
+
+ tf.ConstrainToWidth = width;
+ tf.ConstrainToHeight = height;
+ tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+ TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ }
+
+ // Draw tests - Note that these depend on View
+
+ [Fact]
+ [TestRespondersDisposed]
+ public void Draw_Vertical_Throws_IndexOutOfRangeException_With_Negative_Bounds ()
+ {
+ Application.Init (new FakeDriver ());
+
+ Toplevel top = new ();
+
+ var view = new View { Y = -2, Height = 10, TextDirection = TextDirection.TopBottom_LeftRight, Text = "view" };
+ top.Add (view);
+
+ Application.Iteration += (s, a) =>
+ {
+ Assert.Equal (-2, view.Y);
+
+ Application.RequestStop ();
+ };
+
+ try
+ {
+ Application.Run (top);
+ }
+ catch (IndexOutOfRangeException ex)
+ {
+ // After the fix this exception will not be caught.
+ Assert.IsType (ex);
+ }
+
+ top.Dispose ();
+
+ // Shutdown must be called to safely clean up Application if Init has been called
+ Application.Shutdown ();
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData ("A", 5, 5, "A")]
+ [InlineData (
+ "AB12",
+ 5,
+ 5,
+ @"
+A
+B
+1
+2")]
+ [InlineData (
+ "AB\n12",
+ 5,
+ 5,
+ @"
+A1
+B2")]
+ [InlineData ("", 5, 1, "")]
+ [InlineData (
+ "Hello Worlds",
+ 1,
+ 12,
+ @"
+H
+e
+l
+l
+o
+
+W
+o
+r
+l
+d
+s")]
+ [InlineData ("Hello Worlds", 12, 1, @"HelloWorlds")]
+ public void Draw_Vertical_TopBottom_LeftRight (string text, int width, int height, string expectedText)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Direction = TextDirection.TopBottom_LeftRight
+ };
+
+ tf.ConstrainToWidth = width;
+ tf.ConstrainToHeight = height;
+ tf.Draw (new (0, 0, 20, 20), Attribute.Default, Attribute.Default);
+
+ TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+
+ // The expectedY param is to probe that the expectedText param start at that Y coordinate
+ [InlineData ("A", 0, "", 0)]
+ [InlineData ("A", 1, "A", 0)]
+ [InlineData ("A", 2, "A", 0)]
+ [InlineData ("A", 3, "A", 1)]
+ [InlineData ("AB", 1, "A", 0)]
+ [InlineData ("AB", 2, "A\nB", 0)]
+ [InlineData ("ABC", 2, "A\nB", 0)]
+ [InlineData ("ABC", 3, "A\nB\nC", 0)]
+ [InlineData ("ABC", 4, "A\nB\nC", 0)]
+ [InlineData ("ABC", 5, "A\nB\nC", 1)]
+ [InlineData ("ABC", 6, "A\nB\nC", 1)]
+ [InlineData ("ABC", 9, "A\nB\nC", 3)]
+ [InlineData ("ABCD", 2, "B\nC", 0)]
+ [InlineData ("こんにちは", 0, "", 0)]
+ [InlineData ("こんにちは", 1, "に", 0)]
+ [InlineData ("こんにちは", 2, "ん\nに", 0)]
+ [InlineData ("こんにちは", 3, "ん\nに\nち", 0)]
+ [InlineData ("こんにちは", 4, "こ\nん\nに\nち", 0)]
+ [InlineData ("こんにちは", 5, "こ\nん\nに\nち\nは", 0)]
+ [InlineData ("こんにちは", 6, "こ\nん\nに\nち\nは", 0)]
+ [InlineData ("ABCD\nこんにちは", 7, "Aこ\nBん\nCに\nDち\n は", 1)]
+ [InlineData ("こんにちは\nABCD", 7, "こA\nんB\nにC\nちD\nは ", 1)]
+ public void Draw_Vertical_TopBottom_LeftRight_Middle (string text, int height, string expectedText, int expectedY)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Direction = TextDirection.TopBottom_LeftRight,
+ VerticalAlignment = Alignment.Center
+ };
+
+ int width = text.ToRunes ().Max (r => r.GetColumns ());
+
+ if (text.Contains ("\n"))
+ {
+ width++;
+ }
+
+ tf.ConstrainToWidth = width;
+ tf.ConstrainToHeight = height;
+ tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default);
+
+ Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ Assert.Equal (expectedY, rect.Y);
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData ("A", 5, "A")]
+ [InlineData (
+ "AB12",
+ 5,
+ @"
+A
+B
+1
+2")]
+ [InlineData (
+ "AB\n12",
+ 5,
+ @"
+A1
+B2")]
+ [InlineData ("", 1, "")]
+ [InlineData (
+ "AB1 2",
+ 2,
+ @"
+A12
+B ")]
+ [InlineData (
+ "こんにちは",
+ 1,
+ @"
+こん")]
+ [InlineData (
+ "こんにちは",
+ 2,
+ @"
+こに
+んち")]
+ [InlineData (
+ "こんにちは",
+ 5,
+ @"
+こ
+ん
+に
+ち
+は")]
+ public void Draw_Vertical_TopBottom_LeftRight_Top (string text, int height, string expectedText)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Direction = TextDirection.TopBottom_LeftRight
+ };
+
+ tf.ConstrainToWidth = 5;
+ tf.ConstrainToHeight = height;
+ tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default);
+
+ TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ }
+
+ [Theory]
+ [InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misęrables")]
+ [InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę\nr\na\nb\nl\ne\ns")]
+ [InlineData (
+ 4,
+ 4,
+ TextDirection.TopBottom_LeftRight,
+ @"
+LMre
+eias
+ssb
+ ęl "
+ )]
+ public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected)
+ {
+ var driver = new FakeDriver ();
+ driver.Init ();
+
+ var text = "Les Mise\u0328\u0301rables";
+
+ var tf = new TextFormatter ();
+ tf.Direction = textDirection;
+ tf.Text = text;
+
+ Assert.True (tf.WordWrap);
+
+ tf.ConstrainToSize = new (width, height);
+
+ tf.Draw (
+ new (0, 0, width, height),
+ new (ColorName.White, ColorName.Black),
+ new (ColorName.Blue, ColorName.Black),
+ default (Rectangle),
+ driver
+ );
+ TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
+
+ driver.End ();
+ }
+
+ [Fact]
+ [SetupFakeDriver]
+ public void FillRemaining_True_False ()
+ {
+ ((FakeDriver)Application.Driver!).SetBufferSize (22, 5);
+
+ Attribute [] attrs =
+ {
+ Attribute.Default, new (ColorName.Green, ColorName.BrightMagenta),
+ new (ColorName.Blue, ColorName.Cyan)
+ };
+ var tf = new TextFormatter { ConstrainToSize = new (14, 3), Text = "Test\nTest long\nTest long long\n", MultiLine = true };
+
+ tf.Draw (
+ new (1, 1, 19, 3),
+ attrs [1],
+ attrs [2]);
+
+ Assert.False (tf.FillRemaining);
+
+ TestHelpers.AssertDriverContentsWithFrameAre (
+ @"
+ Test
+ Test long
+ Test long long",
+ _output);
+
+ TestHelpers.AssertDriverAttributesAre (
+ @"
+000000000000000000000
+011110000000000000000
+011111111100000000000
+011111111111111000000
+000000000000000000000",
+ null,
+ attrs);
+
+ tf.FillRemaining = true;
+
+ tf.Draw (
+ new (1, 1, 19, 3),
+ attrs [1],
+ attrs [2]);
+
+ TestHelpers.AssertDriverAttributesAre (
+ @"
+000000000000000000000
+011111111111111111110
+011111111111111111110
+011111111111111111110
+000000000000000000000",
+ null,
+ attrs);
+ }
+
+ [Theory]
+ [InlineData ("_k Before", true, 0, (KeyCode)'K')] // lower case should return uppercase Hotkey
+ [InlineData ("a_k Second", true, 1, (KeyCode)'K')]
+ [InlineData ("Last _k", true, 5, (KeyCode)'K')]
+ [InlineData ("After k_", false, -1, KeyCode.Null)]
+ [InlineData ("Multiple _k and _R", true, 9, (KeyCode)'K')]
+ [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к')] // Lower case Cryllic K (к)
+ [InlineData ("_k Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results
+ [InlineData ("a_k Second", true, 1, (KeyCode)'K', true)]
+ [InlineData ("Last _k", true, 5, (KeyCode)'K', true)]
+ [InlineData ("After k_", false, -1, KeyCode.Null, true)]
+ [InlineData ("Multiple _k and _r", true, 9, (KeyCode)'K', true)]
+ [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к', true)] // Cryllic K (К)
+ public void FindHotKey_AlphaLowerCase_Succeeds (
+ string text,
+ bool expectedResult,
+ int expectedHotPos,
+ KeyCode expectedKey,
+ bool supportFirstUpperCase = false
+ )
+ {
+ var hotKeySpecifier = (Rune)'_';
+
+ bool result = TextFormatter.FindHotKey (
+ text,
+ hotKeySpecifier,
+ out int hotPos,
+ out Key hotKey,
+ supportFirstUpperCase
+ );
+
+ if (expectedResult)
+ {
+ Assert.True (result);
+ }
+ else
+ {
+ Assert.False (result);
+ }
+
+ Assert.Equal (expectedResult, result);
+ Assert.Equal (expectedHotPos, hotPos);
+ Assert.Equal (expectedKey, hotKey);
+ }
+
+ [Theory]
+ [InlineData ("_K Before", true, 0, (KeyCode)'K')]
+ [InlineData ("a_K Second", true, 1, (KeyCode)'K')]
+ [InlineData ("Last _K", true, 5, (KeyCode)'K')]
+ [InlineData ("After K_", false, -1, KeyCode.Null)]
+ [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K')]
+ [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
+ [InlineData ("_K Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results
+ [InlineData ("a_K Second", true, 1, (KeyCode)'K', true)]
+ [InlineData ("Last _K", true, 5, (KeyCode)'K', true)]
+ [InlineData ("After K_", false, -1, KeyCode.Null, true)]
+ [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K', true)]
+ [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К', true)] // Cryllic K (К)
+ public void FindHotKey_AlphaUpperCase_Succeeds (
+ string text,
+ bool expectedResult,
+ int expectedHotPos,
+ KeyCode expectedKey,
+ bool supportFirstUpperCase = false
+ )
+ {
+ var hotKeySpecifier = (Rune)'_';
+
+ bool result = TextFormatter.FindHotKey (
+ text,
+ hotKeySpecifier,
+ out int hotPos,
+ out Key hotKey,
+ supportFirstUpperCase
+ );
+
+ if (expectedResult)
+ {
+ Assert.True (result);
+ }
+ else
+ {
+ Assert.False (result);
+ }
+
+ Assert.Equal (expectedResult, result);
+ Assert.Equal (expectedHotPos, hotPos);
+ Assert.Equal (expectedKey, hotKey);
+ }
+
+ [Theory]
+ [InlineData (null)]
+ [InlineData ("")]
+ [InlineData ("no hotkey")]
+ [InlineData ("No hotkey, Upper Case")]
+ [InlineData ("Non-english: Сохранить")]
+ public void FindHotKey_Invalid_ReturnsFalse (string text)
+ {
+ var hotKeySpecifier = (Rune)'_';
+ var supportFirstUpperCase = false;
+ var hotPos = 0;
+ Key hotKey = KeyCode.Null;
+ var result = false;
+
+ result = TextFormatter.FindHotKey (
+ text,
+ hotKeySpecifier,
+ out hotPos,
+ out hotKey,
+ supportFirstUpperCase
+ );
+ Assert.False (result);
+ Assert.Equal (-1, hotPos);
+ Assert.Equal (KeyCode.Null, hotKey);
+ }
+
+ [Theory]
+ [InlineData ("\"k before")]
+ [InlineData ("ak second")]
+ [InlineData ("last k")]
+ [InlineData ("multiple k and r")]
+ [InlineData ("12345")]
+ [InlineData ("`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?")] // punctuation
+ [InlineData (" ~ s gui.cs master ↑10")] // ~IsLetterOrDigit + Unicode
+ [InlineData ("non-english: кдать")] // Lower case Cryllic K (к)
+ public void FindHotKey_Legacy_FirstUpperCase_NotFound_Returns_False (string text)
+ {
+ var supportFirstUpperCase = true;
+
+ var hotKeySpecifier = (Rune)0;
+
+ bool result = TextFormatter.FindHotKey (
+ text,
+ hotKeySpecifier,
+ out int hotPos,
+ out Key hotKey,
+ supportFirstUpperCase
+ );
+ Assert.False (result);
+ Assert.Equal (-1, hotPos);
+ Assert.Equal (KeyCode.Null, hotKey);
+ }
+
+ [Theory]
+ [InlineData ("K Before", true, 0, (KeyCode)'K')]
+ [InlineData ("aK Second", true, 1, (KeyCode)'K')]
+ [InlineData ("last K", true, 5, (KeyCode)'K')]
+ [InlineData ("multiple K and R", true, 9, (KeyCode)'K')]
+ [InlineData ("non-english: Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
+ public void FindHotKey_Legacy_FirstUpperCase_Succeeds (
+ string text,
+ bool expectedResult,
+ int expectedHotPos,
+ KeyCode expectedKey
+ )
+ {
+ var supportFirstUpperCase = true;
+
+ var hotKeySpecifier = (Rune)0;
+
+ bool result = TextFormatter.FindHotKey (
+ text,
+ hotKeySpecifier,
+ out int hotPos,
+ out Key hotKey,
+ supportFirstUpperCase
+ );
+
+ if (expectedResult)
+ {
+ Assert.True (result);
+ }
+ else
+ {
+ Assert.False (result);
+ }
+
+ Assert.Equal (expectedResult, result);
+ Assert.Equal (expectedHotPos, hotPos);
+ Assert.Equal (expectedKey, hotKey);
+ }
+
+ [Theory]
+ [InlineData ("_1 Before", true, 0, (KeyCode)'1')] // Digits
+ [InlineData ("a_1 Second", true, 1, (KeyCode)'1')]
+ [InlineData ("Last _1", true, 5, (KeyCode)'1')]
+ [InlineData ("After 1_", false, -1, KeyCode.Null)]
+ [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1')]
+ [InlineData ("_1 Before", true, 0, (KeyCode)'1', true)] // Turn on FirstUpperCase and verify same results
+ [InlineData ("a_1 Second", true, 1, (KeyCode)'1', true)]
+ [InlineData ("Last _1", true, 5, (KeyCode)'1', true)]
+ [InlineData ("After 1_", false, -1, KeyCode.Null, true)]
+ [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1', true)]
+ public void FindHotKey_Numeric_Succeeds (
+ string text,
+ bool expectedResult,
+ int expectedHotPos,
+ KeyCode expectedKey,
+ bool supportFirstUpperCase = false
+ )
+ {
+ var hotKeySpecifier = (Rune)'_';
+
+ bool result = TextFormatter.FindHotKey (
+ text,
+ hotKeySpecifier,
+ out int hotPos,
+ out Key hotKey,
+ supportFirstUpperCase
+ );
+
+ if (expectedResult)
+ {
+ Assert.True (result);
+ }
+ else
+ {
+ Assert.False (result);
+ }
+
+ Assert.Equal (expectedResult, result);
+ Assert.Equal (expectedHotPos, hotPos);
+ Assert.Equal (expectedKey, hotKey);
+ }
+
+ [Theory]
+ [InlineData ("_\"k before", true, (KeyCode)'"')] // BUGBUG: Not sure why this fails. " is a normal char
+ [InlineData ("\"_k before", true, KeyCode.K)]
+ [InlineData ("_`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'`')]
+ [InlineData ("`_~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'~')]
+ [InlineData (
+ "`~!@#$%^&*()-__=+[{]}\\|;:'\",<.>/?",
+ true,
+ (KeyCode)'='
+ )] // BUGBUG: Not sure why this fails. Ignore the first and consider the second
+ [InlineData ("_ ~ s gui.cs master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
+ [InlineData (" ~ s gui.cs _ master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
+ [InlineData ("non-english: _кдать", true, (KeyCode)'к')] // Lower case Cryllic K (к)
+ public void FindHotKey_Symbols_Returns_Symbol (string text, bool found, KeyCode expected)
+ {
+ var hotKeySpecifier = (Rune)'_';
+
+ bool result = TextFormatter.FindHotKey (text, hotKeySpecifier, out int _, out Key hotKey);
+ Assert.Equal (found, result);
+ Assert.Equal (expected, hotKey);
+ }
+
+ [Fact]
+ public void Format_Dont_Throw_ArgumentException_With_WordWrap_As_False_And_Keep_End_Spaces_As_True ()
+ {
+ Exception exception = Record.Exception (
+ () =>
+ TextFormatter.Format (
+ "Some text",
+ 4,
+ Alignment.Start,
+ false,
+ true
+ )
+ );
+ Assert.Null (exception);
+ }
+
+ [Theory]
+ [InlineData (
+ "Hello world, how are you today? Pretty neat!",
+ 44,
+ 80,
+ "Hello world, how are you today? Pretty neat!"
+ )]
+ public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Horizontal (
+ string text,
+ int runeCount,
+ int maxWidth,
+ string justifiedText
+ )
+ {
+ Assert.Equal (runeCount, text.GetRuneCount ());
+
+ var fmtText = string.Empty;
+
+ for (int i = text.GetRuneCount (); i < maxWidth; i++)
+ {
+ fmtText = TextFormatter.Format (text, i, Alignment.Fill, true) [0];
+ Assert.Equal (i, fmtText.GetRuneCount ());
+ char c = fmtText [^1];
+ Assert.True (text.EndsWith (c));
+ }
+
+ Assert.Equal (justifiedText, fmtText);
+ }
+
+ [Theory]
+ [InlineData (
+ "Hello world, how are you today? Pretty neat!",
+ 44,
+ 80,
+ "Hello world, how are you today? Pretty neat!"
+ )]
+ public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Vertical (
+ string text,
+ int runeCount,
+ int maxWidth,
+ string justifiedText
+ )
+ {
+ Assert.Equal (runeCount, text.GetRuneCount ());
+
+ var fmtText = string.Empty;
+
+ for (int i = text.GetRuneCount (); i < maxWidth; i++)
+ {
+ fmtText = TextFormatter.Format (
+ text,
+ i,
+ Alignment.Fill,
+ false,
+ true,
+ 0,
+ TextDirection.TopBottom_LeftRight
+ ) [0];
+ Assert.Equal (i, fmtText.GetRuneCount ());
+ char c = fmtText [^1];
+ Assert.True (text.EndsWith (c));
+ }
+
+ Assert.Equal (justifiedText, fmtText);
+ }
+
+ [Theory]
+ [InlineData ("Truncate", 3, "Tru")]
+ [InlineData ("デモエムポンズ", 3, "デ")]
+ public void Format_Truncate_Simple_And_Wide_Runes (string text, int width, string expected)
+ {
+ List list = TextFormatter.Format (text, width, false, false);
+ Assert.Equal (expected, list [^1]);
+ }
+
+ [Theory]
+ [MemberData (nameof (FormatEnvironmentNewLine))]
+ public void Format_With_PreserveTrailingSpaces_And_Without_PreserveTrailingSpaces (
+ string text,
+ int width,
+ IEnumerable expected
+ )
+ {
+ var preserveTrailingSpaces = false;
+ List formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces);
+ Assert.Equal (expected, formated);
+
+ preserveTrailingSpaces = true;
+ formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces);
+ Assert.Equal (expected, formated);
+ }
+
+ [Theory]
+ [InlineData (
+ " A sentence has words. \n This is the second Line - 2. ",
+ 4,
+ -50,
+ Alignment.Start,
+ true,
+ false,
+ new [] { " A", "sent", "ence", "has", "word", "s. ", " Thi", "s is", "the", "seco", "nd", "Line", "- 2." },
+ " Asentencehaswords. This isthesecondLine- 2."
+ )]
+ [InlineData (
+ " A sentence has words. \n This is the second Line - 2. ",
+ 4,
+ -50,
+ Alignment.Start,
+ true,
+ true,
+ new []
+ {
+ " A ",
+ "sent",
+ "ence",
+ " ",
+ "has ",
+ "word",
+ "s. ",
+ " ",
+ "This",
+ " is ",
+ "the ",
+ "seco",
+ "nd ",
+ "Line",
+ " - ",
+ "2. "
+ },
+ " A sentence has words. This is the second Line - 2. "
+ )]
+ public void Format_WordWrap_PreserveTrailingSpaces (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ bool preserveTrailingSpaces,
+ IEnumerable resultLines,
+ string expectedWrappedText
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
+ Assert.Equal (list.Count, resultLines.Count ());
+ Assert.Equal (resultLines, list);
+ var wrappedText = string.Empty;
+
+ foreach (string txt in list)
+ {
+ wrappedText += txt;
+ }
+
+ Assert.Equal (expectedWrappedText, wrappedText);
+ }
+
+ public static IEnumerable FormatEnvironmentNewLine =>
+ new List
+ {
+ new object []
+ {
+ $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3{Environment.NewLine}",
+ 60,
+ new [] { "Line1", "Line2", "Line3" }
+ }
+ };
+
+ [Theory]
+ [InlineData ("Hello World", 11)]
+ [InlineData ("こんにちは世界", 14)]
+ public void GetColumns_Simple_And_Wide_Runes (string text, int width) { Assert.Equal (width, text.GetColumns ()); }
+
+ [Theory]
+ [InlineData (new [] { "0123456789" }, 1)]
+ [InlineData (new [] { "Hello World" }, 1)]
+ [InlineData (new [] { "Hello", "World" }, 2)]
+ [InlineData (new [] { "こんにちは", "世界" }, 4)]
+ public void GetColumnsRequiredForVerticalText_List_GetsWidth (IEnumerable text, int expectedWidth)
+ {
+ Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
+ }
+
+ [Theory]
+ [InlineData (new [] { "Hello World" }, 1, 0, 1, 1)]
+ [InlineData (new [] { "Hello", "World" }, 2, 1, 1, 1)]
+ [InlineData (new [] { "こんにちは", "世界" }, 4, 1, 1, 2)]
+ public void GetColumnsRequiredForVerticalText_List_Simple_And_Wide_Runes (
+ IEnumerable text,
+ int expectedWidth,
+ int index,
+ int length,
+ int expectedIndexWidth
+ )
+ {
+ Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
+ Assert.Equal (expectedIndexWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList (), index, length));
+ }
+
+ [Fact]
+ public void GetColumnsRequiredForVerticalText_List_With_Combining_Runes ()
+ {
+ List text = new () { "Les Mis", "e\u0328\u0301", "rables" };
+ Assert.Equal (1, TextFormatter.GetColumnsRequiredForVerticalText (text, 1, 1));
+ }
+
+ [Theory]
+ [InlineData ("Hello World", 6, 6)]
+ [InlineData ("こんにちは 世界", 6, 3)]
+ [MemberData (nameof (CMGlyphs))]
+ public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
+ {
+ List runes = text.ToRuneList ();
+ Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
+ }
+
+ [Theory]
+ [InlineData ("test", 3, 3)]
+ [InlineData ("test", 4, 4)]
+ [InlineData ("test", 10, 4)]
+ public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength)
+ {
+ List runes = text.ToRuneList ();
+
+ Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
+ }
+
+ [Theory]
+ [InlineData ("Hello World", 6, 6)]
+ [InlineData ("こんにちは 世界", 6, 3)]
+ public void GetLengthThatFits_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
+ {
+ Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
+ }
+
+ [Theory]
+ [InlineData ("test", 3, 3)]
+ [InlineData ("test", 4, 4)]
+ [InlineData ("test", 10, 4)]
+ [InlineData ("test", 1, 1)]
+ [InlineData ("test", 0, 0)]
+ [InlineData ("test", -1, 0)]
+ [InlineData (null, -1, 0)]
+ [InlineData ("", -1, 0)]
+ public void GetLengthThatFits_String (string text, int columns, int expectedLength)
+ {
+ Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
+ }
+
+ [Fact]
+ public void GetLengthThatFits_With_Combining_Runes ()
+ {
+ var text = "Les Mise\u0328\u0301rables";
+ Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14));
+ }
+
+ [Fact]
+ public void GetMaxColsForWidth_With_Combining_Runes ()
+ {
+ List text = new () { "Les Mis", "e\u0328\u0301", "rables" };
+ Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1));
+ }
+
+ //[Fact]
+ //public void GetWidestLineLength_With_Combining_Runes ()
+ //{
+ // var text = "Les Mise\u0328\u0301rables";
+ // Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1));
+ //}
+
+ [Fact]
+ public void Internal_Tests ()
+ {
+ var tf = new TextFormatter ();
+ Assert.Equal (KeyCode.Null, tf.HotKey);
+ tf.HotKey = KeyCode.CtrlMask | KeyCode.Q;
+ Assert.Equal (KeyCode.CtrlMask | KeyCode.Q, tf.HotKey);
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData ("Hello World", 15, 1, "Hello World")]
+ [InlineData (
+ "Well Done\nNice Work",
+ 15,
+ 2,
+ @"
+Well Done
+Nice Work")]
+ [InlineData ("你好 世界", 15, 1, "你好 世界")]
+ [InlineData (
+ "做 得好\n幹 得好",
+ 15,
+ 2,
+ @"
+做 得好
+幹 得好")]
+ public void Justify_Horizontal (string text, int width, int height, string expectedText)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Alignment = Alignment.Fill,
+ ConstrainToSize = new Size (width, height),
+ MultiLine = true
+ };
+
+ tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+ TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ }
+
+ [Theory]
+ [InlineData ("")]
+ [InlineData (null)]
+ [InlineData ("test")]
+ public void Justify_Invalid (string text)
+ {
+ Assert.Equal (text, TextFormatter.Justify (text, 0));
+ Assert.Equal (text, TextFormatter.Justify (text, 0));
+ Assert.Throws (() => TextFormatter.Justify (text, -1));
+ }
+
+ [Theory]
+
+ // Even # of spaces
+ // 0123456789
+ [InlineData ("012 456 89", "012 456 89", 10, 0, "+", true)]
+ [InlineData ("012 456 89", "012++456+89", 11, 1)]
+ [InlineData ("012 456 89", "012 456 89", 12, 2, "++", true)]
+ [InlineData ("012 456 89", "012+++456++89", 13, 3)]
+ [InlineData ("012 456 89", "012 456 89", 14, 4, "+++", true)]
+ [InlineData ("012 456 89", "012++++456+++89", 15, 5)]
+ [InlineData ("012 456 89", "012 456 89", 16, 6, "++++", true)]
+ [InlineData ("012 456 89", "012 456 89", 30, 20, "+++++++++++", true)]
+ [InlineData ("012 456 89", "012+++++++++++++456++++++++++++89", 33, 23)]
+
+ // Odd # of spaces
+ // 01234567890123
+ [InlineData ("012 456 89 end", "012 456 89 end", 14, 0, "+", true)]
+ [InlineData ("012 456 89 end", "012++456+89+end", 15, 1)]
+ [InlineData ("012 456 89 end", "012++456++89+end", 16, 2)]
+ [InlineData ("012 456 89 end", "012 456 89 end", 17, 3, "++", true)]
+ [InlineData ("012 456 89 end", "012+++456++89++end", 18, 4)]
+ [InlineData ("012 456 89 end", "012+++456+++89++end", 19, 5)]
+ [InlineData ("012 456 89 end", "012 456 89 end", 20, 6, "+++", true)]
+ [InlineData ("012 456 89 end", "012++++++++456++++++++89+++++++end", 34, 20)]
+ [InlineData ("012 456 89 end", "012+++++++++456+++++++++89++++++++end", 37, 23)]
+
+ // Unicode
+ // Even # of chars
+ // 0123456789
+ [InlineData ("пÑРвРÑ", "пÑРвРÑ", 10, 0, "+", true)]
+ [InlineData ("пÑРвРÑ", "пÑÐ++вÐ+Ñ", 11, 1)]
+ [InlineData ("пÑРвРÑ", "пÑРвРÑ", 12, 2, "++", true)]
+ [InlineData ("пÑРвРÑ", "пÑÐ+++вÐ++Ñ", 13, 3)]
+ [InlineData ("пÑРвРÑ", "пÑРвРÑ", 14, 4, "+++", true)]
+ [InlineData ("пÑРвРÑ", "пÑÐ++++вÐ+++Ñ", 15, 5)]
+ [InlineData ("пÑРвРÑ", "пÑРвРÑ", 16, 6, "++++", true)]
+ [InlineData ("пÑРвРÑ", "пÑРвРÑ", 30, 20, "+++++++++++", true)]
+ [InlineData ("пÑРвРÑ", "пÑÐ+++++++++++++вÐ++++++++++++Ñ", 33, 23)]
+
+ // Unicode
+ // Odd # of chars
+ // 0123456789
+ [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 10, 0, "+", true)]
+ [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ+вÐ+Ñ", 11, 1)]
+ [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ++вÐ+Ñ", 12, 2)]
+ [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 13, 3, "++", true)]
+ [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ++вÐ++Ñ", 14, 4)]
+ [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ+++вÐ++Ñ", 15, 5)]
+ [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 16, 6, "+++", true)]
+ [InlineData ("Ð ÑРвРÑ", "Ð++++++++ÑÐ++++++++вÐ+++++++Ñ", 30, 20)]
+ [InlineData ("Ð ÑРвРÑ", "Ð+++++++++ÑÐ+++++++++вÐ++++++++Ñ", 33, 23)]
+ public void Justify_Sentence (
+ string text,
+ string justifiedText,
+ int forceToWidth,
+ int widthOffset,
+ string replaceWith = null,
+ bool replace = false
+ )
+ {
+ var fillChar = '+';
+
+ Assert.Equal (forceToWidth, text.GetRuneCount () + widthOffset);
+
+ if (replace)
+ {
+ justifiedText = text.Replace (" ", replaceWith);
+ }
+
+ Assert.Equal (justifiedText, TextFormatter.Justify (text, forceToWidth, fillChar));
+ Assert.True (Math.Abs (forceToWidth - justifiedText.GetRuneCount ()) < text.Count (s => s == ' '));
+ Assert.True (Math.Abs (forceToWidth - justifiedText.GetColumns ()) < text.Count (s => s == ' '));
+ }
+
+ [Theory]
+ [InlineData ("word")] // Even # of chars
+ [InlineData ("word.")] // Odd # of chars
+ [InlineData ("пÑивеÑ")] // Unicode (even #)
+ [InlineData ("пÑивеÑ.")] // Unicode (odd # of chars)
+ public void Justify_SingleWord (string text)
+ {
+ string justifiedText = text;
+ var fillChar = '+';
+
+ int width = text.GetRuneCount ();
+ Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
+ width = text.GetRuneCount () + 1;
+ Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
+ width = text.GetRuneCount () + 2;
+ Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
+ width = text.GetRuneCount () + 10;
+ Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
+ width = text.GetRuneCount () + 11;
+ Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData ("Hello World", 1, 15, "H\ne\nl\nl\no\n \n \n \n \n \nW\no\nr\nl\nd")]
+ [InlineData (
+ "Well Done\nNice Work",
+ 2,
+ 15,
+ @"
+WN
+ei
+lc
+le
+
+
+
+
+
+
+
+DW
+oo
+nr
+ek")]
+ [InlineData ("你好 世界", 2, 15, "你\n好\n \n \n \n \n \n \n \n \n \n \n \n世\n界")]
+ [InlineData (
+ "做 得好\n幹 得好",
+ 4,
+ 15,
+ @"
+做幹
+
+
+
+
+
+
+
+
+
+
+
+
+得得
+好好")]
+ public void Justify_Vertical (string text, int width, int height, string expectedText)
+ {
+ TextFormatter tf = new ()
+ {
+ Text = text,
+ Direction = TextDirection.TopBottom_LeftRight,
+ VerticalAlignment = Alignment.Fill,
+ ConstrainToSize = new Size (width, height),
+ MultiLine = true
+ };
+
+ tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+ TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+ }
+
+ [Theory]
+ [InlineData ("Single Line 界", 14)]
+ [InlineData ("First Line 界\nSecond Line 界\nThird Line 界\n", 14)]
+ public void MaxWidthLine_With_And_Without_Newlines (string text, int expected) { Assert.Equal (expected, TextFormatter.GetWidestLineLength (text)); }
+
+ [Theory]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 0,
+ false,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 1,
+ false,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 1,
+ 0,
+ false,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 0,
+ true,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 1,
+ true,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 1,
+ 0,
+ true,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 6,
+ 5,
+ false,
+ new [] { "First " }
+ )]
+ [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3 " })]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 6,
+ 5,
+ true,
+ new [] { "First ", "Second", "Third ", "Forty ", "Fiftee" }
+ )]
+ [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一" })]
+ [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一", "第二", "第三", "四十", "第十" })]
+ public void MultiLine_WordWrap_False_Horizontal_Direction (
+ string text,
+ int maxWidth,
+ int maxHeight,
+ bool multiLine,
+ IEnumerable resultLines
+ )
+ {
+ var tf = new TextFormatter
+ {
+ Text = text, ConstrainToSize = new (maxWidth, maxHeight), WordWrap = false, MultiLine = multiLine
+ };
+
+ Assert.False (tf.WordWrap);
+ Assert.True (tf.MultiLine == multiLine);
+ Assert.Equal (TextDirection.LeftRight_TopBottom, tf.Direction);
+
+ List splitLines = tf.GetLines ();
+ Assert.Equal (splitLines.Count, resultLines.Count ());
+ Assert.Equal (splitLines, resultLines);
+ }
+
+ [Theory]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 0,
+ false,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 1,
+ false,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 1,
+ 0,
+ false,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 0,
+ true,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 0,
+ 1,
+ true,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 1,
+ 0,
+ true,
+ new [] { "" }
+ )]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 6,
+ 5,
+ false,
+ new [] { "First" }
+ )]
+ [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3" })]
+ [InlineData (
+ "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
+ 6,
+ 5,
+ true,
+ new [] { "First", "Secon", "Third", "Forty", "Fifte", "Seven" }
+ )]
+ [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一行 第" })]
+ [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一行", "第二行" })]
+ public void MultiLine_WordWrap_False_Vertical_Direction (
+ string text,
+ int maxWidth,
+ int maxHeight,
+ bool multiLine,
+ IEnumerable resultLines
+ )
+ {
+ var tf = new TextFormatter
+ {
+ Text = text,
+ ConstrainToSize = new (maxWidth, maxHeight),
+ WordWrap = false,
+ MultiLine = multiLine,
+ Direction = TextDirection.TopBottom_LeftRight
+ };
+
+ Assert.False (tf.WordWrap);
+ Assert.True (tf.MultiLine == multiLine);
+ Assert.Equal (TextDirection.TopBottom_LeftRight, tf.Direction);
+
+ List splitLines = tf.GetLines ();
+ Assert.Equal (splitLines.Count, resultLines.Count ());
+ Assert.Equal (splitLines, resultLines);
+ }
+
+ [Fact]
+ public void NeedsFormat_Sets ()
+ {
+ var testText = "test";
+ var testBounds = new Rectangle (0, 0, 100, 1);
+ var tf = new TextFormatter ();
+
+ tf.Text = "test";
+ Assert.True (tf.NeedsFormat); // get_Lines causes a Format
+ Assert.NotEmpty (tf.GetLines ());
+ Assert.False (tf.NeedsFormat); // get_Lines causes a Format
+ Assert.Equal (testText, tf.Text);
+ tf.Draw (testBounds, new (), new ());
+ Assert.False (tf.NeedsFormat);
+
+ tf.ConstrainToSize = new (1, 1);
+ Assert.True (tf.NeedsFormat);
+ Assert.NotEmpty (tf.GetLines ());
+ Assert.False (tf.NeedsFormat); // get_Lines causes a Format
+
+ tf.Alignment = Alignment.Center;
+ Assert.True (tf.NeedsFormat);
+ Assert.NotEmpty (tf.GetLines ());
+ Assert.False (tf.NeedsFormat); // get_Lines causes a Format
+ }
+
// Test that changing TextFormatter does not impact View dimensions if Dim.Auto is not in play
[Fact]
public void Not_Used_TextFormatter_Does_Not_Change_View_Size ()
@@ -6998,6 +5194,1835 @@ B ")]
Assert.Equal (Size.Empty, view.Frame.Size);
}
+ [Theory]
+ [InlineData ("", -1, Alignment.Start, false, 0)]
+ [InlineData (null, 0, Alignment.Start, false, 1)]
+ [InlineData (null, 0, Alignment.Start, true, 1)]
+ [InlineData ("", 0, Alignment.Start, false, 1)]
+ [InlineData ("", 0, Alignment.Start, true, 1)]
+ public void Reformat_Invalid (string text, int maxWidth, Alignment alignment, bool wrap, int linesCount)
+ {
+ if (maxWidth < 0)
+ {
+ Assert.Throws (
+ () =>
+ TextFormatter.Format (text, maxWidth, alignment, wrap)
+ );
+ }
+ else
+ {
+ List list = TextFormatter.Format (text, maxWidth, alignment, wrap);
+ Assert.NotEmpty (list);
+ Assert.True (list.Count == linesCount);
+ Assert.Equal (string.Empty, list [0]);
+ }
+ }
+
+ [Theory]
+ [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true)]
+ [InlineData ("A sentence has words.\nLine 2.", 1, -28, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.\nLine 2.", 5, -24, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.\nLine 2.", 28, -1, Alignment.Start, false, 1, false)]
+
+ // no clip
+ [InlineData ("A sentence has words.\nLine 2.", 29, 0, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.\nLine 2.", 30, 1, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true)]
+ [InlineData ("A sentence has words.\r\nLine 2.", 1, -29, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.\r\nLine 2.", 5, -25, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.\r\nLine 2.", 29, -1, Alignment.Start, false, 1, false, 1)]
+ [InlineData ("A sentence has words.\r\nLine 2.", 30, 0, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.\r\nLine 2.", 31, 1, Alignment.Start, false, 1, false)]
+ public void Reformat_NoWordrap_NewLines_MultiLine_False (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ int linesCount,
+ bool stringEmpty,
+ int clipWidthOffset = 0
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth) + clipWidthOffset;
+ List list = TextFormatter.Format (text, maxWidth, alignment, wrap);
+ Assert.NotEmpty (list);
+ Assert.True (list.Count == linesCount);
+
+ if (stringEmpty)
+ {
+ Assert.Equal (string.Empty, list [0]);
+ }
+ else
+ {
+ Assert.NotEqual (string.Empty, list [0]);
+ }
+
+ if (text.Contains ("\r\n") && maxWidth > 0)
+ {
+ Assert.Equal (
+ StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth])
+ .Replace ("\r\n", " "),
+ list [0]
+ );
+ }
+ else if (text.Contains ('\n') && maxWidth > 0)
+ {
+ Assert.Equal (
+ StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth])
+ .Replace ("\n", " "),
+ list [0]
+ );
+ }
+ else
+ {
+ Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]);
+ }
+ }
+
+ [Theory]
+ [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 1,
+ -28,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A", "L" }
+ )]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 5,
+ -24,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sen", "Line " }
+ )]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 28,
+ -1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ //// no clip
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 29,
+ 0,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 30,
+ 1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 1,
+ -29,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A", "L" }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 5,
+ -25,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sen", "Line " }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 29,
+ -1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 30,
+ 0,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 31,
+ 1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ public void Reformat_NoWordrap_NewLines_MultiLine_True (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ int linesCount,
+ bool stringEmpty,
+ IEnumerable resultLines
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+
+ List list = TextFormatter.Format (
+ text,
+ maxWidth,
+ alignment,
+ wrap,
+ false,
+ 0,
+ TextDirection.LeftRight_TopBottom,
+ true
+ );
+ Assert.NotEmpty (list);
+ Assert.True (list.Count == linesCount);
+
+ if (stringEmpty)
+ {
+ Assert.Equal (string.Empty, list [0]);
+ }
+ else
+ {
+ Assert.NotEqual (string.Empty, list [0]);
+ }
+
+ Assert.Equal (list, resultLines);
+ }
+
+ [Theory]
+ [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 1,
+ -28,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A", "L" }
+ )]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 5,
+ -24,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sen", "Line " }
+ )]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 28,
+ -1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ //// no clip
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 29,
+ 0,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData (
+ "A sentence has words.\nLine 2.",
+ 30,
+ 1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 1,
+ -29,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A", "L" }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 5,
+ -25,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sen", "Line " }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 29,
+ -1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 30,
+ 0,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ [InlineData (
+ "A sentence has words.\r\nLine 2.",
+ 31,
+ 1,
+ Alignment.Start,
+ false,
+ 2,
+ false,
+ new [] { "A sentence has words.", "Line 2." }
+ )]
+ public void Reformat_NoWordrap_NewLines_MultiLine_True_Vertical (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ int linesCount,
+ bool stringEmpty,
+ IEnumerable resultLines
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+
+ List list = TextFormatter.Format (
+ text,
+ maxWidth,
+ alignment,
+ wrap,
+ false,
+ 0,
+ TextDirection.TopBottom_LeftRight,
+ true
+ );
+ Assert.NotEmpty (list);
+ Assert.True (list.Count == linesCount);
+
+ if (stringEmpty)
+ {
+ Assert.Equal (string.Empty, list [0]);
+ }
+ else
+ {
+ Assert.NotEqual (string.Empty, list [0]);
+ }
+
+ Assert.Equal (list, resultLines);
+ }
+
+ [Theory]
+ [InlineData ("", 0, 0, Alignment.Start, false, 1, true)]
+ [InlineData ("", 1, 1, Alignment.Start, false, 1, true)]
+ [InlineData ("A sentence has words.", 0, -21, Alignment.Start, false, 1, true)]
+ [InlineData ("A sentence has words.", 1, -20, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.", 5, -16, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.", 20, -1, Alignment.Start, false, 1, false)]
+
+ // no clip
+ [InlineData ("A sentence has words.", 21, 0, Alignment.Start, false, 1, false)]
+ [InlineData ("A sentence has words.", 22, 1, Alignment.Start, false, 1, false)]
+ public void Reformat_NoWordrap_SingleLine (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ int linesCount,
+ bool stringEmpty
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ List list = TextFormatter.Format (text, maxWidth, alignment, wrap);
+ Assert.NotEmpty (list);
+ Assert.True (list.Count == linesCount);
+
+ if (stringEmpty)
+ {
+ Assert.Equal (string.Empty, list [0]);
+ }
+ else
+ {
+ Assert.NotEqual (string.Empty, list [0]);
+ }
+
+ Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]);
+ }
+
+ [Theory]
+
+ // Unicode
+ [InlineData (
+ "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
+ 8,
+ -1,
+ Alignment.Start,
+ true,
+ false,
+ new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
+ )]
+
+ // no clip
+ [InlineData (
+ "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
+ 9,
+ 0,
+ Alignment.Start,
+ true,
+ false,
+ new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
+ )]
+ [InlineData (
+ "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
+ 10,
+ 1,
+ Alignment.Start,
+ true,
+ false,
+ new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
+ )]
+ public void Reformat_Unicode_Wrap_Spaces_NewLines (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ bool preserveTrailingSpaces,
+ IEnumerable resultLines
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
+ Assert.Equal (list.Count, resultLines.Count ());
+ Assert.Equal (resultLines, list);
+ }
+
+ [Theory]
+
+ // Unicode
+ // Even # of chars
+ // 0123456789
+ [InlineData ("\u2660пÑРвРÑ", 10, -1, Alignment.Start, true, false, new [] { "\u2660пÑРвÐ", "Ñ" })]
+
+ // no clip
+ [InlineData ("\u2660пÑРвРÑ", 11, 0, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })]
+ [InlineData ("\u2660пÑРвРÑ", 12, 1, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })]
+
+ // Unicode
+ // Odd # of chars
+ // 0123456789
+ [InlineData ("\u2660 ÑРвРÑ", 9, -1, Alignment.Start, true, false, new [] { "\u2660 ÑРвÐ", "Ñ" })]
+
+ // no clip
+ [InlineData ("\u2660 ÑРвРÑ", 10, 0, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })]
+ [InlineData ("\u2660 ÑРвРÑ", 11, 1, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })]
+ public void Reformat_Unicode_Wrap_Spaces_No_NewLines (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ bool preserveTrailingSpaces,
+ IEnumerable resultLines
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
+ Assert.Equal (list.Count, resultLines.Count ());
+ Assert.Equal (resultLines, list);
+ }
+
+ [Theory]
+
+ // Even # of spaces
+ // 0123456789
+ [InlineData ("012 456 89", 0, -10, Alignment.Start, true, true, true, new [] { "" })]
+ [InlineData (
+ "012 456 89",
+ 1,
+ -9,
+ Alignment.Start,
+ true,
+ true,
+ false,
+ new [] { "0", "1", "2", " ", "4", "5", "6", " ", "8", "9" },
+ "01245689"
+ )]
+ [InlineData ("012 456 89", 5, -5, Alignment.Start, true, true, false, new [] { "012 ", "456 ", "89" })]
+ [InlineData ("012 456 89", 9, -1, Alignment.Start, true, true, false, new [] { "012 456 ", "89" })]
+
+ // no clip
+ [InlineData ("012 456 89", 10, 0, Alignment.Start, true, true, false, new [] { "012 456 89" })]
+ [InlineData ("012 456 89", 11, 1, Alignment.Start, true, true, false, new [] { "012 456 89" })]
+
+ // Odd # of spaces
+ // 01234567890123
+ [InlineData ("012 456 89 end", 13, -1, Alignment.Start, true, true, false, new [] { "012 456 89 ", "end" })]
+
+ // no clip
+ [InlineData ("012 456 89 end", 14, 0, Alignment.Start, true, true, false, new [] { "012 456 89 end" })]
+ [InlineData ("012 456 89 end", 15, 1, Alignment.Start, true, true, false, new [] { "012 456 89 end" })]
+ public void Reformat_Wrap_Spaces_No_NewLines (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ Alignment alignment,
+ bool wrap,
+ bool preserveTrailingSpaces,
+ bool stringEmpty,
+ IEnumerable resultLines,
+ string noSpaceText = ""
+ )
+ {
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces);
+ Assert.NotEmpty (list);
+ Assert.True (list.Count == resultLines.Count ());
+
+ if (stringEmpty)
+ {
+ Assert.Equal (string.Empty, list [0]);
+ }
+ else
+ {
+ Assert.NotEqual (string.Empty, list [0]);
+ }
+
+ Assert.Equal (resultLines, list);
+
+ if (maxWidth > 0)
+ {
+ // remove whitespace chars
+ if (maxWidth < 5)
+ {
+ expectedClippedWidth = text.GetRuneCount () - text.Sum (r => r == ' ' ? 1 : 0);
+ }
+ else
+ {
+ expectedClippedWidth = Math.Min (
+ text.GetRuneCount (),
+ maxWidth - text.Sum (r => r == ' ' ? 1 : 0)
+ );
+ }
+
+ list = TextFormatter.Format (text, maxWidth, Alignment.Start, wrap);
+
+ if (maxWidth == 1)
+ {
+ Assert.Equal (expectedClippedWidth, list.Count);
+ Assert.Equal (noSpaceText, string.Concat (list.ToArray ()));
+ }
+
+ if (maxWidth > 1 && maxWidth < 10)
+ {
+ Assert.Equal (
+ StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]),
+ list [0]
+ );
+ }
+ }
+ }
+
+ [Theory]
+ [InlineData (null)]
+ [InlineData ("")]
+ [InlineData ("a")]
+ public void RemoveHotKeySpecifier_InValid_ReturnsOriginal (string text)
+ {
+ var hotKeySpecifier = (Rune)'_';
+
+ if (text == null)
+ {
+ Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier));
+ Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier));
+ Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier));
+ }
+ else
+ {
+ Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier));
+ Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier));
+ Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier));
+ }
+ }
+
+ [Theory]
+ [InlineData ("all lower case", 0)]
+ [InlineData ("K Before", 0)]
+ [InlineData ("aK Second", 1)]
+ [InlineData ("Last K", 5)]
+ [InlineData ("fter K", 7)]
+ [InlineData ("Multiple K and R", 9)]
+ [InlineData ("Non-english: Кдать", 13)]
+ public void RemoveHotKeySpecifier_Valid_Legacy_ReturnsOriginal (string text, int hotPos)
+ {
+ var hotKeySpecifier = (Rune)'_';
+
+ Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier));
+ }
+
+ [Theory]
+ [InlineData ("_K Before", 0, "K Before")]
+ [InlineData ("a_K Second", 1, "aK Second")]
+ [InlineData ("Last _K", 5, "Last K")]
+ [InlineData ("After K_", 7, "After K")]
+ [InlineData ("Multiple _K and _R", 9, "Multiple K and _R")]
+ [InlineData ("Non-english: _Кдать", 13, "Non-english: Кдать")]
+ public void RemoveHotKeySpecifier_Valid_ReturnsStripped (string text, int hotPos, string expectedText)
+ {
+ var hotKeySpecifier = (Rune)'_';
+
+ Assert.Equal (expectedText, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier));
+ }
+
+ [Theory]
+ [InlineData ("test", 0, 't', "test")]
+ [InlineData ("test", 1, 'e', "test")]
+ [InlineData ("Ok", 0, 'O', "Ok")]
+ [InlineData ("[◦ Ok ◦]", 3, 'O', "[◦ Ok ◦]")]
+ [InlineData ("^k", 0, '^', "^k")]
+ public void ReplaceHotKeyWithTag (string text, int hotPos, uint tag, string expected)
+ {
+ var tf = new TextFormatter ();
+ List runes = text.ToRuneList ();
+ Rune rune;
+
+ if (Rune.TryGetRuneAt (text, hotPos, out rune))
+ {
+ Assert.Equal (rune, (Rune)tag);
+ }
+
+ string result = TextFormatter.ReplaceHotKeyWithTag (text, hotPos);
+ Assert.Equal (result, expected);
+ Assert.Equal ((Rune)tag, result.ToRunes () [hotPos]);
+ Assert.Equal (text.GetRuneCount (), runes.Count);
+ Assert.Equal (text, StringExtensions.ToString (runes));
+ }
+
+ public static IEnumerable SplitEnvironmentNewLine =>
+ new List
+ {
+ new object []
+ {
+ $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界",
+ new [] { "First Line 界", "Second Line 界", "Third Line 界" }
+ },
+ new object []
+ {
+ $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界{Environment.NewLine}",
+ new [] { "First Line 界", "Second Line 界", "Third Line 界", "" }
+ }
+ };
+
+ [Theory]
+ [MemberData (nameof (SplitEnvironmentNewLine))]
+ public void SplitNewLine_Ending__With_Or_Without_NewLine_Probably_CRLF (
+ string text,
+ IEnumerable expected
+ )
+ {
+ List splited = TextFormatter.SplitNewLine (text);
+ Assert.Equal (expected, splited);
+ }
+
+ [Theory]
+ [InlineData (
+ "First Line 界\nSecond Line 界\nThird Line 界\n",
+ new [] { "First Line 界", "Second Line 界", "Third Line 界", "" }
+ )]
+ public void SplitNewLine_Ending_With_NewLine_Only_LF (string text, IEnumerable expected)
+ {
+ List splited = TextFormatter.SplitNewLine (text);
+ Assert.Equal (expected, splited);
+ }
+
+ [Theory]
+ [InlineData (
+ "First Line 界\nSecond Line 界\nThird Line 界",
+ new [] { "First Line 界", "Second Line 界", "Third Line 界" }
+ )]
+ public void SplitNewLine_Ending_Without_NewLine_Only_LF (string text, IEnumerable expected)
+ {
+ List splited = TextFormatter.SplitNewLine (text);
+ Assert.Equal (expected, splited);
+ }
+
+ [Theory]
+ [InlineData ("New Test 你", 10, 10, 20320, 20320, 9, "你")]
+ [InlineData ("New Test \U0001d539", 10, 11, 120121, 55349, 9, "𝔹")]
+ public void String_Array_Is_Not_Always_Equal_ToRunes_Array (
+ string text,
+ int runesLength,
+ int stringLength,
+ int runeValue,
+ int stringValue,
+ int index,
+ string expected
+ )
+ {
+ Rune [] usToRunes = text.ToRunes ();
+ Assert.Equal (runesLength, usToRunes.Length);
+ Assert.Equal (stringLength, text.Length);
+ Assert.Equal (runeValue, usToRunes [index].Value);
+ Assert.Equal (stringValue, text [index]);
+ Assert.Equal (expected, usToRunes [index].ToString ());
+
+ if (char.IsHighSurrogate (text [index]))
+ {
+ // Rune array length isn't equal to string array
+ Assert.Equal (expected, new (new [] { text [index], text [index + 1] }));
+ }
+ else
+ {
+ // Rune array length is equal to string array
+ Assert.Equal (expected, text [index].ToString ());
+ }
+ }
+
+ [Theory]
+ [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
+ [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
+ [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
+ [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
+ public void TabWith_PreserveTrailingSpaces_False (
+ int width,
+ int height,
+ TextDirection textDirection,
+ int tabWidth,
+ string expected
+ )
+ {
+ var driver = new FakeDriver ();
+ driver.Init ();
+
+ var text = "This is a \tTab";
+ var tf = new TextFormatter ();
+ tf.Direction = textDirection;
+ tf.TabWidth = tabWidth;
+ tf.Text = text;
+ tf.ConstrainToWidth = 20;
+ tf.ConstrainToHeight = 20;
+
+ Assert.True (tf.WordWrap);
+ Assert.False (tf.PreserveTrailingSpaces);
+
+ tf.Draw (
+ new (0, 0, width, height),
+ new (ColorName.White, ColorName.Black),
+ new (ColorName.Blue, ColorName.Black),
+ default (Rectangle),
+ driver
+ );
+ TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
+
+ driver.End ();
+ }
+
+ [Theory]
+ [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
+ [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
+ [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
+ [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
+ public void TabWith_PreserveTrailingSpaces_True (
+ int width,
+ int height,
+ TextDirection textDirection,
+ int tabWidth,
+ string expected
+ )
+ {
+ var driver = new FakeDriver ();
+ driver.Init ();
+
+ var text = "This is a \tTab";
+ var tf = new TextFormatter ();
+
+ tf.Direction = textDirection;
+ tf.TabWidth = tabWidth;
+ tf.PreserveTrailingSpaces = true;
+ tf.Text = text;
+ tf.ConstrainToWidth = 20;
+ tf.ConstrainToHeight = 20;
+
+ Assert.True (tf.WordWrap);
+
+ tf.Draw (
+ new (0, 0, width, height),
+ new (ColorName.White, ColorName.Black),
+ new (ColorName.Blue, ColorName.Black),
+ default (Rectangle),
+ driver
+ );
+ TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
+
+ driver.End ();
+ }
+
+ [Theory]
+ [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
+ [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
+ [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
+ [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
+ public void TabWith_WordWrap_True (
+ int width,
+ int height,
+ TextDirection textDirection,
+ int tabWidth,
+ string expected
+ )
+ {
+ var driver = new FakeDriver ();
+ driver.Init ();
+
+ var text = "This is a \tTab";
+ var tf = new TextFormatter ();
+
+ tf.Direction = textDirection;
+ tf.TabWidth = tabWidth;
+ tf.WordWrap = true;
+ tf.Text = text;
+ tf.ConstrainToWidth = 20;
+ tf.ConstrainToHeight = 20;
+
+ Assert.False (tf.PreserveTrailingSpaces);
+
+ tf.Draw (
+ new (0, 0, width, height),
+ new (ColorName.White, ColorName.Black),
+ new (ColorName.Blue, ColorName.Black),
+ default (Rectangle),
+ driver
+ );
+ TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
+
+ driver.End ();
+ }
+
+ [Theory]
+ [InlineData ("123456789", 3, "123")]
+ [InlineData ("Hello World", 8, "Hello Wo")]
+ public void TestClipOrPad_LongWord (string text, int fillPad, string expectedText)
+ {
+ // word is long but we want it to fill # space only
+ Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad));
+ }
+
+ [Theory]
+ [InlineData ("fff", 6, "fff ")]
+ [InlineData ("Hello World", 16, "Hello World ")]
+ public void TestClipOrPad_ShortWord (string text, int fillPad, string expectedText)
+ {
+ // word is short but we want it to fill # so it should be padded
+ Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad));
+ }
+
+ [Theory]
+ [InlineData ("你", TextDirection.LeftRight_TopBottom, 2, 1)]
+ [InlineData ("你", TextDirection.TopBottom_LeftRight, 2, 1)]
+ [InlineData ("你你", TextDirection.LeftRight_TopBottom, 4, 1)]
+ [InlineData ("你你", TextDirection.TopBottom_LeftRight, 2, 2)]
+ public void Text_Set_SizeIsCorrect (string text, TextDirection textDirection, int expectedWidth, int expectedHeight)
+ {
+ var tf = new TextFormatter { Direction = textDirection, Text = text };
+ tf.ConstrainToWidth = 10;
+ tf.ConstrainToHeight = 10;
+
+ Assert.Equal (new (expectedWidth, expectedHeight), tf.FormatAndGetSize ());
+ }
+
+ [Fact]
+ [SetupFakeDriver]
+ public void UICatalog_AboutBox_Text ()
+ {
+ TextFormatter tf = new ()
+ {
+ Text = UICatalogApp.GetAboutBoxMessage (),
+ Alignment = Alignment.Center,
+ VerticalAlignment = Alignment.Start,
+ WordWrap = false,
+ MultiLine = true,
+ HotKeySpecifier = (Rune)0xFFFF
+ };
+
+ Size tfSize = tf.FormatAndGetSize ();
+ Assert.Equal (new (58, 13), tfSize);
+
+ ((FakeDriver)Application.Driver).SetBufferSize (tfSize.Width, tfSize.Height);
+
+ Application.Driver.FillRect (Application.Screen, (Rune)'*');
+ tf.Draw (Application.Screen, Attribute.Default, Attribute.Default);
+
+ var expectedText = """
+ ******UI Catalog: A comprehensive sample library for******
+ **********************************************************
+ _______ _ _ _____ _
+ |__ __| (_) | | / ____| (_)
+ | | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _
+ | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |
+ | | __/ | | | | | | | | | | | (_| | || |__| | |_| | |
+ |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|
+ **********************************************************
+ **********************v2 - Pre-Alpha**********************
+ **********************************************************
+ **********https://github.com/gui-cs/Terminal.Gui**********
+ **********************************************************
+ """;
+
+ TestHelpers.AssertDriverContentsAre (expectedText.ReplaceLineEndings (), _output);
+ }
+
+ [Fact]
+ public void WordWrap_BigWidth ()
+ {
+ List wrappedLines;
+
+ var text = "Constantinople";
+ wrappedLines = TextFormatter.WordWrapText (text, 100);
+ Assert.True (wrappedLines.Count == 1);
+ Assert.Equal ("Constantinople", wrappedLines [0]);
+ }
+
+ [Fact]
+ public void WordWrap_Invalid ()
+ {
+ var text = string.Empty;
+ var width = 0;
+
+ Assert.Empty (TextFormatter.WordWrapText (null, width));
+ Assert.Empty (TextFormatter.WordWrapText (text, width));
+ Assert.Throws (() => TextFormatter.WordWrapText (text, -1));
+ }
+
+ [Theory]
+ [InlineData ("A sentence has words.", 3, -18, new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." })]
+ [InlineData (
+ "A sentence has words.",
+ 2,
+ -19,
+ new [] { "A", "se", "nt", "en", "ce", "ha", "s", "wo", "rd", "s." }
+ )]
+ [InlineData (
+ "A sentence has words.",
+ 1,
+ -20,
+ new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." }
+ )]
+ public void WordWrap_Narrow_Default (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("A sentence has words.", 21, 0, new [] { "A sentence has words." })]
+ [InlineData ("A sentence has words.", 20, -1, new [] { "A sentence has", "words." })]
+ [InlineData ("A sentence has words.", 15, -6, new [] { "A sentence has", "words." })]
+ [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence has", "words." })]
+ [InlineData ("A sentence has words.", 13, -8, new [] { "A sentence", "has words." })]
+
+ // Unicode
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.",
+ 42,
+ 0,
+ new [] { "A Unicode sentence (пÑивеÑ) has words." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.",
+ 41,
+ -1,
+ new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.",
+ 36,
+ -6,
+ new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.",
+ 35,
+ -7,
+ new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.",
+ 34,
+ -8,
+ new [] { "A Unicode sentence (пÑивеÑ)", "has words." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.",
+ 25,
+ -17,
+ new [] { "A Unicode sentence", "(пÑивеÑ) has words." }
+ )]
+ public void WordWrap_NoNewLines_Default (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("これが最初の行です。 こんにちは世界。 これが2行目です。", 29, 0, new [] { "これが最初の行です。", "こんにちは世界。", "これが2行目です。" })]
+ public void WordWrap_PreserveTrailingSpaces_False_Unicode_Wide_Runes (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉", "があり ます。" })]
+ [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
+ [InlineData ("文に は言葉 があり ます。", 2, -12, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
+ [InlineData (
+ "文に は言葉 があり ます。",
+ 1,
+ -13,
+ new [] { " ", " ", " " }
+ )] // Just Spaces; should result in a single space for each line
+ public void WordWrap_PreserveTrailingSpaces_False_Wide_Runes (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData (null, 1, new string [] { })] // null input
+ [InlineData ("", 1, new string [] { })] // Empty input
+ [InlineData ("1 34", 1, new [] { "1", "3", "4" })] // Single Spaces
+ [InlineData ("1", 1, new [] { "1" })] // Short input
+ [InlineData ("12", 1, new [] { "1", "2" })]
+ [InlineData ("123", 1, new [] { "1", "2", "3" })]
+ [InlineData ("123456", 1, new [] { "1", "2", "3", "4", "5", "6" })] // No spaces
+ [InlineData (" ", 1, new [] { " " })] // Just Spaces; should result in a single space
+ [InlineData (" ", 1, new [] { " " })]
+ [InlineData (" ", 1, new [] { " ", " " })]
+ [InlineData (" ", 1, new [] { " ", " " })]
+ [InlineData ("12 456", 1, new [] { "1", "2", "4", "5", "6" })] // Single Spaces
+ [InlineData (" 2 456", 1, new [] { " ", "2", "4", "5", "6" })] // Leading spaces should be preserved.
+ [InlineData (" 2 456 8", 1, new [] { " ", "2", "4", "5", "6", "8" })]
+ [InlineData (
+ "A sentence has words. ",
+ 1,
+ new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." }
+ )] // Complex example
+ [InlineData ("12 567", 1, new [] { "1", "2", " ", "5", "6", "7" })] // Double Spaces
+ [InlineData (" 3 567", 1, new [] { " ", "3", "5", "6", "7" })] // Double Leading spaces should be preserved.
+ [InlineData (" 3 678 1", 1, new [] { " ", "3", " ", "6", "7", "8", " ", "1" })]
+ [InlineData ("1 456", 1, new [] { "1", " ", "4", "5", "6" })]
+ [InlineData (
+ "A sentence has words. ",
+ 1,
+ new []
+ {
+ "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", "w", "o", "r", "d", "s", ".", " "
+ }
+ )] // Double space Complex example
+ public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_1 (
+ string text,
+ int width,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines = TextFormatter.WordWrapText (text, width);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+ Assert.Equal (resultLines, wrappedLines);
+ var breakLines = "";
+
+ foreach (string line in wrappedLines)
+ {
+ breakLines += $"{line}{Environment.NewLine}";
+ }
+
+ var expected = string.Empty;
+
+ foreach (string line in resultLines)
+ {
+ expected += $"{line}{Environment.NewLine}";
+ }
+
+ Assert.Equal (expected, breakLines);
+ }
+
+ [Theory]
+ [InlineData (null, 3, new string [] { })] // null input
+ [InlineData ("", 3, new string [] { })] // Empty input
+ [InlineData ("1", 3, new [] { "1" })] // Short input
+ [InlineData ("12", 3, new [] { "12" })]
+ [InlineData ("123", 3, new [] { "123" })]
+ [InlineData ("123456", 3, new [] { "123", "456" })] // No spaces
+ [InlineData ("1234567", 3, new [] { "123", "456", "7" })] // No spaces
+ [InlineData (" ", 3, new [] { " " })] // Just Spaces; should result in a single space
+ [InlineData (" ", 3, new [] { " " })]
+ [InlineData (" ", 3, new [] { " " })]
+ [InlineData (" ", 3, new [] { " " })]
+ [InlineData ("12 456", 3, new [] { "12", "456" })] // Single Spaces
+ [InlineData (" 2 456", 3, new [] { " 2", "456" })] // Leading spaces should be preserved.
+ [InlineData (" 2 456 8", 3, new [] { " 2", "456", "8" })]
+ [InlineData (
+ "A sentence has words. ",
+ 3,
+ new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." }
+ )] // Complex example
+ [InlineData ("12 567", 3, new [] { "12 ", "567" })] // Double Spaces
+ [InlineData (" 3 567", 3, new [] { " 3", "567" })] // Double Leading spaces should be preserved.
+ [InlineData (" 3 678 1", 3, new [] { " 3", " 67", "8 ", "1" })]
+ [InlineData ("1 456", 3, new [] { "1 ", "456" })]
+ [InlineData (
+ "A sentence has words. ",
+ 3,
+ new [] { "A ", "sen", "ten", "ce ", " ", "has", "wor", "ds.", " " }
+ )] // Double space Complex example
+ public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_3 (
+ string text,
+ int width,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines = TextFormatter.WordWrapText (text, width);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+ Assert.Equal (resultLines, wrappedLines);
+ var breakLines = "";
+
+ foreach (string line in wrappedLines)
+ {
+ breakLines += $"{line}{Environment.NewLine}";
+ }
+
+ var expected = string.Empty;
+
+ foreach (string line in resultLines)
+ {
+ expected += $"{line}{Environment.NewLine}";
+ }
+
+ Assert.Equal (expected, breakLines);
+ }
+
+ [Theory]
+ [InlineData (null, 50, new string [] { })] // null input
+ [InlineData ("", 50, new string [] { })] // Empty input
+ [InlineData ("1", 50, new [] { "1" })] // Short input
+ [InlineData ("12", 50, new [] { "12" })]
+ [InlineData ("123", 50, new [] { "123" })]
+ [InlineData ("123456", 50, new [] { "123456" })] // No spaces
+ [InlineData ("1234567", 50, new [] { "1234567" })] // No spaces
+ [InlineData (" ", 50, new [] { " " })] // Just Spaces; should result in a single space
+ [InlineData (" ", 50, new [] { " " })]
+ [InlineData (" ", 50, new [] { " " })]
+ [InlineData ("12 456", 50, new [] { "12 456" })] // Single Spaces
+ [InlineData (" 2 456", 50, new [] { " 2 456" })] // Leading spaces should be preserved.
+ [InlineData (" 2 456 8", 50, new [] { " 2 456 8" })]
+ [InlineData ("A sentence has words. ", 50, new [] { "A sentence has words. " })] // Complex example
+ [InlineData ("12 567", 50, new [] { "12 567" })] // Double Spaces
+ [InlineData (" 3 567", 50, new [] { " 3 567" })] // Double Leading spaces should be preserved.
+ [InlineData (" 3 678 1", 50, new [] { " 3 678 1" })]
+ [InlineData ("1 456", 50, new [] { "1 456" })]
+ [InlineData (
+ "A sentence has words. ",
+ 50,
+ new [] { "A sentence has words. " }
+ )] // Double space Complex example
+ public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_50 (
+ string text,
+ int width,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines = TextFormatter.WordWrapText (text, width);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+ Assert.Equal (resultLines, wrappedLines);
+ var breakLines = "";
+
+ foreach (string line in wrappedLines)
+ {
+ breakLines += $"{line}{Environment.NewLine}";
+ }
+
+ var expected = string.Empty;
+
+ foreach (string line in resultLines)
+ {
+ expected += $"{line}{Environment.NewLine}";
+ }
+
+ Assert.Equal (expected, breakLines);
+ }
+
+ [Theory]
+ [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence ", "has words." })]
+ [InlineData ("A sentence has words.", 8, -13, new [] { "A ", "sentence", " has ", "words." })]
+ [InlineData ("A sentence has words.", 6, -15, new [] { "A ", "senten", "ce ", "has ", "words." })]
+ [InlineData ("A sentence has words.", 3, -18, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds." })]
+ [InlineData (
+ "A sentence has words.",
+ 2,
+ -19,
+ new [] { "A ", "se", "nt", "en", "ce", " ", "ha", "s ", "wo", "rd", "s." }
+ )]
+ [InlineData (
+ "A sentence has words.",
+ 1,
+ -20,
+ new []
+ {
+ "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", " ", "w", "o", "r", "d", "s", "."
+ }
+ )]
+ public void WordWrap_PreserveTrailingSpaces_True (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉 ", "があり ます。" })]
+ [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に ", "は", "言", "葉 ", "が", "あ", "り ", "ま", "す", "。" })]
+ [InlineData (
+ "文に は言葉 があり ます。",
+ 2,
+ -12,
+ new [] { "文", "に", " ", "は", "言", "葉", " ", "が", "あ", "り", " ", "ま", "す", "。" }
+ )]
+ [InlineData ("文に は言葉 があり ます。", 1, -13, new string [] { })]
+ public void WordWrap_PreserveTrailingSpaces_True_Wide_Runes (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("A sentence has words. ", 3, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds.", " " })]
+ [InlineData (
+ "A sentence has words. ",
+ 3,
+ new [] { "A ", " ", "sen", "ten", "ce ", " ", " ", " ", "has", " ", "wor", "ds.", " " }
+ )]
+ public void WordWrap_PreserveTrailingSpaces_True_With_Simple_Runes_Width_3 (
+ string text,
+ int width,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines = TextFormatter.WordWrapText (text, width, true);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+ Assert.Equal (resultLines, wrappedLines);
+ var breakLines = "";
+
+ foreach (string line in wrappedLines)
+ {
+ breakLines += $"{line}{Environment.NewLine}";
+ }
+
+ var expected = string.Empty;
+
+ foreach (string line in resultLines)
+ {
+ expected += $"{line}{Environment.NewLine}";
+ }
+
+ Assert.Equal (expected, breakLines);
+
+ // Double space Complex example - this is how VS 2022 does it
+ //text = "A sentence has words. ";
+ //breakLines = "";
+ //wrappedLines = TextFormatter.WordWrapText (text, width, preserveTrailingSpaces: true);
+ //foreach (var line in wrappedLines) {
+ // breakLines += $"{line}{Environment.NewLine}";
+ //}
+ //expected = "A " + Environment.NewLine +
+ // " se" + Environment.NewLine +
+ // " nt" + Environment.NewLine +
+ // " en" + Environment.NewLine +
+ // " ce" + Environment.NewLine +
+ // " " + Environment.NewLine +
+ // " " + Environment.NewLine +
+ // " " + Environment.NewLine +
+ // " ha" + Environment.NewLine +
+ // " s " + Environment.NewLine +
+ // " wo" + Environment.NewLine +
+ // " rd" + Environment.NewLine +
+ // " s." + Environment.NewLine;
+ //Assert.Equal (expected, breakLines);
+ }
+
+ [Theory]
+ [InlineData ("A sentence\t\t\t has words.", 14, -10, new [] { "A sentence\t", "\t\t has ", "words." })]
+ [InlineData (
+ "A sentence\t\t\t has words.",
+ 8,
+ -16,
+ new [] { "A ", "sentence", "\t\t", "\t ", "has ", "words." }
+ )]
+ [InlineData (
+ "A sentence\t\t\t has words.",
+ 3,
+ -21,
+ new [] { "A ", "sen", "ten", "ce", "\t", "\t", "\t", " ", "has", " ", "wor", "ds." }
+ )]
+ [InlineData (
+ "A sentence\t\t\t has words.",
+ 2,
+ -22,
+ new [] { "A ", "se", "nt", "en", "ce", "\t", "\t", "\t", " ", "ha", "s ", "wo", "rd", "s." }
+ )]
+ [InlineData (
+ "A sentence\t\t\t has words.",
+ 1,
+ -23,
+ new []
+ {
+ "A",
+ " ",
+ "s",
+ "e",
+ "n",
+ "t",
+ "e",
+ "n",
+ "c",
+ "e",
+ "\t",
+ "\t",
+ "\t",
+ " ",
+ "h",
+ "a",
+ "s",
+ " ",
+ "w",
+ "o",
+ "r",
+ "d",
+ "s",
+ "."
+ }
+ )]
+ public void WordWrap_PreserveTrailingSpaces_True_With_Tab (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines,
+ int tabWidth = 4
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true, tabWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("Constantinople", 14, 0, new [] { "Constantinople" })]
+ [InlineData ("Constantinople", 12, -2, new [] { "Constantinop", "le" })]
+ [InlineData ("Constantinople", 9, -5, new [] { "Constanti", "nople" })]
+ [InlineData ("Constantinople", 7, -7, new [] { "Constan", "tinople" })]
+ [InlineData ("Constantinople", 5, -9, new [] { "Const", "antin", "ople" })]
+ [InlineData ("Constantinople", 4, -10, new [] { "Cons", "tant", "inop", "le" })]
+ [InlineData (
+ "Constantinople",
+ 1,
+ -13,
+ new [] { "C", "o", "n", "s", "t", "a", "n", "t", "i", "n", "o", "p", "l", "e" }
+ )]
+ public void WordWrap_SingleWordLine (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 20, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
+ [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 19, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
+ [InlineData (
+ "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence.",
+ 19,
+ 0,
+ new [] { "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence." }
+ )]
+ public void WordWrap_Unicode_2LinesWithNonBreakingSpace (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 19, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
+ [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 18, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence", "." })]
+ [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 17, -2, new [] { "This\u00A0is\u00A0a\u00A0sentenc", "e." })]
+ [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 14, -5, new [] { "This\u00A0is\u00A0a\u00A0sent", "ence." })]
+ [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 10, -9, new [] { "This\u00A0is\u00A0a\u00A0", "sentence." })]
+ [InlineData (
+ "This\u00A0is\u00A0a\u00A0sentence.",
+ 7,
+ -12,
+ new [] { "This\u00A0is", "\u00A0a\u00A0sent", "ence." }
+ )]
+ [InlineData (
+ "This\u00A0is\u00A0a\u00A0sentence.",
+ 5,
+ -14,
+ new [] { "This\u00A0", "is\u00A0a\u00A0", "sente", "nce." }
+ )]
+ [InlineData (
+ "This\u00A0is\u00A0a\u00A0sentence.",
+ 1,
+ -18,
+ new []
+ {
+ "T", "h", "i", "s", "\u00A0", "i", "s", "\u00A0", "a", "\u00A0", "s", "e", "n", "t", "e", "n", "c", "e", "."
+ }
+ )]
+ public void WordWrap_Unicode_LineWithNonBreakingSpace (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ [Theory]
+ [InlineData (
+ "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
+ 51,
+ 0,
+ new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
+ )]
+ [InlineData (
+ "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
+ 50,
+ -1,
+ new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
+ )]
+ [InlineData (
+ "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
+ 46,
+ -5,
+ new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" }
+ )]
+ [InlineData (
+ "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
+ 26,
+ -25,
+ new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
+ )]
+ [InlineData (
+ "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
+ 17,
+ -34,
+ new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑ", "ฒณดตถทธนบปผฝพฟภมย", "รฤลฦวศษสหฬอฮฯะัาำ" }
+ )]
+ [InlineData (
+ "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
+ 13,
+ -38,
+ new [] { "กขฃคฅฆงจฉชซฌญ", "ฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦว", "ศษสหฬอฮฯะัาำ" }
+ )]
+ [InlineData (
+ "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
+ 1,
+ -50,
+ new []
+ {
+ "ก",
+ "ข",
+ "ฃ",
+ "ค",
+ "ฅ",
+ "ฆ",
+ "ง",
+ "จ",
+ "ฉ",
+ "ช",
+ "ซ",
+ "ฌ",
+ "ญ",
+ "ฎ",
+ "ฏ",
+ "ฐ",
+ "ฑ",
+ "ฒ",
+ "ณ",
+ "ด",
+ "ต",
+ "ถ",
+ "ท",
+ "ธ",
+ "น",
+ "บ",
+ "ป",
+ "ผ",
+ "ฝ",
+ "พ",
+ "ฟ",
+ "ภ",
+ "ม",
+ "ย",
+ "ร",
+ "ฤ",
+ "ล",
+ "ฦ",
+ "ว",
+ "ศ",
+ "ษ",
+ "ส",
+ "ห",
+ "ฬ",
+ "อ",
+ "ฮ",
+ "ฯ",
+ "ะั",
+ "า",
+ "ำ"
+ }
+ )]
+ public void WordWrap_Unicode_SingleWordLine (
+ string text,
+ int maxWidth,
+ int widthOffset,
+ IEnumerable resultLines
+ )
+ {
+ List wrappedLines;
+
+ IEnumerable zeroWidth = text.EnumerateRunes ().Where (r => r.GetColumns () == 0);
+ Assert.Single (zeroWidth);
+ Assert.Equal ('ั', zeroWidth.ElementAt (0).Value);
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth
+ >= (wrappedLines.Count > 0
+ ? wrappedLines.Max (
+ l => l.GetRuneCount ()
+ + zeroWidth.Count ()
+ - 1
+ + widthOffset
+ )
+ : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
+ /// WordWrap strips CRLF
+ [Theory]
+ [InlineData (
+ "A sentence has words.\nA paragraph has lines.",
+ 44,
+ 0,
+ new [] { "A sentence has words.A paragraph has lines." }
+ )]
+ [InlineData (
+ "A sentence has words.\nA paragraph has lines.",
+ 43,
+ -1,
+ new [] { "A sentence has words.A paragraph has lines." }
+ )]
+ [InlineData (
+ "A sentence has words.\nA paragraph has lines.",
+ 38,
+ -6,
+ new [] { "A sentence has words.A paragraph has", "lines." }
+ )]
+ [InlineData (
+ "A sentence has words.\nA paragraph has lines.",
+ 34,
+ -10,
+ new [] { "A sentence has words.A paragraph", "has lines." }
+ )]
+ [InlineData (
+ "A sentence has words.\nA paragraph has lines.",
+ 27,
+ -17,
+ new [] { "A sentence has words.A", "paragraph has lines." }
+ )]
+
+ // Unicode
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
+ 69,
+ 0,
+ new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
+ 68,
+ -1,
+ new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
+ 63,
+ -6,
+ new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has", "Линии." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
+ 59,
+ -10,
+ new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт", "has Линии." }
+ )]
+ [InlineData (
+ "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
+ 52,
+ -17,
+ new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode", "Пункт has Линии." }
+ )]
+ public void WordWrap_WithNewLines (string text, int maxWidth, int widthOffset, IEnumerable resultLines)
+ {
+ List wrappedLines;
+
+ Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+ int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
+ wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
+ Assert.Equal (wrappedLines.Count, resultLines.Count ());
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
+ );
+
+ Assert.True (
+ expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
+ );
+ Assert.Equal (resultLines, wrappedLines);
+ }
+
#region FormatAndGetSizeTests
// TODO: Add multi-line examples
@@ -7193,46 +7218,4 @@ B ")]
}
#endregion
-
- [Fact]
- [SetupFakeDriver]
- public void UICatalog_AboutBox_Text ()
- {
- TextFormatter tf = new ()
- {
- Text = UICatalog.UICatalogApp.GetAboutBoxMessage (),
- Alignment = Alignment.Center,
- VerticalAlignment = Alignment.Start,
- WordWrap = false,
- MultiLine = true,
- HotKeySpecifier = (Rune)0xFFFF
- };
-
- Size tfSize = tf.FormatAndGetSize ();
- Assert.Equal (new Size (58, 13), tfSize);
-
- ((FakeDriver)Application.Driver).SetBufferSize (tfSize.Width, tfSize.Height);
-
- Application.Driver.FillRect (Application.Screen, (Rune)'*');
- tf.Draw (Application.Screen, Attribute.Default, Attribute.Default);
-
- string expectedText = """
- ******UI Catalog: A comprehensive sample library for******
- **********************************************************
- _______ _ _ _____ _
- |__ __| (_) | | / ____| (_)
- | | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _
- | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |
- | | __/ | | | | | | | | | | | (_| | || |__| | |_| | |
- |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|
- **********************************************************
- **********************v2 - Pre-Alpha**********************
- **********************************************************
- **********https://github.com/gui-cs/Terminal.Gui**********
- **********************************************************
- """;
-
- TestHelpers.AssertDriverContentsAre (expectedText.ReplaceLineEndings (), _output);
-
- }
}
diff --git a/UnitTests/View/Layout/Dim.AutoTests.cs b/UnitTests/View/Layout/Dim.AutoTests.cs
index bd0e2eaf7..5f6767310 100644
--- a/UnitTests/View/Layout/Dim.AutoTests.cs
+++ b/UnitTests/View/Layout/Dim.AutoTests.cs
@@ -4,33 +4,36 @@ using static Terminal.Gui.Dim;
namespace Terminal.Gui.LayoutTests;
+[Trait("Category", "Layout")]
public partial class DimAutoTests (ITestOutputHelper output)
{
private readonly ITestOutputHelper _output = output;
- private class DimAutoTestView : View
+ [SetupFakeDriver]
+ [Fact]
+ public void Change_To_Non_Auto_Resets_ContentSize ()
{
- public DimAutoTestView ()
+ View view = new ()
{
- ValidatePosDim = true;
- Width = Auto ();
- Height = Auto ();
- }
+ Width = Auto (),
+ Height = Auto (),
+ Text = "01234"
+ };
+ view.SetRelativeLayout (new (100, 100));
+ Assert.Equal (new (0, 0, 5, 1), view.Frame);
+ Assert.Equal (new (5, 1), view.GetContentSize ());
- public DimAutoTestView (Dim width, Dim height)
- {
- ValidatePosDim = true;
- Width = width;
- Height = height;
- }
+ // Change text to a longer string
+ view.Text = "0123456789";
- public DimAutoTestView (string text, Dim width, Dim height)
- {
- ValidatePosDim = true;
- Text = text;
- Width = width;
- Height = height;
- }
+ Assert.Equal (new (0, 0, 10, 1), view.Frame);
+ Assert.Equal (new (10, 1), view.GetContentSize ());
+
+ // If ContentSize was reset, these should cause it to update
+ view.Width = 5;
+ view.Height = 1;
+
+ Assert.Equal (new (5, 1), view.GetContentSize ());
}
[Theory]
@@ -74,6 +77,46 @@ public partial class DimAutoTests (ITestOutputHelper output)
Assert.Equal (new (0, 0, 10, expectedHeight), superView.Frame);
}
+ [Theory]
+ [CombinatorialData]
+ public void HotKey_TextFormatter_Height_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
+ {
+ View view = new ()
+ {
+ HotKeySpecifier = (Rune)'_',
+ Text = text,
+ Width = Auto (),
+ Height = 1
+ };
+ Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
+ Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
+
+ view = new ()
+ {
+ HotKeySpecifier = (Rune)'_',
+ TextDirection = TextDirection.TopBottom_LeftRight,
+ Text = text,
+ Width = 1,
+ Height = Auto ()
+ };
+ Assert.Equal (1, view.TextFormatter.ConstrainToWidth);
+ Assert.Equal (4, view.TextFormatter.ConstrainToHeight);
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public void HotKey_TextFormatter_Width_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
+ {
+ View view = new ()
+ {
+ Text = text,
+ Height = 1,
+ Width = Auto ()
+ };
+ Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
+ Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
+ }
+
[Fact]
public void NoSubViews_Does_Nothing ()
{
@@ -158,6 +201,122 @@ public partial class DimAutoTests (ITestOutputHelper output)
Assert.Equal (new (0, 0, expectedWidth, expectedHeight), superView.Frame);
}
+ [Fact]
+ public void TestEquality ()
+ {
+ var a = new DimAuto (
+ MaximumContentDim: null,
+ MinimumContentDim: 1,
+ Style: DimAutoStyle.Auto
+ );
+
+ var b = new DimAuto (
+ MaximumContentDim: null,
+ MinimumContentDim: 1,
+ Style: DimAutoStyle.Auto
+ );
+
+ var c = new DimAuto(
+ MaximumContentDim: 2,
+ MinimumContentDim: 1,
+ Style: DimAutoStyle.Auto
+ );
+
+ var d = new DimAuto (
+ MaximumContentDim: null,
+ MinimumContentDim: 1,
+ Style: DimAutoStyle.Content
+ );
+
+ var e = new DimAuto (
+ MaximumContentDim: null,
+ MinimumContentDim: 2,
+ Style: DimAutoStyle.Auto
+ );
+
+ // Test equality with same values
+ Assert.True (a.Equals (b));
+ Assert.True (a.GetHashCode () == b.GetHashCode ());
+
+ // Test inequality with different MaximumContentDim
+ Assert.False (a.Equals (c));
+ Assert.False (a.GetHashCode () == c.GetHashCode ());
+
+ // Test inequality with different Style
+ Assert.False (a.Equals (d));
+ Assert.False (a.GetHashCode () == d.GetHashCode ());
+
+ // Test inequality with different MinimumContentDim
+ Assert.False (a.Equals (e));
+ Assert.False (a.GetHashCode () == e.GetHashCode ());
+
+ // Test inequality with null
+ Assert.False (a.Equals (null));
+ }
+
+ [Fact]
+ public void TestEquality_Simple ()
+ {
+ Dim a = Auto ();
+ Dim b = Auto ();
+ Assert.True (a.Equals (b));
+ Assert.True (a.GetHashCode () == b.GetHashCode ());
+ }
+
+ [Fact]
+ public void TextFormatter_Settings_Change_View_Size ()
+ {
+ View view = new ()
+ {
+ Text = "_1234",
+ Width = Auto ()
+ };
+ Assert.Equal (new (4, 0), view.Frame.Size);
+
+ view.Height = 1;
+ view.SetRelativeLayout (Application.Screen.Size);
+ Assert.Equal (new (4, 1), view.Frame.Size);
+ Size lastSize = view.Frame.Size;
+
+ view.TextAlignment = Alignment.Fill;
+ Assert.Equal (lastSize, view.Frame.Size);
+
+ view = new ()
+ {
+ Text = "_1234",
+ Width = Auto (),
+ Height = 1
+ };
+ view.SetRelativeLayout (Application.Screen.Size);
+
+ lastSize = view.Frame.Size;
+ view.VerticalTextAlignment = Alignment.Center;
+ Assert.Equal (lastSize, view.Frame.Size);
+
+ view = new ()
+ {
+ Text = "_1234",
+ Width = Auto (),
+ Height = 1
+ };
+ view.SetRelativeLayout (Application.Screen.Size);
+ lastSize = view.Frame.Size;
+ view.HotKeySpecifier = (Rune)'*';
+ view.SetRelativeLayout (Application.Screen.Size);
+ Assert.NotEqual (lastSize, view.Frame.Size);
+
+ view = new ()
+ {
+ Text = "_1234",
+ Width = Auto (),
+ Height = 1
+ };
+ view.SetRelativeLayout (Application.Screen.Size);
+ lastSize = view.Frame.Size;
+ view.Text = "*ABCD";
+ Assert.NotEqual (lastSize, view.Frame.Size);
+ }
+
// Test validation
[Fact]
public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Dims ()
@@ -410,46 +569,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
Assert.Equal (expectedWidth, superView.Frame.Width);
}
- [Theory]
- [InlineData (0, 1, 1)]
- [InlineData (1, 1, 1)]
- [InlineData (9, 1, 1)]
- [InlineData (10, 1, 1)]
- [InlineData (0, 10, 10)]
- [InlineData (1, 10, 10)]
- [InlineData (9, 10, 10)]
- [InlineData (10, 10, 10)]
- public void Width_Auto_Text_Does_Not_Constrain_To_SuperView (int subX, int textLen, int expectedSubWidth)
- {
- var superView = new View
- {
- X = 0,
- Y = 0,
- Width = 10,
- Height = 1,
- ValidatePosDim = true
- };
-
- var subView = new View
- {
- Text = new ('*', textLen),
- X = subX,
- Y = 0,
- Width = Auto (DimAutoStyle.Text),
- Height = 1,
- ValidatePosDim = true
- };
-
- superView.Add (subView);
-
- superView.BeginInit ();
- superView.EndInit ();
- superView.SetRelativeLayout (superView.GetContentSize ());
-
- superView.LayoutSubviews ();
- Assert.Equal (expectedSubWidth, subView.Frame.Width);
- }
-
[Theory]
[InlineData (0, 1, 1)]
[InlineData (1, 1, 1)]
@@ -499,31 +618,69 @@ public partial class DimAutoTests (ITestOutputHelper output)
Assert.Equal (expectedSubWidth, subView.Frame.Width);
}
- [SetupFakeDriver]
- [Fact]
- public void Change_To_Non_Auto_Resets_ContentSize ()
+ [Theory]
+ [InlineData (0, 1, 1)]
+ [InlineData (1, 1, 1)]
+ [InlineData (9, 1, 1)]
+ [InlineData (10, 1, 1)]
+ [InlineData (0, 10, 10)]
+ [InlineData (1, 10, 10)]
+ [InlineData (9, 10, 10)]
+ [InlineData (10, 10, 10)]
+ public void Width_Auto_Text_Does_Not_Constrain_To_SuperView (int subX, int textLen, int expectedSubWidth)
{
- View view = new ()
+ var superView = new View
{
- Width = Auto (),
- Height = Auto (),
- Text = "01234"
+ X = 0,
+ Y = 0,
+ Width = 10,
+ Height = 1,
+ ValidatePosDim = true
};
- view.SetRelativeLayout (new (100, 100));
- Assert.Equal (new (0, 0, 5, 1), view.Frame);
- Assert.Equal (new (5, 1), view.GetContentSize ());
- // Change text to a longer string
- view.Text = "0123456789";
+ var subView = new View
+ {
+ Text = new ('*', textLen),
+ X = subX,
+ Y = 0,
+ Width = Auto (DimAutoStyle.Text),
+ Height = 1,
+ ValidatePosDim = true
+ };
- Assert.Equal (new (0, 0, 10, 1), view.Frame);
- Assert.Equal (new (10, 1), view.GetContentSize ());
+ superView.Add (subView);
- // If ContentSize was reset, these should cause it to update
- view.Width = 5;
- view.Height = 1;
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (superView.GetContentSize ());
- Assert.Equal (new (5, 1), view.GetContentSize ());
+ superView.LayoutSubviews ();
+ Assert.Equal (expectedSubWidth, subView.Frame.Width);
+ }
+
+ private class DimAutoTestView : View
+ {
+ public DimAutoTestView ()
+ {
+ ValidatePosDim = true;
+ Width = Auto ();
+ Height = Auto ();
+ }
+
+ public DimAutoTestView (Dim width, Dim height)
+ {
+ ValidatePosDim = true;
+ Width = width;
+ Height = height;
+ }
+
+ public DimAutoTestView (string text, Dim width, Dim height)
+ {
+ ValidatePosDim = true;
+ Text = text;
+ Width = width;
+ Height = height;
+ }
}
#region DimAutoStyle.Auto tests
@@ -544,7 +701,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
view.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
-
}
[Fact]
@@ -653,7 +809,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
view.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
-
}
[Theory]
@@ -665,15 +820,14 @@ public partial class DimAutoTests (ITestOutputHelper output)
public void DimAutoStyle_Text_Sizes_Correctly_With_Min (string text, int minWidth, int minHeight, int expectedW, int expectedH)
{
var view = new View ();
- view.Width = Auto (DimAutoStyle.Text, minimumContentDim: minWidth);
- view.Height = Auto (DimAutoStyle.Text, minimumContentDim: minHeight);
+ view.Width = Auto (DimAutoStyle.Text, minWidth);
+ view.Height = Auto (DimAutoStyle.Text, minHeight);
view.Text = text;
view.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
-
}
[Theory]
@@ -699,7 +853,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
[InlineData ("01234", 5, 1)]
[InlineData ("01234ABCDE", 10, 1)]
[InlineData ("01234\nABCDE", 5, 2)]
-
public void DimAutoStyle_Text_NoMin_Not_Constrained_By_ContentSize (string text, int expectedW, int expectedH)
{
var view = new View ();
@@ -711,7 +864,6 @@ public partial class DimAutoTests (ITestOutputHelper output)
Assert.Equal (new (expectedW, expectedH), view.Frame.Size);
}
-
[Theory]
[InlineData ("", 0, 0)]
[InlineData (" ", 1, 1)]
@@ -720,7 +872,7 @@ public partial class DimAutoTests (ITestOutputHelper output)
[InlineData ("01234\nABCDE", 5, 2)]
public void DimAutoStyle_Text_NoMin_Not_Constrained_By_SuperView (string text, int expectedW, int expectedH)
{
- var superView = new View ()
+ var superView = new View
{
Width = 1, Height = 1
};
@@ -870,100 +1022,5 @@ public partial class DimAutoTests (ITestOutputHelper output)
#endregion DimAutoStyle.Content tests
- [Fact]
- public void TextFormatter_Settings_Change_View_Size ()
- {
- View view = new ()
- {
- Text = "_1234",
- Width = Auto ()
- };
- Assert.Equal (new (4, 0), view.Frame.Size);
-
- view.Height = 1;
- view.SetRelativeLayout (Application.Screen.Size);
- Assert.Equal (new (4, 1), view.Frame.Size);
- Size lastSize = view.Frame.Size;
-
- view.TextAlignment = Alignment.Fill;
- Assert.Equal (lastSize, view.Frame.Size);
-
- view = new ()
- {
- Text = "_1234",
- Width = Auto (),
- Height = 1
- };
- view.SetRelativeLayout (Application.Screen.Size);
-
- lastSize = view.Frame.Size;
- view.VerticalTextAlignment = Alignment.Center;
- Assert.Equal (lastSize, view.Frame.Size);
-
- view = new ()
- {
- Text = "_1234",
- Width = Auto (),
- Height = 1
- };
- view.SetRelativeLayout (Application.Screen.Size);
- lastSize = view.Frame.Size;
- view.HotKeySpecifier = (Rune)'*';
- view.SetRelativeLayout (Application.Screen.Size);
- Assert.NotEqual (lastSize, view.Frame.Size);
-
- view = new ()
- {
- Text = "_1234",
- Width = Auto (),
- Height = 1
- };
- view.SetRelativeLayout (Application.Screen.Size);
- lastSize = view.Frame.Size;
- view.Text = "*ABCD";
- Assert.NotEqual (lastSize, view.Frame.Size);
- }
-
-
- [Theory]
- [CombinatorialData]
- public void HotKey_TextFormatter_Width_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
- {
- View view = new ()
- {
- Text = text,
- Height = 1,
- Width = Auto ()
- };
- Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
- Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
- }
-
- [Theory]
- [CombinatorialData]
- public void HotKey_TextFormatter_Height_Correct ([CombinatorialValues ("1234", "_1234", "1_234", "____")] string text)
- {
- View view = new ()
- {
- HotKeySpecifier = (Rune)'_',
- Text = text,
- Width = Auto (),
- Height = 1
- };
- Assert.Equal (4, view.TextFormatter.ConstrainToWidth);
- Assert.Equal (1, view.TextFormatter.ConstrainToHeight);
-
- view = new ()
- {
- HotKeySpecifier = (Rune)'_',
- TextDirection = TextDirection.TopBottom_LeftRight,
- Text = text,
- Width = 1,
- Height = Auto ()
- };
- Assert.Equal (1, view.TextFormatter.ConstrainToWidth);
- Assert.Equal (4, view.TextFormatter.ConstrainToHeight);
- }
-
// Test variations of Frame
}
diff --git a/UnitTests/View/Layout/Dim.FuncTests.cs b/UnitTests/View/Layout/Dim.FuncTests.cs
index 12b9c562a..ab40cbf85 100644
--- a/UnitTests/View/Layout/Dim.FuncTests.cs
+++ b/UnitTests/View/Layout/Dim.FuncTests.cs
@@ -14,9 +14,12 @@ public class DimFuncTests (ITestOutputHelper output)
Func f2 = () => 0;
Dim dim1 = Func (f1);
- Dim dim2 = Func (f2);
+ Dim dim2 = Func (f1);
Assert.Equal (dim1, dim2);
+ dim2 = Func (f2);
+ Assert.NotEqual (dim1, dim2);
+
f2 = () => 1;
dim2 = Func (f2);
Assert.NotEqual (dim1, dim2);
diff --git a/UnitTests/View/Layout/Dim.PercentTests.cs b/UnitTests/View/Layout/Dim.PercentTests.cs
index adf23b46c..443e4e96f 100644
--- a/UnitTests/View/Layout/Dim.PercentTests.cs
+++ b/UnitTests/View/Layout/Dim.PercentTests.cs
@@ -15,7 +15,6 @@ public class DimPercentTests
Assert.Equal (50, result);
}
-
[Fact]
public void DimPercent_Equals ()
{
@@ -63,6 +62,15 @@ public class DimPercentTests
Assert.NotEqual (dim1, dim2);
}
+ [Fact]
+ public void TestEquality ()
+ {
+ var a = Dim.Percent (32);
+ var b = Dim.Percent (32);
+ Assert.True (a.Equals (b));
+ Assert.True (a.GetHashCode () == b.GetHashCode ());
+ }
+
[Fact]
public void DimPercent_Invalid_Throws ()
{
@@ -157,7 +165,7 @@ public class DimPercentTests
}
[Theory]
- [InlineData(0)]
+ [InlineData (0)]
[InlineData (1)]
[InlineData (50)]
[InlineData (100)]
diff --git a/UnitTests/View/Layout/Pos.AlignTests.cs b/UnitTests/View/Layout/Pos.AlignTests.cs
index 216c7bdc6..05931e648 100644
--- a/UnitTests/View/Layout/Pos.AlignTests.cs
+++ b/UnitTests/View/Layout/Pos.AlignTests.cs
@@ -27,58 +27,58 @@ public class PosAlignTests
Assert.Equal (0, posAlign.Aligner.ContainerSize);
}
- [Theory]
- [InlineData (Alignment.Start, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
- [InlineData (Alignment.Center, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
- [InlineData (Alignment.Start, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
- [InlineData (Alignment.Center, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
- [InlineData (Alignment.Start, Alignment.Start, AlignmentModes.StartToEnd, AlignmentModes.AddSpaceBetweenItems, false)]
- public void PosAlign_Equals (Alignment align1, Alignment align2, AlignmentModes mode1, AlignmentModes mode2, bool expectedEquals)
- {
- var posAlign1 = new PosAlign
- {
- Aligner = new()
- {
- Alignment = align1,
- AlignmentModes = mode1
- }
- };
+ //[Theory]
+ //[InlineData (Alignment.Start, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
+ //[InlineData (Alignment.Center, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, true)]
+ //[InlineData (Alignment.Start, Alignment.Center, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
+ //[InlineData (Alignment.Center, Alignment.Start, AlignmentModes.AddSpaceBetweenItems, AlignmentModes.AddSpaceBetweenItems, false)]
+ //[InlineData (Alignment.Start, Alignment.Start, AlignmentModes.StartToEnd, AlignmentModes.AddSpaceBetweenItems, false)]
+ //public void PosAlign_Equals (Alignment align1, Alignment align2, AlignmentModes mode1, AlignmentModes mode2, bool expectedEquals)
+ //{
+ // var posAlign1 = new PosAlign
+ // {
+ // Aligner = new ()
+ // {
+ // Alignment = align1,
+ // AlignmentModes = mode1
+ // }
+ // };
- var posAlign2 = new PosAlign
- {
- Aligner = new()
- {
- Alignment = align2,
- AlignmentModes = mode2
- }
- };
+ // var posAlign2 = new PosAlign
+ // {
+ // Aligner = new ()
+ // {
+ // Alignment = align2,
+ // AlignmentModes = mode2
+ // }
+ // };
- Assert.Equal (expectedEquals, posAlign1.Equals (posAlign2));
- Assert.Equal (expectedEquals, posAlign2.Equals (posAlign1));
- }
+ // Assert.Equal (expectedEquals, posAlign1.Equals (posAlign2));
+ // Assert.Equal (expectedEquals, posAlign2.Equals (posAlign1));
+ //}
- [Fact]
- public void PosAlign_Equals_CachedLocation_Not_Used ()
- {
- View superView = new ()
- {
- Width = 10,
- Height = 25
- };
- View view = new ();
- superView.Add (view);
+ //[Fact]
+ //public void PosAlign_Equals_CachedLocation_Not_Used ()
+ //{
+ // View superView = new ()
+ // {
+ // Width = 10,
+ // Height = 25
+ // };
+ // View view = new ();
+ // superView.Add (view);
- Pos posAlign1 = Pos.Align (Alignment.Center);
- view.X = posAlign1;
- int pos1 = posAlign1.Calculate (10, Dim.Absolute (0)!, view, Dimension.Width);
+ // Pos posAlign1 = Pos.Align (Alignment.Center);
+ // view.X = posAlign1;
+ // int pos1 = posAlign1.Calculate (10, Dim.Absolute (0)!, view, Dimension.Width);
- Pos posAlign2 = Pos.Align (Alignment.Center);
- view.Y = posAlign2;
- int pos2 = posAlign2.Calculate (25, Dim.Absolute (0)!, view, Dimension.Height);
+ // Pos posAlign2 = Pos.Align (Alignment.Center);
+ // view.Y = posAlign2;
+ // int pos2 = posAlign2.Calculate (25, Dim.Absolute (0)!, view, Dimension.Height);
- Assert.NotEqual (pos1, pos2);
- Assert.Equal (posAlign1, posAlign2);
- }
+ // Assert.NotEqual (pos1, pos2);
+ // Assert.Equal (posAlign1, posAlign2);
+ //}
[Fact]
public void PosAlign_ToString ()
diff --git a/UnitTests/View/Layout/Pos.AnchorEndTests.cs b/UnitTests/View/Layout/Pos.AnchorEndTests.cs
index 199e9e50e..b5dd8eb34 100644
--- a/UnitTests/View/Layout/Pos.AnchorEndTests.cs
+++ b/UnitTests/View/Layout/Pos.AnchorEndTests.cs
@@ -27,15 +27,6 @@ public class PosAnchorEndTests (ITestOutputHelper output)
Assert.Equal (expectedEquals, posAnchorEnd2.Equals (posAnchorEnd1));
}
- [Fact]
- public void PosAnchorEnd_GetHashCode ()
- {
- var posAnchorEnd = new PosAnchorEnd (10);
- var expectedHashCode = 10.GetHashCode ();
-
- Assert.Equal (expectedHashCode, posAnchorEnd.GetHashCode ());
- }
-
[Fact]
public void PosAnchorEnd_ToString ()
{
diff --git a/UnitTests/View/Layout/Pos.CenterTests.cs b/UnitTests/View/Layout/Pos.CenterTests.cs
index 7aa7a2ff9..ef7d5cc8b 100644
--- a/UnitTests/View/Layout/Pos.CenterTests.cs
+++ b/UnitTests/View/Layout/Pos.CenterTests.cs
@@ -15,16 +15,6 @@ public class PosCenterTests (ITestOutputHelper output)
Assert.NotNull (posCenter);
}
- [Fact]
- public void PosCenter_Equals ()
- {
- var posCenter1 = new PosCenter ();
- var posCenter2 = new PosCenter ();
-
- Assert.False (posCenter1.Equals (posCenter2));
- Assert.False (posCenter2.Equals (posCenter1));
- }
-
[Fact]
public void PosCenter_ToString ()
{
diff --git a/UnitTests/View/Layout/Pos.FuncTests.cs b/UnitTests/View/Layout/Pos.FuncTests.cs
index 48b48a681..9874c9160 100644
--- a/UnitTests/View/Layout/Pos.FuncTests.cs
+++ b/UnitTests/View/Layout/Pos.FuncTests.cs
@@ -13,7 +13,7 @@ public class PosFuncTests (ITestOutputHelper output)
Func f2 = () => 0;
Pos pos1 = Pos.Func (f1);
- Pos pos2 = Pos.Func (f2);
+ Pos pos2 = Pos.Func (f1);
Assert.Equal (pos1, pos2);
f2 = () => 1;
diff --git a/UnitTests/View/Layout/Pos.Tests.cs b/UnitTests/View/Layout/Pos.Tests.cs
index c3e6bb9a2..4b37d27ea 100644
--- a/UnitTests/View/Layout/Pos.Tests.cs
+++ b/UnitTests/View/Layout/Pos.Tests.cs
@@ -145,21 +145,6 @@ public class PosTests ()
);
}
- [Fact]
- public void PosFunction_Equal ()
- {
- Func f1 = () => 0;
- Func f2 = () => 0;
-
- Pos pos1 = Pos.Func (f1);
- Pos pos2 = Pos.Func (f2);
- Assert.Equal (pos1, pos2);
-
- f2 = () => 1;
- pos2 = Pos.Func (f2);
- Assert.NotEqual (pos1, pos2);
- }
-
[Fact]
public void PosFunction_SetsValue ()
{
diff --git a/UnitTests/View/TitleTests.cs b/UnitTests/View/TitleTests.cs
index 0c2a6a5b7..c808e66e4 100644
--- a/UnitTests/View/TitleTests.cs
+++ b/UnitTests/View/TitleTests.cs
@@ -3,7 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
-public class TitleTests (ITestOutputHelper output)
+public class TitleTests
{
// Unit tests that verify look & feel of title are in BorderTests.cs
diff --git a/UnitTests/Views/ContextMenuTests.cs b/UnitTests/Views/ContextMenuTests.cs
index efeb709fb..99f9e3dfe 100644
--- a/UnitTests/Views/ContextMenuTests.cs
+++ b/UnitTests/Views/ContextMenuTests.cs
@@ -1359,4 +1359,53 @@ public class ContextMenuTests (ITestOutputHelper output)
)
};
}
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Handling_TextField_With_Opened_ContextMenu_By_Mouse_HasFocus ()
+ {
+ var tf1 = new TextField { Width = 10, Text = "TextField 1" };
+ var tf2 = new TextField { Y = 2, Width = 10, Text = "TextField 2" };
+ var win = new Window ();
+ win.Add (tf1, tf2);
+ var rs = Application.Begin (win);
+
+ Assert.True (tf1.HasFocus);
+ Assert.False (tf2.HasFocus);
+ Assert.Equal (2, win.Subviews.Count);
+ Assert.Null (Application.MouseEnteredView);
+
+ // Right click on tf2 to open context menu
+ Application.OnMouseEvent (new () { Position = new (1, 3), Flags = MouseFlags.Button3Clicked });
+ Assert.False (tf1.HasFocus);
+ Assert.False (tf2.HasFocus);
+ Assert.Equal (3, win.Subviews.Count);
+ Assert.True (tf2.ContextMenu.MenuBar.IsMenuOpen);
+ Assert.True (win.Focused is Menu);
+ Assert.True (Application.MouseGrabView is MenuBar);
+ Assert.Equal (tf2, Application.MouseEnteredView);
+
+ // Click on tf1 to focus it, which cause context menu being closed
+ Application.OnMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked });
+ Assert.True (tf1.HasFocus);
+ Assert.False (tf2.HasFocus);
+ Assert.Equal (2, win.Subviews.Count);
+ Assert.Null (tf2.ContextMenu.MenuBar);
+ Assert.Equal (win.Focused, tf1);
+ Assert.Null (Application.MouseGrabView);
+ Assert.Equal (tf1, Application.MouseEnteredView);
+
+ // Click on tf2 to focus it
+ Application.OnMouseEvent (new () { Position = new (1, 3), Flags = MouseFlags.Button1Clicked });
+ Assert.False (tf1.HasFocus);
+ Assert.True (tf2.HasFocus);
+ Assert.Equal (2, win.Subviews.Count);
+ Assert.Null (tf2.ContextMenu.MenuBar);
+ Assert.Equal (win.Focused, tf2);
+ Assert.Null (Application.MouseGrabView);
+ Assert.Equal (tf2, Application.MouseEnteredView);
+
+ Application.End (rs);
+ win.Dispose ();
+ }
}
diff --git a/UnitTests/Views/ListViewTests.cs b/UnitTests/Views/ListViewTests.cs
index 20180b45f..399dbf9c2 100644
--- a/UnitTests/Views/ListViewTests.cs
+++ b/UnitTests/Views/ListViewTests.cs
@@ -674,8 +674,10 @@ Item 6",
private class NewListDataSource : IListDataSource
{
+#pragma warning disable CS0067
///
public event NotifyCollectionChangedEventHandler CollectionChanged;
+#pragma warning restore CS0067
public int Count => 0;
public int Length => 0;
diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs
index ac8de4db0..9b1067290 100644
--- a/UnitTests/Views/MenuBarTests.cs
+++ b/UnitTests/Views/MenuBarTests.cs
@@ -3800,7 +3800,7 @@ Edit
menuItem.RemoveMenuItem ();
Assert.Single (menuBar.Menus);
- Assert.Equal (null, menuBar.Menus [0].Children);
+ Assert.Null (menuBar.Menus [0].Children);
Assert.Contains (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
Assert.DoesNotContain (Key.I, menuBar.KeyBindings.Bindings);
diff --git a/UnitTests/Views/NumericUpDownTests.cs b/UnitTests/Views/NumericUpDownTests.cs
index 9743de019..571a9fab4 100644
--- a/UnitTests/Views/NumericUpDownTests.cs
+++ b/UnitTests/Views/NumericUpDownTests.cs
@@ -3,7 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
-public class NumericUpDownTests (ITestOutputHelper _output)
+public class NumericUpDownTests
{
[Fact]
public void WhenCreated_ShouldHaveDefaultValues_int ()
diff --git a/UnitTests/Views/StatusBarTests.cs b/UnitTests/Views/StatusBarTests.cs
index 6f6f93061..14866a154 100644
--- a/UnitTests/Views/StatusBarTests.cs
+++ b/UnitTests/Views/StatusBarTests.cs
@@ -1,7 +1,7 @@
using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
-public class StatusBarTests (ITestOutputHelper output)
+public class StatusBarTests
{
[Fact]
public void AddItemAt_RemoveItem_Replacing ()
diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs
index 6db72eef7..0850689f6 100644
--- a/UnitTests/Views/TableViewTests.cs
+++ b/UnitTests/Views/TableViewTests.cs
@@ -2582,7 +2582,9 @@ A B C
TestHelpers.AssertDriverContentsAre (expected, output);
+#pragma warning disable xUnit2029
Assert.Empty (pets.Where (p => p.IsPicked));
+#pragma warning restore xUnit2029
tv.NewKeyDownEvent (Key.Space);
@@ -2795,7 +2797,9 @@ A B C
tv.NewKeyDownEvent (Key.Space);
+#pragma warning disable xUnit2029
Assert.Empty (pets.Where (p => p.IsPicked));
+#pragma warning restore xUnit2029
tv.Draw ();
@@ -2924,7 +2928,9 @@ A B C
TestHelpers.AssertDriverContentsAre (expected, output);
+#pragma warning disable xUnit2029
Assert.Empty (pets.Where (p => p.IsPicked));
+#pragma warning restore xUnit2029
tv.NewKeyDownEvent (Key.Space);
@@ -3089,7 +3095,9 @@ A B C
// Can untoggle at 1,0 even though 0,0 was initial toggle because FullRowSelect is on
tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.Space });
+#pragma warning disable xUnit2029
Assert.Empty (tableView.MultiSelectedRegions.Where (r => r.IsToggled));
+#pragma warning restore xUnit2029
}
[Fact]
diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs
index 98c0aa150..19b63c0a6 100644
--- a/UnitTests/Views/ToplevelTests.cs
+++ b/UnitTests/Views/ToplevelTests.cs
@@ -246,14 +246,26 @@ public partial class ToplevelTests (ITestOutputHelper output)
top.OnUnloaded ();
Assert.Equal ("Unloaded", eventInvoked);
- top.AddMenuStatusBar (new MenuBar ());
+ top.Add (new MenuBar ());
Assert.NotNull (top.MenuBar);
- top.AddMenuStatusBar (new StatusBar ());
+ top.Add (new StatusBar ());
Assert.NotNull (top.StatusBar);
- top.RemoveMenuStatusBar (top.MenuBar);
+ var menuBar = top.MenuBar;
+ top.Remove (top.MenuBar);
Assert.Null (top.MenuBar);
- top.RemoveMenuStatusBar (top.StatusBar);
+ Assert.NotNull (menuBar);
+ var statusBar = top.StatusBar;
+ top.Remove (top.StatusBar);
Assert.Null (top.StatusBar);
+ Assert.NotNull (statusBar);
+#if true
+ Assert.False (menuBar.WasDisposed);
+ Assert.False (statusBar.WasDisposed);
+ menuBar.Dispose ();
+ statusBar.Dispose ();
+ Assert.True (menuBar.WasDisposed);
+ Assert.True (statusBar.WasDisposed);
+#endif
Application.Begin (top);
Assert.Equal (top, Application.Top);
@@ -265,7 +277,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (0, ny);
Assert.Null (sb);
- top.AddMenuStatusBar (new MenuBar ());
+ top.Add (new MenuBar ());
Assert.NotNull (top.MenuBar);
// Application.Top with a menu and without status bar.
@@ -274,7 +286,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (1, ny);
Assert.Null (sb);
- top.AddMenuStatusBar (new StatusBar ());
+ top.Add (new StatusBar ());
Assert.NotNull (top.StatusBar);
// Application.Top with a menu and status bar.
@@ -286,8 +298,10 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (2, ny);
Assert.NotNull (sb);
- top.RemoveMenuStatusBar (top.MenuBar);
+ menuBar = top.MenuBar;
+ top.Remove (top.MenuBar);
Assert.Null (top.MenuBar);
+ Assert.NotNull (menuBar);
// Application.Top without a menu and with a status bar.
View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny, out sb);
@@ -298,8 +312,10 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (2, ny);
Assert.NotNull (sb);
- top.RemoveMenuStatusBar (top.StatusBar);
+ statusBar = top.StatusBar;
+ top.Remove (top.StatusBar);
Assert.Null (top.StatusBar);
+ Assert.NotNull (statusBar);
Assert.Null (top.MenuBar);
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
@@ -318,7 +334,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (0, ny);
Assert.Null (sb);
- top.AddMenuStatusBar (new MenuBar ());
+ top.Add (new MenuBar ());
Assert.NotNull (top.MenuBar);
// Application.Top with a menu and without status bar.
@@ -327,7 +343,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (1, ny);
Assert.Null (sb);
- top.AddMenuStatusBar (new StatusBar ());
+ top.Add (new StatusBar ());
Assert.NotNull (top.StatusBar);
// Application.Top with a menu and status bar.
@@ -339,10 +355,14 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (20, ny);
Assert.NotNull (sb);
- top.RemoveMenuStatusBar (top.MenuBar);
- top.RemoveMenuStatusBar (top.StatusBar);
- Assert.Null (top.StatusBar);
+ menuBar = top.MenuBar;
+ statusBar = top.StatusBar;
+ top.Remove (top.MenuBar);
Assert.Null (top.MenuBar);
+ Assert.NotNull (menuBar);
+ top.Remove (top.StatusBar);
+ Assert.Null (top.StatusBar);
+ Assert.NotNull (statusBar);
top.Remove (win);
@@ -355,7 +375,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (0, ny);
Assert.Null (sb);
- top.AddMenuStatusBar (new MenuBar ());
+ top.Add (new MenuBar ());
Assert.NotNull (top.MenuBar);
// Application.Top with a menu and without status bar.
@@ -364,7 +384,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
Assert.Equal (2, ny);
Assert.Null (sb);
- top.AddMenuStatusBar (new StatusBar ());
+ top.Add (new StatusBar ());
Assert.NotNull (top.StatusBar);
// Application.Top with a menu and status bar.
@@ -387,7 +407,21 @@ public partial class ToplevelTests (ITestOutputHelper output)
win.NewMouseEvent (new () { Position = new (6, 0), Flags = MouseFlags.Button1Pressed });
//Assert.Null (Toplevel._dragPosition);
+#if true
+ Assert.False (top.MenuBar.WasDisposed);
+ Assert.False (top.StatusBar.WasDisposed);
+#endif
+ menuBar = top.MenuBar;
+ statusBar = top.StatusBar;
top.Dispose ();
+ Assert.Null (top.MenuBar);
+ Assert.Null (top.StatusBar);
+ Assert.NotNull (menuBar);
+ Assert.NotNull (statusBar);
+#if true
+ Assert.True (menuBar.WasDisposed);
+ Assert.True (statusBar.WasDisposed);
+#endif
}
[Fact (Skip = "#2491 - Test is broken until #2491 is more mature.")]
@@ -1569,4 +1603,31 @@ public partial class ToplevelTests (ITestOutputHelper output)
t.Dispose ();
Application.Shutdown ();
}
+
+ [Fact]
+ public void Remove_Do_Not_Dispose_MenuBar_Or_StatusBar ()
+ {
+ var mb = new MenuBar ();
+ var sb = new StatusBar ();
+ var tl = new Toplevel ();
+
+#if DEBUG
+ Assert.False (mb.WasDisposed);
+ Assert.False (sb.WasDisposed);
+#endif
+ tl.Add (mb, sb);
+ Assert.NotNull (tl.MenuBar);
+ Assert.NotNull (tl.StatusBar);
+#if DEBUG
+ Assert.False (mb.WasDisposed);
+ Assert.False (sb.WasDisposed);
+#endif
+ tl.RemoveAll ();
+ Assert.Null (tl.MenuBar);
+ Assert.Null (tl.StatusBar);
+#if DEBUG
+ Assert.False (mb.WasDisposed);
+ Assert.False (sb.WasDisposed);
+#endif
+ }
}
diff --git a/docfx/docs/migratingfromv1.md b/docfx/docs/migratingfromv1.md
index 0f6cd698d..feb73c967 100644
--- a/docfx/docs/migratingfromv1.md
+++ b/docfx/docs/migratingfromv1.md
@@ -167,6 +167,7 @@ The API for handling keyboard input is significantly improved. See [Keyboard API
* The preferred way to enable Application-wide or View-heirarchy-dependent keystrokes is to use the [Shortcut](~/api/Terminal.Gui.Shortcut.yml) View or the built-in View's that utilize it, such as the [Bar](~/api/Terminal.Gui.Bar.yml)-based views.
* The preferred way to handle single keystrokes is to use **Key Bindings**. Key Bindings map a key press to a [Command](~/api/Terminal.Gui.Command.yml). A view can declare which commands it supports, and provide a lambda that implements the functionality of the command, using `View.AddCommand()`. Use the [View.Keybindings](~/api/Terminal.Gui.View.Keybindings.yml) to configure the key bindings.
* For better consistency and user experience, the default key for closing an app or `Toplevel` is now `Esc` (it was previously `Ctrl+Q`).
+* The `Application.RootKeyEvent` method has been replaced with `Application.KeyDown`
### How to Fix
@@ -176,6 +177,12 @@ The API for handling keyboard input is significantly improved. See [Keyboard API
* It should be very uncommon for v2 code to override `OnKeyPressed` etc...
* Anywhere `Ctrl+Q` was hard-coded as the "quit key", replace with `Application.QuitKey`.
* See *Navigation* below for more information on v2's navigation keys.
+* Replace `Application.RootKeyEvent` with `Application.KeyDown`. If the reason for subscribing to RootKeyEvent was to enable an application-wide action based on a key-press, consider using Application.KeyBindings instead.
+
+```diff
+- Application.RootKeyEvent(KeyEvent arg)
++ Application.KeyDown(object? sender, Key e)
+```
## Updated Mouse API
@@ -186,6 +193,7 @@ The API for mouse input is now internally consistent and easier to use.
* Views can use the [View.Highlight](~/api/Terminal.Gui.View.Highlight.yml) event to have the view be visibly highlighted on various mouse events.
* Views can set `View.WantContinousButtonPresses = true` to have their [Command.Accept](~/api/Terminal.Gui.Command.Accept.yml) command be invoked repeatedly as the user holds a mouse button down on the view.
* Mouse and draw events now provide coordinates relative to the `Viewport` not the `Screen`.
+* The `Application.RootMouseEvent` method has been replaced with `Application.MouseEvent`
### How to Fix
@@ -193,6 +201,12 @@ The API for mouse input is now internally consistent and easier to use.
* Use the [View.Highlight](~/api/Terminal.Gui.View.Highlight.yml) event to have the view be visibly highlighted on various mouse events.
* Set `View.WantContinousButtonPresses = true` to have the [Command.Accept](~/api/Terminal.Gui.Command.Accept.yml) command be invoked repeatedly as the user holds a mouse button down on the view.
* Update any code that assumed mouse events provided coordinates relative to the `Screen`.
+* Replace `Application.RootMouseEvent` with `Application.MouseEvent`.
+
+```diff
+- Application.RootMouseEvent(KeyEvent arg)
++ Application.MouseEvent(object? sender, MouseEvent mouseEvent)
+```
## Navigation - `Cursor`, `Focus`, `TabStop` etc...
@@ -270,6 +284,21 @@ These keys are all registered as `KeyBindingScope.Application` key bindings by `
...
+## Button.Clicked Event Renamed
+
+The `Button.Clicked` event has been renamed `Button.Accept`
+
+## How to Fix
+
+Rename all instances of `Button.Clicked` to `Button.Accept`. Note the signature change to mouse events below.
+
+```diff
+- btnLogin.Clicked
++ btnLogin.Accept
+```
+
+Alternatively, if you want to have key events as well as mouse events to fire an event, use `Button.Accept`.
+
## Events now use `object sender, EventArgs args` signature
Previously events in Terminal.Gui used a mixture of `Action` (no arguments), `Action` (or other raw datatype) and `Action`. Now all events use the `EventHandler` [standard .net design pattern](https://learn.microsoft.com/en-us/dotnet/csharp/event-pattern#event-delegate-signatures).
@@ -302,8 +331,9 @@ If you previously had a lambda expression, you can simply add the extra argument
```diff
- btnLogin.Clicked += () => { /*do something*/ };
-+ btnLogin.Clicked += (s,e) => { /*do something*/ };
++ btnLogin.Accept += (s,e) => { /*do something*/ };
```
+Note that the event name has also changed as noted above.
If you have used a named method instead of a lamda you will need to update the signature e.g.
@@ -391,4 +421,16 @@ Additionally, the `Toggle` event was renamed `CheckStateChanging` and made cance
+}
+preventChange = false;
+cb.AdvanceCheckState ();
-```
\ No newline at end of file
+```
+
+## `MainLoop` is no longer accessible from `Application`
+
+In v1, you could add timeouts via `Application.MainLoop.AddTimeout` among other things. In v2, the `MainLoop` object is internal to `Application` and methods previously accessed via `MainLoop` can now be accessed directly via `Application`
+
+### How to Fix
+
+```diff
+- Application.MainLoop.AddTimeout (TimeSpan time, Func callback)
++ Application.AddTimeout (TimeSpan time, Func callback)
+```
+