This repository has been archived on 2022-08-05. You can view files and clone it, but cannot push or open issues or pull requests.
CC-Functions/Commandline/TUI/Screen.cs
2020-05-23 17:22:45 +02:00

185 lines
6.9 KiB
C#

using System;
using System.Drawing;
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>
/// 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);
/// <summary>
/// Called when the selected control is changed
/// </summary>
/// <param name="screen">This instance of the screen class</param>
/// <param name="args">Args</param>
public delegate void OnTabChanged(Screen screen, EventArgs args);
/// <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>
/// Whether to output in color. Recommended for most terminals, might cause slowdowns in others
/// </summary>
public readonly bool Color;
private int _wndHeight = Console.WindowHeight;
private int _wndWidth = Console.WindowWidth;
/// <summary>
/// The current index of the tab-selected control in an array of selectable controls
/// </summary>
public int TabPoint;
/// <summary>
/// Creates a screen object. Multiple can be instantiated but drawing one overrides others. Use panels for that
/// </summary>
/// <param name="width">The screens with</param>
/// <param name="height">The screens height</param>
/// <param name="color">Whether to output in color</param>
public Screen(int width, int height, bool color = true)
{
Color = color;
Border = false;
Resize(width, height);
Tab();
}
/// <summary>
/// Resizes the screen. Make sure that this does not exceed the console size
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public new void Resize(int width, int height)
{
Size = new Size(width, height);
DiffDraw.Clear(width, height);
}
/// <summary>
/// Renders the screen, draws it to the console and outputs the new state
/// </summary>
/// <returns>The new state of the screen</returns>
public new Pixel[,] Render()
{
Pixel[,] tmp = base.Render();
DiffDraw.Clear(tmp);
DiffDraw.Draw(Color);
return tmp;
}
/// <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();
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);
render = true;
}
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);
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())
{
if (positive)
{
TabPoint++;
if (TabPoint >= selectable.Length) TabPoint = 0;
}
else
{
TabPoint--;
if (TabPoint < 0) TabPoint = selectable.Length - 1;
}
foreach (Control control in selectable) control.Selected = false;
selectable[TabPoint].Selected = true;
TabChanged?.Invoke(this, new EventArgs());
Render();
}
}
/// <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>
public event OnWindowResize WindowResize;
/// <summary>
/// Called when the selected control is changed
/// </summary>
public event OnTabChanged TabChanged;
}
}