diff --git a/Commandline/TUI/Button.cs b/Commandline/TUI/Button.cs index d943b14..6285536 100644 --- a/Commandline/TUI/Button.cs +++ b/Commandline/TUI/Button.cs @@ -3,19 +3,30 @@ using CC_Functions.Misc; namespace CC_Functions.Commandline.TUI { + /// + /// A basic button type + /// public class Button : Control { + /// + /// The text inside this button + /// public string Content; + /// + /// Creates a new button + /// + /// The text inside this button public Button(string content) { Content = content; - char[,] tmp = Content.ToNDArray2D(); + char[,] tmp = Content.ToNdArray2D(); Size = new Size(tmp.GetLength(0), tmp.GetLength(1)); } + /// 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 - } - + /// public override bool Selectable { get; } = true; } } \ No newline at end of file diff --git a/Commandline/TUI/CheckBox.cs b/Commandline/TUI/CheckBox.cs index 3472213..cddda3a 100644 --- a/Commandline/TUI/CheckBox.cs +++ b/Commandline/TUI/CheckBox.cs @@ -4,10 +4,23 @@ using CC_Functions.Misc; namespace CC_Functions.Commandline.TUI { + /// + /// Provides a control for users to select a boolean + /// public class CheckBox : Control { + /// + /// The text inside this checkbox + /// public string Content; + /// + /// Whether the box is checked + /// public bool Checked = false; + /// + /// Creates a new checkbox + /// + /// The text inside this CheckBox public CheckBox(string content) { Content = content; @@ -30,14 +43,15 @@ namespace CC_Functions.Commandline.TUI }; } + /// 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 - } - + /// public override bool Selectable { get; } = true; + + /// + /// Called when the state of this checkbox is changed + /// + /// The current screen instance + /// Args public delegate void OnCheckedChanged(Screen screen, EventArgs e); + /// + /// Called when the state of this checkbox is changed + /// public event OnClick CheckedChanged; } } \ No newline at end of file diff --git a/Commandline/TUI/Control.cs b/Commandline/TUI/Control.cs index 7ff955c..88c7939 100644 --- a/Commandline/TUI/Control.cs +++ b/Commandline/TUI/Control.cs @@ -3,32 +3,62 @@ using System.Drawing; namespace CC_Functions.Commandline.TUI { + /// + /// Abstract class inherited by all controls + /// public abstract class Control { + /// + /// Called when the controls Size property is changed + /// + /// The calling control + /// Args + public delegate void OnResize(Control caller, EventArgs e); + /// + /// Called when the controls Size property is changed + /// + public event OnResize Resize; private Point _point; private Size _size; + /// + /// Renders the control + /// + /// The rendered pixels public abstract Pixel[,] Render(); - protected abstract void Resize(int width, int height); - private void Resize(Size size) => Resize(size.Width, size.Height); - + /// + /// The size of the control + /// public Size Size { set { - _size = value; - Resize(value); + if (_size != value) + { + _size = value; + Resize?.Invoke(this, new EventArgs()); + } } get => _size; } - + /// + /// The position of this control + /// public Point Point { get => _point; set => _point = value; } - + /// + /// The foreground color for this control + /// public ConsoleColor ForeColor { get; set; } = Console.ForegroundColor; + /// + /// The background color for this control + /// public ConsoleColor BackColor { get; set; } = Console.BackgroundColor; + /// + /// Whether the control can be selected + /// public abstract bool Selectable { get; } /// @@ -72,10 +102,13 @@ namespace CC_Functions.Commandline.TUI { Input?.Invoke(screen, new InputEventArgs(info)); } - /// /// Whether the control should be rendered /// public bool Visible = true; + /// + /// Whether the control can be interacted with + /// + public bool Enabled = true; } } \ No newline at end of file diff --git a/Commandline/TUI/DiffDraw.cs b/Commandline/TUI/DiffDraw.cs index 65033f8..a500de3 100644 --- a/Commandline/TUI/DiffDraw.cs +++ b/Commandline/TUI/DiffDraw.cs @@ -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]; + /// + /// The regions width + /// public static int Width => Screen.GetLength(1); + /// + /// The regions height + /// public static int Height => Screen.GetLength(0); - + /// + /// Draws to the console + /// + /// Whether to use color public static void Draw(bool color) { Console.CursorTop = 0; @@ -43,7 +52,10 @@ namespace CC_Functions.Commandline.TUI Console.BackgroundColor = bcol; _last = Screen; } - + /// + /// Redraws the entire screen (should be done from time to time to prevent corruption) + /// + /// Whether to use color public static void FullDraw(bool color) { Console.CursorTop = 0; @@ -73,26 +85,76 @@ namespace CC_Functions.Commandline.TUI Console.BackgroundColor = bcol; _last = Screen; } - + /// + /// Gets the char at a location + /// + /// The location + /// The char public static char Get(Point p) => Get(p.X, p.Y); + /// + /// Gets the char at a location + /// + /// The locations X coordinate + /// The locations Y coordinate + /// The char public static char Get(int x, int y) => Screen[y, x].Content; + /// + /// Gets the foreground color at a location + /// + /// The location + /// The color public static ConsoleColor GetForeColor(Point p) => GetForeColor(p.X, p.Y); + /// + /// Gets the foreground color at a location + /// + /// The locations X coordinate + /// The locations Y coordinate + /// The color public static ConsoleColor GetForeColor(int x, int y) => Screen[y, x].ForeColor; + /// + /// Gets the background color at a location + /// + /// The location + /// The color public static ConsoleColor GetBackColor(Point p) => GetBackColor(p.X, p.Y); + /// + /// Gets the background color at a location + /// + /// The locations X coordinate + /// The locations Y coordinate + /// The color public static ConsoleColor GetBackColor(int x, int y) => Screen[y, x].BackColor; - + /// + /// Sets a pixel at a point + /// + /// The point to place at + /// The pixel to place public static void Set(Point p, Pixel c) => Set(p.X, p.Y, c); - + /// + /// Sets a pixel at a location + /// + /// The locations X coordinate + /// The locations Y coordinate + /// The pixel to place public static void Set(int x, int y, Pixel c) => Screen[y, x] = c; - + /// + /// Clears the screen + /// public static void Clear() => Clear(Width, Height); - + /// + /// Resizes and clears the screen + /// + /// The new width + /// The new height public static void Clear(int width, int height) { Screen = new Pixel[height, width]; _last = _last.Resize(height, width); } - + /// + /// Replaces the screen state + /// + /// The new state public static void Clear(Pixel[,] content) { Screen = content; diff --git a/Commandline/TUI/InputEventArgs.cs b/Commandline/TUI/InputEventArgs.cs index f528784..431d31f 100644 --- a/Commandline/TUI/InputEventArgs.cs +++ b/Commandline/TUI/InputEventArgs.cs @@ -2,11 +2,20 @@ using System; namespace CC_Functions.Commandline.TUI { + /// + /// Arguments containing input data + /// public class InputEventArgs : EventArgs { private readonly ConsoleKeyInfo _info; + /// + /// The inputs data + /// public ConsoleKeyInfo Info => _info; - + /// + /// Generates new arguments + /// + /// The input data public InputEventArgs(ConsoleKeyInfo info) => _info = info; } } \ No newline at end of file diff --git a/Commandline/TUI/Label.cs b/Commandline/TUI/Label.cs index 27f3a67..c0e1069 100644 --- a/Commandline/TUI/Label.cs +++ b/Commandline/TUI/Label.cs @@ -3,14 +3,24 @@ using CC_Functions.Misc; namespace CC_Functions.Commandline.TUI { + /// + /// A basic text control + /// public class Label : Control { + /// + /// The text inside this label + /// public string Content; + /// + /// Creates a new label + /// + /// The text inside this label public Label(string content) => Content = content; - + /// 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 - } - + /// public override bool Selectable { get; } = false; } } \ No newline at end of file diff --git a/Commandline/TUI/Panel.cs b/Commandline/TUI/Panel.cs index a47f244..6ed8218 100644 --- a/Commandline/TUI/Panel.cs +++ b/Commandline/TUI/Panel.cs @@ -4,14 +4,24 @@ using CC_Functions.Misc; namespace CC_Functions.Commandline.TUI { + /// + /// A panel containing other components. MUST be inherited for all other controls that contain others + /// public class Panel : Control { + /// + /// The controls inside this panel + /// public List Controls = new List(); - + + /// + /// Renders the control and all contained controls + /// + /// The rendered pixels 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) - { - } - + /// public override bool Selectable { get; } = false; - + /// + /// Recursively enumerates all controls + /// + /// A list of all controls public Control[] EnumerateRecursive() { List output = Controls.ToList(); diff --git a/Commandline/TUI/Pixel.cs b/Commandline/TUI/Pixel.cs index c76a9ba..2ebaedf 100644 --- a/Commandline/TUI/Pixel.cs +++ b/Commandline/TUI/Pixel.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; namespace CC_Functions.Commandline.TUI { + /// + /// Represents a pixel + /// public class Pixel { private sealed class ColorContentEqualityComparer : IEqualityComparer @@ -18,11 +21,21 @@ namespace CC_Functions.Commandline.TUI public int GetHashCode(Pixel obj) => obj.GetHashCode(); } - + /// + /// Use this in functions that require equality comparers + /// public static IEqualityComparer ColorContentComparer { get; } = new ColorContentEqualityComparer(); - + /// + /// Whether this is equal to another pixel + /// + /// The other pixel to compare + /// Whether they are equal protected bool Equals(Pixel other) => ColorContentComparer.Equals(this, other); - + /// + /// Whether this is equal to another object + /// + /// The other object to compare + /// 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); } + /// + /// Generates an integer for comparing this object + /// + /// The generated hash public override int GetHashCode() => HashCode.Combine((int) BackColor, (int) ForeColor, Content); - + /// + /// This pixels background color + /// public ConsoleColor BackColor; + /// + /// This pixels foregound color + /// public ConsoleColor ForeColor; + /// + /// This pixels content character + /// public char Content; - + /// + /// Generates a new pixel + /// + /// The background color + /// The foreground color + /// The new content public Pixel(ConsoleColor backColor, ConsoleColor foreColor, char content) { BackColor = backColor; ForeColor = foreColor; Content = content; } - + /// + /// Generates a new pixel + /// + /// The content for this pixel public Pixel(char content) : this(Console.BackgroundColor, Console.ForegroundColor, content) { } - + /// + /// Generates a new pixel + /// public Pixel() : this(' ') { } - + /// + /// Whether two pixels are equal + /// + /// First pixel to compare + /// Second pixel to compare + /// Whether they are equal public static bool operator ==(Pixel a, Pixel b) => a.Equals(b); + /// + /// Whether to pixels are not equal + /// + /// First pixel to compare + /// Second pixel to compare + /// Whether they are not equal public static bool operator !=(Pixel a, Pixel b) => !a.Equals(b); - + /// + /// Returns the content of this pixel + /// + /// The content of this pixel public override string ToString() => Content.ToString(); } } \ No newline at end of file diff --git a/Commandline/TUI/Screen.cs b/Commandline/TUI/Screen.cs index ea65a3c..642cae0 100644 --- a/Commandline/TUI/Screen.cs +++ b/Commandline/TUI/Screen.cs @@ -4,10 +4,21 @@ using System.Linq; namespace CC_Functions.Commandline.TUI { + /// + /// Provides a front-end renderer for panels, draws using DiffDraw + /// public class Screen : Panel { + /// + /// Whether to output in color. Recommended for most terminals, might cause slowdowns in others + /// public readonly bool Color; + /// + /// The current index of the tab-selected control in an array of selectable controls + /// public int TabPoint = 0; + private int _wndWidth = Console.WindowWidth; + private int _wndHeight = Console.WindowHeight; /// /// Creates a screen object. Multiple can be instantiated but drawing one overrides others. Use panels for that /// @@ -26,7 +37,7 @@ namespace CC_Functions.Commandline.TUI /// /// /// - 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 /// /// The new state of the screen - 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() + /// + /// Reads input from Console and calls according functions + /// + /// Set to false to prevent redrawing if the screen should be updated. You can Render manually in that case + 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(); } - + /// + /// Increases the TabPoint or reverts back to 0 if at the end of selectables + /// + /// Set to false to decrease instead public void Tab(bool positive = true) { Control[] controls = EnumerateRecursive(); Control[] selectable = controls.Where(s => s.Selectable).ToArray(); Tab(selectable, positive); } - + /// + /// Increases the TabPoint or reverts back to 0 if at the end of selectables + /// + /// The array of selectable controls to select from. You should most likely not use this + /// Set to false to decrease instead public void Tab(Control[] selectable, bool positive) { if (selectable.Any()) @@ -103,9 +135,25 @@ namespace CC_Functions.Commandline.TUI Render(); } } - + /// + /// Called if Escape is pressed, use this for flow control + /// + /// This instance of the screen class + /// Args public delegate void OnClose(Screen screen, EventArgs e); - - public event OnClick Close; + /// + /// Called if Escape is pressed, use this for flow control + /// + public event OnClose Close; + /// + /// Called by ReadInput if a change in the window size is detected. Use this for positioning + /// + /// This instance of the screen class + /// Args + public delegate void OnWindowResize(Screen screen, EventArgs e); + /// + /// Called by ReadInput if a change in the window size is detected. Use this for positioning + /// + public event OnWindowResize WindowResize; } } \ No newline at end of file diff --git a/Commandline/TUI/Slider.cs b/Commandline/TUI/Slider.cs index f324246..fbdabfe 100644 --- a/Commandline/TUI/Slider.cs +++ b/Commandline/TUI/Slider.cs @@ -3,8 +3,14 @@ using CC_Functions.Misc; namespace CC_Functions.Commandline.TUI { + /// + /// Provides a control to select a number from a range of numbers + /// public class Slider : Control { + /// + /// Generates a new slider + /// public Slider() { Input += (screen, args) => @@ -26,7 +32,10 @@ namespace CC_Functions.Commandline.TUI } }; } - + /// + /// The maximum value for this slider + /// + /// Thrown if too low/high 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"); } } - + /// + /// The minimal value for this slider + /// + /// Thrown if too low/high 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"); } } - + /// + /// The current value of this slider + /// + /// Thrown if too low/high public int Value { get => _value; @@ -62,19 +77,21 @@ namespace CC_Functions.Commandline.TUI throw new ArgumentOutOfRangeException("Value must be between MinValue and MaxValue"); } } - + /// + /// The size of steps in this slider + /// public int StepSize = 1; private int _value = 5; private int _maxValue = 10; private int _minValue = 0; - + /// 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) - { - - } - + /// public override bool Selectable { get; } = true; } } \ No newline at end of file diff --git a/Commandline/TUI/TextBox.cs b/Commandline/TUI/TextBox.cs index dc2b77c..0f62bc3 100644 --- a/Commandline/TUI/TextBox.cs +++ b/Commandline/TUI/TextBox.cs @@ -6,16 +6,28 @@ using CC_Functions.Misc; namespace CC_Functions.Commandline.TUI { + /// + /// A basic non-scrolling text-editor control + /// public class TextBox : Control { + /// + /// The text inside this textbox + /// public string Content; private string[] Lines { get => Content.Split('\n'); set => Content = string.Join('\n', value); } - + /// + /// The "Cursors" position in this text box + /// public Point Cursor = new Point(0, 0); + /// + /// Creates a new text box + /// + /// The text inside this text box public TextBox(string content) { Content = content; @@ -25,7 +37,11 @@ namespace CC_Functions.Commandline.TUI }; Click += (screen, args) => ProcessInput(ConsoleKey.Enter, new ConsoleKeyInfo()); } - + /// + /// Function to process input + /// + /// The pressed key + /// Input metadata 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; } } + /// 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 - } - + /// public override bool Selectable { get; } = true; } } \ No newline at end of file diff --git a/Commandline/TableParser.cs b/Commandline/TableParser.cs index 7fd2e93..09359c2 100644 --- a/Commandline/TableParser.cs +++ b/Commandline/TableParser.cs @@ -13,9 +13,24 @@ namespace CC_Functions.Commandline /// public static class TableParser { + /// + /// Parses the enumerable to a table using with the specified headers and transformed to strings with the specified selector + /// + /// The values to display + /// The headers for columns + /// Functions to get data for the cells + /// The type of the elements in the enumerable + /// The generated table public static string ToStringTable(this IEnumerable values, string[] columnHeaders, params Func[] valueSelectors) => ToStringTable(values.ToArray(), columnHeaders, valueSelectors); - + /// + /// Parses the array to a table using with the specified headers and transformed to strings with the specified selector + /// + /// The values to display + /// The headers for columns + /// Functions to get data for the cells + /// The type of the elements in the array + /// The generated table public static string ToStringTable(this T[] values, string[] columnHeaders, params Func[] valueSelectors) { @@ -38,7 +53,11 @@ namespace CC_Functions.Commandline return ToStringTable(arrValues); } - + /// + /// Parses the array to a table + /// + /// The cells of the table + /// The generated table public static string ToStringTable(this string[,] arrValues) { int[] maxColumnsWidth = GetMaxColumnsWidth(arrValues); @@ -85,7 +104,13 @@ namespace CC_Functions.Commandline return maxColumnsWidth; } - + /// + /// Parses the enumerable to a table, transformed to strings with the specified selector + /// + /// The values to display + /// Functions to get data for the cells + /// The type of the elements in the enumerable + /// The generated table public static string ToStringTable(this IEnumerable values, params Expression>[] valueSelectors) { diff --git a/Misc/ArrayFormatter.cs b/Misc/ArrayFormatter.cs index 28c3aef..b8127cb 100644 --- a/Misc/ArrayFormatter.cs +++ b/Misc/ArrayFormatter.cs @@ -4,11 +4,38 @@ using System.Linq; namespace CC_Functions.Misc { + /// + /// Contains extension functions to work with 1D and 2D arrays + /// public static class ArrayFormatter { - public static T[,] Resize(this T[,] original, int rows, int cols) + /// + /// Copies and resizes the array + /// + /// The original array. This is not modified + /// The new amount of elements + /// The type of elements in the array + /// The new, resized array + public static T[] Resize(this T[] original, int elements) + { + T[] output = new T[original.Length]; + original.CopyTo(output, 0); + Array.Resize(ref output, elements); + return output; + } + /// + /// Copies and resizes the array + /// + /// The original array. This is not modified + /// The new amount of elements in dimension 0 + /// The new amount of elements in dimension 1 + /// The element to place in empty fields of the new array + /// The type of elements in the array + /// The new, resized array + public static T[,] Resize(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) + /// + /// Converts a string to a 2d char array using newlines + /// + /// The source string + /// The element to place in empty fields of the new array + /// The generated array + 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(this T[] arr, T value) { - for ( int i = 0; i < arr.Length;i++ ) arr[i] = value; + /// + /// Clears and fills the array with the specified value + /// + /// The array to populate + /// The value to copy to the array, defaults to the default value (usually null) + /// The type of elements in the array + public static void Populate(this T[] arr, T value = default) { + for (int i = 0; i < arr.Length; i++) arr[i] = value; } - + /// + /// Clears and fills the array with the specified value + /// + /// The array to populate + /// The value to copy to the array, defaults to the default value (usually null) + /// The type of elements in the array public static void Populate(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; } - + /// + /// Copies the content of a 2D array to another with offset + /// + /// The array to copy from + /// The array to copy to + /// The copy offset + /// The type of elements in the array public static void CopyTo(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]; } - + /// + /// Copies and rotates the 2d array (row->column, column->row) + /// + /// The array to copy from + /// The type of elements in the array + /// The new, rotated array public static T[,] Rotate(this T[,] arr) { int w = arr.GetLength(0); diff --git a/Misc/Crypto.cs b/Misc/Crypto.cs index 95c3395..937aa44 100644 --- a/Misc/Crypto.cs +++ b/Misc/Crypto.cs @@ -4,8 +4,18 @@ using System.Security.Cryptography; namespace CC_Functions.Misc { + /// + /// Contains cryptographic functions + /// public static class Crypto { + /// + /// Encrypts an array of bytes using SHA512. Use with Decrypt + /// + /// The array of bytes to encrypt + /// The key for encryption, later required to decrypt + /// The encrypted data + /// Thrown if provided data is invalid public static byte[] Encrypt(byte[] data, byte[] key) { if (key is null) @@ -39,7 +49,13 @@ namespace CC_Functions.Misc return combined; } - + /// + /// Decrypts an SHA512-encrypted byte array. Use with Encrypt + /// + /// The array of bytes to decrypt + /// The key the data was encrypted with + /// The decrypted data + /// Thrown if provided data is invalid public static byte[] Decrypt(byte[] encrypted, byte[] key) { if (key is null) diff --git a/Misc/GenericExtensions.cs b/Misc/GenericExtensions.cs index ebb226f..45a2106 100644 --- a/Misc/GenericExtensions.cs +++ b/Misc/GenericExtensions.cs @@ -8,22 +8,48 @@ using System.Net; namespace CC_Functions.Misc { + /// + /// Extension methods for various types + /// public static class GenericExtensions { - public static T get(this Dictionary dict, G key, T def) + /// + /// Gets an element from the dictionary or adds the default + /// + /// The dictionary to get from + /// The key to check + /// The default value to place + /// The key type + /// The value type + /// The element at the key + public static TValue Get(this IDictionary dict, TKey key, TValue def = default) { if (!dict.ContainsKey(key)) dict[key] = def; return dict[key]; } - - public static T set(this Dictionary dict, G key, T val) + /// + /// Sets an element and returns it + /// + /// The dictionary to set in + /// The key to set at + /// The value to place + /// The key type + /// The value type + /// The value that was placed + public static TValue Set(this IDictionary dict, TKey key, TValue val = default) { dict[key] = val; return dict[key]; } - - public static bool tryCast(this object o, out T parsed) + /// + /// Tries to cast an object + /// + /// The object to try to parse + /// The parsed object (if successful) or the default (usually null) + /// The type to cast to + /// Whether the cast was successful + public static bool TryCast(this object o, out T parsed) { try { @@ -36,58 +62,126 @@ namespace CC_Functions.Misc return false; } } - - public static G selectO(this T self, Func func) => func.Invoke(self); - - public static void runIf(bool condition, Action func) + /// + /// Runs a function that transforms an object in-line + /// + /// The object to run on + /// The function to run + /// The input type + /// The output type + /// + public static TOut SelectO(this TIn self, Func func) => func.Invoke(self); + /// + /// Runs a function under a condition in-line (equal to if) + /// + /// The condition to check + /// The function to run + public static void RunIf(bool condition, Action func) { if (condition) func(); } + /// + /// Parses a string to a value of an enum + /// + /// The string to parse + /// The enum type (MUST be an enum) + /// The element + public static TEnum ParseToEnum(string value) => (TEnum) Enum.Parse(typeof(TEnum), + Enum.GetNames(typeof(TEnum)).First(s => s.ToLower() == value.ToLower())); - public static T ParseToEnum(string value) => (T) Enum.Parse(typeof(T), - Enum.GetNames(typeof(T)).First(s => s.ToLower() == value.ToLower())); - + /// + /// Parses a string to a nullable bool (defaults to null if parse fails) + /// + /// The st string to parse + /// The output nullable bool 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(this Dictionary dict, int index) => - dict.Remove(dict.Keys.OfType().ToArray()[index]); - + bool.TryParse(value, out bool tmp) ? (bool?)tmp : null; + /// + /// AND operation for nullable bools (uses True) + /// + /// First bool to check + /// Second bool to check + /// The operation result + public static bool And(this bool? left, bool? right) => left.True() && right.True(); + /// + /// OR operation for nullable bools (uses True) + /// + /// First bool to check + /// Second bool to check + /// The operation result + public static bool Or(this bool? left, bool? right) => left.True() || right.True(); + /// + /// XOR operation for nullable bools (uses True) + /// + /// First bool to check + /// Second bool to check + /// The operation result + public static bool Xor(this bool? left, bool? right) => left.Or(right) && !left.And(right); + /// + /// Whether the nullable bool is true (null->false) + /// + /// Value to check + /// Whether it is true + public static bool True(this bool? self) => self == true; + /// + /// Whether the nullable bool is false (null->false) + /// + /// Value to check + /// Whether it is false + public static bool False(this bool? self) => self == false; + /// + /// Whether the nullable bool is null + /// + /// Value to check + /// Whether it is null + public static bool Null(this bool? self) => self == null; + /// + /// Removes an element from a dictionary by its index (not key) + /// + /// The dictionary to remove from + /// The index of the value + /// The key type + /// The value type + public static void RemoveAt(this Dictionary dict, int index) => + dict.Remove(dict.Keys.ToArray()[index]); + /// + /// Gets the size of a dictionary + /// + /// The dictionary to check + /// The size of the dictionary public static long GetSize(this DirectoryInfo directory) => IO.GetDirectorySize(directory.FullName); - + /// + /// Adds a directory to an archive recursively + /// + /// The archive to add to + /// The directory to add + /// The name of the directory in-archive + /// Extensions for files to ignore + /// Paths to exclude from adding + /// The new entry 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; } - + /// + /// "Unshorten" (follow) an URL + /// + /// The URL to unshorten + /// The unshortened URL 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; } - + /// + /// Pings an URL to check for availability + /// + /// The URL to check + /// Whether the service is online public static bool Ping(this Uri self) { try @@ -112,10 +210,31 @@ namespace CC_Functions.Misc return false; } } - + /// + /// Rounds a RectangleF to a Rectangle instead of flooring + /// + /// The RectangleF to round + /// The rounded Rectangle public static Rectangle Round(this RectangleF self) => Rectangle.Round(self); + /// + /// Ceilings a RectangleF to a Rectangle instead of flooring + /// + /// The RectangleF to ceil (?) + /// The ceiled (?) Rectangle public static Rectangle Ceiling(this RectangleF self) => Rectangle.Ceiling(self); + /// + /// Extension method for Crypto's Encrypt + /// + /// The data to encrypt + /// The key to encrypt with + /// The encrypted data public static byte[] Encrypt(this byte[] self, byte[] key) => Crypto.Encrypt(self, key); + /// + /// Extension method for Crypto's Decrypt + /// + /// The data to decrypt + /// The key to decrypt with + /// The decrypted data public static byte[] Decrypt(this byte[] self, byte[] key) => Crypto.Decrypt(self, key); } } \ No newline at end of file diff --git a/Misc/HID.cs b/Misc/HID.cs index 34cb9fe..85e04ae 100644 --- a/Misc/HID.cs +++ b/Misc/HID.cs @@ -7,9 +7,15 @@ using System.Text; namespace CC_Functions.Misc { - public static class HID + /// + /// Functions for hardware identidiers + /// + public static class Hid { - public static bool forceWindows = false; + /// + /// Whether to force Win32-based operation + /// + 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"; - + /// + /// The HID for this machine + /// 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; } } - + /// + /// Encrypts data using Crypto's Encrypt and the HID + /// + /// The data to encrypt + /// The encrypted data public static byte[] EncryptLocal(byte[] unencrypted) => Crypto.Encrypt(unencrypted, Value); - + /// + /// Decrypts data using Crypto's Decrypt and the HID + /// + /// The data to decrypt + /// The decrypted data public static byte[] DecryptLocal(byte[] encrypted) => Crypto.Decrypt(encrypted, Value); } diff --git a/Misc/IO.cs b/Misc/IO.cs index 11407d3..3f40a02 100644 --- a/Misc/IO.cs +++ b/Misc/IO.cs @@ -3,16 +3,30 @@ using System.IO; namespace CC_Functions.Misc { + /// + /// IO functions + /// public static class IO { + /// + /// Recursively gets the size of an directory + /// + /// The path of the directory + /// The size of the directory 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; } - + /// + /// Check whether the paths are equivalent (ignores case) + /// + /// The first path to check + /// The second path to check + /// Whether the paths are equal public static bool CheckPathEqual(string path1, string path2) => Path.GetFullPath(path1) .Equals(Path.GetFullPath(path2), StringComparison.InvariantCultureIgnoreCase); diff --git a/Misc/SpecialChars.cs b/Misc/SpecialChars.cs index 03e7372..4483dc1 100644 --- a/Misc/SpecialChars.cs +++ b/Misc/SpecialChars.cs @@ -1,7 +1,13 @@ namespace CC_Functions.Misc { + /// + /// Characters for use in CC-Functions.CommandLine + /// public static class SpecialChars { - public const char empty = ' '; + /// + /// The space character + /// + public const char Empty = ' '; } } \ No newline at end of file