🧪 Refactoring tests.

This commit is contained in:
Kolosov Alexandr 2024-03-16 00:13:09 +05:00
parent 3c2f945480
commit 634beddf77
85 changed files with 863 additions and 706 deletions

View File

@ -2,7 +2,9 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUI", "src\TUI\TUI.csproj", "{F92C03F7-2A65-4D0A-9736-13E749AF6903}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUI.Tests", "tests\WIdgets\TUI.Tests\TUI.Tests.csproj", "{2F0611D2-073F-4E26-BD1B-ACC433FC6F4E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUI.Engine.Tests", "tests\TUI.Engine.Tests\TUI.Engine.Tests.csproj", "{2F0611D2-073F-4E26-BD1B-ACC433FC6F4E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUI.Engine", "src\TUI.Engine\TUI.Engine.csproj", "{38E7E2DD-40C1-4B7C-9A7A-E3677AD55431}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -22,5 +24,9 @@ Global
{2F0611D2-073F-4E26-BD1B-ACC433FC6F4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F0611D2-073F-4E26-BD1B-ACC433FC6F4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F0611D2-073F-4E26-BD1B-ACC433FC6F4E}.Release|Any CPU.Build.0 = Release|Any CPU
{38E7E2DD-40C1-4B7C-9A7A-E3677AD55431}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{38E7E2DD-40C1-4B7C-9A7A-E3677AD55431}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38E7E2DD-40C1-4B7C-9A7A-E3677AD55431}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38E7E2DD-40C1-4B7C-9A7A-E3677AD55431}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -1,10 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -1,9 +0,0 @@
namespace Dashboard;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}

View File

@ -1,3 +1,3 @@
namespace TUI.Engine.Nodes.Attributes.Alignments;
namespace TUI.Engine.Attributes.Alignments;
public record Alignment(Horizontal Horizontal, Vertical Vertical);

View File

@ -1,4 +1,4 @@
namespace TUI.Engine.Nodes.Attributes.Alignments;
namespace TUI.Engine.Attributes.Alignments;
public enum Horizontal
{

View File

@ -0,0 +1,10 @@
namespace TUI.Engine.Attributes.Alignments;
public interface IWithAlignment
{
internal Alignment Alignment { get; }
void SetAlignment(Vertical vertical);
void SetAlignment(Horizontal horizontal);
}

View File

@ -1,4 +1,4 @@
namespace TUI.Engine.Nodes.Attributes.Alignments;
namespace TUI.Engine.Attributes.Alignments;
public enum Vertical
{

View File

@ -0,0 +1,9 @@
namespace TUI.Engine.Attributes.Orientations;
public interface IWithOrientation
{
internal Orientation Orientation { get; }
public void SetOrientationHorizontal();
public void SetOrientationVertical();
}

View File

@ -1,4 +1,4 @@
namespace TUI.Engine.Nodes.Attributes.Orientations;
namespace TUI.Engine.Attributes.Orientations;
public enum Orientation
{

View File

@ -1,8 +1,8 @@
using TUI.Engine.Theme;
namespace TUI.Engine.Nodes.Attributes.Paddings;
namespace TUI.Engine.Attributes.Paddings;
public interface IPaddingable
public interface IWithPadding
{
Padding Padding { get; }

View File

@ -1,6 +1,6 @@
using TUI.Engine.Theme;
namespace TUI.Engine.Nodes.Attributes.Paddings;
namespace TUI.Engine.Attributes.Paddings;
public record Padding(
Level Left = Level.None,

View File

@ -0,0 +1,16 @@
using TUI.Engine.Attributes.Orientations;
namespace TUI.Engine.Attributes.Resizings;
public interface IResizable
{
internal Resizing ResizingHorizontal { get; }
internal Resizing ResizingVertical { get; }
internal Size GetFixedSize();
void SetAdaptive(Orientation orientation);
void SetFixed(Orientation orientation, int value);
}

View File

@ -0,0 +1,7 @@
namespace TUI.Engine.Attributes.Resizings;
public enum Resizing
{
Adaptive,
Fixed
}

View File

@ -0,0 +1,58 @@
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Containers;
namespace TUI.Engine.Attributes.Resizings;
internal static class ResizingExtensions
{
internal static int GetHeight(this IResizable node, IContainer container, int maxHeight, int nodeIndex)
{
if (node.ResizingVertical == Resizing.Fixed)
{
return node.GetFixedSize().Height;
}
if (container.Orientation == Orientation.Horizontal)
{
return maxHeight;
}
var fixedNodes = container.GetFixedNodes().ToArray();
var fixedHeight = fixedNodes.Sum(s => s.GetFixedSize().Height);
var allowableHeight = maxHeight - fixedHeight;
var allowableCount = container.GetNodes().Count - fixedNodes.Length;
var nodeHeight = (allowableHeight / allowableCount).Min(1);
var nodeNumber = nodeIndex + 1 - container.GetFixedNodes(nodeIndex).Sum(c => c.GetFixedSize().Height);
if (allowableHeight - nodeNumber * nodeHeight < nodeHeight)
{
return allowableHeight + nodeHeight - nodeNumber * nodeHeight;
}
return nodeHeight;
}
internal static int GetWidth(this IResizable node, IContainer container, int maxWidth)
{
if (node.ResizingHorizontal == Resizing.Fixed)
{
return node.GetFixedSize().Width;
}
if (container.Orientation == Orientation.Vertical)
{
return maxWidth;
}
var fixedNodes = container
.GetNodes()
.Where(n => n.ResizingHorizontal == Resizing.Fixed).ToArray();
var allowableWidth = maxWidth - fixedNodes.Sum(s => s.GetFixedSize().Width);
var allowableCount = container.GetNodes().Count - fixedNodes.Length;
return allowableWidth / allowableCount;
}
}

View File

@ -1,4 +1,6 @@
namespace TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes;
namespace TUI.Engine.Attributes;
public readonly record struct Size(int Width, int Height)
{

View File

@ -0,0 +1,50 @@
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Attributes.Paddings;
using TUI.Engine.Nodes;
using TUI.Engine.Theme;
namespace TUI.Engine.Components;
public abstract class ComponentAttribute : NodeBase, IComponent
{
protected abstract Sketch DrawComponent();
Sketch IComponent.MakeSketch() => DrawComponent();
#region Alignments
internal Alignment Alignment { get; private set; } = new(Defaults.HorizontalAlignment, Defaults.VerticalAlignment);
Alignment IWithAlignment.Alignment => Alignment;
public void SetAlignment(Vertical vertical)
{
Alignment = Alignment with { Vertical = vertical };
}
public void SetAlignment(Horizontal horizontal)
{
Alignment = Alignment with { Horizontal = horizontal };
}
#endregion
#region Paddings
internal Padding Padding { get; private set; } = new(Defaults.Padding);
Padding IWithPadding.Padding => Padding;
public void SetPadding(Level level) => Padding = new Padding(level);
public void SetPaddingTop(Level level) => Padding = Padding with { Top = level };
public void SetPaddingRight(Level level) => Padding = Padding with { Right = level };
public void SetPaddingBottom(Level level) => Padding = Padding with { Bottom = level };
public void SetPaddingLeft(Level level) => Padding = Padding with { Left = level };
#endregion
}

View File

@ -0,0 +1,40 @@
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Nodes;
namespace TUI.Engine.Components;
internal static class ComponentExtensions
{
internal static Position CorrectContentPosition(this IComponent component,
Position pencil,
Size maxSize,
Size sketchSize)
{
var padding = component.Padding;
var alignment = component.Alignment;
var alignmentCompensationLeft = GetAlignmentCompensationLeft(alignment.Horizontal, maxSize, sketchSize);
var alignmentCompensationTop = GetAlignmentCompensationTop(alignment.Vertical, maxSize, sketchSize);
var left = pencil.Left + (int)padding.Left + alignmentCompensationLeft;
var top = pencil.Top + (int)padding.Top + alignmentCompensationTop;
return new Position(left, top);
}
private static int GetAlignmentCompensationLeft(Horizontal alignment, Size maxSize, Size sketchSize) =>
alignment switch
{
Horizontal.Left => 0,
Horizontal.Center => (maxSize.Width - sketchSize.Width) / 2,
Horizontal.Right => maxSize.Width - sketchSize.Width,
_ => 0
};
private static int GetAlignmentCompensationTop(Vertical alignment, Size maxSize, Size sketchSize) =>
alignment switch
{
Vertical.Top => 0,
Vertical.Center => (maxSize.Height - sketchSize.Height) / 2,
Vertical.Bottom => maxSize.Height - sketchSize.Height,
_ => 0
};
}

View File

@ -0,0 +1,10 @@
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Attributes.Paddings;
using TUI.Engine.Nodes;
namespace TUI.Engine.Components;
public interface IComponent : INode, IWithAlignment, IWithPadding
{
internal Sketch MakeSketch();
}

View File

@ -1,6 +1,6 @@
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Attributes;
namespace TUI.Engine.Nodes.Components;
namespace TUI.Engine.Components;
public sealed class Sketch : IEnumerable<string>
{

View File

@ -1,14 +1,14 @@
using System.Text;
namespace TUI.Engine.Nodes.Components;
namespace TUI.Engine.Components;
public abstract class ComponentStaticBase : ComponentBase
public abstract class StaticComponentAttribute : ComponentAttribute
{
private Sketch? _cache;
protected abstract void RenderWithCache(StringBuilder builder);
public override Sketch DrawComponent()
protected override Sketch DrawComponent()
{
if (_cache is not null)
{
@ -16,7 +16,9 @@ public abstract class ComponentStaticBase : ComponentBase
}
var builder = new StringBuilder();
RenderWithCache(builder);
_cache = new Sketch(builder.ToString());
return _cache;

View File

@ -0,0 +1,24 @@
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Nodes;
using TUI.Engine.Theme;
namespace TUI.Engine.Containers;
public abstract class ContainerBase : NodeBase, IContainer
{
private Orientation _orientation = Defaults.Orientation;
Orientation IWithOrientation.Orientation => _orientation;
public void SetOrientationHorizontal()
{
_orientation = Orientation.Horizontal;
}
public void SetOrientationVertical()
{
_orientation = Orientation.Vertical;
}
public abstract Nodes.Nodes GetNodes();
}

View File

@ -0,0 +1,22 @@
using TUI.Engine.Attributes.Resizings;
using TUI.Engine.Nodes;
namespace TUI.Engine.Containers;
internal static class ContainerExtensions
{
internal static IEnumerable<INode> GetFixedNodes(this IContainer container, int? takeNodeNumber = null)
{
if (takeNodeNumber is not null)
{
return container
.GetNodes()
.Take(takeNodeNumber.Value + 1)
.Where(n => n.ResizingVertical == Resizing.Fixed);
}
return container
.GetNodes()
.Where(n => n.ResizingVertical == Resizing.Fixed);
}
}

View File

@ -0,0 +1,9 @@
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Nodes;
namespace TUI.Engine.Containers;
public interface IContainer : INode, IWithOrientation
{
public Nodes.Nodes GetNodes();
}

View File

@ -1,11 +1,20 @@
using System.Globalization;
using System.Text.RegularExpressions;
namespace TUI;
namespace TUI.Engine;
public static class Extensions
{
public static int Max(this int value, int maxValue)
{
return value <= maxValue ? value : maxValue;
}
public static int Min(this int value, int minValue)
{
return value > minValue ? value : minValue;
}
public static bool Have(this IEnumerable<string> array, string findValue)
{
return array.Any(item => item == findValue);

View File

@ -1,6 +1,9 @@
using System.Runtime.CompilerServices;
using Pastel;
using TUI.Engine.Attributes;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes;
[assembly:InternalsVisibleTo("TUI.Engine.Tests", AllInternalsVisible = true)]
namespace TUI.Engine;

View File

@ -1,4 +1,4 @@
using TUI.Engine.Nodes.Attributes.Resizing;
using TUI.Engine.Attributes.Resizings;
namespace TUI.Engine.Nodes;

View File

@ -1,6 +1,7 @@
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Orientations;
using TUI.Engine.Nodes.Attributes.Resizing;
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Attributes.Resizings;
using TUI.Engine.Theme;
namespace TUI.Engine.Nodes;
@ -9,12 +10,17 @@ public abstract class NodeBase : INode
private int _fixedWidth;
private int _fixedHeight;
public Size GetFixedSize() => new(_fixedWidth, _fixedHeight);
Size IResizable.GetFixedSize() => new(_fixedWidth, _fixedHeight);
#region Resizing
public Resizing ResizingHorizontal { get; private set; } = Resizing.Adaptive;
public Resizing ResizingVertical { get; private set; } = Resizing.Adaptive;
private Resizing ResizingHorizontal { get; set; } = Defaults.HorizontalResizing;
Resizing IResizable.ResizingHorizontal => ResizingHorizontal;
private Resizing ResizingVertical { get; set; } = Defaults.VerticalResizing;
Resizing IResizable.ResizingVertical => ResizingVertical;
public void SetAdaptive(Orientation orientation)
{

View File

@ -0,0 +1,15 @@
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Resizings;
using TUI.Engine.Containers;
namespace TUI.Engine.Nodes;
internal static class NodeExtensions
{
internal static Size GetSize(this INode node, IContainer parentContainer, int nodeNumber, Size allowableSize)
{
var width = node.GetWidth(parentContainer, allowableSize.Width);
var height = node.GetHeight(parentContainer, allowableSize.Height, nodeNumber);
return new Size(width, height);
}
}

View File

@ -3,5 +3,6 @@ namespace TUI.Engine.Nodes;
public record Position(int Left, int Top)
{
public static readonly Position Default = new(0, 0);
public override string ToString() => $"L[{Left}] T[{Top}]";
}

View File

@ -0,0 +1,35 @@
using TUI.Engine.Attributes;
using TUI.Engine.Nodes;
namespace TUI.Engine.Rendering.Canvas;
public class ConsoleCanvas : ICanvas
{
private readonly DrawCraftsman _drawCraftsman;
public Size Size { get; } = new(Console.WindowWidth, Console.WindowHeight);
public ConsoleCanvas()
{
var componentCraftsman = new ComponentCraftsman(this);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
_drawCraftsman = new DrawCraftsman(componentCraftsman, containerCraftsman);
}
public void SetPencil(Position pencilPosition)
{
Console.SetCursorPosition(pencilPosition.Left, pencilPosition.Top);
}
public void Paint(string value) => Console.Write(value);
public void Draw(INode node)
{
_drawCraftsman.Draw(node, Position.Default, Size);
}
public void Draw(INode node, Position pencil, Size maxSize)
{
_drawCraftsman.Draw(node, pencil, maxSize);
}
}

View File

@ -0,0 +1,17 @@
using TUI.Engine.Attributes;
using TUI.Engine.Nodes;
namespace TUI.Engine.Rendering.Canvas;
public interface ICanvas
{
Size Size { get; }
void SetPencil(Position pencilPosition);
void Paint(string value);
void Draw(INode node);
void Draw(INode node, Position pencil, Size maxSize);
}

View File

@ -1,11 +1,11 @@
using TUI.Engine.Attributes;
using TUI.Engine.Components;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Nodes.Containers;
using TUI.Engine.Rendering.Canvas;
namespace TUI.Engine.Rendering;
public sealed class ComponentCraftsman : CraftsmanBase, IDrawable<IComponent>
internal sealed class ComponentCraftsman : CraftsmanBase, IDrawable<IComponent>
{
private readonly ICanvas _canvas;
@ -19,13 +19,13 @@ public sealed class ComponentCraftsman : CraftsmanBase, IDrawable<IComponent>
var sketch = component.MakeSketch();
var sketchSize = sketch.GetSize();
var correctedPencil = component.CorrectPosition(pencil, maxSize, sketchSize);
var correctedPencil = component.CorrectContentPosition(pencil, maxSize, sketchSize);
Debug(pencil, maxSize);
foreach (var line in sketch.Crop(maxSize))
{
_canvas.SetPencil(correctedPencil.Left, correctedPencil.Top);
_canvas.SetPencil(correctedPencil);
_canvas.Paint(line);
correctedPencil = correctedPencil with { Top = correctedPencil.Top + 1 };

View File

@ -1,13 +1,13 @@
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Attributes.Resizings;
using TUI.Engine.Components;
using TUI.Engine.Containers;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Orientations;
using TUI.Engine.Nodes.Attributes.Resizing;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Nodes.Containers;
namespace TUI.Engine.Rendering;
public sealed class ContainerCraftsman : CraftsmanBase, IDrawable<IContainer>
internal sealed class ContainerCraftsman : CraftsmanBase, IDrawable<IContainer>
{
private readonly IDrawable<IComponent> _componentCraftsman;

View File

@ -1,6 +1,6 @@
using System.Diagnostics;
using TUI.Engine.Attributes;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes;
namespace TUI.Engine.Rendering;

View File

@ -1,5 +1,5 @@
using TUI.Engine.Attributes;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes;
namespace TUI.Engine.Rendering;

View File

@ -1,19 +1,16 @@
using TUI.Engine.Attributes;
using TUI.Engine.Components;
using TUI.Engine.Containers;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Nodes.Containers;
namespace TUI.Engine.Rendering;
/// <summary>
/// 🍀
/// </summary>
public sealed class NodeCraftsman : IDrawable<INode>
internal sealed class DrawCraftsman : IDrawable<INode>
{
private readonly IDrawable<IComponent> _componentCraftsman;
private readonly IDrawable<IContainer> _containerCraftsman;
public NodeCraftsman(
public DrawCraftsman(
IDrawable<IComponent> componentCraftsman,
IDrawable<IContainer> containerCraftsman)
{

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Pastel" Version="4.1.0"/>
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(MSBuildProjectName).Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Other.Assembly.Name</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project>

View File

@ -0,0 +1,20 @@
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Attributes.Resizings;
namespace TUI.Engine.Theme;
public static class Defaults
{
public const Horizontal HorizontalAlignment = Horizontal.Center;
public const Vertical VerticalAlignment = Vertical.Top;
public const Level Padding = Level.None;
public const Resizing HorizontalResizing = Resizing.Adaptive;
public const Resizing VerticalResizing = Resizing.Adaptive;
public const Orientation Orientation = TUI.Engine.Attributes.Orientations.Orientation.Horizontal;
}

View File

@ -1,18 +1,18 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Components;
namespace TUI.Components.Controls;
public class CellsComponentBase : ComponentBase, IComponent
public class CellsComponentAttribute : ComponentAttribute, IComponent
{
private const int MaxCellWidth = 10;
private readonly IEnumerable<string> _cells;
public CellsComponentBase(IEnumerable<string> cells)
public CellsComponentAttribute(IEnumerable<string> cells)
{
_cells = cells;
}
@ -29,7 +29,7 @@ public class CellsComponentBase : ComponentBase, IComponent
// base.Render(content, position, size);
}
public override Sketch DrawComponent()
protected override Sketch DrawComponent()
{
throw new NotImplementedException();
}

View File

@ -1,11 +1,11 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Components;
using TUI.Engine.Theme;
namespace TUI.Components.Controls;
public class Copyright : ComponentStaticBase
public class Copyright : StaticComponentAttribute
{
protected override void RenderWithCache(StringBuilder builder)
{

View File

@ -1,13 +1,13 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Components;
using TUI.Engine.Theme;
namespace TUI.Components.Controls;
public class Dashboard : ComponentBase, IComponent
public class Dashboard : ComponentAttribute, IComponent
{
private readonly string _title;
@ -61,7 +61,7 @@ public class Dashboard : ComponentBase, IComponent
dashboardBuilder.Append(Symbols.Angles.RightBottom);
}
public override Sketch DrawComponent()
protected override Sketch DrawComponent()
{
throw new NotImplementedException();
}

View File

@ -1,8 +1,8 @@
using TUI.Components.Controls.Statics;
using TUI.Components.Controls.Statics.Hints;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Containers;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Containers;
using TUI.Engine.Theme;
namespace TUI.Components.Controls;

View File

@ -1,12 +1,12 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Components;
using TUI.Engine.Theme;
using TUI.UserInterface;
namespace TUI.Components.Controls.Statics.Hints;
public class AppTypeHints : ComponentStaticBase
public class AppTypeHints : StaticComponentAttribute
{
private readonly Dictionary<string, string> _hints = new()
{

View File

@ -1,11 +1,11 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Components;
using TUI.Engine.Theme;
namespace TUI.Components.Controls.Statics.Hints;
public class HotkeysHint : ComponentStaticBase
public class HotkeysHint : StaticComponentAttribute
{
private readonly Dictionary<string, string> _hints = new()
{

View File

@ -1,12 +1,12 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Components;
using TUI.Engine.Theme;
using TUI.UserInterface;
namespace TUI.Components.Controls.Statics.Hints;
public class TagHints : ComponentStaticBase
public class TagHints : StaticComponentAttribute
{
private readonly Dictionary<string, string> _hints = new()
{

View File

@ -1,11 +1,11 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Components;
using TUI.Engine.Theme;
namespace TUI.Components.Controls.Statics.Hints;
public class VersionHints : ComponentStaticBase
public class VersionHints : StaticComponentAttribute
{
private readonly Dictionary<string, string> _hints = new()
{

View File

@ -1,11 +1,11 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Components;
using TUI.Engine.Theme;
namespace TUI.Components.Controls.Statics;
public class Logo : ComponentStaticBase
public class Logo : StaticComponentAttribute
{
protected override void RenderWithCache(StringBuilder builder)
{

View File

@ -1,23 +1,18 @@
using System.Text;
using TUI.Engine;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Rendering;
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Components;
using TUI.Engine.Theme;
using TUI.UserInterface;
namespace TUI.Components.Controls;
public class Tag : ComponentBase
public class Tag : ComponentAttribute
{
private IEnumerable<string> _tags;
private string _gitType;
public Tag(NodeCraftsman drawEngine)
{
}
public void Bind(IEnumerable<string> tags, string gitType)
{
_tags = tags;
@ -59,7 +54,7 @@ public class Tag : ComponentBase
_ => Symbols.Git
};
public override Sketch DrawComponent()
protected override Sketch DrawComponent()
{
throw new NotImplementedException();
}

View File

@ -1,14 +1,18 @@
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Components;
using TUI.Engine.Containers;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes.Orientations;
using TUI.Engine.Nodes.Attributes.Resizing;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Nodes.Containers;
namespace TUI.Components.Layouts;
public class DashboardLayout : ContainerBase, IContainer
{
public new Orientation Orientation => Orientation.Vertical;
public DashboardLayout()
{
SetOrientationVertical();
SetAdaptive(Orientation.Horizontal);
SetAdaptive(Orientation.Vertical);
}
private INode _header;
private INode _footer;
@ -35,8 +39,4 @@ public class DashboardLayout : ContainerBase, IContainer
{
throw new NotImplementedException();
}
public Resizing ResizingHorizontal => Resizing.Adaptive;
public Resizing ResizingVertical => Resizing.Adaptive;
}

View File

@ -1,13 +1,13 @@
using TUI.Components.Controls;
using TUI.Domain;
using TUI.Engine;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Components;
namespace TUI.Components.Views;
public class DependenciesView : ComponentBase, IComponent
public class DependenciesView : ComponentAttribute, IComponent
{
private const string ViewName = "Dependencies";
@ -153,7 +153,7 @@ public class DependenciesView : ComponentBase, IComponent
// {
// _table.Previous();
// }
public override Sketch DrawComponent()
protected override Sketch DrawComponent()
{
throw new NotImplementedException();
}

View File

@ -1,5 +1,6 @@
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using TUI.Engine;
namespace TUI.Domain;

View File

@ -1,10 +0,0 @@
namespace TUI.Engine.Nodes.Attributes.Alignments;
public interface IAlignable
{
Alignment Alignment { get; }
void SetAlignment(Vertical vertical);
void SetAlignment(Horizontal horizontal);
}

View File

@ -1,6 +0,0 @@
namespace TUI.Engine.Nodes.Attributes.Orientations;
public interface IWithOrientation
{
public Orientation Orientation { get; }
}

View File

@ -1,14 +0,0 @@
using TUI.Engine.Nodes.Attributes.Orientations;
namespace TUI.Engine.Nodes.Attributes.Resizing;
public interface IResizable
{
Resizing ResizingHorizontal { get; }
Resizing ResizingVertical { get; }
void SetAdaptive(Orientation orientation);
void SetFixed(Orientation orientation, int value);
Size GetFixedSize();
}

View File

@ -1,7 +0,0 @@
namespace TUI.Engine.Nodes.Attributes.Resizing;
public enum Resizing
{
Adaptive,
Fixed
}

View File

@ -1,63 +0,0 @@
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Attributes.Paddings;
using TUI.Engine.Nodes.Attributes.Resizing;
using TUI.Engine.Theme;
namespace TUI.Engine.Nodes.Components;
public abstract class ComponentBase : NodeBase, IComponent
{
private Size _sketchSize;
public abstract Sketch DrawComponent();
public Sketch MakeSketch()
{
var sketch = DrawComponent();
_sketchSize = sketch.GetSize();
return sketch;
}
public Resizing ResizingHorizontal { get; }
// protected override Size GetAllowableSize() =>
// new(
// AllowableSize.Width <= _sketchSize.Width ? _sketchSize.Width : AllowableSize.Width,
// AllowableSize.Height <= _sketchSize.Height ? _sketchSize.Height : AllowableSize.Height
// );
#region Alignments
public Alignment Alignment { get; private set; } = new(Horizontal.Center, Vertical.Top);
public void SetAlignment(Vertical vertical)
{
Alignment = Alignment with { Vertical = vertical };
}
public void SetAlignment(Horizontal horizontal)
{
Alignment = Alignment with { Horizontal = horizontal };
}
#endregion
#region Paddings
public Padding Padding { get; private set; } = new(Level.None);
public void SetPadding(Level level) => Padding = new Padding(level);
public void SetPaddingTop(Level level) => Padding = Padding with { Top = level };
public void SetPaddingRight(Level level) => Padding = Padding with { Right = level };
public void SetPaddingBottom(Level level) => Padding = Padding with { Bottom = level };
public void SetPaddingLeft(Level level) => Padding = Padding with { Left = level };
#endregion
}

View File

@ -1,9 +0,0 @@
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Attributes.Paddings;
namespace TUI.Engine.Nodes.Components;
public interface IComponent : INode, IAlignable, IPaddingable
{
Sketch MakeSketch();
}

View File

@ -1,16 +0,0 @@
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Orientations;
namespace TUI.Engine.Nodes.Containers;
public abstract class ContainerBase : NodeBase, IContainer
{
public Orientation Orientation => Orientation.Horizontal;
public Size GetSketchSize()
{
throw new NotImplementedException();
}
public abstract Nodes GetNodes();
}

View File

@ -1,116 +0,0 @@
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Attributes.Orientations;
using TUI.Engine.Nodes.Attributes.Resizing;
using TUI.Engine.Nodes.Components;
using TUI.Engine.Rendering;
namespace TUI.Engine.Nodes.Containers;
public static class ContainerExtension
{
public static Size GetSize(this INode node, IContainer parentContainer, int nodeNumber, Size allowableSize)
{
var width = GetWidth(node, parentContainer, allowableSize.Width);
var height = GetHeight(node, parentContainer, allowableSize.Height, nodeNumber);
return new Size(width, height);
}
private static IEnumerable<INode> GetFixedNodes(this IContainer container, int? takeNodeNumber = null)
{
if (takeNodeNumber is not null)
{
return container
.GetNodes()
.Take(takeNodeNumber.Value + 1)
.Where(n => n.ResizingVertical == Resizing.Fixed);
}
return container
.GetNodes()
.Where(n => n.ResizingVertical == Resizing.Fixed);
}
private static int GetHeight(IResizable node, IContainer container, int maxHeight, int nodeIndex)
{
if (node.ResizingVertical == Resizing.Fixed)
{
return node.GetFixedSize().Height;
}
if (container.Orientation == Orientation.Horizontal)
{
return maxHeight;
}
var fixedNodes = container.GetFixedNodes().ToArray();
var fixedHeight = fixedNodes.Sum(s => s.GetFixedSize().Height);
var allowableHeight = maxHeight - fixedHeight;
var allowableCount = container.GetNodes().Count - fixedNodes.Length;
var nodeHeight = (allowableHeight / allowableCount).Min(1);
var nodeNumber = nodeIndex + 1 - container.GetFixedNodes(nodeIndex).Sum(c => c.GetFixedSize().Height);
if (allowableHeight - nodeNumber * nodeHeight < nodeHeight)
{
return allowableHeight + nodeHeight - nodeNumber * nodeHeight;
}
return nodeHeight;
}
private static int GetWidth(IResizable node, IContainer container, int maxWidth)
{
if (node.ResizingHorizontal == Resizing.Fixed)
{
return node.GetFixedSize().Width;
}
if (container.Orientation == Orientation.Vertical)
{
return maxWidth;
}
var fixedNodes = container
.GetNodes()
.Where(n => n.ResizingHorizontal == Resizing.Fixed).ToArray();
var allowableWidth = maxWidth - fixedNodes.Sum(s => s.GetFixedSize().Width);
var allowableCount = container.GetNodes().Count - fixedNodes.Length;
return allowableWidth / allowableCount;
}
}
public static class ComponentExtensions
{
public static Position CorrectPosition(this IComponent component, Position pencil, Size maxSize, Size sketchSize)
{
var padding = component.Padding;
var alignment = component.Alignment;
var alignmentCompensationLeft = GetAlignmentCompensationLeft(alignment.Horizontal, maxSize, sketchSize);
var alignmentCompensationTop = GetAlignmentCompensationTop(alignment.Vertical, maxSize, sketchSize);
var left = pencil.Left + (int)padding.Left + alignmentCompensationLeft;
var top = pencil.Top + (int)padding.Top + alignmentCompensationTop;
return new Position(left, top);
}
private static int GetAlignmentCompensationLeft(Horizontal alignment, Size maxSize, Size sketchSize) =>
alignment switch
{
Horizontal.Left => 0,
Horizontal.Center => (maxSize.Width - sketchSize.Width) / 2,
Horizontal.Right => maxSize.Width - sketchSize.Width,
_ => 0
};
private static int GetAlignmentCompensationTop(Vertical alignment, Size maxSize, Size sketchSize) =>
alignment switch
{
Vertical.Top => 0,
Vertical.Center => (maxSize.Height - sketchSize.Height) / 2,
Vertical.Bottom => maxSize.Height - sketchSize.Height,
_ => 0
};
}

View File

@ -1,10 +0,0 @@
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Orientations;
namespace TUI.Engine.Nodes.Containers;
public interface IContainer : INode, IWithOrientation
{
Size GetSketchSize();
Nodes GetNodes();
}

View File

@ -1,8 +0,0 @@
using TUI.Engine.Nodes.Attributes;
namespace TUI.Engine.Rendering;
public static class CanvasExtensions
{
public static Size GetSize(this ICanvas canvas) => new(canvas.Width, canvas.Height);
}

View File

@ -1,13 +0,0 @@
using TUI.Engine.Nodes.Attributes;
namespace TUI.Engine.Rendering;
public class ConsoleCanvas : ICanvas
{
public int Width => Console.WindowWidth;
public int Height => Console.WindowHeight;
public void SetPencil(int left, int top) => Console.SetCursorPosition(left, top);
public void Paint(string value) => Console.Write(value);
public Size GetSize() => new(Width, Height);
}

View File

@ -1,9 +0,0 @@
namespace TUI.Engine.Rendering;
public interface ICanvas
{
int Width { get; }
int Height { get; }
void SetPencil(int left, int top);
void Paint(string value);
}

View File

@ -1,7 +0,0 @@
namespace TUI.Engine.Rendering;
public static class IntegerExtension
{
public static int Max(this int value, int maxValue) => value <= maxValue ? value : maxValue;
public static int Min(this int value, int minValue) => value > minValue ? value : minValue;
}

View File

@ -1,10 +1,9 @@
using System.Diagnostics;
using TUI.Components.Controls;
using TUI.Components.Layouts;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Attributes.Orientations;
using TUI.Engine.Rendering;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Rendering.Canvas;
using TUI.Engine.Theme;
namespace TUI.Pages;
@ -15,11 +14,7 @@ public class DependenciesPage
{
Debugger.Log(0, "Event", "Open page dependencies\n");
var canvas = new ConsoleCanvas();
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
var nodeCraftsman = new NodeCraftsman(componentCraftsman, containerCraftsman);
ICanvas canvas = new ConsoleCanvas();
var header = new HeaderContainer();
header.SetFixed(Orientation.Vertical, 6);
@ -33,7 +28,7 @@ public class DependenciesPage
// CommandLine = new CommandLine();
// DependenciesView = new DependenciesView();
nodeCraftsman.Draw(layout, Position.Default, canvas.GetSize());
canvas.Draw(layout);
}
// private bool _commandLineInDisplay;

View File

@ -1,5 +1,6 @@
using System.Text.Json;
using TUI.Domain;
using TUI.Engine;
using TUI.Settings;
namespace TUI.Store;

View File

@ -19,4 +19,8 @@
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TUI.Engine\TUI.Engine.csproj" />
</ItemGroup>
</Project>

View File

@ -1,16 +1,16 @@
using FluentAssertions;
using TUI.Components.Controls.Statics;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Tests.Stubs;
using TUI.Engine.Theme;
namespace Widgets.Tests;
namespace TUI.Engine.Tests.DrawTests;
public class ComponentBaseTests
public class ComponentAttributeTests
{
[Fact]
public void WhenUseChainingSaveAllChange()
{
var logo = new Logo();
var logo = new TestComponent();
logo.SetPadding(Level.Normal);
logo.SetAlignment(Vertical.Center);
logo.SetAlignment(Horizontal.Center);
@ -26,7 +26,7 @@ public class ComponentBaseTests
[Fact]
public void WhenSetPaddingsSaveAllChange()
{
var component = new Logo();
var component = new TestComponent();
component.SetPadding(Level.Normal);
@ -42,7 +42,7 @@ public class ComponentBaseTests
[InlineData(Vertical.Top)]
public void WhenSetVerticalAlignSaveAllChange(Vertical alignment)
{
var component = new Logo();
var component = new TestComponent();
component.SetAlignment(alignment);
@ -55,7 +55,7 @@ public class ComponentBaseTests
[InlineData(Horizontal.Right)]
public void WhenSetHorizontalAlignSaveAllChange(Horizontal alignment)
{
var component = new Logo();
var component = new TestComponent();
component.SetAlignment(alignment);

View File

@ -0,0 +1,55 @@
using Moq;
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Nodes;
using TUI.Engine.Rendering;
using TUI.Engine.Rendering.Canvas;
using TUI.Engine.Tests.Stubs;
namespace TUI.Engine.Tests.DrawTests;
public class DrawResizingTests
{
private readonly ICanvas _canvas;
private readonly TestContainer _container;
private readonly ContainerCraftsman _craftsman;
private readonly TestContainer _root;
public DrawResizingTests()
{
var component = Prepare.Component();
_canvas = Mock.Of<ICanvas>(w => w.Size == new Size(20, 2));
_container = Prepare.Container(component);
_root = Prepare.Container(_container, component);
_root.SetOrientationHorizontal();
var componentCraftsman = new ComponentCraftsman(_canvas);
_craftsman = new ContainerCraftsman(componentCraftsman);
}
[Fact]
public void DrawResizingFixedContainer()
{
_container.SetFixed(Orientation.Horizontal, 6);
_container.SetFixed(Orientation.Vertical, 2);
_craftsman.Draw(_root, Position.Default, _canvas.Size);
Mock.Get(_canvas).VerifyPositionOnce(Position.Default);
Mock.Get(_canvas).VerifyPositionOnce(6, 0);
Mock.Get(_canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
[Fact]
public void DrawResizingAdaptiveContainer()
{
_container.SetAdaptive(Orientation.Horizontal);
_craftsman.Draw(_root, Position.Default, _canvas.Size);
Mock.Get(_canvas).VerifyPositionOnce(Position.Default);
Mock.Get(_canvas).VerifyPositionOnce(10, 0);
Mock.Get(_canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
}

View File

@ -0,0 +1,218 @@
using Moq;
using TUI.Engine.Attributes;
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Attributes.Orientations;
using TUI.Engine.Containers;
using TUI.Engine.Nodes;
using TUI.Engine.Rendering;
using TUI.Engine.Rendering.Canvas;
using TUI.Engine.Tests.Stubs;
namespace TUI.Engine.Tests.DrawTests;
public class DrawCraftsmanTests
{
public TestComponent _component;
public DrawCraftsmanTests()
{
_component = Prepare.Component();
}
[Fact]
public void DrawSimple()
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(9, 1));
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(_component, Position.Default, canvas.Size);
Mock.Get(canvas).Verify(w => w.SetPencil(Position.Default), Times.Once());
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Once());
}
[Theory]
[InlineData(Horizontal.Left, "Lorem", 10, 0)]
[InlineData(Horizontal.Center, "Lorem", 10, 2)]
[InlineData(Horizontal.Center, "Lo", 10, 4)]
[InlineData(Horizontal.Center, "Lorem", 9, 2)]
[InlineData(Horizontal.Center, "Lorem", 11, 3)]
[InlineData(Horizontal.Right, "Lorem", 10, 5)]
[InlineData(Horizontal.Right, "Lo", 10, 8)]
public void DrawWithHorizontalAlignment(Horizontal alignment, string content, int canvasSize,
int expectedPosition)
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(canvasSize, canvasSize));
var component = Prepare.Component();
component.SetContent(content);
component.SetAlignment(Vertical.Top);
component.SetAlignment(alignment);
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(component, Position.Default, canvas.Size);
Mock.Get(canvas).Verify(w => w.Paint(content), Times.Once());
Mock.Get(canvas).Verify(w => w.SetPencil(new Position(expectedPosition, 0)), Times.Once());
}
[Theory]
[InlineData(Vertical.Top, "v", 5, new[] { 0 })]
[InlineData(Vertical.Top, "v\nv", 5, new[] { 0, 1 })]
[InlineData(Vertical.Top, "v\nv\nv", 5, new[] { 0, 1, 2 })]
[InlineData(Vertical.Center, "v", 1, new[] { 0 })]
[InlineData(Vertical.Center, "v", 4, new[] { 1 })]
[InlineData(Vertical.Center, "v", 5, new[] { 2 })]
[InlineData(Vertical.Center, "v", 6, new[] { 2 })]
[InlineData(Vertical.Center, "v\nv", 4, new[] { 1, 2 })]
[InlineData(Vertical.Center, "v\nv", 5, new[] { 1, 2 })]
[InlineData(Vertical.Center, "v\nv", 6, new[] { 2, 3 })]
[InlineData(Vertical.Bottom, "v", 5, new[] { 4 })]
[InlineData(Vertical.Bottom, "v\nv", 2, new[] { 0, 1 })]
[InlineData(Vertical.Bottom, "v\nv", 3, new[] { 1, 2 })]
[InlineData(Vertical.Bottom, "v\nv\nv\nv", 5, new[] { 1, 2, 3, 4 })]
public void DrawWithVerticalAlignment(Vertical alignment, string content, int canvasSize, int[] expectedPositions)
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(canvasSize, canvasSize));
_component.SetContent(content);
_component.SetAlignment(Horizontal.Left);
_component.SetAlignment(alignment);
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(_component, Position.Default, canvas.Size);
foreach (var expectedPencilPosition in expectedPositions)
{
Mock.Get(canvas).VerifyPositionOnce(0, expectedPencilPosition);
}
}
[Theory]
[InlineData(Horizontal.Left, Vertical.Top, 0, 0)]
[InlineData(Horizontal.Left, Vertical.Center, 0, 2)]
[InlineData(Horizontal.Left, Vertical.Bottom, 0, 4)]
[InlineData(Horizontal.Center, Vertical.Top, 2, 0)]
[InlineData(Horizontal.Center, Vertical.Center, 2, 2)]
[InlineData(Horizontal.Center, Vertical.Bottom, 2, 4)]
[InlineData(Horizontal.Right, Vertical.Top, 4, 0)]
[InlineData(Horizontal.Right, Vertical.Center, 4, 2)]
[InlineData(Horizontal.Right, Vertical.Bottom, 4, 4)]
public void DrawWithAlignment(Horizontal horizontal, Vertical vertical, int expectedLeft,
int expectedTop)
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(6, 5));
_component.SetContent("VV");
_component.SetAlignment(horizontal);
_component.SetAlignment(vertical);
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(_component, Position.Default, canvas.Size);
Mock.Get(canvas).VerifyPositionOnce(expectedLeft, expectedTop);
}
[Theory]
[InlineData(Orientation.Horizontal, 9, 1)]
public void DrawWithOverloadHorizontal(Orientation orientation, int rootWidth, int rootHeight)
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(rootWidth, rootHeight));
var root = Prepare.Container(_component, _component);
root.SetOrientationHorizontal();
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.Size);
Mock.Get(canvas).VerifyPositionOnce(Position.Default);
Mock.Get(canvas).VerifyPositionOnce(4, 0);
Mock.Get(canvas).Verify(w => w.Paint("Lore"), Times.Exactly(2));
}
[Theory]
[InlineData(4, 4, new[] { 0, 1, 2, 3 })]
public void DrawWithOverloadVertical(int rootWidth, int rootHeight, int[] expectedTopPositions)
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(rootWidth, rootHeight));
_component.SetContent("Lorem\nLorem\nLorem");
var root = Prepare.Container(_component, _component);
root.SetOrientationVertical();
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.Size);
foreach (var expectedTopPosition in expectedTopPositions)
{
Mock.Get(canvas).VerifyPositionOnce(0, expectedTopPosition);
}
Mock.Get(canvas).Verify(w => w.Paint("Lore"), Times.Exactly(rootHeight));
}
[Fact]
public void DrawVerticalWithDoubleComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(10, 2));
var root = Prepare.Container(_component, _component);
root.SetOrientationVertical();
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
new DrawCraftsman(componentCraftsman, containerCraftsman).Draw(root, Position.Default, canvas.Size);
Mock.Get(canvas).VerifyPositionOnce(Position.Default);
Mock.Get(canvas).VerifyPositionOnce(0, 1);
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
[Fact]
public void DrawHorizontalWithDoubleComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(10, 1));
var nodes = new Nodes.Nodes { _component, _component };
var container = Mock.Of<ContainerBase>(g => g.GetNodes() == nodes);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
new DrawCraftsman(componentCraftsman, containerCraftsman).Draw(container, Position.Default, canvas.Size);
Mock.Get(canvas).VerifyPositionOnce(Position.Default);
Mock.Get(canvas).VerifyPositionOnce(5, 0);
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
[Fact]
public void DrawWithMultipleComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(24, 1));
var root = Prepare.Container(_component, _component, _component, _component);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.Size);
Mock.Get(canvas).VerifyPositionOnce(Position.Default);
Mock.Get(canvas).VerifyPositionOnce(6, 0);
Mock.Get(canvas).VerifyPositionOnce(12, 0);
Mock.Get(canvas).VerifyPositionOnce(18, 0);
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(4));
}
[Fact]
public void DrawWithContainerAndComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Size == new Size(10, 2));
var container = Prepare.Container(_component);
var root = Prepare.Container(container, _component);
root.SetAdaptive(Orientation.Vertical);
root.SetOrientationVertical();
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.Size);
Mock.Get(canvas).VerifyPositionOnce(Position.Default);
Mock.Get(canvas).VerifyPositionOnce(0, 1);
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
}

View File

@ -0,0 +1,26 @@
using FluentAssertions;
namespace TUI.Engine.Tests.DrawTests;
public class IntegerTests
{
[Theory]
[InlineData(5, 10, 5)]
[InlineData(5, 5, 5)]
[InlineData(5, 3, 3)]
public void Max(int value, int max, int expected)
{
var result = value.Max(max);
result.Should().Be(expected);
}
[Theory]
[InlineData(5, 10, 10)]
[InlineData(5, 5, 5)]
[InlineData(5, 3, 5)]
public void Min(int value, int min, int expected)
{
var result = value.Min(min);
result.Should().Be(expected);
}
}

View File

@ -0,0 +1,18 @@
using Moq;
using TUI.Engine.Nodes;
using TUI.Engine.Rendering.Canvas;
namespace TUI.Engine.Tests;
public static class MockExtensions
{
public static void VerifyPositionOnce<T>(this Mock<T> mock, int left, int top) where T : class, ICanvas
{
mock.Verify(w => w.SetPencil(new Position(left, top)), Times.Exactly(1));
}
public static void VerifyPositionOnce<T>(this Mock<T> mock, Position position) where T : class, ICanvas
{
mock.Verify(w => w.SetPencil(position), Times.Exactly(1));
}
}

View File

@ -0,0 +1,22 @@
using TUI.Engine.Attributes.Alignments;
using TUI.Engine.Nodes;
namespace TUI.Engine.Tests.Stubs;
public static class Prepare
{
public static TestComponent Component()
{
var testComponent = new TestComponent();
testComponent.SetAlignment(Horizontal.Left);
testComponent.SetAlignment(Vertical.Top);
return testComponent;
}
public static TestContainer Container(params INode[] nodes)
{
var testContainer = new TestContainer();
testContainer.SetNodes(nodes);
return testContainer;
}
}

View File

@ -1,8 +1,8 @@
using TUI.Engine.Nodes.Components;
using TUI.Engine.Components;
namespace Widgets.Tests;
namespace TUI.Engine.Tests.Stubs;
internal class TestComponent : ComponentBase
public class TestComponent : ComponentAttribute
{
private string _content = "Lorem";
@ -11,7 +11,7 @@ internal class TestComponent : ComponentBase
_content = content;
}
public override Sketch DrawComponent()
protected override Sketch DrawComponent()
{
return new Sketch(_content);
}

View File

@ -0,0 +1,20 @@
using TUI.Engine.Containers;
using TUI.Engine.Nodes;
namespace TUI.Engine.Tests.Stubs;
public class TestContainer : ContainerBase
{
private Nodes.Nodes _nodes = new();
public override Nodes.Nodes GetNodes()
{
return _nodes;
}
public TestContainer SetNodes(params INode[] nodes)
{
_nodes.AddRange(nodes);
return this;
}
}

View File

@ -7,7 +7,8 @@
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<RootNamespace>Widgets.Tests</RootNamespace>
<RootNamespace>TUI.Engine.Tests</RootNamespace>
<AssemblyName>TUI.Engine.Tests</AssemblyName>
</PropertyGroup>
<ItemGroup>
@ -26,7 +27,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\TUI\TUI.csproj" />
<ProjectReference Include="..\..\src\TUI.Engine\TUI.Engine.csproj" />
</ItemGroup>
</Project>

View File

@ -1,240 +0,0 @@
using Moq;
using TUI.Engine.Nodes;
using TUI.Engine.Nodes.Attributes;
using TUI.Engine.Nodes.Attributes.Alignments;
using TUI.Engine.Nodes.Attributes.Orientations;
using TUI.Engine.Nodes.Attributes.Resizing;
using TUI.Engine.Nodes.Containers;
using TUI.Engine.Rendering;
namespace Widgets.Tests;
public class NodeCraftsmanTests
{
private readonly TestComponent _component;
public NodeCraftsmanTests()
{
_component = new TestComponent();
_component.SetAlignment(Horizontal.Left);
_component.SetAlignment(Vertical.Top);
}
[Fact]
public void DrawSimple()
{
var canvas = Mock.Of<ICanvas>(w => w.Width == 9 && w.Height == 1);
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(_component, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Once());
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Once());
}
[Theory]
[InlineData(Horizontal.Left, "Lorem", 10, 0)]
[InlineData(Horizontal.Center, "Lorem", 10, 2)]
[InlineData(Horizontal.Center, "Lo", 10, 4)]
[InlineData(Horizontal.Center, "Lorem", 9, 2)]
[InlineData(Horizontal.Center, "Lorem", 11, 3)]
[InlineData(Horizontal.Right, "Lorem", 10, 5)]
[InlineData(Horizontal.Right, "Lo", 10, 8)]
public void DrawWithHorizontalAlignment(Horizontal alignment, string content, int canvasSize,
int expectedPosition)
{
var canvas = Mock.Of<ICanvas>(w => w.Width == canvasSize && w.Height == canvasSize);
_component.SetContent(content);
_component.SetAlignment(Vertical.Top);
_component.SetAlignment(alignment);
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(_component, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.Paint(content), Times.Once());
Mock.Get(canvas).Verify(w => w.SetPencil(expectedPosition, 0), Times.Once());
}
[Theory]
[InlineData(Vertical.Top, "v", 5, new[] { 0 })]
[InlineData(Vertical.Top, "v\nv", 5, new[] { 0, 1 })]
[InlineData(Vertical.Top, "v\nv\nv", 5, new[] { 0, 1, 2 })]
[InlineData(Vertical.Center, "v", 1, new[] { 0 })]
[InlineData(Vertical.Center, "v", 4, new[] { 1 })]
[InlineData(Vertical.Center, "v", 5, new[] { 2 })]
[InlineData(Vertical.Center, "v", 6, new[] { 2 })]
[InlineData(Vertical.Center, "v\nv", 4, new[] { 1, 2 })]
[InlineData(Vertical.Center, "v\nv", 5, new[] { 1, 2 })]
[InlineData(Vertical.Center, "v\nv", 6, new[] { 2, 3 })]
[InlineData(Vertical.Bottom, "v", 5, new[] { 4 })]
[InlineData(Vertical.Bottom, "v\nv", 2, new[] { 0, 1 })]
[InlineData(Vertical.Bottom, "v\nv", 3, new[] { 1, 2 })]
[InlineData(Vertical.Bottom, "v\nv\nv\nv", 5, new[] { 1, 2, 3, 4 })]
public void DrawWithVerticalAlignment(Vertical alignment, string content, int canvasSize, int[] expectedPositions)
{
var canvas = Mock.Of<ICanvas>(w => w.Width == canvasSize && w.Height == canvasSize);
_component.SetContent(content);
_component.SetAlignment(Horizontal.Left);
_component.SetAlignment(alignment);
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(_component, Position.Default, canvas.GetSize());
foreach (var expectedCursorPosition in expectedPositions)
{
Mock.Get(canvas).Verify(w => w.SetPencil(0, expectedCursorPosition), Times.Once());
}
}
[Theory]
[InlineData(Horizontal.Left, Vertical.Top, 0, 0)]
[InlineData(Horizontal.Left, Vertical.Center, 0, 2)]
[InlineData(Horizontal.Left, Vertical.Bottom, 0, 4)]
[InlineData(Horizontal.Center, Vertical.Top, 2, 0)]
[InlineData(Horizontal.Center, Vertical.Center, 2, 2)]
[InlineData(Horizontal.Center, Vertical.Bottom, 2, 4)]
[InlineData(Horizontal.Right, Vertical.Top, 4, 0)]
[InlineData(Horizontal.Right, Vertical.Center, 4, 2)]
[InlineData(Horizontal.Right, Vertical.Bottom, 4, 4)]
public void DrawWithAlignment(Horizontal horizontal, Vertical vertical, int expectedLeft,
int expectedTop)
{
var canvas = Mock.Of<ICanvas>(w => w.Width == 6 && w.Height == 5);
_component.SetContent("VV");
_component.SetAlignment(horizontal);
_component.SetAlignment(vertical);
var componentCraftsman = new ComponentCraftsman(canvas);
componentCraftsman.Draw(_component, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(expectedLeft, expectedTop), Times.Once());
}
[Theory]
[InlineData(Orientation.Horizontal, 9, 1)]
public void DrawWithOverloadHorizontal(Orientation orientation, int rootWidth, int rootHeight)
{
var canvas = Mock.Of<ICanvas>(w => w.Width == rootWidth && w.Height == rootHeight);
var nodes = new Nodes { _component, _component };
var root = Mock.Of<IContainer>(r => r.GetNodes() == nodes && r.Orientation == orientation);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Once());
Mock.Get(canvas).Verify(w => w.SetPencil(4, 0), Times.Once());
Mock.Get(canvas).Verify(w => w.Paint("Lore"), Times.Exactly(2));
}
[Theory]
[InlineData(4, 4, new[] { 0, 1, 2, 3 })]
public void DrawWithOverloadVertical(int rootWidth, int rootHeight, int[] expectedTopPositions)
{
var canvas = Mock.Of<ICanvas>(w => w.Width == rootWidth && w.Height == rootHeight);
_component.SetContent("Lorem\nLorem\nLorem");
var nodes = new Nodes { _component, _component };
var root = Mock.Of<IContainer>(r => r.GetNodes() == nodes && r.Orientation == Orientation.Vertical);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.GetSize());
foreach (var expectedTopPosition in expectedTopPositions)
{
Mock.Get(canvas).Verify(w => w.SetPencil(0, expectedTopPosition), Times.Once());
}
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(rootHeight));
}
[Fact]
public void DrawVerticalWithDoubleComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Height == 2 && w.Width == 10);
var nodes = new Nodes { _component, _component };
var root = Mock.Of<IContainer>(r => r.GetNodes() == nodes && r.Orientation == Orientation.Vertical);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
new NodeCraftsman(componentCraftsman, containerCraftsman).Draw(root, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Once());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 1), Times.Once());
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
[Fact]
public void DrawHorizontalWithDoubleComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Width == 10 && w.Height == 1);
var nodes = new Nodes { _component, _component };
var container = Mock.Of<ContainerBase>(g => g.GetNodes() == nodes);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
new NodeCraftsman(componentCraftsman, containerCraftsman).Draw(container, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.SetPencil(5, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
[Fact]
public void DrawWithMultipleComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Width == 24 && w.Height == 1);
var nodes = new Nodes { _component, _component, _component, _component };
var root = Mock.Of<IContainer>(r => r.GetNodes() == nodes);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.SetPencil(6, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.SetPencil(12, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.SetPencil(18, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(4));
}
[Fact]
public void DrawWithContainerAndComponent()
{
var canvas = Mock.Of<ICanvas>(w => w.Width == 10 && w.Height == 2);
var container = Mock.Of<IContainer>(c => c.GetNodes() == new Nodes { _component });
var nodes = new Nodes { container, _component };
var root = Mock.Of<IContainer>(r => r.GetNodes() == nodes && r.Orientation == Orientation.Vertical);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.SetPencil(0, 1), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
[Theory]
[InlineData(Resizing.Fixed, 6)]
[InlineData(Resizing.Adaptive, 10)]
public void DrawResizingContainer(Resizing resizing, int expectedCursorPosition)
{
var canvas = Mock.Of<ICanvas>(w => w.Width == 20 && w.Height == 2);
var container =
Mock.Of<IContainer>(c =>
c.GetNodes() == new Nodes { _component } && c.ResizingHorizontal == resizing &&
c.GetFixedSize() == new Size(6, 2));
var nodes = new Nodes { container, _component };
var root = Mock.Of<IContainer>(r => r.GetNodes() == nodes && r.Orientation == Orientation.Horizontal);
var componentCraftsman = new ComponentCraftsman(canvas);
var containerCraftsman = new ContainerCraftsman(componentCraftsman);
containerCraftsman.Draw(root, Position.Default, canvas.GetSize());
Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.SetPencil(expectedCursorPosition, 0), Times.Exactly(1));
Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2));
}
}

View File

@ -1,18 +0,0 @@
using TUI.Components.Controls.Statics;
namespace Widgets.Tests.Controls;
public class LogoTests
{
[Fact]
public void Simple()
{
var logo = new Logo();
var render = logo.MakeSketch().ToString();
Assert.Equal(
" \u001b[38;2;132;186;100m\u256d\u2501\u2501\u2501\u2501\u2533\u256e\u001b[0m\u001b[38;2;113;121;126m\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u256d\u2501\u2501\u2501\u256e\u001b[0m\n \u001b[38;2;132;186;100m\u2503\u256d\u256e\u256d\u256e\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2570\u256e\u256d\u256e\u2503\u001b[0m\n \u001b[38;2;132;186;100m\u2570\u256f\u2503\u2503\u2570\u252b\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u2503\u2503\u001b[0m\n \u001b[38;2;113;121;126m\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u256d\u256e\u2503\u2503\u2503\u2503\u001b[0m\n \u001b[38;2;113;121;126m\u2571\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2570\u2501\u256f\u2523\u256f\u2570\u256f\u2503\u001b[0m\n\u001b[38;2;113;121;126m\u2571\u2571\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2570\u256f\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u2570\u2501\u2501\u2501\u253b\u2501\u2501\u2501\u256f\u001b[0m\n",
render);
}
}

View File

@ -1,22 +0,0 @@
using FluentAssertions;
using TUI.Engine.Rendering;
namespace Widgets.Tests;
public class IntegerTests
{
[Fact]
public void IntegerGreaterMax()
{
var result = 5.Max(10);
result.Should().Be(5);
}
[Fact]
public void IntegerLessMax()
{
var result = 5.Max(3);
result.Should().Be(3);
}
}