217 lines
6.3 KiB
C#
217 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Consolus
|
|
{
|
|
public class ConsoleRenderer
|
|
{
|
|
private readonly ConsoleTextWriter _consoleWriter;
|
|
private int _previousDisplayLength;
|
|
private int _cursorIndex;
|
|
private int _beforeEditLineSize;
|
|
|
|
public ConsoleRenderer()
|
|
{
|
|
SupportEscapeSequences = ConsoleHelper.SupportEscapeSequences;
|
|
_consoleWriter = new ConsoleTextWriter(Console.Out);
|
|
Prompt = new ConsoleText();
|
|
EditLine = new ConsoleText();
|
|
AfterEditLine = new ConsoleText();
|
|
SelectionIndex = -1;
|
|
EnableCursorChanged = true;
|
|
}
|
|
|
|
public bool SupportEscapeSequences { get; set; }
|
|
|
|
public ConsoleTextWriter ConsoleWriter => _consoleWriter;
|
|
|
|
public int LineTop
|
|
{
|
|
get
|
|
{
|
|
var currentLine = (_beforeEditLineSize + CursorIndex) / Console.BufferWidth;
|
|
return Console.CursorTop - currentLine;
|
|
}
|
|
}
|
|
|
|
public bool IsClean => _previousDisplayLength == 0;
|
|
|
|
public bool EnableCursorChanged { get; set; }
|
|
|
|
public int CursorIndex
|
|
{
|
|
get => _cursorIndex;
|
|
set
|
|
{
|
|
if (value < 0 || value > EditLine.Count) throw new ArgumentOutOfRangeException(nameof(value), $"Value must be >= 0 and <= {EditLine.Count}");
|
|
|
|
var lineTop = LineTop;
|
|
bool changed = _cursorIndex != value;
|
|
_cursorIndex = value;
|
|
UpdateCursorPosition(lineTop);
|
|
if (changed)
|
|
{
|
|
NotifyCursorChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public Action CursorChanged { get; set; }
|
|
|
|
private void NotifyCursorChanged()
|
|
{
|
|
var cursorChanged = CursorChanged;
|
|
if (EnableCursorChanged) CursorChanged?.Invoke();
|
|
}
|
|
|
|
public bool HasSelection => SelectionIndex >= 0 && CursorIndex != SelectionIndex;
|
|
|
|
public int SelectionStartIndex => HasSelection ? SelectionIndex < CursorIndex ? SelectionIndex : CursorIndex : -1;
|
|
|
|
public int SelectionEndIndex => HasSelection ? SelectionIndex < CursorIndex ? CursorIndex : SelectionIndex : -1;
|
|
|
|
public int SelectionIndex { get; private set; }
|
|
|
|
public ConsoleText Prompt { get; }
|
|
|
|
public ConsoleText EditLine { get; }
|
|
|
|
public ConsoleText AfterEditLine { get; }
|
|
|
|
public Action BeforeRender { get; set; }
|
|
|
|
public void BeginSelection()
|
|
{
|
|
if (SelectionIndex >= 0) return;
|
|
SelectionIndex = CursorIndex;
|
|
}
|
|
|
|
public void EndSelection()
|
|
{
|
|
if (SelectionIndex < 0) return;
|
|
SelectionIndex = -1;
|
|
Render();
|
|
}
|
|
|
|
public void RemoveSelection()
|
|
{
|
|
if (!HasSelection) return;
|
|
|
|
var start = SelectionStartIndex;
|
|
var count = SelectionEndIndex - start;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (start >= EditLine.Count) break;
|
|
EditLine.RemoveAt(start);
|
|
}
|
|
|
|
var cursorIndex = start;
|
|
SelectionIndex = -1;
|
|
Render(cursorIndex);
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
AfterEditLine.Clear();
|
|
EditLine.Clear();
|
|
_cursorIndex = 0;
|
|
_beforeEditLineSize = 0;
|
|
_previousDisplayLength = 0;
|
|
}
|
|
|
|
public void Render(int? newCursorIndex = null, bool reset = false)
|
|
{
|
|
// Invoke before rendering to update the highlighting
|
|
BeforeRender?.Invoke();
|
|
|
|
Console.CursorVisible = false;
|
|
Console.SetCursorPosition(0, LineTop);
|
|
|
|
if (SupportEscapeSequences)
|
|
{
|
|
ConsoleStyle.Reset.Render(_consoleWriter);
|
|
}
|
|
else
|
|
{
|
|
Console.ResetColor();
|
|
}
|
|
|
|
Prompt.Render(_consoleWriter);
|
|
|
|
EditLine.SelectionStart = SelectionStartIndex;
|
|
EditLine.SelectionEnd = SelectionEndIndex;
|
|
EditLine.Render(_consoleWriter, SupportEscapeSequences);
|
|
|
|
AfterEditLine.Render(_consoleWriter);
|
|
|
|
// Fill remaining space with space
|
|
var newLength = _consoleWriter.VisibleCharacterCount;
|
|
var visualLength = newLength;
|
|
if (newLength < _previousDisplayLength)
|
|
{
|
|
if (SupportEscapeSequences)
|
|
{
|
|
ConsoleStyle.Reset.Render(_consoleWriter);
|
|
}
|
|
|
|
for (int i = newLength; i < _previousDisplayLength; i++)
|
|
{
|
|
_consoleWriter.Write(' ');
|
|
}
|
|
visualLength = _previousDisplayLength;
|
|
}
|
|
|
|
if (SupportEscapeSequences)
|
|
{
|
|
ConsoleStyle.Reset.Render(_consoleWriter);
|
|
}
|
|
|
|
_consoleWriter.Commit();
|
|
|
|
if (!SupportEscapeSequences)
|
|
{
|
|
Console.ResetColor();
|
|
}
|
|
|
|
_previousDisplayLength = newLength;
|
|
|
|
if (!reset)
|
|
{
|
|
// Calculate the current line based on the visual
|
|
var lineTop = Console.CursorTop - (visualLength - 1) / Console.BufferWidth;
|
|
|
|
_beforeEditLineSize = EditLine.VisibleCharacterStart;
|
|
if (newCursorIndex.HasValue)
|
|
{
|
|
bool changed = _cursorIndex != newCursorIndex.Value;
|
|
_cursorIndex = newCursorIndex.Value;
|
|
|
|
if (changed)
|
|
{
|
|
NotifyCursorChanged();
|
|
}
|
|
}
|
|
|
|
if (_cursorIndex > EditLine.Count) _cursorIndex = EditLine.Count;
|
|
|
|
UpdateCursorPosition(lineTop);
|
|
}
|
|
else
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
Console.CursorVisible = true;
|
|
}
|
|
|
|
private void UpdateCursorPosition(int lineTop)
|
|
{
|
|
var position = _beforeEditLineSize + _cursorIndex;
|
|
|
|
var deltaX = position % Console.BufferWidth;
|
|
var deltaY = position / Console.BufferWidth;
|
|
Console.SetCursorPosition(deltaX, lineTop + deltaY);
|
|
}
|
|
}
|
|
} |