using NStack;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Terminal.Gui;
namespace UICatalog {
///
/// Main program for the Terminal.gui UI Catalog app. This app provides a chooser that allows
/// for a calalog of UI demos, examples, and tests.
///
internal class Program {
private static Toplevel _top;
private static MenuBar _menu;
private static int _nameColumnWidth;
private static Window _leftPane;
private static List _categories;
private static ListView _categoryListView;
private static Window _rightPane;
private static List _scenarios;
private static ListView _scenarioListView;
private static StatusBar _statusBar;
private static Scenario _selectedScenario = null;
static void Main (string [] args)
{
if (Debugger.IsAttached)
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
_scenarios = Scenario.GetDerivedClassesCollection ().OrderBy (t => Scenario.ScenarioMetadata.GetName (t)).ToList();
if (args.Length > 0) {
var item = _scenarios.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals (args [0], StringComparison.OrdinalIgnoreCase));
_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item]);
_selectedScenario.Init (Application.Top);
_selectedScenario.Setup ();
_selectedScenario.Run ();
_selectedScenario = null;
return;
}
Scenario scenario = GetScenarioToRun ();
while (scenario != null) {
scenario.Init (Application.Top);
scenario.Setup ();
scenario.Run ();
scenario = GetScenarioToRun ();
}
}
///
/// Create all controls. This gets called once and the controls remain with their state between Sceanrio runs.
///
private static void Setup ()
{
_menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_File", new MenuItem [] {
new MenuItem ("_Quit", "", () => Application.RequestStop() )
}),
new MenuBarItem ("_About...", "About this app", () => MessageBox.Query (0, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")),
});
_leftPane = new Window ("Categories") {
X = 0,
Y = 1, // for menu
Width = 25,
Height = Dim.Fill (),
CanFocus = false,
};
_categories = Scenario.GetAllCategories ().OrderBy(c => c).ToList();
_categoryListView = new ListView (_categories) {
X = 1,
Y = 0,
Width = Dim.Fill (0),
Height = Dim.Fill (2),
AllowsMarking = false,
CanFocus = true,
};
_categoryListView.OpenSelectedItem += (o, a) => {
_top.SetFocus (_rightPane);
};
_categoryListView.SelectedChanged += CategoryListView_SelectedChanged;
_leftPane.Add (_categoryListView);
_rightPane = new Window ("Scenarios") {
X = 25,
Y = 1, // for menu
Width = Dim.Fill (),
Height = Dim.Fill (),
CanFocus = false,
};
_nameColumnWidth = Scenario.ScenarioMetadata.GetName (_scenarios.OrderByDescending (t => Scenario.ScenarioMetadata.GetName (t).Length).FirstOrDefault ()).Length;
_scenarioListView = new ListView () {
X = 0,
Y = 0,
Width = Dim.Fill (0),
Height = Dim.Fill (0),
AllowsMarking = false,
CanFocus = true,
};
//_scenarioListView.OnKeyPress += (KeyEvent ke) => {
// if (_top.MostFocused == _scenarioListView && ke.Key == Key.Enter) {
// _scenarioListView_OpenSelectedItem (null, null);
// }
//};
_scenarioListView.OpenSelectedItem += _scenarioListView_OpenSelectedItem;
_rightPane.Add (_scenarioListView);
_categoryListView.SelectedItem = 0;
_categoryListView.OnSelectedChanged ();
_statusBar = new StatusBar (new StatusItem [] {
//new StatusItem(Key.F1, "~F1~ Help", () => Help()),
new StatusItem(Key.ControlQ, "~CTRL-Q~ Quit", () => {
if (_selectedScenario is null){
// This causes GetScenarioToRun to return null
_selectedScenario = null;
Application.RequestStop();
} else {
_selectedScenario.RequestStop();
}
}),
});
}
///
/// This shows the selection UI. Each time it is run, it calls Application.Init to reset everything.
///
///
private static Scenario GetScenarioToRun ()
{
Application.Init ();
if (_menu == null) {
Setup ();
}
_top = Application.Top;
_top.KeyUp += KeyUpHandler;
_top.Add (_menu);
_top.Add (_leftPane);
_top.Add (_rightPane);
_top.Add (_statusBar);
// HACK: There is no other way to SetFocus before Application.Run. See Issue #445
#if false
if (_runningScenario != null)
Application.Iteration += Application_Iteration;
#else
_top.Ready += (o, a) => {
if (_selectedScenario != null) {
_top.SetFocus (_rightPane);
_selectedScenario = null;
}
};
#endif
Application.Run (_top);
Application.Shutdown ();
return _selectedScenario;
}
#if false
private static void Application_Iteration (object sender, EventArgs e)
{
Application.Iteration -= Application_Iteration;
_top.SetFocus (_rightPane);
}
#endif
private static void _scenarioListView_OpenSelectedItem (object sender, EventArgs e)
{
if (_selectedScenario is null) {
var source = _scenarioListView.Source as ScenarioListDataSource;
_selectedScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]);
Application.RequestStop ();
}
}
internal class ScenarioListDataSource : IListDataSource {
public List Scenarios { get; set; }
public bool IsMarked (int item) => false;// Scenarios [item].IsMarked;
public int Count => Scenarios.Count;
public ScenarioListDataSource (List itemList) => Scenarios = itemList;
public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width)
{
container.Move (col, line);
// Equivalent to an interpolated string like $"{Scenarios[item].Name, -widtestname}"; if such a thing were possible
var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [item]));
RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width);
}
public void SetMark (int item, bool value)
{
}
// A slightly adapted method from: https://github.com/migueldeicaza/gui.cs/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461
private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width)
{
int used = 0;
int index = 0;
while (index < ustr.Length) {
(var rune, var size) = Utf8.DecodeRune (ustr, index, index - ustr.Length);
var count = Rune.ColumnWidth (rune);
if (used + count >= width) break;
driver.AddRune (rune);
used += count;
index += size;
}
while (used < width) {
driver.AddRune (' ');
used++;
}
}
public IList ToList ()
{
return Scenarios;
}
}
///
/// When Scenarios are running we need to override the behavior of the Menu
/// and Statusbar to enable Scenarios that use those (or related key input)
/// to not be impacted. Same as for tabs.
///
///
private static void KeyUpHandler (object sender, View.KeyEventEventArgs a)
{
if (_selectedScenario != null) {
//switch (ke.Key) {
//case Key.Esc:
// //_runningScenario.RequestStop ();
// break;
//case Key.Enter:
// break;
//}<
} else if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.Key == Key.BackTab) {
// BUGBUG: Work around Issue #434 by implementing our own TAB navigation
if (_top.MostFocused == _categoryListView)
_top.SetFocus (_rightPane);
else
_top.SetFocus (_leftPane);
}
}
private static void CategoryListView_SelectedChanged (object sender, ListViewItemEventArgs e)
{
var item = _categories [_categoryListView.SelectedItem];
List newlist;
if (item.Equals ("All")) {
newlist = _scenarios;
} else {
newlist = _scenarios.Where (t => Scenario.ScenarioCategory.GetCategories (t).Contains (item)).ToList ();
}
_scenarioListView.Source = new ScenarioListDataSource (newlist);
_scenarioListView.SelectedItem = 0;
}
}
}