using System; using System.Drawing; using System.Linq; namespace CC_Functions.Commandline.TUI { /// /// Provides a front-end renderer for panels, draws using DiffDraw /// public class Screen : Panel { /// /// 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); /// /// Called when the selected control is changed /// /// This instance of the screen class /// Args public delegate void OnTabChanged(Screen screen, EventArgs args); /// /// 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); private bool _color; /// /// Whether to output in color. Recommended for most terminals, might cause slowdowns in others /// public bool Color { get => _color; set { if (_color != value) { _color = value; DiffDraw.Draw(_color); } } } /// /// The current index of the tab-selected control in an array of selectable controls /// public int TabPoint { get => _tabPoint; set { if (_tabPoint != value) { _tabPoint = value; } } } private int _wndHeight = Console.WindowHeight; private int _wndWidth = Console.WindowWidth; private int _tabPoint; /// /// Creates a screen object. Multiple can be instantiated but drawing one overrides others. Use panels for that /// /// The screens with /// The screens height /// Whether to output in color public Screen(int width, int height, bool color = true) { Color = color; Border = false; Resize(width, height); Tab(); } /// /// Resizes the screen. Make sure that this does not exceed the console size /// /// /// public new void Resize(int width, int height) { Size = new Size(width, height); DiffDraw.Clear(width, height); } /// /// Renders the screen, draws it to the console and outputs the new state /// /// The new state of the screen public new Pixel[,] Render() { FixSelection(); Pixel[,] tmp = base.Render(); DiffDraw.Clear(tmp); DiffDraw.Draw(Color); return tmp; } /// /// 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(); Control[] selectable = controls.Where(s => s.Selectable).ToArray(); ConsoleKeyInfo input = Console.ReadKey(); switch (input.Key) { case ConsoleKey.Tab: Tab(selectable, (input.Modifiers & ConsoleModifiers.Shift) == 0); break; case ConsoleKey.Enter: if (selectable.Any() && selectable.Length >= TabPoint && selectable[TabPoint].Enabled) selectable[TabPoint].InvokeClick(this); break; case ConsoleKey.Escape: Close?.Invoke(this, new EventArgs()); break; default: if (selectable.Any() && selectable.Length >= TabPoint && selectable[TabPoint].Enabled) selectable[TabPoint].InvokeInput(this, input); InvokeInput(this, input); break; } render = true; } if (_wndWidth != Console.WindowWidth || _wndHeight != Console.WindowHeight) { render = true; _wndWidth = Console.WindowWidth; _wndHeight = Console.WindowHeight; WindowResize?.Invoke(this, new EventArgs()); } if (canRedraw && render) Render(); else FixSelection(); } /// /// 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()) { if (positive) { TabPoint++; if (TabPoint >= selectable.Length) TabPoint = 0; } else { TabPoint--; if (TabPoint < 0) TabPoint = selectable.Length - 1; } FixSelection(true); } } private void FixSelection(bool draw = false) { Control[] controls = EnumerateRecursive(); Control[] selectable = controls.Where(s => s.Selectable).ToArray(); if (selectable.Any()) { foreach (Control control in selectable) control.Selected = false; selectable[TabPoint].Selected = true; TabChanged?.Invoke(this, new EventArgs()); if (draw) Render(); } } /// /// 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 /// public event OnWindowResize WindowResize; /// /// Called when the selected control is changed /// public event OnTabChanged TabChanged; } }