diff --git a/dotnet-tools.json b/dotnet-tools.json
index 4c4c563..72ca7f2 100644
--- a/dotnet-tools.json
+++ b/dotnet-tools.json
@@ -15,7 +15,7 @@
]
},
"dotnet-example": {
- "version": "0.8.0",
+ "version": "0.10.0",
"commands": [
"dotnet-example"
]
diff --git a/examples/Cursor/Cursor.csproj b/examples/Cursor/Cursor.csproj
new file mode 100644
index 0000000..1c06842
--- /dev/null
+++ b/examples/Cursor/Cursor.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ netcoreapp3.1
+ false
+ Cursor
+ Demonstrates how to move the cursor.
+
+
+
+
+
+
+
diff --git a/examples/Cursor/Program.cs b/examples/Cursor/Program.cs
new file mode 100644
index 0000000..ebfdfbf
--- /dev/null
+++ b/examples/Cursor/Program.cs
@@ -0,0 +1,21 @@
+using System;
+using Spectre.Console;
+
+namespace Cursor
+{
+ public static class Program
+ {
+ public static void Main(string[] args)
+ {
+ AnsiConsole.Write("Hello");
+
+ // Move the cursor 3 cells to the right
+ AnsiConsole.Cursor.Move(CursorDirection.Right, 3);
+ AnsiConsole.Write("World");
+
+ // Move the cursor 5 cells to the left.
+ AnsiConsole.Cursor.Move(CursorDirection.Left, 5);
+ AnsiConsole.WriteLine("Universe");
+ }
+ }
+}
diff --git a/src/Spectre.Console.Tests/Tools/AnsiConsoleFixture.cs b/src/Spectre.Console.Tests/Tools/AnsiConsoleFixture.cs
index 7101d6d..de71485 100644
--- a/src/Spectre.Console.Tests/Tools/AnsiConsoleFixture.cs
+++ b/src/Spectre.Console.Tests/Tools/AnsiConsoleFixture.cs
@@ -17,6 +17,7 @@ namespace Spectre.Console.Tests
public Encoding Encoding => _console.Encoding;
public int Width { get; }
public int Height => _console.Height;
+ public IAnsiConsoleCursor Cursor => _console.Cursor;
public TestableAnsiConsole(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80)
{
@@ -37,6 +38,11 @@ namespace Spectre.Console.Tests
_writer?.Dispose();
}
+ public void Clear(bool home)
+ {
+ _console.Clear(home);
+ }
+
public void Write(Segment segment)
{
_console.Write(segment);
diff --git a/src/Spectre.Console.Tests/Tools/MarkupConsoleFixture.cs b/src/Spectre.Console.Tests/Tools/MarkupConsoleFixture.cs
index c9b9be5..d4ce6c3 100644
--- a/src/Spectre.Console.Tests/Tools/MarkupConsoleFixture.cs
+++ b/src/Spectre.Console.Tests/Tools/MarkupConsoleFixture.cs
@@ -14,6 +14,7 @@ namespace Spectre.Console.Tests.Tools
public Capabilities Capabilities => _console.Capabilities;
public Encoding Encoding => _console.Encoding;
+ public IAnsiConsoleCursor Cursor => _console.Cursor;
public int Width { get; }
public int Height => _console.Height;
@@ -36,6 +37,11 @@ namespace Spectre.Console.Tests.Tools
_writer?.Dispose();
}
+ public void Clear(bool home)
+ {
+ _console.Clear(home);
+ }
+
public void Write(Segment segment)
{
_console.Write(segment);
diff --git a/src/Spectre.Console.Tests/Tools/PlainConsole.cs b/src/Spectre.Console.Tests/Tools/PlainConsole.cs
index 5f00fe3..ca31b53 100644
--- a/src/Spectre.Console.Tests/Tools/PlainConsole.cs
+++ b/src/Spectre.Console.Tests/Tools/PlainConsole.cs
@@ -11,6 +11,7 @@ namespace Spectre.Console.Tests
{
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
+ public IAnsiConsoleCursor Cursor => throw new NotSupportedException();
public int Width { get; }
public int Height { get; }
@@ -42,6 +43,10 @@ namespace Spectre.Console.Tests
Writer.Dispose();
}
+ public void Clear(bool home)
+ {
+ }
+
public void Write(Segment segment)
{
if (segment is null)
diff --git a/src/Spectre.Console.sln b/src/Spectre.Console.sln
index 3109e9c..f40c75f 100644
--- a/src/Spectre.Console.sln
+++ b/src/Spectre.Console.sln
@@ -48,6 +48,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calendars", "..\examples\Ca
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules", "..\examples\Rules\Rules.csproj", "{8622A261-02C6-40CA-9797-E3F01ED87D6B}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cursor", "..\examples\Cursor\Cursor.csproj", "{75C608C3-ABB4-4168-A229-7F8250B946D1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -226,6 +228,18 @@ Global
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x64.Build.0 = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x86.ActiveCfg = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x86.Build.0 = Release|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x64.Build.0 = Debug|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x86.Build.0 = Debug|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x64.ActiveCfg = Release|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x64.Build.0 = Release|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x86.ActiveCfg = Release|Any CPU
+ {75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -244,6 +258,7 @@ Global
{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D} = {20595AD4-8D75-4AF8-B6BC-9C38C160423F}
{57691C7D-683D-46E6-AA4F-57A8C5F65D25} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{8622A261-02C6-40CA-9797-E3F01ED87D6B} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
+ {75C608C3-ABB4-4168-A229-7F8250B946D1} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
diff --git a/src/Spectre.Console/AnsiConsole.cs b/src/Spectre.Console/AnsiConsole.cs
index f1d09e1..dcdc80f 100644
--- a/src/Spectre.Console/AnsiConsole.cs
+++ b/src/Spectre.Console/AnsiConsole.cs
@@ -27,6 +27,11 @@ namespace Spectre.Console
///
public static IAnsiConsole Console => _recorder ?? _console.Value;
+ ///
+ /// Gets the .
+ ///
+ public static IAnsiConsoleCursor Cursor => _recorder?.Cursor ?? _console.Value.Cursor;
+
///
/// Gets the console's capabilities.
///
@@ -56,7 +61,7 @@ namespace Spectre.Console
/// An instance.
public static IAnsiConsole Create(AnsiConsoleSettings settings)
{
- return AnsiConsoleBuilder.Build(settings);
+ return BackendBuilder.Build(settings);
}
}
}
diff --git a/src/Spectre.Console/CursorDirection.cs b/src/Spectre.Console/CursorDirection.cs
new file mode 100644
index 0000000..bfea9ea
--- /dev/null
+++ b/src/Spectre.Console/CursorDirection.cs
@@ -0,0 +1,28 @@
+namespace Spectre.Console
+{
+ ///
+ /// Represents cursor direction.
+ ///
+ public enum CursorDirection
+ {
+ ///
+ /// Up
+ ///
+ Up,
+
+ ///
+ /// Down
+ ///
+ Down,
+
+ ///
+ /// Left
+ ///
+ Left,
+
+ ///
+ /// Right
+ ///
+ Right,
+ }
+}
diff --git a/src/Spectre.Console/Extensions/CursorExtensions.cs b/src/Spectre.Console/Extensions/CursorExtensions.cs
new file mode 100644
index 0000000..57cd8b8
--- /dev/null
+++ b/src/Spectre.Console/Extensions/CursorExtensions.cs
@@ -0,0 +1,152 @@
+namespace Spectre.Console
+{
+ ///
+ /// Contains extension methods for .
+ ///
+ public static class CursorExtensions
+ {
+ ///
+ /// Shows the cursor.
+ ///
+ /// The cursor.
+ public static void Show(this IAnsiConsoleCursor cursor)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Show(true);
+ }
+
+ ///
+ /// Hides the cursor.
+ ///
+ /// The cursor.
+ public static void Hide(this IAnsiConsoleCursor cursor)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Show(false);
+ }
+
+ ///
+ /// Moves the cursor up.
+ ///
+ /// The cursor.
+ public static void MoveUp(this IAnsiConsoleCursor cursor)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Up, 1);
+ }
+
+ ///
+ /// Moves the cursor up.
+ ///
+ /// The cursor.
+ /// The number of steps to move the cursor.
+ public static void MoveUp(this IAnsiConsoleCursor cursor, int steps)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Up, steps);
+ }
+
+ ///
+ /// Moves the cursor down.
+ ///
+ /// The cursor.
+ public static void MoveDown(this IAnsiConsoleCursor cursor)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Down, 1);
+ }
+
+ ///
+ /// Moves the cursor down.
+ ///
+ /// The cursor.
+ /// The number of steps to move the cursor.
+ public static void MoveDown(this IAnsiConsoleCursor cursor, int steps)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Down, steps);
+ }
+
+ ///
+ /// Moves the cursor to the left.
+ ///
+ /// The cursor.
+ public static void MoveLeft(this IAnsiConsoleCursor cursor)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Left, 1);
+ }
+
+ ///
+ /// Moves the cursor to the left.
+ ///
+ /// The cursor.
+ /// The number of steps to move the cursor.
+ public static void MoveLeft(this IAnsiConsoleCursor cursor, int steps)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Left, steps);
+ }
+
+ ///
+ /// Moves the cursor to the right.
+ ///
+ /// The cursor.
+ public static void MoveRight(this IAnsiConsoleCursor cursor)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Right, 1);
+ }
+
+ ///
+ /// Moves the cursor to the right.
+ ///
+ /// The cursor.
+ /// The number of steps to move the cursor.
+ public static void MoveRight(this IAnsiConsoleCursor cursor, int steps)
+ {
+ if (cursor is null)
+ {
+ throw new System.ArgumentNullException(nameof(cursor));
+ }
+
+ cursor.Move(CursorDirection.Right, steps);
+ }
+ }
+}
diff --git a/src/Spectre.Console/Extensions/ExceptionExtensions.cs b/src/Spectre.Console/Extensions/ExceptionExtensions.cs
index aa0c0aa..e34c0e8 100644
--- a/src/Spectre.Console/Extensions/ExceptionExtensions.cs
+++ b/src/Spectre.Console/Extensions/ExceptionExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console
diff --git a/src/Spectre.Console/IAnsiConsole.cs b/src/Spectre.Console/IAnsiConsole.cs
index a4334b9..592dc4b 100644
--- a/src/Spectre.Console/IAnsiConsole.cs
+++ b/src/Spectre.Console/IAnsiConsole.cs
@@ -18,6 +18,11 @@ namespace Spectre.Console
///
Encoding Encoding { get; }
+ ///
+ /// Gets the console cursor.
+ ///
+ IAnsiConsoleCursor Cursor { get; }
+
///
/// Gets the buffer width of the console.
///
@@ -28,6 +33,12 @@ namespace Spectre.Console
///
int Height { get; }
+ ///
+ /// Clears the console.
+ ///
+ /// If the cursor should be moved to the home position.
+ void Clear(bool home);
+
///
/// Writes a string followed by a line terminator to the console.
///
diff --git a/src/Spectre.Console/IAnsiConsoleCursor.cs b/src/Spectre.Console/IAnsiConsoleCursor.cs
new file mode 100644
index 0000000..c9019ee
--- /dev/null
+++ b/src/Spectre.Console/IAnsiConsoleCursor.cs
@@ -0,0 +1,28 @@
+namespace Spectre.Console
+{
+ ///
+ /// Represents the console's cursor.
+ ///
+ public interface IAnsiConsoleCursor
+ {
+ ///
+ /// Shows or hides the cursor.
+ ///
+ /// true to show the cursor, false to hide it.
+ void Show(bool show);
+
+ ///
+ /// Sets the cursor position.
+ ///
+ /// The column to move the cursor to.
+ /// The line to move the cursor to.
+ void SetPosition(int column, int line);
+
+ ///
+ /// Moves the cursor relative to the current position.
+ ///
+ /// The direction to move the cursor.
+ /// The number of steps to move the cursor.
+ void Move(CursorDirection direction, int steps);
+ }
+}
diff --git a/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs b/src/Spectre.Console/Internal/Backends/Ansi/AnsiBackend.cs
similarity index 78%
rename from src/Spectre.Console/Internal/AnsiConsoleRenderer.cs
rename to src/Spectre.Console/Internal/Backends/Ansi/AnsiBackend.cs
index 51810fe..082fac8 100644
--- a/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs
+++ b/src/Spectre.Console/Internal/Backends/Ansi/AnsiBackend.cs
@@ -5,13 +5,15 @@ using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
- internal sealed class AnsiConsoleRenderer : IAnsiConsole
+ internal sealed class AnsiBackend : IAnsiConsole
{
private readonly TextWriter _out;
private readonly AnsiBuilder _ansiBuilder;
+ private readonly AnsiCursor _cursor;
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
+ public IAnsiConsoleCursor Cursor => _cursor;
public int Width
{
@@ -39,7 +41,7 @@ namespace Spectre.Console.Internal
}
}
- public AnsiConsoleRenderer(TextWriter @out, Capabilities capabilities, ILinkIdentityGenerator? linkHasher)
+ public AnsiBackend(TextWriter @out, Capabilities capabilities, ILinkIdentityGenerator? linkHasher)
{
_out = @out ?? throw new ArgumentNullException(nameof(@out));
@@ -47,6 +49,17 @@ namespace Spectre.Console.Internal
Encoding = _out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8;
_ansiBuilder = new AnsiBuilder(Capabilities, linkHasher);
+ _cursor = new AnsiCursor(this);
+ }
+
+ public void Clear(bool home)
+ {
+ Write(Segment.Control("\u001b[2J"));
+
+ if (home)
+ {
+ Cursor.SetPosition(0, 0);
+ }
}
public void Write(Segment segment)
diff --git a/src/Spectre.Console/Internal/Backends/Ansi/AnsiCursor.cs b/src/Spectre.Console/Internal/Backends/Ansi/AnsiCursor.cs
new file mode 100644
index 0000000..28b00d5
--- /dev/null
+++ b/src/Spectre.Console/Internal/Backends/Ansi/AnsiCursor.cs
@@ -0,0 +1,56 @@
+using System;
+using Spectre.Console.Rendering;
+
+namespace Spectre.Console.Internal
+{
+ internal sealed class AnsiCursor : IAnsiConsoleCursor
+ {
+ private readonly AnsiBackend _renderer;
+
+ public AnsiCursor(AnsiBackend renderer)
+ {
+ _renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
+ }
+
+ public void Show(bool show)
+ {
+ if (show)
+ {
+ _renderer.Write(Segment.Control("\u001b[?25h"));
+ }
+ else
+ {
+ _renderer.Write(Segment.Control("\u001b[?25l"));
+ }
+ }
+
+ public void Move(CursorDirection direction, int steps)
+ {
+ if (steps == 0)
+ {
+ return;
+ }
+
+ switch (direction)
+ {
+ case CursorDirection.Up:
+ _renderer.Write(Segment.Control($"\u001b[{steps}A"));
+ break;
+ case CursorDirection.Down:
+ _renderer.Write(Segment.Control($"\u001b[{steps}B"));
+ break;
+ case CursorDirection.Right:
+ _renderer.Write(Segment.Control($"\u001b[{steps}C"));
+ break;
+ case CursorDirection.Left:
+ _renderer.Write(Segment.Control($"\u001b[{steps}D"));
+ break;
+ }
+ }
+
+ public void SetPosition(int column, int line)
+ {
+ _renderer.Write(Segment.Control($"\u001b[{line};{column}H"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spectre.Console/Internal/AnsiConsoleBuilder.cs b/src/Spectre.Console/Internal/Backends/BackendBuilder.cs
similarity index 91%
rename from src/Spectre.Console/Internal/AnsiConsoleBuilder.cs
rename to src/Spectre.Console/Internal/Backends/BackendBuilder.cs
index 0ec2d84..6069e2a 100644
--- a/src/Spectre.Console/Internal/AnsiConsoleBuilder.cs
+++ b/src/Spectre.Console/Internal/Backends/BackendBuilder.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Spectre.Console.Internal
{
- internal static class AnsiConsoleBuilder
+ internal static class BackendBuilder
{
public static IAnsiConsole Build(AnsiConsoleSettings settings)
{
@@ -60,11 +60,11 @@ namespace Spectre.Console.Internal
// Create the renderer
if (supportsAnsi)
{
- return new AnsiConsoleRenderer(buffer, capabilities, settings.LinkIdentityGenerator);
+ return new AnsiBackend(buffer, capabilities, settings.LinkIdentityGenerator);
}
else
{
- return new FallbackConsoleRenderer(buffer, capabilities);
+ return new FallbackBackend(buffer, capabilities);
}
}
}
diff --git a/src/Spectre.Console/Internal/Backends/Fallback/FallbackBackend.cs b/src/Spectre.Console/Internal/Backends/Fallback/FallbackBackend.cs
new file mode 100644
index 0000000..44c7c7b
--- /dev/null
+++ b/src/Spectre.Console/Internal/Backends/Fallback/FallbackBackend.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+using System.Text;
+using Spectre.Console.Rendering;
+
+namespace Spectre.Console.Internal
+{
+ internal sealed class FallbackBackend : IAnsiConsole
+ {
+ private readonly ColorSystem _system;
+ private readonly FallbackCursor _cursor;
+ private Style? _lastStyle;
+
+ public Capabilities Capabilities { get; }
+ public Encoding Encoding { get; }
+ public IAnsiConsoleCursor Cursor => _cursor;
+
+ public int Width
+ {
+ get { return ConsoleHelper.GetSafeBufferWidth(Constants.DefaultBufferWidth); }
+ }
+
+ public int Height
+ {
+ get { return ConsoleHelper.GetSafeBufferHeight(Constants.DefaultBufferHeight); }
+ }
+
+ public FallbackBackend(TextWriter @out, Capabilities capabilities)
+ {
+ if (capabilities == null)
+ {
+ throw new ArgumentNullException(nameof(capabilities));
+ }
+
+ _system = capabilities.ColorSystem;
+ _cursor = new FallbackCursor();
+
+ if (@out != System.Console.Out)
+ {
+ System.Console.SetOut(@out ?? throw new ArgumentNullException(nameof(@out)));
+ }
+
+ Encoding = System.Console.OutputEncoding;
+ Capabilities = capabilities;
+ }
+
+ public void Clear(bool home)
+ {
+ var (x, y) = (System.Console.CursorLeft, System.Console.CursorTop);
+
+ System.Console.Clear();
+
+ if (!home)
+ {
+ // Set the cursor position
+ System.Console.SetCursorPosition(x, y);
+ }
+ }
+
+ public void Write(Segment segment)
+ {
+ if (_lastStyle?.Equals(segment.Style) != true)
+ {
+ SetStyle(segment.Style);
+ }
+
+ System.Console.Write(segment.Text.NormalizeLineEndings(native: true));
+ }
+
+ private void SetStyle(Style style)
+ {
+ _lastStyle = style;
+
+ System.Console.ResetColor();
+
+ var background = Color.ToConsoleColor(style.Background);
+ if (_system != ColorSystem.NoColors && (int)background != -1)
+ {
+ System.Console.BackgroundColor = background;
+ }
+
+ var foreground = Color.ToConsoleColor(style.Foreground);
+ if (_system != ColorSystem.NoColors && (int)foreground != -1)
+ {
+ System.Console.ForegroundColor = foreground;
+ }
+ }
+ }
+}
diff --git a/src/Spectre.Console/Internal/Backends/Fallback/FallbackCursor.cs b/src/Spectre.Console/Internal/Backends/Fallback/FallbackCursor.cs
new file mode 100644
index 0000000..475aa87
--- /dev/null
+++ b/src/Spectre.Console/Internal/Backends/Fallback/FallbackCursor.cs
@@ -0,0 +1,40 @@
+namespace Spectre.Console.Internal
+{
+ internal sealed class FallbackCursor : IAnsiConsoleCursor
+ {
+ public void Show(bool show)
+ {
+ System.Console.CursorVisible = show;
+ }
+
+ public void Move(CursorDirection direction, int steps)
+ {
+ if (steps == 0)
+ {
+ return;
+ }
+
+ switch (direction)
+ {
+ case CursorDirection.Up:
+ System.Console.CursorTop -= steps;
+ break;
+ case CursorDirection.Down:
+ System.Console.CursorTop += steps;
+ break;
+ case CursorDirection.Left:
+ System.Console.CursorLeft -= steps;
+ break;
+ case CursorDirection.Right:
+ System.Console.CursorLeft += steps;
+ break;
+ }
+ }
+
+ public void SetPosition(int x, int y)
+ {
+ System.Console.CursorLeft = x;
+ System.Console.CursorTop = y;
+ }
+ }
+}
diff --git a/src/Spectre.Console/Internal/Collections/ListWithCallback.cs b/src/Spectre.Console/Internal/Collections/ListWithCallback.cs
index 180899f..32e58b7 100644
--- a/src/Spectre.Console/Internal/Collections/ListWithCallback.cs
+++ b/src/Spectre.Console/Internal/Collections/ListWithCallback.cs
@@ -2,7 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
-namespace Spectre.Console.Internal.Collections
+namespace Spectre.Console.Internal
{
internal sealed class ListWithCallback : IList, IReadOnlyList
{
diff --git a/src/Spectre.Console/Internal/Utilities/ConsoleHelper.cs b/src/Spectre.Console/Internal/ConsoleHelper.cs
similarity index 100%
rename from src/Spectre.Console/Internal/Utilities/ConsoleHelper.cs
rename to src/Spectre.Console/Internal/ConsoleHelper.cs
diff --git a/src/Spectre.Console/Internal/ExceptionFormatter.cs b/src/Spectre.Console/Internal/ExceptionFormatter.cs
index 2f5c67a..d55168e 100644
--- a/src/Spectre.Console/Internal/ExceptionFormatter.cs
+++ b/src/Spectre.Console/Internal/ExceptionFormatter.cs
@@ -1,10 +1,9 @@
using System;
using System.Linq;
using System.Text;
-using Spectre.Console.Internal;
using Spectre.Console.Rendering;
-namespace Spectre.Console
+namespace Spectre.Console.Internal
{
internal static class ExceptionFormatter
{
diff --git a/src/Spectre.Console/Internal/FallbackConsoleRenderer.cs b/src/Spectre.Console/Internal/FallbackConsoleRenderer.cs
deleted file mode 100644
index 150e961..0000000
--- a/src/Spectre.Console/Internal/FallbackConsoleRenderer.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.IO;
-using System.Text;
-using Spectre.Console.Rendering;
-
-namespace Spectre.Console.Internal
-{
- internal sealed class FallbackConsoleRenderer : IAnsiConsole
- {
- private readonly TextWriter _out;
- private readonly ColorSystem _system;
- private Style? _lastStyle;
-
- public Capabilities Capabilities { get; }
- public Encoding Encoding { get; }
-
- public int Width
- {
- get
- {
- if (_out.IsStandardOut())
- {
- return ConsoleHelper.GetSafeBufferWidth(Constants.DefaultBufferWidth);
- }
-
- return Constants.DefaultBufferWidth;
- }
- }
-
- public int Height
- {
- get
- {
- if (_out.IsStandardOut())
- {
- return ConsoleHelper.GetSafeBufferHeight(Constants.DefaultBufferHeight);
- }
-
- return Constants.DefaultBufferHeight;
- }
- }
-
- public FallbackConsoleRenderer(TextWriter @out, Capabilities capabilities)
- {
- if (capabilities == null)
- {
- throw new ArgumentNullException(nameof(capabilities));
- }
-
- _out = @out ?? throw new ArgumentNullException(nameof(@out));
- _system = capabilities.ColorSystem;
-
- if (_out.IsStandardOut())
- {
- Encoding = System.Console.OutputEncoding;
- }
- else
- {
- Encoding = Encoding.UTF8;
- }
-
- Capabilities = capabilities;
- }
-
- public void Write(Segment segment)
- {
- if (_lastStyle?.Equals(segment.Style) != true)
- {
- SetStyle(segment.Style);
- }
-
- _out.Write(segment.Text.NormalizeLineEndings(native: true));
- }
-
- private void SetStyle(Style style)
- {
- _lastStyle = style;
-
- if (_out.IsStandardOut())
- {
- System.Console.ResetColor();
-
- var background = Color.ToConsoleColor(style.Background);
- if (_system != ColorSystem.NoColors && _out.IsStandardOut() && (int)background != -1)
- {
- System.Console.BackgroundColor = background;
- }
-
- var foreground = Color.ToConsoleColor(style.Foreground);
- if (_system != ColorSystem.NoColors && _out.IsStandardOut() && (int)foreground != -1)
- {
- System.Console.ForegroundColor = foreground;
- }
- }
- }
- }
-}
diff --git a/src/Spectre.Console/Internal/Utilities/Ratio.cs b/src/Spectre.Console/Internal/Ratio.cs
similarity index 100%
rename from src/Spectre.Console/Internal/Utilities/Ratio.cs
rename to src/Spectre.Console/Internal/Ratio.cs
diff --git a/src/Spectre.Console/Recorder.cs b/src/Spectre.Console/Recorder.cs
index 140319b..1ba1afb 100644
--- a/src/Spectre.Console/Recorder.cs
+++ b/src/Spectre.Console/Recorder.cs
@@ -19,6 +19,9 @@ namespace Spectre.Console
///
public Encoding Encoding => _console.Encoding;
+ ///
+ public IAnsiConsoleCursor Cursor => _console.Cursor;
+
///
public int Width => _console.Width;
@@ -41,10 +44,26 @@ namespace Spectre.Console
// Only used for scoping.
}
+ ///
+ public void Clear(bool home)
+ {
+ _console.Clear(home);
+ }
+
///
public void Write(Segment segment)
{
- _recorded.Add(segment);
+ if (segment is null)
+ {
+ throw new ArgumentNullException(nameof(segment));
+ }
+
+ // Don't record control codes.
+ if (!segment.IsControlCode)
+ {
+ _recorded.Add(segment);
+ }
+
_console.Write(segment);
}
diff --git a/src/Spectre.Console/Rendering/Segment.cs b/src/Spectre.Console/Rendering/Segment.cs
index 84b8e95..c7e045f 100644
--- a/src/Spectre.Console/Rendering/Segment.cs
+++ b/src/Spectre.Console/Rendering/Segment.cs
@@ -31,6 +31,12 @@ namespace Spectre.Console.Rendering
///
public bool IsWhiteSpace { get; }
+ ///
+ /// Gets a value indicating whether or not his is a
+ /// control code such as cursor movement.
+ ///
+ public bool IsControlCode { get; }
+
///
/// Gets the segment style.
///
@@ -39,12 +45,12 @@ namespace Spectre.Console.Rendering
///
/// Gets a segment representing a line break.
///
- public static Segment LineBreak { get; } = new Segment(Environment.NewLine, Style.Plain, true);
+ public static Segment LineBreak { get; } = new Segment(Environment.NewLine, Style.Plain, true, false);
///
/// Gets an empty segment.
///
- public static Segment Empty { get; } = new Segment(string.Empty, Style.Plain, false);
+ public static Segment Empty { get; } = new Segment(string.Empty, Style.Plain, false, false);
///
/// Initializes a new instance of the class.
@@ -61,16 +67,27 @@ namespace Spectre.Console.Rendering
/// The segment text.
/// The segment style.
public Segment(string text, Style style)
- : this(text, style, false)
+ : this(text, style, false, false)
{
}
- private Segment(string text, Style style, bool lineBreak)
+ private Segment(string text, Style style, bool lineBreak, bool control)
{
Text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text));
Style = style ?? throw new ArgumentNullException(nameof(style));
IsLineBreak = lineBreak;
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
+ IsControlCode = control;
+ }
+
+ ///
+ /// Creates a control segment.
+ ///
+ /// The control code.
+ /// A segment representing a control code.
+ public static Segment Control(string control)
+ {
+ return new Segment(control, Style.Plain, false, true);
}
///
diff --git a/src/Spectre.Console/Widgets/Calendar.cs b/src/Spectre.Console/Widgets/Calendar.cs
index 059b1a7..cd93b0d 100644
--- a/src/Spectre.Console/Widgets/Calendar.cs
+++ b/src/Spectre.Console/Widgets/Calendar.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Spectre.Console.Internal;
-using Spectre.Console.Internal.Collections;
using Spectre.Console.Rendering;
namespace Spectre.Console
diff --git a/src/Spectre.Console/Widgets/Grid.cs b/src/Spectre.Console/Widgets/Grid.cs
index 0403e72..9fccaf9 100644
--- a/src/Spectre.Console/Widgets/Grid.cs
+++ b/src/Spectre.Console/Widgets/Grid.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Spectre.Console.Internal.Collections;
+using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console
diff --git a/src/Spectre.Console/Widgets/Table.cs b/src/Spectre.Console/Widgets/Table.cs
index 673cb78..f9c297b 100644
--- a/src/Spectre.Console/Widgets/Table.cs
+++ b/src/Spectre.Console/Widgets/Table.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
-using Spectre.Console.Widgets;
namespace Spectre.Console
{
diff --git a/src/Spectre.Console/Widgets/TableRow.cs b/src/Spectre.Console/Widgets/TableRow.cs
index a491148..2ad6466 100644
--- a/src/Spectre.Console/Widgets/TableRow.cs
+++ b/src/Spectre.Console/Widgets/TableRow.cs
@@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using Spectre.Console.Rendering;
-namespace Spectre.Console.Widgets
+namespace Spectre.Console
{
///
/// Represents a table row.