Document all non-mathematical functions

This commit is contained in:
JFronny 2020-05-23 15:32:24 +02:00
parent bc6d83aa15
commit d05f215a62
18 changed files with 681 additions and 160 deletions

View File

@ -3,19 +3,30 @@ using CC_Functions.Misc;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// A basic button type
/// </summary>
public class Button : Control
{
/// <summary>
/// The text inside this button
/// </summary>
public string Content;
/// <summary>
/// Creates a new button
/// </summary>
/// <param name="content">The text inside this button</param>
public Button(string content)
{
Content = content;
char[,] tmp = Content.ToNDArray2D();
char[,] tmp = Content.ToNdArray2D();
Size = new Size(tmp.GetLength(0), tmp.GetLength(1));
}
/// <inheritdoc />
public override Pixel[,] Render()
{
char[,] inp = Content.ToNDArray2D();
char[,] inp = Content.ToNdArray2D();
inp = inp.Resize(Size.Width, Size.Height);
Pixel[,] output = new Pixel[Size.Width, Size.Height];
for (int x = 0; x < Size.Width; x++)
@ -24,11 +35,7 @@ namespace CC_Functions.Commandline.TUI
return output;
}
protected override void Resize(int width, int height)
{
//ignored for [Render]s sake, do not use
}
/// <inheritdoc />
public override bool Selectable { get; } = true;
}
}

View File

@ -4,10 +4,23 @@ using CC_Functions.Misc;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// Provides a control for users to select a boolean
/// </summary>
public class CheckBox : Control
{
/// <summary>
/// The text inside this checkbox
/// </summary>
public string Content;
/// <summary>
/// Whether the box is checked
/// </summary>
public bool Checked = false;
/// <summary>
/// Creates a new checkbox
/// </summary>
/// <param name="content">The text inside this CheckBox</param>
public CheckBox(string content)
{
Content = content;
@ -30,14 +43,15 @@ namespace CC_Functions.Commandline.TUI
};
}
/// <inheritdoc />
public override Pixel[,] Render()
{
char[,] inp1 = Content.ToNDArray2D();
char[,] inp1 = Content.ToNdArray2D();
char[,] inp = new char[inp1.GetLength(0), inp1.GetLength(1) + 4];
inp.Populate(' ');
inp1.CopyTo(inp, new Point(4, 0));
inp[0, 0] = '[';
inp[0, 1] = Checked ? 'X' : SpecialChars.empty;
inp[0, 1] = Checked ? 'X' : SpecialChars.Empty;
inp[0, 2] = ']';
int w = inp.GetLength(0);
int h = inp.GetLength(1);
@ -49,13 +63,18 @@ namespace CC_Functions.Commandline.TUI
return output;
}
protected override void Resize(int width, int height)
{
//ignored for [Render]s sake, do not use
}
/// <inheritdoc />
public override bool Selectable { get; } = true;
/// <summary>
/// Called when the state of this checkbox is changed
/// </summary>
/// <param name="screen">The current screen instance</param>
/// <param name="e">Args</param>
public delegate void OnCheckedChanged(Screen screen, EventArgs e);
/// <summary>
/// Called when the state of this checkbox is changed
/// </summary>
public event OnClick CheckedChanged;
}
}

View File

@ -3,32 +3,62 @@ using System.Drawing;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// Abstract class inherited by all controls
/// </summary>
public abstract class Control
{
/// <summary>
/// Called when the controls Size property is changed
/// </summary>
/// <param name="caller">The calling control</param>
/// <param name="e">Args</param>
public delegate void OnResize(Control caller, EventArgs e);
/// <summary>
/// Called when the controls Size property is changed
/// </summary>
public event OnResize Resize;
private Point _point;
private Size _size;
/// <summary>
/// Renders the control
/// </summary>
/// <returns>The rendered pixels</returns>
public abstract Pixel[,] Render();
protected abstract void Resize(int width, int height);
private void Resize(Size size) => Resize(size.Width, size.Height);
/// <summary>
/// The size of the control
/// </summary>
public Size Size
{
set
{
_size = value;
Resize(value);
if (_size != value)
{
_size = value;
Resize?.Invoke(this, new EventArgs());
}
}
get => _size;
}
/// <summary>
/// The position of this control
/// </summary>
public Point Point
{
get => _point;
set => _point = value;
}
/// <summary>
/// The foreground color for this control
/// </summary>
public ConsoleColor ForeColor { get; set; } = Console.ForegroundColor;
/// <summary>
/// The background color for this control
/// </summary>
public ConsoleColor BackColor { get; set; } = Console.BackgroundColor;
/// <summary>
/// Whether the control can be selected
/// </summary>
public abstract bool Selectable { get; }
/// <summary>
@ -72,10 +102,13 @@ namespace CC_Functions.Commandline.TUI
{
Input?.Invoke(screen, new InputEventArgs(info));
}
/// <summary>
/// Whether the control should be rendered
/// </summary>
public bool Visible = true;
/// <summary>
/// Whether the control can be interacted with
/// </summary>
public bool Enabled = true;
}
}

View File

@ -11,9 +11,18 @@ namespace CC_Functions.Commandline.TUI
{
private static Pixel[,] Screen { get; set; } = new Pixel[0, 0];
private static Pixel[,] _last = new Pixel[0,0];
/// <summary>
/// The regions width
/// </summary>
public static int Width => Screen.GetLength(1);
/// <summary>
/// The regions height
/// </summary>
public static int Height => Screen.GetLength(0);
/// <summary>
/// Draws to the console
/// </summary>
/// <param name="color">Whether to use color</param>
public static void Draw(bool color)
{
Console.CursorTop = 0;
@ -43,7 +52,10 @@ namespace CC_Functions.Commandline.TUI
Console.BackgroundColor = bcol;
_last = Screen;
}
/// <summary>
/// Redraws the entire screen (should be done from time to time to prevent corruption)
/// </summary>
/// <param name="color">Whether to use color</param>
public static void FullDraw(bool color)
{
Console.CursorTop = 0;
@ -73,26 +85,76 @@ namespace CC_Functions.Commandline.TUI
Console.BackgroundColor = bcol;
_last = Screen;
}
/// <summary>
/// Gets the char at a location
/// </summary>
/// <param name="p">The location</param>
/// <returns>The char</returns>
public static char Get(Point p) => Get(p.X, p.Y);
/// <summary>
/// Gets the char at a location
/// </summary>
/// <param name="x">The locations X coordinate</param>
/// <param name="y">The locations Y coordinate</param>
/// <returns>The char</returns>
public static char Get(int x, int y) => Screen[y, x].Content;
/// <summary>
/// Gets the foreground color at a location
/// </summary>
/// <param name="p">The location</param>
/// <returns>The color</returns>
public static ConsoleColor GetForeColor(Point p) => GetForeColor(p.X, p.Y);
/// <summary>
/// Gets the foreground color at a location
/// </summary>
/// <param name="x">The locations X coordinate</param>
/// <param name="y">The locations Y coordinate</param>
/// <returns>The color</returns>
public static ConsoleColor GetForeColor(int x, int y) => Screen[y, x].ForeColor;
/// <summary>
/// Gets the background color at a location
/// </summary>
/// <param name="p">The location</param>
/// <returns>The color</returns>
public static ConsoleColor GetBackColor(Point p) => GetBackColor(p.X, p.Y);
/// <summary>
/// Gets the background color at a location
/// </summary>
/// <param name="x">The locations X coordinate</param>
/// <param name="y">The locations Y coordinate</param>
/// <returns>The color</returns>
public static ConsoleColor GetBackColor(int x, int y) => Screen[y, x].BackColor;
/// <summary>
/// Sets a pixel at a point
/// </summary>
/// <param name="p">The point to place at</param>
/// <param name="c">The pixel to place</param>
public static void Set(Point p, Pixel c) => Set(p.X, p.Y, c);
/// <summary>
/// Sets a pixel at a location
/// </summary>
/// <param name="x">The locations X coordinate</param>
/// <param name="y">The locations Y coordinate</param>
/// <param name="c">The pixel to place</param>
public static void Set(int x, int y, Pixel c) => Screen[y, x] = c;
/// <summary>
/// Clears the screen
/// </summary>
public static void Clear() => Clear(Width, Height);
/// <summary>
/// Resizes and clears the screen
/// </summary>
/// <param name="width">The new width</param>
/// <param name="height">The new height</param>
public static void Clear(int width, int height)
{
Screen = new Pixel[height, width];
_last = _last.Resize(height, width);
}
/// <summary>
/// Replaces the screen state
/// </summary>
/// <param name="content">The new state</param>
public static void Clear(Pixel[,] content)
{
Screen = content;

View File

@ -2,11 +2,20 @@ using System;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// Arguments containing input data
/// </summary>
public class InputEventArgs : EventArgs
{
private readonly ConsoleKeyInfo _info;
/// <summary>
/// The inputs data
/// </summary>
public ConsoleKeyInfo Info => _info;
/// <summary>
/// Generates new arguments
/// </summary>
/// <param name="info">The input data</param>
public InputEventArgs(ConsoleKeyInfo info) => _info = info;
}
}

View File

@ -3,14 +3,24 @@ using CC_Functions.Misc;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// A basic text control
/// </summary>
public class Label : Control
{
/// <summary>
/// The text inside this label
/// </summary>
public string Content;
/// <summary>
/// Creates a new label
/// </summary>
/// <param name="content">The text inside this label</param>
public Label(string content) => Content = content;
/// <inheritdoc />
public override Pixel[,] Render()
{
char[,] inp = Content.ToNDArray2D();
char[,] inp = Content.ToNdArray2D();
int w = inp.GetLength(0);
int h = inp.GetLength(1);
Pixel[,] output = new Pixel[w, h];
@ -20,12 +30,7 @@ namespace CC_Functions.Commandline.TUI
Size = new Size(w, h);
return output;
}
protected override void Resize(int width, int height)
{
//ignored for [Render]s sake, do not use
}
/// <inheritdoc />
public override bool Selectable { get; } = false;
}
}

View File

@ -4,14 +4,24 @@ using CC_Functions.Misc;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// A panel containing other components. MUST be inherited for all other controls that contain others
/// </summary>
public class Panel : Control
{
/// <summary>
/// The controls inside this panel
/// </summary>
public List<Control> Controls = new List<Control>();
/// <summary>
/// Renders the control and all contained controls
/// </summary>
/// <returns>The rendered pixels</returns>
public override Pixel[,] Render()
{
Pixel[,] tmp = new Pixel[Size.Height, Size.Width];
tmp.Populate(new Pixel(BackColor, ForeColor, SpecialChars.empty));
tmp.Populate(new Pixel(BackColor, ForeColor, SpecialChars.Empty));
foreach (Control control in Controls)
{
if (control.Visible)
@ -22,13 +32,12 @@ namespace CC_Functions.Commandline.TUI
}
return tmp;
}
protected override void Resize(int width, int height)
{
}
/// <inheritdoc />
public override bool Selectable { get; } = false;
/// <summary>
/// Recursively enumerates all controls
/// </summary>
/// <returns>A list of all controls</returns>
public Control[] EnumerateRecursive()
{
List<Control> output = Controls.ToList();

View File

@ -3,6 +3,9 @@ using System.Collections.Generic;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// Represents a pixel
/// </summary>
public class Pixel
{
private sealed class ColorContentEqualityComparer : IEqualityComparer<Pixel>
@ -18,11 +21,21 @@ namespace CC_Functions.Commandline.TUI
public int GetHashCode(Pixel obj) => obj.GetHashCode();
}
/// <summary>
/// Use this in functions that require equality comparers
/// </summary>
public static IEqualityComparer<Pixel> ColorContentComparer { get; } = new ColorContentEqualityComparer();
/// <summary>
/// Whether this is equal to another pixel
/// </summary>
/// <param name="other">The other pixel to compare</param>
/// <returns>Whether they are equal</returns>
protected bool Equals(Pixel other) => ColorContentComparer.Equals(this, other);
/// <summary>
/// Whether this is equal to another object
/// </summary>
/// <param name="obj">The other object to compare</param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
@ -31,30 +44,66 @@ namespace CC_Functions.Commandline.TUI
return Equals((Pixel) obj);
}
/// <summary>
/// Generates an integer for comparing this object
/// </summary>
/// <returns>The generated hash</returns>
public override int GetHashCode() => HashCode.Combine((int) BackColor, (int) ForeColor, Content);
/// <summary>
/// This pixels background color
/// </summary>
public ConsoleColor BackColor;
/// <summary>
/// This pixels foregound color
/// </summary>
public ConsoleColor ForeColor;
/// <summary>
/// This pixels content character
/// </summary>
public char Content;
/// <summary>
/// Generates a new pixel
/// </summary>
/// <param name="backColor">The background color</param>
/// <param name="foreColor">The foreground color</param>
/// <param name="content">The new content</param>
public Pixel(ConsoleColor backColor, ConsoleColor foreColor, char content)
{
BackColor = backColor;
ForeColor = foreColor;
Content = content;
}
/// <summary>
/// Generates a new pixel
/// </summary>
/// <param name="content">The content for this pixel</param>
public Pixel(char content) : this(Console.BackgroundColor, Console.ForegroundColor, content)
{
}
/// <summary>
/// Generates a new pixel
/// </summary>
public Pixel() : this(' ')
{
}
/// <summary>
/// Whether two pixels are equal
/// </summary>
/// <param name="a">First pixel to compare</param>
/// <param name="b">Second pixel to compare</param>
/// <returns>Whether they are equal</returns>
public static bool operator ==(Pixel a, Pixel b) => a.Equals(b);
/// <summary>
/// Whether to pixels are not equal
/// </summary>
/// <param name="a">First pixel to compare</param>
/// <param name="b">Second pixel to compare</param>
/// <returns>Whether they are not equal</returns>
public static bool operator !=(Pixel a, Pixel b) => !a.Equals(b);
/// <summary>
/// Returns the content of this pixel
/// </summary>
/// <returns>The content of this pixel</returns>
public override string ToString() => Content.ToString();
}
}

View File

@ -4,10 +4,21 @@ using System.Linq;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// Provides a front-end renderer for panels, draws using DiffDraw
/// </summary>
public class Screen : Panel
{
/// <summary>
/// Whether to output in color. Recommended for most terminals, might cause slowdowns in others
/// </summary>
public readonly bool Color;
/// <summary>
/// The current index of the tab-selected control in an array of selectable controls
/// </summary>
public int TabPoint = 0;
private int _wndWidth = Console.WindowWidth;
private int _wndHeight = Console.WindowHeight;
/// <summary>
/// Creates a screen object. Multiple can be instantiated but drawing one overrides others. Use panels for that
/// </summary>
@ -26,7 +37,7 @@ namespace CC_Functions.Commandline.TUI
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public void Resize(int width, int height)
public new void Resize(int width, int height)
{
Size = new Size(width, height);
DiffDraw.Clear(width, height);
@ -36,7 +47,7 @@ namespace CC_Functions.Commandline.TUI
/// Renders the screen, draws it to the console and outputs the new state
/// </summary>
/// <returns>The new state of the screen</returns>
public Pixel[,] Render()
public new Pixel[,] Render()
{
Pixel[,] tmp = base.Render();
DiffDraw.Clear(tmp);
@ -44,8 +55,13 @@ namespace CC_Functions.Commandline.TUI
return tmp;
}
public void ReadInput()
/// <summary>
/// Reads input from Console and calls according functions
/// </summary>
/// <param name="canRedraw">Set to false to prevent redrawing if the screen should be updated. You can Render manually in that case</param>
public void ReadInput(bool canRedraw = true)
{
bool render = false;
while (Console.KeyAvailable)
{
Control[] controls = EnumerateRecursive();
@ -57,33 +73,49 @@ namespace CC_Functions.Commandline.TUI
Tab(selectable, (input.Modifiers & ConsoleModifiers.Shift) != 0);
break;
case ConsoleKey.Enter:
if (selectable.Any() && selectable.Length >= TabPoint)
if (selectable.Any() && selectable.Length >= TabPoint && selectable[TabPoint].Enabled)
{
selectable[TabPoint].InvokeClick(this);
Render();
render = true;
}
break;
case ConsoleKey.Escape:
Close?.Invoke(this, new EventArgs());
break;
default:
if (selectable.Any() && selectable.Length >= TabPoint)
if (selectable.Any() && selectable.Length >= TabPoint && selectable[TabPoint].Enabled)
{
selectable[TabPoint].InvokeInput(this, input);
Render();
render = true;
}
break;
}
}
if (_wndWidth != Console.WindowWidth || _wndHeight != Console.WindowHeight)
{
render = true;
_wndWidth = Console.WindowWidth;
_wndHeight = Console.WindowHeight;
WindowResize?.Invoke(this, new EventArgs());
}
if (canRedraw && render)
Render();
}
/// <summary>
/// Increases the TabPoint or reverts back to 0 if at the end of selectables
/// </summary>
/// <param name="positive">Set to false to decrease instead</param>
public void Tab(bool positive = true)
{
Control[] controls = EnumerateRecursive();
Control[] selectable = controls.Where(s => s.Selectable).ToArray();
Tab(selectable, positive);
}
/// <summary>
/// Increases the TabPoint or reverts back to 0 if at the end of selectables
/// </summary>
/// <param name="selectable">The array of selectable controls to select from. You should most likely not use this</param>
/// <param name="positive">Set to false to decrease instead</param>
public void Tab(Control[] selectable, bool positive)
{
if (selectable.Any())
@ -103,9 +135,25 @@ namespace CC_Functions.Commandline.TUI
Render();
}
}
/// <summary>
/// Called if Escape is pressed, use this for flow control
/// </summary>
/// <param name="screen">This instance of the screen class</param>
/// <param name="e">Args</param>
public delegate void OnClose(Screen screen, EventArgs e);
public event OnClick Close;
/// <summary>
/// Called if Escape is pressed, use this for flow control
/// </summary>
public event OnClose Close;
/// <summary>
/// Called by ReadInput if a change in the window size is detected. Use this for positioning
/// </summary>
/// <param name="screen">This instance of the screen class</param>
/// <param name="e">Args</param>
public delegate void OnWindowResize(Screen screen, EventArgs e);
/// <summary>
/// Called by ReadInput if a change in the window size is detected. Use this for positioning
/// </summary>
public event OnWindowResize WindowResize;
}
}

View File

@ -3,8 +3,14 @@ using CC_Functions.Misc;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// Provides a control to select a number from a range of numbers
/// </summary>
public class Slider : Control
{
/// <summary>
/// Generates a new slider
/// </summary>
public Slider()
{
Input += (screen, args) =>
@ -26,7 +32,10 @@ namespace CC_Functions.Commandline.TUI
}
};
}
/// <summary>
/// The maximum value for this slider
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if too low/high</exception>
public int MaxValue
{
get => _maxValue;
@ -38,7 +47,10 @@ namespace CC_Functions.Commandline.TUI
throw new ArgumentOutOfRangeException("MaxValue must be larger than MinValue and equal to or larger than Value");
}
}
/// <summary>
/// The minimal value for this slider
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if too low/high</exception>
public int MinValue
{
get => _minValue;
@ -50,7 +62,10 @@ namespace CC_Functions.Commandline.TUI
throw new ArgumentOutOfRangeException("MaxValue must be larger than MinValue and equal to or smaller than Value");
}
}
/// <summary>
/// The current value of this slider
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if too low/high</exception>
public int Value
{
get => _value;
@ -62,19 +77,21 @@ namespace CC_Functions.Commandline.TUI
throw new ArgumentOutOfRangeException("Value must be between MinValue and MaxValue");
}
}
/// <summary>
/// The size of steps in this slider
/// </summary>
public int StepSize = 1;
private int _value = 5;
private int _maxValue = 10;
private int _minValue = 0;
/// <inheritdoc />
public override Pixel[,] Render()
{
int delta = MaxValue - MinValue;
int litValLen = Math.Max(MaxValue.ToString().Length, MinValue.ToString().Length);
int prevpts = Math.Max((Value - MinValue) * Size.Width / delta - litValLen - 2, 0);
int postpts = Math.Max(Size.Width - prevpts - litValLen - 2, 0);
char[,] rend = $"{new string('=', prevpts)}[{Value.ToString($"D{litValLen}")}]{new string('=', postpts)}".ToNDArray2D();
char[,] rend = $"{new string('=', prevpts)}[{Value.ToString($"D{litValLen}")}]{new string('=', postpts)}".ToNdArray2D();
int f1 = rend.GetLength(0);
int f2 = rend.GetLength(1);
Pixel[,] output = new Pixel[f1, f2];
@ -83,12 +100,7 @@ namespace CC_Functions.Commandline.TUI
for (int j = 0; j < f2; j++) output[i, j] = new Pixel(Selected ? ForeColor : BackColor, Selected ? BackColor : ForeColor, rend[i, j]);
return output;
}
protected override void Resize(int width, int height)
{
}
/// <inheritdoc />
public override bool Selectable { get; } = true;
}
}

View File

@ -6,16 +6,28 @@ using CC_Functions.Misc;
namespace CC_Functions.Commandline.TUI
{
/// <summary>
/// A basic non-scrolling text-editor control
/// </summary>
public class TextBox : Control
{
/// <summary>
/// The text inside this textbox
/// </summary>
public string Content;
private string[] Lines
{
get => Content.Split('\n');
set => Content = string.Join('\n', value);
}
/// <summary>
/// The "Cursors" position in this text box
/// </summary>
public Point Cursor = new Point(0, 0);
/// <summary>
/// Creates a new text box
/// </summary>
/// <param name="content">The text inside this text box</param>
public TextBox(string content)
{
Content = content;
@ -25,7 +37,11 @@ namespace CC_Functions.Commandline.TUI
};
Click += (screen, args) => ProcessInput(ConsoleKey.Enter, new ConsoleKeyInfo());
}
/// <summary>
/// Function to process input
/// </summary>
/// <param name="key">The pressed key</param>
/// <param name="info">Input metadata</param>
private void ProcessInput(ConsoleKey key, ConsoleKeyInfo info)
{
string[] lines = Lines;
@ -113,25 +129,32 @@ namespace CC_Functions.Commandline.TUI
Lines = lines;
break;
case ConsoleKey.Enter:
tmp = lines.ToList();
lines[Cursor.Y] = lines[Cursor.Y].Insert(Cursor.X, "\n");
Cursor.Y++;
Cursor.X = 0;
Lines = lines;
if (lines.Length < Size.Height)
{
tmp = lines.ToList();
lines[Cursor.Y] = lines[Cursor.Y].Insert(Cursor.X, "\n");
Cursor.Y++;
Cursor.X = 0;
Lines = lines;
}
break;
default:
lines[Cursor.Y] = lines[Cursor.Y].Insert(Cursor.X, info.KeyChar.ToString());
Lines = lines;
if (lines[Cursor.Y].Length < Size.Width)
{
lines[Cursor.Y] = lines[Cursor.Y].Insert(Cursor.X, info.KeyChar.ToString());
Lines = lines;
}
break;
}
}
/// <inheritdoc />
public override Pixel[,] Render()
{
char[,] inp1 = Content.ToNDArray2D();
char[,] inp1 = Content.ToNdArray2D();
inp1 = inp1.Resize(Size.Height, Size.Width - 2);
char[,] inp = new char[Size.Width, Size.Height];
inp.Populate(SpecialChars.empty);
inp.Populate(SpecialChars.Empty);
for (int i = 0; i < Size.Height; i++)
{
inp[0, i] = '[';
@ -148,11 +171,7 @@ namespace CC_Functions.Commandline.TUI
return output;
}
protected override void Resize(int width, int height)
{
//ignored for [Render]s sake, do not use
}
/// <inheritdoc />
public override bool Selectable { get; } = true;
}
}

View File

@ -13,9 +13,24 @@ namespace CC_Functions.Commandline
/// </summary>
public static class TableParser
{
/// <summary>
/// Parses the enumerable to a table using with the specified headers and transformed to strings with the specified selector
/// </summary>
/// <param name="values">The values to display</param>
/// <param name="columnHeaders">The headers for columns</param>
/// <param name="valueSelectors">Functions to get data for the cells</param>
/// <typeparam name="T">The type of the elements in the enumerable</typeparam>
/// <returns>The generated table</returns>
public static string ToStringTable<T>(this IEnumerable<T> values, string[] columnHeaders,
params Func<T, object>[] valueSelectors) => ToStringTable(values.ToArray(), columnHeaders, valueSelectors);
/// <summary>
/// Parses the array to a table using with the specified headers and transformed to strings with the specified selector
/// </summary>
/// <param name="values">The values to display</param>
/// <param name="columnHeaders">The headers for columns</param>
/// <param name="valueSelectors">Functions to get data for the cells</param>
/// <typeparam name="T">The type of the elements in the array</typeparam>
/// <returns>The generated table</returns>
public static string ToStringTable<T>(this T[] values, string[] columnHeaders,
params Func<T, object>[] valueSelectors)
{
@ -38,7 +53,11 @@ namespace CC_Functions.Commandline
return ToStringTable(arrValues);
}
/// <summary>
/// Parses the array to a table
/// </summary>
/// <param name="arrValues">The cells of the table</param>
/// <returns>The generated table</returns>
public static string ToStringTable(this string[,] arrValues)
{
int[] maxColumnsWidth = GetMaxColumnsWidth(arrValues);
@ -85,7 +104,13 @@ namespace CC_Functions.Commandline
return maxColumnsWidth;
}
/// <summary>
/// Parses the enumerable to a table, transformed to strings with the specified selector
/// </summary>
/// <param name="values">The values to display</param>
/// <param name="valueSelectors">Functions to get data for the cells</param>
/// <typeparam name="T">The type of the elements in the enumerable</typeparam>
/// <returns>The generated table</returns>
public static string ToStringTable<T>(this IEnumerable<T> values,
params Expression<Func<T, object>>[] valueSelectors)
{

View File

@ -4,11 +4,38 @@ using System.Linq;
namespace CC_Functions.Misc
{
/// <summary>
/// Contains extension functions to work with 1D and 2D arrays
/// </summary>
public static class ArrayFormatter
{
public static T[,] Resize<T>(this T[,] original, int rows, int cols)
/// <summary>
/// Copies and resizes the array
/// </summary>
/// <param name="original">The original array. This is not modified</param>
/// <param name="elements">The new amount of elements</param>
/// <typeparam name="T">The type of elements in the array</typeparam>
/// <returns>The new, resized array</returns>
public static T[] Resize<T>(this T[] original, int elements)
{
T[] output = new T[original.Length];
original.CopyTo(output, 0);
Array.Resize(ref output, elements);
return output;
}
/// <summary>
/// Copies and resizes the array
/// </summary>
/// <param name="original">The original array. This is not modified</param>
/// <param name="rows">The new amount of elements in dimension 0</param>
/// <param name="cols">The new amount of elements in dimension 1</param>
/// <param name="defaultEl">The element to place in empty fields of the new array</param>
/// <typeparam name="T">The type of elements in the array</typeparam>
/// <returns>The new, resized array</returns>
public static T[,] Resize<T>(this T[,] original, int rows, int cols, T defaultEl = default)
{
T[,] newArray = new T[rows, cols];
newArray.Populate(defaultEl);
int minRows = Math.Min(rows, original.GetLength(0));
int minCols = Math.Min(cols, original.GetLength(1));
for (int i = 0; i < minRows; i++)
@ -16,8 +43,13 @@ namespace CC_Functions.Misc
newArray[i, j] = original[i, j];
return newArray;
}
public static char[,] ToNDArray2D(this string source, char defaultEl = SpecialChars.empty)
/// <summary>
/// Converts a string to a 2d char array using newlines
/// </summary>
/// <param name="source">The source string</param>
/// <param name="defaultEl">The element to place in empty fields of the new array</param>
/// <returns>The generated array</returns>
public static char[,] ToNdArray2D(this string source, char defaultEl = SpecialChars.Empty)
{
string[] sourceArr = source.Split('\n');
int width = sourceArr.Select(s => s.Length).OrderBy(s => s).Last();
@ -32,11 +64,21 @@ namespace CC_Functions.Misc
}
return output;
}
public static void Populate<T>(this T[] arr, T value) {
for ( int i = 0; i < arr.Length;i++ ) arr[i] = value;
/// <summary>
/// Clears and fills the array with the specified value
/// </summary>
/// <param name="arr">The array to populate</param>
/// <param name="value">The value to copy to the array, defaults to the default value (usually null)</param>
/// <typeparam name="T">The type of elements in the array</typeparam>
public static void Populate<T>(this T[] arr, T value = default) {
for (int i = 0; i < arr.Length; i++) arr[i] = value;
}
/// <summary>
/// Clears and fills the array with the specified value
/// </summary>
/// <param name="arr">The array to populate</param>
/// <param name="value">The value to copy to the array, defaults to the default value (usually null)</param>
/// <typeparam name="T">The type of elements in the array</typeparam>
public static void Populate<T>(this T[,] arr, T value)
{
int w = arr.GetLength(0);
@ -44,7 +86,13 @@ namespace CC_Functions.Misc
for (int i = 0; i < w; i++)
for (int j = 0; j < h; j++) arr[i, j] = value;
}
/// <summary>
/// Copies the content of a 2D array to another with offset
/// </summary>
/// <param name="arr">The array to copy from</param>
/// <param name="target">The array to copy to</param>
/// <param name="offset">The copy offset</param>
/// <typeparam name="T">The type of elements in the array</typeparam>
public static void CopyTo<T>(this T[,] arr, T[,] target, Point offset)
{
int w = arr.GetLength(1);
@ -57,7 +105,12 @@ namespace CC_Functions.Misc
for (int y = oh; y < Math.Min(mh, h + oh); y++)
target[y, x] = arr[y - oh, x - ow];
}
/// <summary>
/// Copies and rotates the 2d array (row->column, column->row)
/// </summary>
/// <param name="arr">The array to copy from</param>
/// <typeparam name="T">The type of elements in the array</typeparam>
/// <returns>The new, rotated array</returns>
public static T[,] Rotate<T>(this T[,] arr)
{
int w = arr.GetLength(0);

View File

@ -4,8 +4,18 @@ using System.Security.Cryptography;
namespace CC_Functions.Misc
{
/// <summary>
/// Contains cryptographic functions
/// </summary>
public static class Crypto
{
/// <summary>
/// Encrypts an array of bytes using SHA512. Use with <see cref="Decrypt">Decrypt</see>
/// </summary>
/// <param name="data">The array of bytes to encrypt</param>
/// <param name="key">The key for encryption, later required to decrypt</param>
/// <returns>The encrypted data</returns>
/// <exception cref="ArgumentException">Thrown if provided data is invalid</exception>
public static byte[] Encrypt(byte[] data, byte[] key)
{
if (key is null)
@ -39,7 +49,13 @@ namespace CC_Functions.Misc
return combined;
}
/// <summary>
/// Decrypts an SHA512-encrypted byte array. Use with <see cref="Encrypt">Encrypt</see>
/// </summary>
/// <param name="encrypted">The array of bytes to decrypt</param>
/// <param name="key">The key the data was encrypted with</param>
/// <returns>The decrypted data</returns>
/// <exception cref="ArgumentException">Thrown if provided data is invalid</exception>
public static byte[] Decrypt(byte[] encrypted, byte[] key)
{
if (key is null)

View File

@ -8,22 +8,48 @@ using System.Net;
namespace CC_Functions.Misc
{
/// <summary>
/// Extension methods for various types
/// </summary>
public static class GenericExtensions
{
public static T get<G, T>(this Dictionary<G, T> dict, G key, T def)
/// <summary>
/// Gets an element from the dictionary or adds the default
/// </summary>
/// <param name="dict">The dictionary to get from</param>
/// <param name="key">The key to check</param>
/// <param name="def">The default value to place</param>
/// <typeparam name="TKey">The key type</typeparam>
/// <typeparam name="TValue">The value type</typeparam>
/// <returns>The element at the key</returns>
public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue def = default)
{
if (!dict.ContainsKey(key))
dict[key] = def;
return dict[key];
}
public static T set<G, T>(this Dictionary<G, T> dict, G key, T val)
/// <summary>
/// Sets an element and returns it
/// </summary>
/// <param name="dict">The dictionary to set in</param>
/// <param name="key">The key to set at</param>
/// <param name="val">The value to place</param>
/// <typeparam name="TKey">The key type</typeparam>
/// <typeparam name="TValue">The value type</typeparam>
/// <returns>The value that was placed</returns>
public static TValue Set<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue val = default)
{
dict[key] = val;
return dict[key];
}
public static bool tryCast<T>(this object o, out T parsed)
/// <summary>
/// Tries to cast an object
/// </summary>
/// <param name="o">The object to try to parse</param>
/// <param name="parsed">The parsed object (if successful) or the default (usually null)</param>
/// <typeparam name="T">The type to cast to</typeparam>
/// <returns>Whether the cast was successful</returns>
public static bool TryCast<T>(this object o, out T parsed)
{
try
{
@ -36,58 +62,126 @@ namespace CC_Functions.Misc
return false;
}
}
public static G selectO<T, G>(this T self, Func<T, G> func) => func.Invoke(self);
public static void runIf(bool condition, Action func)
/// <summary>
/// Runs a function that transforms an object in-line
/// </summary>
/// <param name="self">The object to run on</param>
/// <param name="func">The function to run</param>
/// <typeparam name="TIn">The input type</typeparam>
/// <typeparam name="TOut">The output type</typeparam>
/// <returns></returns>
public static TOut SelectO<TIn, TOut>(this TIn self, Func<TIn, TOut> func) => func.Invoke(self);
/// <summary>
/// Runs a function under a condition in-line (equal to if)
/// </summary>
/// <param name="condition">The condition to check</param>
/// <param name="func">The function to run</param>
public static void RunIf(bool condition, Action func)
{
if (condition)
func();
}
/// <summary>
/// Parses a string to a value of an enum
/// </summary>
/// <param name="value">The string to parse</param>
/// <typeparam name="TEnum">The enum type (MUST be an enum)</typeparam>
/// <returns>The element</returns>
public static TEnum ParseToEnum<TEnum>(string value) => (TEnum) Enum.Parse(typeof(TEnum),
Enum.GetNames(typeof(TEnum)).First(s => s.ToLower() == value.ToLower()));
public static T ParseToEnum<T>(string value) => (T) Enum.Parse(typeof(T),
Enum.GetNames(typeof(T)).First(s => s.ToLower() == value.ToLower()));
/// <summary>
/// Parses a string to a nullable bool (defaults to null if parse fails)
/// </summary>
/// <param name="value">The st string to parse</param>
/// <returns>The output nullable bool</returns>
public static bool? ParseBool(string value) =>
string.IsNullOrWhiteSpace(value) || value.ToLower() == "Indeterminate"
? (bool?) null
: bool.Parse(value);
public static bool AND(this bool? left, bool? right) => left.TRUE() && right.TRUE();
public static bool OR(this bool? left, bool? right) => left.TRUE() || right.TRUE();
public static bool XOR(this bool? left, bool? right) => left.OR(right) && !left.AND(right);
public static bool TRUE(this bool? self) => self == true;
public static bool FALSE(this bool? self) => self == false;
public static bool NULL(this bool? self) => self == null;
public static void RemoveAt<T, G>(this Dictionary<T, G> dict, int index) =>
dict.Remove(dict.Keys.OfType<T>().ToArray()[index]);
bool.TryParse(value, out bool tmp) ? (bool?)tmp : null;
/// <summary>
/// AND operation for nullable bools (uses <see cref="True">True</see>)
/// </summary>
/// <param name="left">First bool to check</param>
/// <param name="right">Second bool to check</param>
/// <returns>The operation result</returns>
public static bool And(this bool? left, bool? right) => left.True() && right.True();
/// <summary>
/// OR operation for nullable bools (uses <see cref="True">True</see>)
/// </summary>
/// <param name="left">First bool to check</param>
/// <param name="right">Second bool to check</param>
/// <returns>The operation result</returns>
public static bool Or(this bool? left, bool? right) => left.True() || right.True();
/// <summary>
/// XOR operation for nullable bools (uses <see cref="True">True</see>)
/// </summary>
/// <param name="left">First bool to check</param>
/// <param name="right">Second bool to check</param>
/// <returns>The operation result</returns>
public static bool Xor(this bool? left, bool? right) => left.Or(right) && !left.And(right);
/// <summary>
/// Whether the nullable bool is true (null->false)
/// </summary>
/// <param name="self">Value to check</param>
/// <returns>Whether it is true</returns>
public static bool True(this bool? self) => self == true;
/// <summary>
/// Whether the nullable bool is false (null->false)
/// </summary>
/// <param name="self">Value to check</param>
/// <returns>Whether it is false</returns>
public static bool False(this bool? self) => self == false;
/// <summary>
/// Whether the nullable bool is null
/// </summary>
/// <param name="self">Value to check</param>
/// <returns>Whether it is null</returns>
public static bool Null(this bool? self) => self == null;
/// <summary>
/// Removes an element from a dictionary by its index (not key)
/// </summary>
/// <param name="dict">The dictionary to remove from</param>
/// <param name="index">The index of the value</param>
/// <typeparam name="TKey">The key type</typeparam>
/// <typeparam name="TValue">The value type</typeparam>
public static void RemoveAt<TKey, TValue>(this Dictionary<TKey, TValue> dict, int index) =>
dict.Remove(dict.Keys.ToArray()[index]);
/// <summary>
/// Gets the size of a dictionary
/// </summary>
/// <param name="directory">The dictionary to check</param>
/// <returns>The size of the dictionary</returns>
public static long GetSize(this DirectoryInfo directory) => IO.GetDirectorySize(directory.FullName);
/// <summary>
/// Adds a directory to an archive recursively
/// </summary>
/// <param name="archive">The archive to add to</param>
/// <param name="folderPath">The directory to add</param>
/// <param name="entryName">The name of the directory in-archive</param>
/// <param name="ignoredExtensions">Extensions for files to ignore</param>
/// <param name="ignoredPaths">Paths to exclude from adding</param>
/// <returns>The new entry</returns>
public static ZipArchiveEntry AddDirectory(this ZipArchive archive, string folderPath, string entryName,
string[] ignoredExtensions, string[] ignoredPaths)
{
entryName = entryName.TrimEnd('/');
ZipArchiveEntry result = archive.CreateEntry($"{entryName}/");
string[] files = Directory.GetFiles(folderPath);
for (int i = 0; i < files.Length; i++)
if (!ignoredExtensions.Contains(Path.GetExtension(files[i])) &&
!ignoredPaths.Any(s => IO.CheckPathEqual(s, files[i])))
archive.CreateEntryFromFile(files[i], $"{entryName}/{Path.GetFileName(files[i])}");
foreach (string t in files)
if (!ignoredExtensions.Contains(Path.GetExtension(t)) &&
!ignoredPaths.Any(s => IO.CheckPathEqual(s, t)))
archive.CreateEntryFromFile(t, $"{entryName}/{Path.GetFileName(t)}");
string[] dirs = Directory.GetDirectories(folderPath);
for (int i = 0; i < dirs.Length; i++)
if (!ignoredPaths.Any(s => IO.CheckPathEqual(s, dirs[i])))
archive.AddDirectory(dirs[i], $"{entryName}/{Path.GetFileName(dirs[i])}", ignoredExtensions,
foreach (string t in dirs)
if (!ignoredPaths.Any(s => IO.CheckPathEqual(s, t)))
archive.AddDirectory(t, $"{entryName}/{Path.GetFileName(t)}", ignoredExtensions,
ignoredPaths);
return result;
}
/// <summary>
/// "Unshorten" (follow) an URL
/// </summary>
/// <param name="self">The URL to unshorten</param>
/// <returns>The unshortened URL</returns>
public static Uri Unshorten(this Uri self)
{
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(self);
@ -96,7 +190,11 @@ namespace CC_Functions.Misc
WebResponse resp = req.GetResponse();
return resp.ResponseUri;
}
/// <summary>
/// Pings an URL to check for availability
/// </summary>
/// <param name="self">The URL to check</param>
/// <returns>Whether the service is online</returns>
public static bool Ping(this Uri self)
{
try
@ -112,10 +210,31 @@ namespace CC_Functions.Misc
return false;
}
}
/// <summary>
/// Rounds a RectangleF to a Rectangle instead of flooring
/// </summary>
/// <param name="self">The RectangleF to round</param>
/// <returns>The rounded Rectangle</returns>
public static Rectangle Round(this RectangleF self) => Rectangle.Round(self);
/// <summary>
/// Ceilings a RectangleF to a Rectangle instead of flooring
/// </summary>
/// <param name="self">The RectangleF to ceil (?)</param>
/// <returns>The ceiled (?) Rectangle</returns>
public static Rectangle Ceiling(this RectangleF self) => Rectangle.Ceiling(self);
/// <summary>
/// Extension method for <see cref="Crypto">Crypto's</see> Encrypt
/// </summary>
/// <param name="self">The data to encrypt</param>
/// <param name="key">The key to encrypt with</param>
/// <returns>The encrypted data</returns>
public static byte[] Encrypt(this byte[] self, byte[] key) => Crypto.Encrypt(self, key);
/// <summary>
/// Extension method for <see cref="Crypto">Crypto's</see> Decrypt
/// </summary>
/// <param name="self">The data to decrypt</param>
/// <param name="key">The key to decrypt with</param>
/// <returns>The decrypted data</returns>
public static byte[] Decrypt(this byte[] self, byte[] key) => Crypto.Decrypt(self, key);
}
}

View File

@ -7,9 +7,15 @@ using System.Text;
namespace CC_Functions.Misc
{
public static class HID
/// <summary>
/// Functions for hardware identidiers
/// </summary>
public static class Hid
{
public static bool forceWindows = false;
/// <summary>
/// Whether to force Win32-based operation
/// </summary>
public static bool ForceWindows = false;
private static byte[] _fingerPrint;
private static readonly string HIDClasses = @"Win32_Processor:UniqueId
@ -27,14 +33,16 @@ Win32_BaseBoard:Manufacturer
Win32_BaseBoard:Name
Win32_BaseBoard:SerialNumber
Win32_NetworkAdapterConfiguration:MACAddress";
/// <summary>
/// The HID for this machine
/// </summary>
public static byte[] Value
{
get
{
if (_fingerPrint != null) return _fingerPrint;
string fingerprintTmp = "";
if (forceWindows || new [] {PlatformID.Xbox, PlatformID.Win32S, PlatformID.Win32Windows, PlatformID.Win32NT, PlatformID.WinCE}.Contains(Environment.OSVersion.Platform))
if (ForceWindows || new [] {PlatformID.Xbox, PlatformID.Win32S, PlatformID.Win32Windows, PlatformID.Win32NT, PlatformID.WinCE}.Contains(Environment.OSVersion.Platform))
{
HIDClasses.Split(new[] {"\r\n"}, StringSplitOptions.None).Select(s =>
{
@ -91,10 +99,18 @@ Win32_NetworkAdapterConfiguration:MACAddress";
return _fingerPrint;
}
}
/// <summary>
/// Encrypts data using <see cref="Crypto">Crypto's</see> Encrypt and the HID
/// </summary>
/// <param name="unencrypted">The data to encrypt</param>
/// <returns>The encrypted data</returns>
public static byte[] EncryptLocal(byte[] unencrypted) =>
Crypto.Encrypt(unencrypted, Value);
/// <summary>
/// Decrypts data using <see cref="Crypto">Crypto's</see> Decrypt and the HID
/// </summary>
/// <param name="encrypted">The data to decrypt</param>
/// <returns>The decrypted data</returns>
public static byte[] DecryptLocal(byte[] encrypted) =>
Crypto.Decrypt(encrypted, Value);
}

View File

@ -3,16 +3,30 @@ using System.IO;
namespace CC_Functions.Misc
{
/// <summary>
/// IO functions
/// </summary>
public static class IO
{
/// <summary>
/// Recursively gets the size of an directory
/// </summary>
/// <param name="path">The path of the directory</param>
/// <returns>The size of the directory</returns>
public static long GetDirectorySize(string path)
{
string[] a = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
long size = 0;
for (int i = 0; i < a.Length; i++) size += new FileInfo(a[i]).Length;
foreach (string t in a)
size += new FileInfo(t).Length;
return size;
}
/// <summary>
/// Check whether the paths are equivalent (ignores case)
/// </summary>
/// <param name="path1">The first path to check</param>
/// <param name="path2">The second path to check</param>
/// <returns>Whether the paths are equal</returns>
public static bool CheckPathEqual(string path1, string path2) =>
Path.GetFullPath(path1)
.Equals(Path.GetFullPath(path2), StringComparison.InvariantCultureIgnoreCase);

View File

@ -1,7 +1,13 @@
namespace CC_Functions.Misc
{
/// <summary>
/// Characters for use in CC-Functions.CommandLine
/// </summary>
public static class SpecialChars
{
public const char empty = ' ';
/// <summary>
/// The space character
/// </summary>
public const char Empty = ' ';
}
}