Base features

This commit is contained in:
2022-01-06 18:36:25 +01:00
parent 033b280175
commit 7b3584543e
60 changed files with 2576 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
namespace FileTime.ConsoleUI.App.UI.Color
{
public class AnsiColor : IConsoleColor
{
public string? Color { get; }
public string? Prefix { get; }
public string? Postfix { get; }
public AnsiColor() { }
public AnsiColor(string color, string preFix = "5;", string postFix = "m")
{
Color = color;
Prefix = preFix;
Postfix = postFix;
}
public static AnsiColor FromRgb(int r, int g, int b) => new($"{r};{g};{b}", "5;", "m");
public static AnsiColor From8bit(byte color) => new($"{color}", "5;", "m");
public override string? ToString()
{
return Color == null ? null : Prefix + Color + Postfix;
}
}
}

View File

@@ -0,0 +1,14 @@
namespace FileTime.ConsoleUI.App.UI.Color
{
public class BasicColor : IConsoleColor
{
public BasicColor() { }
public BasicColor(ConsoleColor color)
{
Color = color;
}
public ConsoleColor? Color { get; }
}
}

View File

@@ -0,0 +1,78 @@
namespace FileTime.ConsoleUI.App.UI.Color
{
public class ColoredConsoleRenderer : IColoredConsoleRenderer
{
private readonly IStyles _styles;
public IConsoleColor? BackgroundColor { get; set; }
public IConsoleColor? ForegroundColor { get; set; }
public ColoredConsoleRenderer(IStyles styles)
{
_styles = styles;
BackgroundColor = _styles.DefaultBackground;
ForegroundColor = _styles.DefaultForeground;
}
public void Write(string text) => DoWrite(text);
public void Write(char c) => DoWrite(c.ToString());
public void Write(string format, params object[] param) => DoWrite(string.Format(format, param));
private void DoWrite(string text)
{
if (BackgroundColor is AnsiColor ansiBackground && ForegroundColor is AnsiColor ansiForeground)
{
var formatting = "\u001b[0m";
if (ansiBackground.Color != null)
{
formatting += "\u001b[48;" + ansiBackground.ToString();
}
if (ansiForeground.Color != null)
{
formatting += "\u001b[38;" + ansiForeground.ToString();
}
Console.Write(formatting + text);
}
else if (BackgroundColor is BasicColor basicBackground && ForegroundColor is BasicColor basicForeground)
{
Console.BackgroundColor = basicBackground.Color ?? Console.BackgroundColor;
Console.ForegroundColor = basicForeground.Color ?? Console.ForegroundColor;
Console.Write(text);
}
else if (BackgroundColor == null && ForegroundColor == null)
{
Console.Write(text);
}
else if (BackgroundColor == null || ForegroundColor == null)
{
throw new Exception($"Either both of {nameof(BackgroundColor)} and {nameof(ForegroundColor)} must be null or neither of them.");
}
else if (BackgroundColor.GetType() != ForegroundColor.GetType())
{
throw new Exception($"Type of {nameof(BackgroundColor)} and {nameof(ForegroundColor)} must be the same.");
}
else
{
throw new Exception($"Unsupported color type: {BackgroundColor.GetType()}");
}
}
public void ResetColor()
{
BackgroundColor = _styles.DefaultBackground;
ForegroundColor = _styles.DefaultForeground;
Console.ResetColor();
}
public void Clear()
{
Console.SetCursorPosition(0, 0);
Write(new string(' ', Console.WindowHeight * Console.WindowWidth));
}
}
}

View File

@@ -0,0 +1,14 @@
namespace FileTime.ConsoleUI.App.UI.Color
{
public interface IColoredConsoleRenderer
{
IConsoleColor? BackgroundColor { get; set; }
IConsoleColor? ForegroundColor { get; set; }
void Clear();
void ResetColor();
void Write(string text);
void Write(char c);
void Write(string format, params object[] param);
}
}

View File

@@ -0,0 +1,4 @@
namespace FileTime.ConsoleUI.App.UI.Color
{
public interface IConsoleColor { }
}

View File

@@ -0,0 +1,89 @@
using FileTime.ConsoleUI.App.UI.Color;
namespace FileTime.ConsoleUI.App.UI
{
public class ConsoleReader
{
private readonly IColoredConsoleRenderer _coloredConsoleRenderer;
public ConsoleReader(IColoredConsoleRenderer coloredConsoleRenderer)
{
_coloredConsoleRenderer = coloredConsoleRenderer;
}
public string ReadText(int? maxLength = null, Action<string>? validator = null)
{
var cursorVisible = false;
try
{
cursorVisible = Console.CursorVisible;
}
catch { }
Console.CursorVisible = true;
var currentConsoleLeft = Console.CursorLeft;
var currentConsoleTop = Console.CursorTop;
maxLength ??= Console.WindowWidth - currentConsoleLeft;
var input = "";
var position = 0;
_coloredConsoleRenderer.Write($"{{0,-{maxLength}}}", input);
var key = Console.ReadKey(true);
while (key.Key != ConsoleKey.Enter)
{
if (key.Key == ConsoleKey.Escape)
{
input = null;
break;
}
else if (key.Key == ConsoleKey.Backspace)
{
if (position != 0)
{
input = input.Length > 0
? input[..(position - 1)] + input[position..]
: input;
position--;
}
}
else if (key.Key == ConsoleKey.LeftArrow)
{
if (position > 0)
position--;
}
else if (key.Key == ConsoleKey.RightArrow)
{
if (position < input.Length)
position++;
}
else if (key.KeyChar != '\0')
{
var newInput = input[..position] + key.KeyChar;
if (position < input.Length)
{
newInput += input[position..];
}
input = newInput;
position++;
}
validator?.Invoke(input);
Console.SetCursorPosition(currentConsoleLeft, currentConsoleTop);
_coloredConsoleRenderer.Write($"{{0,-{maxLength}}}", input);
Console.SetCursorPosition(currentConsoleLeft + position, currentConsoleTop);
key = Console.ReadKey();
}
Console.CursorVisible = cursorVisible;
return input;
}
}
}

View File

@@ -0,0 +1,18 @@
using FileTime.ConsoleUI.App.UI.Color;
namespace FileTime.ConsoleUI.App.UI
{
public interface IStyles
{
IConsoleColor? DefaultBackground { get; }
IConsoleColor? DefaultForeground { get; }
IConsoleColor? ContainerBackground { get; }
IConsoleColor? ContainerForeground { get; }
IConsoleColor? ElementBackground { get; }
IConsoleColor? ElementForeground { get; }
IConsoleColor? ElementSpecialBackground { get; }
IConsoleColor? ElementSpecialForeground { get; }
IConsoleColor? SelectedItemBackground { get; }
IConsoleColor? SelectedItemForeground { get; }
}
}

View File

@@ -0,0 +1,9 @@
namespace FileTime.ConsoleUI.UI.App
{
public enum PrintMode
{
Previous,
Current,
Next
}
}

View File

@@ -0,0 +1,281 @@
using FileTime.App.Core.Pane;
using FileTime.ConsoleUI.App.UI.Color;
using FileTime.ConsoleUI.UI.App;
using FileTime.Core.Components;
using FileTime.Core.Extensions;
using FileTime.Core.Models;
namespace FileTime.ConsoleUI.App.UI
{
public class Render
{
private const int _contentPaddingTop = 1;
private const int _contentPaddingBottom = 2;
private readonly int _contentRowCount;
private readonly IStyles _appStyle;
private readonly IColoredConsoleRenderer _coloredRenderer;
private int _currentDisplayStartY;
private const int ITEMPADDINGLEFT = 1;
private const int ITEMPADDINGRIGHT = 2;
private readonly string _paddingLeft;
private readonly string _paddingRight;
public Pane Pane { get; private set; }
public PaneState PaneState { get; private set; }
public int PageSize => Console.WindowHeight - _contentPaddingTop - _contentPaddingBottom;
public Render(IColoredConsoleRenderer coloredRenderer, IStyles appStyle)
{
_coloredRenderer = coloredRenderer;
_appStyle = appStyle;
_paddingLeft = new string(' ', ITEMPADDINGLEFT);
_paddingRight = new string(' ', ITEMPADDINGRIGHT);
_contentRowCount = Console.WindowHeight - _contentPaddingTop - _contentPaddingBottom;
}
public void Init(Pane pane, PaneState paneState)
{
if (pane == null) throw new Exception($"{nameof(pane)} can not be null");
if (paneState == null) throw new Exception($"{nameof(paneState)} can not be null");
Pane = pane;
Pane.CurrentLocationChanged += (o, e) => _currentDisplayStartY = 0;
PaneState = paneState;
}
public void PrintUI()
{
if (Pane != null)
{
PrintPrompt();
PrintPanes();
}
}
private void PrintPanes()
{
var previousColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.15) - 1;
var currentColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.4) - 1;
var nextColumnWidth = Console.WindowWidth - currentColumnWidth - previousColumnWidth - 2;
var currentVirtualContainer = Pane!.CurrentLocation as VirtualContainer;
if (Pane.CurrentLocation.GetParent() is var parentContainer && parentContainer is not null)
{
parentContainer.Refresh();
PrintColumn(
currentVirtualContainer != null
? currentVirtualContainer.CloneVirtualChainFor(parentContainer, v => v.IsTransitive)
: parentContainer,
currentVirtualContainer != null
? currentVirtualContainer.GetRealContainer()
: Pane.CurrentLocation,
PrintMode.Previous,
0,
_contentPaddingTop,
previousColumnWidth,
_contentRowCount);
}
else
{
CleanColumn(
0,
_contentPaddingTop,
previousColumnWidth,
_contentRowCount);
}
Pane.CurrentLocation.Refresh();
CheckAndSetCurrentDisplayStartY();
PrintColumn(
Pane.CurrentLocation,
Pane.CurrentSelectedItem,
PrintMode.Current,
previousColumnWidth + 1,
_contentPaddingTop,
currentColumnWidth,
_contentRowCount);
if (Pane.CurrentSelectedItem is IContainer selectedContainer)
{
selectedContainer.Refresh();
selectedContainer = currentVirtualContainer != null
? currentVirtualContainer.CloneVirtualChainFor(selectedContainer, v => v.IsTransitive)
: selectedContainer;
PrintColumn(
selectedContainer,
selectedContainer.Items.Count > 0 ? selectedContainer.Items[0] : null,
PrintMode.Next,
previousColumnWidth + currentColumnWidth + 2,
_contentPaddingTop,
nextColumnWidth,
_contentRowCount);
}
else
{
CleanColumn(
previousColumnWidth + currentColumnWidth + 2,
_contentPaddingTop,
nextColumnWidth,
_contentRowCount);
}
}
private void PrintPrompt()
{
Console.SetCursorPosition(0, 0);
_coloredRenderer.ResetColor();
_coloredRenderer.ForegroundColor = AnsiColor.From8bit(2);
_coloredRenderer.Write(Environment.UserName + "@" + Environment.MachineName);
_coloredRenderer.ResetColor();
_coloredRenderer.Write(' ');
_coloredRenderer.ForegroundColor = AnsiColor.From8bit(4);
var path = Pane!.CurrentLocation.FullName + "/";
_coloredRenderer.Write(path);
if (Pane.CurrentSelectedItem?.Name != null)
{
_coloredRenderer.ResetColor();
_coloredRenderer.Write($"{{0,-{300 - path.Length}}}", Pane.CurrentSelectedItem.Name);
}
}
private void PrintColumn(IContainer currentContainer, IItem? currentItem, PrintMode printMode, int startX, int startY, int elementWidth, int availableRows)
{
var allItem = currentContainer.Containers.Cast<IItem>().Concat(currentContainer.Elements).ToList();
var printedItemsCount = 0;
var currentY = 0;
if (allItem.Count > 0)
{
var currentIndex = allItem.FindIndex(i => i == currentItem);
var skipElements = printMode switch
{
PrintMode.Previous => (currentIndex - (availableRows / 2)).Map(r => r < 0 ? 0 : r),
PrintMode.Current => _currentDisplayStartY,
PrintMode.Next => 0,
_ => 0
};
var maxTextWidth = elementWidth - ITEMPADDINGLEFT - ITEMPADDINGRIGHT;
var itemsToPrint = currentContainer.Items.Skip(skipElements).Take(availableRows).ToList();
printedItemsCount = itemsToPrint.Count;
foreach (var item in itemsToPrint)
{
Console.SetCursorPosition(startX, startY + currentY++);
var namePart = item.Name.Length > maxTextWidth
? string.Concat(item.Name.AsSpan(0, maxTextWidth - 1), "~")
: item.Name;
var attributePart = "";
var container = item as IContainer;
var element = item as IElement;
IConsoleColor? backgroundColor = null;
IConsoleColor? foregroundColor = null;
if (container != null)
{
backgroundColor = _appStyle.ContainerBackground;
foregroundColor = _appStyle.ContainerForeground;
}
else if (element != null)
{
if (element.IsSpecial)
{
backgroundColor = _appStyle.ElementSpecialBackground;
foregroundColor = _appStyle.ElementSpecialForeground;
}
else
{
backgroundColor = _appStyle.ElementBackground;
foregroundColor = _appStyle.ElementForeground;
}
}
var isSelected = PaneState.ContainsSelectedItem(item.Provider, currentContainer, item.FullName!);
if (isSelected)
{
backgroundColor = _appStyle.SelectedItemBackground;
foregroundColor = _appStyle.SelectedItemForeground;
}
if (item == currentItem)
{
(backgroundColor, foregroundColor) = (foregroundColor, backgroundColor);
}
_coloredRenderer.BackgroundColor = backgroundColor;
_coloredRenderer.ForegroundColor = foregroundColor;
attributePart = container != null ? "" + container.Items.Count : element!.GetPrimaryAttributeText();
var text = string.Format($"{{0,-{elementWidth}}}", _paddingLeft + (isSelected ? " " : "") + namePart + _paddingRight);
text = string.Concat(text.AsSpan(0, text.Length - attributePart.Length - 1), " ", attributePart);
_coloredRenderer.Write(text);
_coloredRenderer.ResetColor();
}
}
else
{
_coloredRenderer.BackgroundColor = new BasicColor(ConsoleColor.Red);
_coloredRenderer.ForegroundColor = new BasicColor(ConsoleColor.White);
Console.SetCursorPosition(startX, startY + currentY++);
_coloredRenderer.Write($"{{0,-{elementWidth}}}", _paddingLeft + "<empty>" + _paddingRight);
}
var padding = new string(' ', elementWidth);
_coloredRenderer.ResetColor();
for (var i = 0; i < availableRows - printedItemsCount + 1; i++)
{
Console.SetCursorPosition(startX, startY + currentY++);
_coloredRenderer.Write(padding);
}
}
private void CleanColumn(int startX, int startY, int elementWidth, int availableRows)
{
_coloredRenderer.ResetColor();
var currentY = 0;
var placeholder = new string(' ', elementWidth);
for (var i = 0; i < availableRows; i++)
{
Console.SetCursorPosition(startX, startY + currentY++);
_coloredRenderer.Write(placeholder);
}
}
private void CheckAndSetCurrentDisplayStartY()
{
const int padding = 5;
while (Pane.CurrentSelectedIndex < _currentDisplayStartY + padding
&& _currentDisplayStartY > 0)
{
_currentDisplayStartY--;
}
while (Pane.CurrentSelectedIndex > _currentDisplayStartY + _contentRowCount - padding
&& _currentDisplayStartY < Pane.CurrentLocation.Items.Count - _contentRowCount)
{
_currentDisplayStartY++;
}
}
}
}

View File

@@ -0,0 +1,39 @@
using FileTime.ConsoleUI.App.UI.Color;
namespace FileTime.ConsoleUI.App.UI
{
public class Styles : IStyles
{
public IConsoleColor? DefaultBackground { get; }
public IConsoleColor? DefaultForeground { get; }
public IConsoleColor? ContainerBackground { get; }
public IConsoleColor? ContainerForeground { get; }
public IConsoleColor? ElementBackground { get; }
public IConsoleColor? ElementForeground { get; }
public IConsoleColor? ElementSpecialBackground { get; }
public IConsoleColor? ElementSpecialForeground { get; }
public IConsoleColor? SelectedItemBackground { get; }
public IConsoleColor? SelectedItemForeground { get; }
public Styles(bool useAnsiColors)
{
if (useAnsiColors)
{
ContainerForeground = AnsiColor.From8bit(4);
ElementForeground = AnsiColor.From8bit(14);
ElementSpecialForeground = AnsiColor.From8bit(2);
SelectedItemForeground = AnsiColor.From8bit(3);
DefaultForeground = ElementForeground;
SelectedItemBackground = ElementSpecialBackground = ContainerBackground = DefaultBackground = ElementBackground = AnsiColor.From8bit(0);
}
else
{
ContainerBackground = new BasicColor(Console.BackgroundColor);
ContainerForeground = new BasicColor(ConsoleColor.Blue);
ElementBackground = new BasicColor(Console.BackgroundColor);
ElementForeground = new BasicColor(Console.ForegroundColor);
}
}
}
}