using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using amecs;
|
|
|
|
namespace Ameliorated.ConsoleUtils
|
|
{
|
|
public class Menu
|
|
{
|
|
public List<MenuItem> Choices { get; set; } = new List<MenuItem>();
|
|
private int TotalOffset = 2;
|
|
|
|
private int? _offset = null;
|
|
|
|
public int Offset
|
|
{
|
|
get
|
|
{
|
|
if (_offset == null)
|
|
_offset = BindToOpenFrame ? 5 : 2;
|
|
|
|
return _offset.Value;
|
|
}
|
|
set { _offset = value; }
|
|
}
|
|
|
|
public ConsoleColor? SelectionForeground { get; set; } = ConsoleColor.Green;
|
|
public ConsoleColor? SelectionBackgound { get; set; } = null;
|
|
|
|
public bool CloseFrame { get; set; } = true;
|
|
|
|
private bool _bindToOpenFrame;
|
|
|
|
public bool BindToOpenFrame { get; set; } = true;
|
|
|
|
private int menuStart;
|
|
|
|
private int pages;
|
|
private int choicesPerPage;
|
|
|
|
public void Write()
|
|
{
|
|
if (Choices.Count < 1) throw new ArgumentException("Property Choices must have at least one choice.");
|
|
|
|
_bindToOpenFrame = ConsoleTUI.OpenFrame != null && BindToOpenFrame;
|
|
|
|
int totalStaticChoices = Choices.Count(x => x.IsStatic || x.IsNextButton || x.IsPreviousButton);
|
|
|
|
if (_bindToOpenFrame)
|
|
{
|
|
pages = (int)Math.Ceiling(((double)Choices.Count - totalStaticChoices) / (double)(ConsoleTUI.OpenFrame.DisplayHeight - totalStaticChoices + 1));
|
|
choicesPerPage = (ConsoleTUI.OpenFrame.DisplayHeight - totalStaticChoices + 1);
|
|
}
|
|
else
|
|
{
|
|
pages = (int)Math.Ceiling(((double)Choices.Count - totalStaticChoices) / (double)(((Console.WindowHeight - Console.CursorTop) - totalStaticChoices)));
|
|
choicesPerPage = (Console.WindowHeight - Console.CursorTop) - totalStaticChoices;
|
|
}
|
|
|
|
|
|
if (_bindToOpenFrame)
|
|
ConsoleTUI.OpenFrame.Clear();
|
|
|
|
TotalOffset = _bindToOpenFrame ? ConsoleTUI.OpenFrame.DisplayOffset + Offset : Offset;
|
|
|
|
menuStart = Console.CursorTop;
|
|
if (_bindToOpenFrame)
|
|
{
|
|
Console.SetCursorPosition(TotalOffset, Console.CursorTop);
|
|
}
|
|
|
|
var fluidChoices = Choices.Where(x => !x.IsStatic && !x.IsNextButton && !x.IsPreviousButton).Take(choicesPerPage);
|
|
var staticChoices = Choices.Where(x => x.IsStatic || (pages > 1 && x.IsNextButton));
|
|
WriteChoiceList(fluidChoices.Concat(staticChoices));
|
|
|
|
Frame = ConsoleTUI.OpenFrame;
|
|
if (_bindToOpenFrame && CloseFrame)
|
|
{
|
|
try
|
|
{
|
|
var offset = ConsoleTUI.OpenFrame.DisplayOffset;
|
|
if (pages == 1)
|
|
ConsoleTUI.OpenFrame.Close();
|
|
else
|
|
ConsoleTUI.OpenFrame.Close($"Page 1/{pages}");
|
|
Console.WriteLine(new string(' ', offset) + "Use the arrow keys to navigate");
|
|
} catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
Console.ReadLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void WriteChoiceList(IEnumerable<MenuItem> list)
|
|
{
|
|
bool selectedWritten = false;
|
|
Console.SetCursorPosition(0, Console.CursorTop);
|
|
foreach (MenuItem choice in list)
|
|
{
|
|
if (_bindToOpenFrame)
|
|
{
|
|
Console.Write(
|
|
new string(' ', TotalOffset));
|
|
if (choice.IsEnabled && !selectedWritten)
|
|
{
|
|
choice.WriteSelected(SelectionForeground, SelectionBackgound);
|
|
selectedWritten = true;
|
|
Console.WriteLine();
|
|
continue;
|
|
}
|
|
|
|
choice.Write();
|
|
}
|
|
else
|
|
{
|
|
Console.Write(
|
|
new string(' ', TotalOffset));
|
|
if (choice.IsEnabled && !selectedWritten)
|
|
{
|
|
choice.WriteSelected(SelectionForeground, SelectionBackgound);
|
|
selectedWritten = true;
|
|
Console.WriteLine();
|
|
continue;
|
|
}
|
|
|
|
choice.Write();
|
|
}
|
|
|
|
Console.WriteLine();
|
|
}
|
|
|
|
Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop - 1);
|
|
}
|
|
|
|
private List<MenuItem> ValidChoices = new List<MenuItem>();
|
|
|
|
private ConsoleTUI.Frame Frame;
|
|
|
|
public object Load()
|
|
{
|
|
var visCache = Console.CursorVisible;
|
|
Console.CursorVisible = false;
|
|
|
|
|
|
int index = Math.Max(Choices.FindIndex(x => x.IsEnabled), 0);
|
|
int validIndex = 0;
|
|
ConsoleKey keyPressed;
|
|
|
|
Console.SetCursorPosition(Console.CursorLeft, menuStart + index);
|
|
|
|
|
|
var allFluidChoices = Choices.Where(x => !x.IsStatic && !x.IsNextButton && !x.IsPreviousButton).ToList();
|
|
var allStaticChoices = Choices.Where(x => x.IsStatic || x.IsNextButton || x.IsPreviousButton).ToList();
|
|
|
|
int pageIndex = 1;
|
|
ConsoleUtils.ClearInputBuffer();
|
|
|
|
var currentChoices = allFluidChoices.Take(choicesPerPage).Concat(Choices.Where(x => x.IsStatic || (pages > 1 && x.IsNextButton))).ToList();
|
|
var currentValidChoices = currentChoices.Where(x => x.IsEnabled).ToList();
|
|
|
|
while (true)
|
|
{
|
|
keyPressed = Console.ReadKey(true).Key;
|
|
if (keyPressed == ConsoleKey.DownArrow || keyPressed == ConsoleKey.S)
|
|
{
|
|
if (validIndex >= currentValidChoices.Count - 1) continue;
|
|
Console.SetCursorPosition(Math.Max(TotalOffset - 2, 0), Console.CursorTop);
|
|
|
|
Console.Write(" ");
|
|
currentValidChoices[validIndex].Write();
|
|
|
|
var fromTop = currentChoices.Skip(index + 1).TakeWhile(x => !x.IsEnabled).Count() + 1;
|
|
var choice = currentChoices.Skip(index + 1).First(x => x.IsEnabled);
|
|
|
|
Console.SetCursorPosition(TotalOffset, Console.CursorTop + fromTop);
|
|
choice.WriteSelected(SelectionForeground, SelectionBackgound);
|
|
|
|
index += fromTop;
|
|
validIndex += 1;
|
|
}
|
|
|
|
if (keyPressed == ConsoleKey.UpArrow || keyPressed == ConsoleKey.W)
|
|
{
|
|
if (!(validIndex > 0)) continue;
|
|
|
|
Console.SetCursorPosition(Math.Max(TotalOffset - 2, 0), Console.CursorTop);
|
|
|
|
Console.Write(" ");
|
|
currentValidChoices[validIndex].Write();
|
|
|
|
var fromTop = -currentChoices.Take(index).Reverse().TakeWhile(x => !x.IsEnabled).Count() - 1;
|
|
var choice = currentChoices.Take(index).Last(x => x.IsEnabled);
|
|
|
|
Console.SetCursorPosition(TotalOffset, Console.CursorTop + fromTop);
|
|
choice.WriteSelected(SelectionForeground, SelectionBackgound);
|
|
|
|
index += fromTop;
|
|
validIndex -= 1;
|
|
}
|
|
|
|
if (keyPressed == ConsoleKey.RightArrow || keyPressed == ConsoleKey.A || keyPressed == ConsoleKey.PageUp ||
|
|
(keyPressed == ConsoleKey.Enter && currentValidChoices[validIndex].IsNextButton))
|
|
{
|
|
if (pageIndex == pages)
|
|
continue;
|
|
|
|
Frame.Clear();
|
|
|
|
pageIndex++;
|
|
|
|
var pIndex = pageIndex;
|
|
var fluidChoices = allFluidChoices.Skip((pIndex - 1) * choicesPerPage).Take(choicesPerPage).ToList();
|
|
var staticChoices = allStaticChoices.Where(x => !(pages <= pIndex && x.IsNextButton));
|
|
Console.SetCursorPosition(Console.CursorLeft, menuStart);
|
|
|
|
currentChoices = fluidChoices.Concat(staticChoices).ToList();
|
|
WriteChoiceList(currentChoices);
|
|
|
|
if (_bindToOpenFrame && CloseFrame)
|
|
{
|
|
try
|
|
{
|
|
var offset = ConsoleTUI.OpenFrame.DisplayOffset;
|
|
if (pages == 1)
|
|
ConsoleTUI.OpenFrame.Close();
|
|
else
|
|
ConsoleTUI.OpenFrame.Close($"Page {pIndex}/{pages}");
|
|
Console.WriteLine(new string(' ', offset) + "Use the arrow keys to navigate");
|
|
} catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
Console.ReadLine();
|
|
}
|
|
}
|
|
|
|
Console.SetCursorPosition(Console.CursorLeft, menuStart);
|
|
|
|
currentValidChoices = currentChoices.Where(x => x.IsEnabled).ToList();
|
|
index = 0;
|
|
validIndex = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (keyPressed == ConsoleKey.LeftArrow || keyPressed == ConsoleKey.D || keyPressed == ConsoleKey.PageDown ||
|
|
(keyPressed == ConsoleKey.Enter && currentValidChoices[validIndex].IsPreviousButton))
|
|
{
|
|
if (pageIndex == 1)
|
|
continue;
|
|
|
|
Frame.Clear();
|
|
|
|
pageIndex--;
|
|
|
|
var pIndex = pageIndex;
|
|
var fluidChoices = allFluidChoices.Skip((pIndex - 1) * choicesPerPage).Take(choicesPerPage).ToList();
|
|
var staticChoices = allStaticChoices.Where(x => !(pIndex <= 1 && x.IsPreviousButton));
|
|
Console.SetCursorPosition(Console.CursorLeft, menuStart);
|
|
|
|
currentChoices = fluidChoices.Concat(staticChoices).ToList();
|
|
WriteChoiceList(currentChoices);
|
|
|
|
if (_bindToOpenFrame && CloseFrame)
|
|
{
|
|
try
|
|
{
|
|
var offset = ConsoleTUI.OpenFrame.DisplayOffset;
|
|
if (pages == 1)
|
|
ConsoleTUI.OpenFrame.Close();
|
|
else
|
|
ConsoleTUI.OpenFrame.Close($"Page {pIndex}/{pages}");
|
|
Console.WriteLine(new string(' ', offset) + "Use the arrow keys to navigate");
|
|
} catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
Console.ReadLine();
|
|
}
|
|
}
|
|
|
|
|
|
Console.SetCursorPosition(Console.CursorLeft, menuStart);
|
|
|
|
currentValidChoices = currentChoices.Where(x => x.IsEnabled).ToList();
|
|
index = 0;
|
|
validIndex = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (keyPressed == ConsoleKey.Enter)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CloseFrame)
|
|
Frame.Clear();
|
|
|
|
Console.CursorVisible = visCache;
|
|
|
|
return currentValidChoices[validIndex].ReturnValue;
|
|
}
|
|
|
|
private bool NextButtonPresent()
|
|
{
|
|
return Choices.Any(x => x.IsNextButton);
|
|
}
|
|
|
|
private bool PreviousButtonPresent()
|
|
{
|
|
return Choices.Any(x => x.IsNextButton);
|
|
}
|
|
|
|
|
|
public class MenuItem
|
|
{
|
|
public object ReturnValue { get; set; }
|
|
|
|
public static readonly MenuItem Blank = new MenuItem("", null);
|
|
public static readonly MenuItem BlankStatic = new MenuItem("", null) { IsStatic = true };
|
|
|
|
public bool IsNextButton { get; set; } = false;
|
|
public bool IsPreviousButton { get; set; } = false;
|
|
|
|
public bool IsStatic { get; set; } = false;
|
|
|
|
public string PrimaryText { get; set; } = "";
|
|
public string SecondaryText { get; set; } = "";
|
|
|
|
|
|
public MenuItem(string primaryText, object returnValue)
|
|
{
|
|
PrimaryText = primaryText;
|
|
ReturnValue = returnValue;
|
|
}
|
|
|
|
public bool AddBetweenSpace { get; set; } = true;
|
|
|
|
private ConsoleColor? _primaryTextForeground = null;
|
|
|
|
public ConsoleColor? PrimaryTextForeground
|
|
{
|
|
get
|
|
{
|
|
if (_primaryTextForeground.HasValue)
|
|
return _primaryTextForeground;
|
|
if (IsEnabled) return null;
|
|
if (Console.ForegroundColor == ConsoleColor.White) return ConsoleColor.DarkGray;
|
|
if (Console.ForegroundColor == ConsoleColor.White) return ConsoleColor.DarkGray;
|
|
return null;
|
|
}
|
|
set { _primaryTextForeground = value; }
|
|
}
|
|
|
|
public ConsoleColor? PrimaryTextBackground { get; set; } = null;
|
|
public ConsoleColor? SecondaryTextForeground { get; set; } = null;
|
|
public ConsoleColor? SecondaryTextBackground { get; set; } = null;
|
|
|
|
private bool? _isEnabled = null;
|
|
|
|
public bool IsEnabled
|
|
{
|
|
get
|
|
{
|
|
if (_isEnabled.HasValue)
|
|
return _isEnabled.Value;
|
|
if (!String.IsNullOrEmpty(PrimaryText) || !String.IsNullOrEmpty(SecondaryText))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
set { _isEnabled = value; }
|
|
}
|
|
|
|
public bool StretchSelection = false;
|
|
|
|
public void Write()
|
|
{
|
|
if (!String.IsNullOrEmpty(PrimaryText))
|
|
{
|
|
ConsoleUtils.SetColor(PrimaryTextForeground, PrimaryTextBackground);
|
|
Console.Write(PrimaryText);
|
|
ConsoleUtils.ResetColor();
|
|
|
|
if (AddBetweenSpace)
|
|
Console.Write(' ');
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(SecondaryText))
|
|
return;
|
|
|
|
ConsoleUtils.SetColor(SecondaryTextForeground, SecondaryTextBackground);
|
|
Console.Write(SecondaryText);
|
|
ConsoleUtils.ResetColor();
|
|
}
|
|
|
|
public void WriteSelected(ConsoleColor? foreground, ConsoleColor? background)
|
|
{
|
|
Console.SetCursorPosition(Console.CursorLeft - 2, Console.CursorTop);
|
|
|
|
if (!String.IsNullOrEmpty(PrimaryText))
|
|
{
|
|
ConsoleUtils.SetColor(foreground, background);
|
|
Console.Write("> " + PrimaryText);
|
|
|
|
if (!StretchSelection)
|
|
ConsoleUtils.ResetColor();
|
|
|
|
if (AddBetweenSpace)
|
|
Console.Write(' ');
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(SecondaryText))
|
|
{
|
|
if (StretchSelection)
|
|
ConsoleUtils.ResetColor();
|
|
return;
|
|
}
|
|
|
|
if (!String.IsNullOrEmpty(PrimaryText) && !StretchSelection)
|
|
ConsoleUtils.SetColor(SecondaryTextForeground, SecondaryTextBackground);
|
|
|
|
Console.Write(SecondaryText);
|
|
ConsoleUtils.ResetColor();
|
|
}
|
|
}
|
|
}
|
|
}
|