Last active
February 14, 2017 04:54
-
-
Save distantcam/8dbc4769770a6fad19616ff90f0b41b6 to your computer and use it in GitHub Desktop.
CUI - A simple Console UI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using static System.Console; | |
using static System.ConsoleColor; | |
namespace CUI | |
{ | |
public class ConsoleApplication<TScreen> | |
{ | |
Dictionary<TScreen, IScreen<TScreen>> screens = new Dictionary<TScreen, IScreen<TScreen>>(); | |
TScreen startScreen; | |
public ConsoleApplication(TScreen startScreen) | |
{ | |
this.startScreen = startScreen; | |
} | |
public MenuScreen<TScreen> AddMenuScreen(TScreen screen) | |
{ | |
var screenConfig = new MenuScreen<TScreen>(screen); | |
screens.Add(screen, screenConfig); | |
return screenConfig; | |
} | |
public FunctionScreen<TScreen> AddFunctionScreen(TScreen screen) | |
{ | |
var screenConfig = new FunctionScreen<TScreen>(screen, startScreen); | |
screens.Add(screen, screenConfig); | |
return screenConfig; | |
} | |
public async Task Run() | |
{ | |
var currentScreen = startScreen; | |
await screens[currentScreen].Display(); | |
while (true) | |
{ | |
var key = ReadKey(true); | |
TScreen newScreen; | |
if (screens[currentScreen].TryUpdate(key, out newScreen)) | |
{ | |
currentScreen = newScreen; | |
} | |
else | |
{ | |
break; | |
} | |
await screens[currentScreen].Display(); | |
} | |
} | |
} | |
public class MenuScreen<TScreen> : IScreen<TScreen> | |
{ | |
TScreen screen; | |
List<MenuItem<TScreen>> menuItems = new List<MenuItem<TScreen>>(); | |
int selectedItem = 0; | |
int xpadding = 2; | |
int ypadding = 2; | |
public MenuScreen(TScreen screen) | |
{ | |
this.screen = screen; | |
} | |
public MenuScreen<TScreen> WithMenuOption(string text, TScreen screen) | |
{ | |
menuItems.Add(new MenuItem<TScreen> { Text = text, Screen = screen }); | |
return this; | |
} | |
public MenuScreen<TScreen> WithMenuOption(string text) | |
{ | |
menuItems.Add(new MenuItem<TScreen> { Text = text }); | |
return this; | |
} | |
public MenuScreen<TScreen> WithMenuOption(char key, string text, TScreen screen) | |
{ | |
menuItems.Add(new MenuItem<TScreen> { Key = key, Text = text, Screen = screen }); | |
return this; | |
} | |
public MenuScreen<TScreen> WithMenuOption(char key, string text) | |
{ | |
menuItems.Add(new MenuItem<TScreen> { Key = key, Text = text }); | |
return this; | |
} | |
public MenuScreen<TScreen> SetPadding(int x, int y) | |
{ | |
xpadding = x; | |
ypadding = y; | |
return this; | |
} | |
public Task Display() | |
{ | |
Clear(); | |
for (int i = 0; i < menuItems.Count; i++) | |
{ | |
var isSelected = selectedItem == i; | |
SetCursorPosition(xpadding, i + ypadding); | |
if (isSelected) | |
using (new ConsoleColorReset()) | |
{ | |
ConsoleUtil.SetColors(Black, White); | |
Write(">"); | |
} | |
else | |
Write(" "); | |
Write($" {menuItems[i].Key} {menuItems[i].Text}"); | |
} | |
SetCursorPosition(0, WindowHeight); | |
return Task.CompletedTask; | |
} | |
public bool TryUpdate(ConsoleKeyInfo key, out TScreen screen) | |
{ | |
screen = this.screen; | |
if (selectedItem < menuItems.Count - 1 && key.Key == ConsoleKey.DownArrow) | |
{ | |
selectedItem++; | |
return true; | |
} | |
if (selectedItem > 0 && key.Key == ConsoleKey.UpArrow) | |
{ | |
selectedItem--; | |
return true; | |
} | |
if (key.Key == ConsoleKey.Enter) | |
{ | |
if (!menuItems[selectedItem].IsTermination) | |
screen = menuItems[selectedItem].Screen; | |
return !menuItems[selectedItem].IsTermination; | |
} | |
if (key.KeyChar != 0) | |
{ | |
var menuItem = menuItems.FirstOrDefault(mi => mi.Key == key.KeyChar); | |
if (menuItem != null) | |
{ | |
if (!menuItem.IsTermination) | |
screen = menuItem.Screen; | |
return !menuItem.IsTermination; | |
} | |
} | |
return true; | |
} | |
} | |
public class FunctionScreen<TScreen> : IScreen<TScreen> | |
{ | |
TScreen screen; | |
Func<Task> action; | |
TScreen nextScreen; | |
public FunctionScreen(TScreen screen, TScreen nextScreen) | |
{ | |
this.screen = screen; | |
this.nextScreen = nextScreen; | |
} | |
public FunctionScreen<TScreen> SetAction(Func<Task> action) | |
{ | |
this.action = action; | |
return this; | |
} | |
public FunctionScreen<TScreen> SetNextScreen(TScreen nextScreen) | |
{ | |
this.nextScreen = nextScreen; | |
return this; | |
} | |
public async Task Display() | |
{ | |
Clear(); | |
if (action != null) | |
await action(); | |
else | |
using (new ConsoleColorReset()) | |
{ | |
ConsoleUtil.SetColors(White, Red); | |
WriteLine($"ERROR: Action not set for screen '{screen}'."); | |
} | |
WriteLine(); | |
WriteLine("Press any key to continue"); | |
} | |
public bool TryUpdate(ConsoleKeyInfo key, out TScreen screen) | |
{ | |
screen = nextScreen; | |
return true; | |
} | |
} | |
class MenuItem<TScreen> | |
{ | |
TScreen screen; | |
public MenuItem() | |
{ | |
IsTermination = true; | |
} | |
public bool IsTermination { get; private set; } | |
public char Key { get; set; } | |
public string Text { get; set; } | |
public TScreen Screen | |
{ | |
get { return screen; } | |
set | |
{ | |
screen = value; | |
IsTermination = false; | |
} | |
} | |
} | |
interface IScreen<TScreen> | |
{ | |
Task Display(); | |
bool TryUpdate(ConsoleKeyInfo key, out TScreen screen); | |
} | |
static class ConsoleUtil | |
{ | |
public static void SetColors(ConsoleColor foreground, ConsoleColor background) | |
{ | |
ForegroundColor = foreground; | |
BackgroundColor = background; | |
} | |
} | |
class ConsoleColorReset : IDisposable | |
{ | |
ConsoleColor background; | |
ConsoleColor foreground; | |
public ConsoleColorReset() | |
{ | |
background = BackgroundColor; | |
foreground = ForegroundColor; | |
} | |
public void Dispose() | |
{ | |
ConsoleUtil.SetColors(foreground, background); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading.Tasks; | |
using CUI; | |
class Program | |
{ | |
enum Screens | |
{ | |
MainScreen, | |
Processes, | |
Process1, | |
Process2 | |
} | |
static void Main(string[] args) | |
{ | |
var app = new ConsoleApplication<Screens>(Screens.MainScreen); | |
app.AddMenuScreen(Screens.MainScreen) | |
.WithMenuOption('p', "Select Process to run", Screens.Processes) | |
.WithMenuOption('q', "Quit"); | |
app.AddMenuScreen(Screens.Processes) | |
.WithMenuOption("Menu item 1", Screens.Process1) | |
.WithMenuOption("Menu item 2", Screens.Process2); | |
app.AddFunctionScreen(Screens.Process1) | |
.SetAction(async () => | |
{ | |
Console.WriteLine("Running..."); | |
await Task.Delay(1000); | |
Console.WriteLine("Almost done..."); | |
await Task.Delay(1000); | |
Console.WriteLine("Just finishing up..."); | |
await Task.Delay(1000); | |
Console.WriteLine("Done!"); | |
}); | |
app.AddFunctionScreen(Screens.Process2) | |
.SetAction(() => | |
{ | |
Console.WriteLine("I'm done already!"); | |
return Task.CompletedTask; | |
}); | |
app.Run().GetAwaiter().GetResult(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment