Console size scan

This commit is contained in:
2023-08-28 21:29:15 +02:00
parent bc865011d3
commit ba06047a68
43 changed files with 567 additions and 279 deletions

View File

@@ -6,49 +6,24 @@ public class AnsiColorProvider : ColorProviderBase
{
var finalColor = ParseInternal(color, type);
finalColor ??= ParseHexColor(color, type);
finalColor ??= FromRgb(color, type);
if (finalColor is not null) return finalColor;
throw new NotSupportedException($"Color can not be parsed. {color}");
}
private static IColor? ParseHexColor(string color, ColorType colorType)
private IColor? FromRgb(string color, ColorType type)
{
if (!color.StartsWith("#")) return null;
if (color.Length == 4)
if (ParseHexColor(color) is var (r, g, b))
{
var r = ColorCharToColorByte(color[1]);
var g = ColorCharToColorByte(color[2]);
var b = ColorCharToColorByte(color[3]);
r += (byte) (r << 4);
g += (byte) (g << 4);
b += (byte) (b << 4);
return new ColorRgb(r, g, b, colorType);
}
if (color.Length == 7)
{
var r = (byte) (ColorCharToColorByte(color[1]) << 4 | ColorCharToColorByte(color[2]));
var g = (byte) (ColorCharToColorByte(color[3]) << 4 | ColorCharToColorByte(color[4]));
var b = (byte) (ColorCharToColorByte(color[5]) << 4 | ColorCharToColorByte(color[6]));
return new ColorRgb(r, g, b, colorType);
return new ColorRgb(r, g, b, type);
}
throw new NotSupportedException($"Hex color can not be parsed. {color}");
return null;
}
private static byte ColorCharToColorByte(char color) =>
color switch
{
>= '0' and <= '9' => (byte) (color - '0'),
>= 'A' and <= 'F' => (byte) (color - 'A' + 10),
>= 'a' and <= 'f' => (byte) (color - 'a' + 10),
_ => throw new NotSupportedException($"Hex color can not be parsed. {color}")
};
public override IColor FromRgb(Rgb rgb, ColorType type) => new ColorRgb(rgb.R, rgb.G, rgb.B, type);
public override IColor BlackForeground { get; } = new Color256(0, ColorType.Foreground);
public override IColor BlueForeground { get; } = new Color256(12, ColorType.Foreground);

View File

@@ -3,8 +3,46 @@
public abstract class ColorProviderBase : IColorProvider
{
public abstract IColor Parse(string color, ColorType type);
public abstract IColor FromRgb(Rgb rgb, ColorType type);
protected IColor? ParseInternal(string color, ColorType type)
protected static Rgb? ParseHexColor(string color)
{
if (!color.StartsWith("#")) return null;
if (color.Length == 4)
{
var r = ColorCharToColorByte(color[1]);
var g = ColorCharToColorByte(color[2]);
var b = ColorCharToColorByte(color[3]);
r += (byte) (r << 4);
g += (byte) (g << 4);
b += (byte) (b << 4);
return new(r, g, b);
}
if (color.Length == 7)
{
var r = (byte) (ColorCharToColorByte(color[1]) << 4 | ColorCharToColorByte(color[2]));
var g = (byte) (ColorCharToColorByte(color[3]) << 4 | ColorCharToColorByte(color[4]));
var b = (byte) (ColorCharToColorByte(color[5]) << 4 | ColorCharToColorByte(color[6]));
return new(r, g, b);
}
return null;
}
private static byte ColorCharToColorByte(char color) =>
color switch
{
>= '0' and <= '9' => (byte) (color - '0'),
>= 'A' and <= 'F' => (byte) (color - 'A' + 10),
>= 'a' and <= 'f' => (byte) (color - 'a' + 10),
_ => throw new NotSupportedException($"Hex color can not be parsed. {color}")
};
protected IColor? ParseInternal(string color, ColorType type)
=> (color.ToLower(), type) switch
{
("black", ColorType.Foreground) => BlackForeground,

View File

@@ -7,8 +7,8 @@ public readonly record struct ColorRgb(byte R, byte G, byte B, ColorType Type) :
public string ToConsoleColor()
=> Type switch
{
ColorType.Foreground => $"\x1b[38;2;{R};{G};{B};m",
ColorType.Background => $"\x1b[48;2;{R};{G};{B};m",
ColorType.Foreground => $"\x1b[38;2;{R};{G};{B}m",
ColorType.Background => $"\x1b[48;2;{R};{G};{B}m",
_ => throw new InvalidEnumArgumentException(nameof(Type), (int) Type, typeof(ColorType))
};
public IColor AsForeground() => this with {Type = ColorType.Foreground};

View File

@@ -1,48 +1,143 @@
namespace TerminalUI.Color;
// ReSharper disable InconsistentNaming
namespace TerminalUI.Color;
public class ConsoleColorProvider : ColorProviderBase
{
private static readonly List<(Rgb, IColor)> Foregrounds;
private static readonly List<(Rgb, IColor)> Backgrounds;
static ConsoleColorProvider()
{
Foregrounds = new List<(Rgb, IColor)>
{
(new Rgb(0, 0, 0), _blackForeground),
(new Rgb(0, 0, 128), _darkBlueForeground),
(new Rgb(0, 128, 0), _darkGreenForeground),
(new Rgb(0, 128, 128), _darkCyanForeground),
(new Rgb(128, 0, 0), _darkRedForeground),
(new Rgb(128, 0, 128), _darkMagentaForeground),
(new Rgb(128, 128, 0), _darkYellowForeground),
(new Rgb(192, 192, 192), _grayForeground),
(new Rgb(128, 128, 128), _darkGrayForeground),
(new Rgb(0, 0, 255), _blueForeground),
(new Rgb(0, 255, 0), _greenForeground),
(new Rgb(0, 255, 255), _cyanForeground),
(new Rgb(255, 0, 0), _redForeground),
(new Rgb(255, 0, 255), _magentaForeground),
(new Rgb(255, 255, 0), _yellowForeground),
(new Rgb(255, 255, 255), _whiteForeground),
};
Backgrounds = new List<(Rgb, IColor)>
{
(new Rgb(0, 0, 0), _blackBackground),
(new Rgb(0, 0, 128), _darkBlueBackground),
(new Rgb(0, 128, 0), _darkGreenBackground),
(new Rgb(0, 128, 128), _darkCyanBackground),
(new Rgb(128, 0, 0), _darkRedBackground),
(new Rgb(128, 0, 128), _darkMagentaBackground),
(new Rgb(128, 128, 0), _darkYellowBackground),
(new Rgb(192, 192, 192), _grayBackground),
(new Rgb(128, 128, 128), _darkGrayBackground),
(new Rgb(0, 0, 255), _blueBackground),
(new Rgb(0, 255, 0), _greenBackground),
(new Rgb(0, 255, 255), _cyanBackground),
(new Rgb(255, 0, 0), _redBackground),
(new Rgb(255, 0, 255), _magentaBackground),
(new Rgb(255, 255, 0), _yellowBackground),
(new Rgb(255, 255, 255), _whiteBackground),
};
}
public override IColor Parse(string color, ColorType type)
{
var finalColor = ParseInternal(color, type);
if (finalColor is not null) return finalColor;
//TODO get closest color
throw new NotSupportedException($"Color can not be parsed. {color}");
//TODO get closest color
var rgb = ParseHexColor(color);
if (rgb is null)
{
throw new NotSupportedException($"Color can not be parsed. {color}");
}
return FromRgb(rgb.Value, type);
}
public override IColor BlackForeground { get; } = new ConsoleColor(System.ConsoleColor.Black, ColorType.Foreground);
public override IColor BlueForeground { get; } = new ConsoleColor(System.ConsoleColor.Blue, ColorType.Foreground);
public override IColor CyanForeground { get; } = new ConsoleColor(System.ConsoleColor.Cyan, ColorType.Foreground);
public override IColor DarkBlueForeground { get; } = new ConsoleColor(System.ConsoleColor.DarkBlue, ColorType.Foreground);
public override IColor DarkCyanForeground { get; } = new ConsoleColor(System.ConsoleColor.DarkCyan, ColorType.Foreground);
public override IColor DarkGrayForeground { get; } = new ConsoleColor(System.ConsoleColor.DarkGray, ColorType.Foreground);
public override IColor DarkGreenForeground { get; } = new ConsoleColor(System.ConsoleColor.DarkGreen, ColorType.Foreground);
public override IColor DarkMagentaForeground { get; } = new ConsoleColor(System.ConsoleColor.DarkMagenta, ColorType.Foreground);
public override IColor DarkRedForeground { get; } = new ConsoleColor(System.ConsoleColor.DarkRed, ColorType.Foreground);
public override IColor DarkYellowForeground { get; } = new ConsoleColor(System.ConsoleColor.DarkYellow, ColorType.Foreground);
public override IColor GrayForeground { get; } = new ConsoleColor(System.ConsoleColor.Gray, ColorType.Foreground);
public override IColor GreenForeground { get; } = new ConsoleColor(System.ConsoleColor.Green, ColorType.Foreground);
public override IColor MagentaForeground { get; } = new ConsoleColor(System.ConsoleColor.Magenta, ColorType.Foreground);
public override IColor RedForeground { get; } = new ConsoleColor(System.ConsoleColor.Red, ColorType.Foreground);
public override IColor WhiteForeground { get; } = new ConsoleColor(System.ConsoleColor.White, ColorType.Foreground);
public override IColor YellowForeground { get; } = new ConsoleColor(System.ConsoleColor.Yellow, ColorType.Foreground);
public override IColor FromRgb(Rgb rgb, ColorType type)
=> type == ColorType.Background
? ApproximateColor(rgb, Backgrounds)
: ApproximateColor(rgb, Foregrounds);
public override IColor BlackBackground => new ConsoleColor(System.ConsoleColor.Black, ColorType.Background);
public override IColor BlueBackground => new ConsoleColor(System.ConsoleColor.Blue, ColorType.Background);
public override IColor CyanBackground => new ConsoleColor(System.ConsoleColor.Cyan, ColorType.Background);
public override IColor DarkBlueBackground => new ConsoleColor(System.ConsoleColor.DarkBlue, ColorType.Background);
public override IColor DarkCyanBackground => new ConsoleColor(System.ConsoleColor.DarkCyan, ColorType.Background);
public override IColor DarkGrayBackground => new ConsoleColor(System.ConsoleColor.DarkGray, ColorType.Background);
public override IColor DarkGreenBackground => new ConsoleColor(System.ConsoleColor.DarkGreen, ColorType.Background);
public override IColor DarkMagentaBackground => new ConsoleColor(System.ConsoleColor.DarkMagenta, ColorType.Background);
public override IColor DarkRedBackground => new ConsoleColor(System.ConsoleColor.DarkRed, ColorType.Background);
public override IColor DarkYellowBackground => new ConsoleColor(System.ConsoleColor.DarkYellow, ColorType.Background);
public override IColor GrayBackground => new ConsoleColor(System.ConsoleColor.Gray, ColorType.Background);
public override IColor GreenBackground => new ConsoleColor(System.ConsoleColor.Green, ColorType.Background);
public override IColor MagentaBackground => new ConsoleColor(System.ConsoleColor.Magenta, ColorType.Background);
public override IColor RedBackground => new ConsoleColor(System.ConsoleColor.Red, ColorType.Background);
public override IColor WhiteBackground => new ConsoleColor(System.ConsoleColor.White, ColorType.Background);
public override IColor YellowBackground => new ConsoleColor(System.ConsoleColor.Yellow, ColorType.Background);
protected IColor ApproximateColor(Rgb rgb, IEnumerable<(Rgb, IColor)> colors)
=> colors.MinBy(c => c.Item1 - rgb).Item2;
public override IColor BlackForeground => _blackForeground;
public override IColor BlueForeground => _blueForeground;
public override IColor CyanForeground => _cyanForeground;
public override IColor DarkBlueForeground => _darkBlueForeground;
public override IColor DarkCyanForeground => _darkCyanForeground;
public override IColor DarkGrayForeground => _darkGrayForeground;
public override IColor DarkGreenForeground => _darkGreenForeground;
public override IColor DarkMagentaForeground => _darkMagentaForeground;
public override IColor DarkRedForeground => _darkRedForeground;
public override IColor DarkYellowForeground => _darkYellowForeground;
public override IColor GrayForeground => _grayForeground;
public override IColor GreenForeground => _greenForeground;
public override IColor MagentaForeground => _magentaForeground;
public override IColor RedForeground => _redForeground;
public override IColor WhiteForeground => _whiteForeground;
public override IColor YellowForeground => _yellowForeground;
public override IColor BlackBackground => _blackBackground;
public override IColor BlueBackground => _blueBackground;
public override IColor CyanBackground => _cyanBackground;
public override IColor DarkBlueBackground => _darkBlueBackground;
public override IColor DarkCyanBackground => _darkCyanBackground;
public override IColor DarkGrayBackground => _darkGrayBackground;
public override IColor DarkGreenBackground => _darkGreenBackground;
public override IColor DarkMagentaBackground => _darkMagentaBackground;
public override IColor DarkRedBackground => _darkRedBackground;
public override IColor DarkYellowBackground => _darkYellowBackground;
public override IColor GrayBackground => _grayBackground;
public override IColor GreenBackground => _greenBackground;
public override IColor MagentaBackground => _magentaBackground;
public override IColor RedBackground => _redBackground;
public override IColor WhiteBackground => _whiteBackground;
public override IColor YellowBackground => _yellowBackground;
private static readonly IColor _blackForeground = new ConsoleColor(System.ConsoleColor.Black, ColorType.Foreground);
private static readonly IColor _blueForeground = new ConsoleColor(System.ConsoleColor.Blue, ColorType.Foreground);
private static readonly IColor _cyanForeground = new ConsoleColor(System.ConsoleColor.Cyan, ColorType.Foreground);
private static readonly IColor _darkBlueForeground = new ConsoleColor(System.ConsoleColor.DarkBlue, ColorType.Foreground);
private static readonly IColor _darkCyanForeground = new ConsoleColor(System.ConsoleColor.DarkCyan, ColorType.Foreground);
private static readonly IColor _darkGrayForeground = new ConsoleColor(System.ConsoleColor.DarkGray, ColorType.Foreground);
private static readonly IColor _darkGreenForeground = new ConsoleColor(System.ConsoleColor.DarkGreen, ColorType.Foreground);
private static readonly IColor _darkMagentaForeground = new ConsoleColor(System.ConsoleColor.DarkMagenta, ColorType.Foreground);
private static readonly IColor _darkRedForeground = new ConsoleColor(System.ConsoleColor.DarkRed, ColorType.Foreground);
private static readonly IColor _darkYellowForeground = new ConsoleColor(System.ConsoleColor.DarkYellow, ColorType.Foreground);
private static readonly IColor _grayForeground = new ConsoleColor(System.ConsoleColor.Gray, ColorType.Foreground);
private static readonly IColor _greenForeground = new ConsoleColor(System.ConsoleColor.Green, ColorType.Foreground);
private static readonly IColor _magentaForeground = new ConsoleColor(System.ConsoleColor.Magenta, ColorType.Foreground);
private static readonly IColor _redForeground = new ConsoleColor(System.ConsoleColor.Red, ColorType.Foreground);
private static readonly IColor _whiteForeground = new ConsoleColor(System.ConsoleColor.White, ColorType.Foreground);
private static readonly IColor _yellowForeground = new ConsoleColor(System.ConsoleColor.Yellow, ColorType.Foreground);
private static readonly IColor _blackBackground = new ConsoleColor(System.ConsoleColor.Black, ColorType.Background);
private static readonly IColor _blueBackground = new ConsoleColor(System.ConsoleColor.Blue, ColorType.Background);
private static readonly IColor _cyanBackground = new ConsoleColor(System.ConsoleColor.Cyan, ColorType.Background);
private static readonly IColor _darkBlueBackground = new ConsoleColor(System.ConsoleColor.DarkBlue, ColorType.Background);
private static readonly IColor _darkCyanBackground = new ConsoleColor(System.ConsoleColor.DarkCyan, ColorType.Background);
private static readonly IColor _darkGrayBackground = new ConsoleColor(System.ConsoleColor.DarkGray, ColorType.Background);
private static readonly IColor _darkGreenBackground = new ConsoleColor(System.ConsoleColor.DarkGreen, ColorType.Background);
private static readonly IColor _darkMagentaBackground = new ConsoleColor(System.ConsoleColor.DarkMagenta, ColorType.Background);
private static readonly IColor _darkRedBackground = new ConsoleColor(System.ConsoleColor.DarkRed, ColorType.Background);
private static readonly IColor _darkYellowBackground = new ConsoleColor(System.ConsoleColor.DarkYellow, ColorType.Background);
private static readonly IColor _grayBackground = new ConsoleColor(System.ConsoleColor.Gray, ColorType.Background);
private static readonly IColor _greenBackground = new ConsoleColor(System.ConsoleColor.Green, ColorType.Background);
private static readonly IColor _magentaBackground = new ConsoleColor(System.ConsoleColor.Magenta, ColorType.Background);
private static readonly IColor _redBackground = new ConsoleColor(System.ConsoleColor.Red, ColorType.Background);
private static readonly IColor _whiteBackground = new ConsoleColor(System.ConsoleColor.White, ColorType.Background);
private static readonly IColor _yellowBackground = new ConsoleColor(System.ConsoleColor.Yellow, ColorType.Background);
}

View File

@@ -3,6 +3,7 @@
public interface IColorProvider
{
IColor Parse(string color, ColorType type);
IColor FromRgb(Rgb rgb, ColorType type);
IColor BlackForeground { get; }
IColor BlueForeground { get; }

View File

@@ -0,0 +1,33 @@
using System.Diagnostics;
namespace TerminalUI.Color;
[DebuggerDisplay("R = {R}, G = {G}, B = {B}")]
public readonly struct Rgb
{
public readonly byte R;
public readonly byte G;
public readonly byte B;
public Rgb(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
public static double operator -(Rgb left, Rgb right)
{
var r = Math.Abs(left.R - right.R);
var g = Math.Abs(left.G - right.G);
var b = Math.Abs(left.B - right.B);
return (double)(r + g + b) / 3;
}
public void Deconstruct(out byte r, out byte g, out byte b)
{
r = R;
g = G;
b = B;
}
}

View File

@@ -36,6 +36,7 @@ public sealed class XTermDriver : DotnetDriver
public override void ResetStyle()
{
Write("\x1b[0m");
base.ResetStyle();
}
public override void SetBackgroundColor(IColor background)

View File

@@ -127,11 +127,18 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
protected override Size CalculateSize()
{
return WithCalculatedSize(
if (Width.HasValue && Height.HasValue)
{
return new Size(Width.Value, Height.Value);
}
var size = WithCalculatedSize(
RenderContext.Empty,
new Option<Size>(new Size(0, 0), false),
CalculateSizeInternal);
return new Size(Width ?? size.Width, Height ?? size.Height);
Size CalculateSizeInternal(in RenderContext _, ReadOnlySpan<int> columnWidths, ReadOnlySpan<int> rowHeights)
{
var width = 0;
@@ -153,11 +160,14 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size)
{
if (size.Width == 0 || size.Height == 0) return false;
var width = Width ?? size.Width;
var height = Height ?? size.Height;
if (width == 0 || height == 0) return false;
return WithCalculatedSize(
renderContext,
new Option<Size>(size, true),
new Option<Size>(new Size(width, height), true),
DefaultRendererInternal
);

View File

@@ -26,7 +26,8 @@ public sealed partial class Rectangle<T> : View<Rectangle<T>, T>, IDisplayView
protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size)
{
var fillColor = Fill ?? Background ?? renderContext.Background;
var renderState = new RenderState(position, size, fillColor);
var renderSize = new Size(Width ?? size.Width, Height ?? size.Height);
var renderState = new RenderState(position, renderSize, fillColor);
var skipRender = !renderContext.ForceRerender && !NeedsRerender(renderState);
_lastRenderState = renderState;
@@ -41,7 +42,7 @@ public sealed partial class Rectangle<T> : View<Rectangle<T>, T>, IDisplayView
RenderEmpty(
renderContext,
position,
size,
renderSize,
skipRender,
false
);

View File

@@ -146,8 +146,8 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
if (!IsVisible) return false;
ActualWidth = size.Width;
ActualHeight = size.Height;
ActualWidth = Width ?? size.Width;
ActualHeight = Height ?? size.Height;
if (RenderMethod is null)
{
@@ -167,10 +167,14 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
);
size = new Size(
size.Width - Margin.Left - Margin.Right,
size.Height - Margin.Top - Margin.Bottom
ActualWidth - Margin.Left - Margin.Right,
ActualHeight - Margin.Top - Margin.Bottom
);
}
else if (Width.HasValue || Height.HasValue)
{
size = new Size(ActualWidth, ActualHeight);
}
bool renderResult;
if (Background != null || Foreground != null)

View File

@@ -31,6 +31,8 @@ public class BinaryTracker : ExpressionTrackerBase
ExpressionType.GreaterThanOrEqual => (v1, v2) => Comparer.Default.Compare(v1, v2) >= 0,
ExpressionType.LessThan => (v1, v2) => Comparer.Default.Compare(v1, v2) < 0,
ExpressionType.LessThanOrEqual => (v1, v2) => Comparer.Default.Compare(v1, v2) <= 0,
ExpressionType.AndAlso => (v1, v2) => v1 is true && v2 is true,
ExpressionType.OrElse => (v1, v2) => v1 is true || v2 is true,
_ => throw new NotImplementedException()
};

View File

@@ -0,0 +1,26 @@
using System.Linq.Expressions;
namespace TerminalUI.ExpressionTrackers;
public class TypeBinaryTracker : ExpressionTrackerBase
{
private readonly IExpressionTracker _childExpressionTracker;
private readonly Func<object?, bool> _computer;
public TypeBinaryTracker(TypeBinaryExpression typeBinaryExpression, IExpressionTracker childExpressionTracker)
{
_childExpressionTracker = childExpressionTracker;
ArgumentNullException.ThrowIfNull(childExpressionTracker);
SubscribeToValueChanges = false;
SubscribeToTracker(childExpressionTracker);
_computer = typeBinaryExpression.NodeType switch
{
ExpressionType.TypeIs => o => o is not null && typeBinaryExpression.TypeOperand.IsInstanceOfType(o),
_ => throw new NotImplementedException()
};
}
protected override object? ComputeValue()
=> _computer(_childExpressionTracker.GetValue());
}

View File

@@ -80,10 +80,11 @@ public abstract class PropertyTrackerBase<TSource, TExpressionResult>
{
return new ConstantTracker(constantExpression.Value);
}
/*else if (expression is not ConstantExpression)
else if (expression is TypeBinaryExpression typeBinaryExpression)
{
Debug.Assert(false, "Unknown expression type " + expression.GetType());
}*/
var childExpression = FindReactiveProperties(typeBinaryExpression.Expression, parameters);
return new TypeBinaryTracker(typeBinaryExpression, childExpression);
}
throw new NotSupportedException();
}