Răsfoiți Sursa

Merge pull request #2141 from tig/uicat_refactor

Refactors UI Catalog to clean up old code and make more readable
Tig 2 ani în urmă
părinte
comite
117c74e90c

+ 17 - 11
UICatalog/Scenario.cs

@@ -73,14 +73,11 @@ namespace UICatalog {
 		/// Overrides that do not call the base.<see cref="Run"/>, must call <see cref="Application.Init"/> before creating any views or calling other Terminal.Gui APIs.
 		/// </para>
 		/// </remarks>
-		public virtual void Init(Toplevel top, ColorScheme colorScheme)
+		public virtual void Init (Toplevel top, ColorScheme colorScheme)
 		{
 			Application.Init ();
 
-			Top = top;
-			if (Top == null) {
-				Top = Application.Top;
-			}
+			Top = top != null ? top : Application.Top;
 
 			Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
 				X = 0,
@@ -177,7 +174,14 @@ namespace UICatalog {
 		/// <returns>list of category names</returns>
 		public List<string> GetCategories () => ScenarioCategory.GetCategories (this.GetType ());
 
-		public override string ToString () => $"{GetName (),-30}{GetDescription ()}";
+		private static int _maxScenarioNameLen = 30;
+
+		/// <summary>
+		/// Gets the Scenario Name + Description with the Description padded
+		/// based on the longest known Scenario name.
+		/// </summary>
+		/// <returns></returns>
+		public override string ToString () => $"{GetName ().PadRight(_maxScenarioNameLen)}{GetDescription ()}";
 
 		/// <summary>
 		/// Override this to implement the <see cref="Scenario"/> setup logic (create controls, etc...). 
@@ -232,12 +236,14 @@ namespace UICatalog {
 		/// Returns an instance of each <see cref="Scenario"/> defined in the project. 
 		/// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class
 		/// </summary>
-		public static List<Type> GetDerivedClasses<T> ()
+		public static List<Scenario> GetScenarios ()
 		{
-			List<Type> objects = new List<Type> ();
-			foreach (Type type in typeof (T).Assembly.GetTypes ()
-			 .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (T)))) {
-				objects.Add (type);
+			List<Scenario> objects = new List<Scenario> ();
+			foreach (Type type in typeof (Scenario).Assembly.ExportedTypes
+			 .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
+				var scenario = (Scenario)Activator.CreateInstance (type);
+				objects.Add (scenario);
+				_maxScenarioNameLen = Math.Max (_maxScenarioNameLen, scenario.GetName ().Length + 1);
 			}
 			return objects;
 		}

+ 1 - 4
UICatalog/Scenarios/AllViewsTester.cs

@@ -44,10 +44,7 @@ namespace UICatalog.Scenarios {
 		{
 			Application.Init ();
 
-			Top = top;
-			if (Top == null) {
-				Top = Application.Top;
-			}
+			Top = top != null ? top : Application.Top;
 
 			//Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
 			//	X = 0,

+ 0 - 1
UICatalog/Scenarios/BordersComparisons.cs

@@ -7,7 +7,6 @@ namespace UICatalog.Scenarios {
 	public class BordersComparisons : Scenario {
 		public override void Init (Toplevel top, ColorScheme colorScheme)
 		{
-			top.Dispose ();
 			Application.Init ();
 
 			top = Application.Top;

+ 1 - 11
UICatalog/Scenarios/Clipping.cs

@@ -11,19 +11,9 @@ namespace UICatalog.Scenarios {
 		{
 			Application.Init ();
 
-			Top = top;
-			if (Top == null) {
-				Top = Application.Top;
-			}
+			Top = top != null ? top : Application.Top;
 
 			Top.ColorScheme = Colors.Base;
-			//Win = new TopLevel($"CTRL-Q to Close - Scenario: {GetName ()}") {
-			//	X = 0,
-			//	Y = 0,
-			//	Width = Dim.Fill (),
-			//	Height = Dim.Fill ()
-			//};
-			//Top.Add (Win);
 		}
 
 		public override void Setup ()

+ 1 - 4
UICatalog/Scenarios/Editor.cs

@@ -35,10 +35,7 @@ namespace UICatalog.Scenarios {
 		public override void Init (Toplevel top, ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = top;
-			if (Top == null) {
-				Top = Application.Top;
-			}
+			Top = top != null ? top : Application.Top;
 
 			Win = new Window (_fileName ?? "Untitled") {
 				X = 0,

+ 1 - 1
UICatalog/Scenarios/Keys.cs

@@ -51,7 +51,7 @@ namespace UICatalog.Scenarios {
 		public override void Init (Toplevel top, ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = top;
+			Top = top != null ? top : Application.Top;
 
 			Win = new TestWindow ($"CTRL-Q to Close - Scenario: {GetName ()}") {
 				X = 0,

+ 12 - 9
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -3,6 +3,7 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
+using System.Text.Json.Nodes;
 using Terminal.Gui;
 using Attribute = Terminal.Gui.Attribute;
 
@@ -16,11 +17,13 @@ namespace UICatalog.Scenarios {
 		public CheckBox _allowMultipleCB;
 		public ListView _listView;
 
-		public List<Type> _scenarios = Scenario.GetDerivedClasses<Scenario>().OrderBy (t => Scenario.ScenarioMetadata.GetName (t)).ToList ();
+		public List<Scenario> _scenarios;
 
 		public override void Setup ()
 		{
-			_customRenderCB = new CheckBox ("Render with columns") {
+			_scenarios = Scenario.GetScenarios ().OrderBy (s => s.GetName ()).ToList ();
+
+			_customRenderCB = new CheckBox ("Use custom rendering") {
 				X = 0,
 				Y = 0,
 				Height = 1,
@@ -137,11 +140,11 @@ namespace UICatalog.Scenarios {
 		// This is basically the same implementation used by the UICatalog main window
 		internal class ScenarioListDataSource : IListDataSource {
 			int _nameColumnWidth = 30;
-			private List<Type> scenarios;
+			private List<Scenario> scenarios;
 			BitArray marks;
 			int count, len;
 
-			public List<Type> Scenarios {
+			public List<Scenario> Scenarios {
 				get => scenarios;
 				set {
 					if (value != null) {
@@ -163,14 +166,14 @@ namespace UICatalog.Scenarios {
 
 			public int Length => len;
 
-			public ScenarioListDataSource (List<Type> itemList) => Scenarios = itemList;
+			public ScenarioListDataSource (List<Scenario> itemList) => Scenarios = itemList;
 
 			public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0)
 			{
 				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, start);
+				var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenarios [item].GetName ());
+				RenderUstr (driver, $"{s} ({Scenarios [item].GetDescription ()})", col, line, width, start);
 			}
 
 			public void SetMark (int item, bool value)
@@ -187,8 +190,8 @@ namespace UICatalog.Scenarios {
 
 				int maxLength = 0;
 				for (int i = 0; i < scenarios.Count; i++) {
-					var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [i]));
-					var sc = $"{s}  {Scenario.ScenarioMetadata.GetDescription (Scenarios [i])}";
+					var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenarios [i].GetName ());
+					var sc = $"{s}  {Scenarios [i].GetDescription ()}";
 					var l = sc.Length;
 					if (l > maxLength) {
 						maxLength = l;

+ 1 - 5
UICatalog/Scenarios/Notepad.cs

@@ -14,11 +14,7 @@ namespace UICatalog.Scenarios {
 		public override void Init (Toplevel top, ColorScheme colorScheme)
 		{
 			Application.Init ();
-
-			Top = top;
-			if (Top == null) {
-				Top = Application.Top;
-			}
+			Top = top != null ? top : Application.Top;
 			Top.ColorScheme = Colors.Base;
 		}
 

+ 1 - 4
UICatalog/Scenarios/WindowsAndFrameViews.cs

@@ -10,10 +10,7 @@ namespace UICatalog.Scenarios {
 		{
 			Application.Init ();
 
-			Top = top;
-			if (Top == null) {
-				Top = Application.Top;
-			}
+			Top = top != null ? top : Application.Top;
 		}
 
 		public override void RequestStop ()

+ 111 - 245
UICatalog/UICatalog.cs

@@ -1,6 +1,5 @@
 using NStack;
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
@@ -46,56 +45,75 @@ namespace UICatalog {
 	/// UI Catalog is a comprehensive sample app and scenario library for <see cref="Terminal.Gui"/>
 	/// </summary>
 	public class UICatalogApp {
-		private static Toplevel _top;
-		private static MenuBar _menu;
 		private static int _nameColumnWidth;
 		private static FrameView _leftPane;
 		private static List<string> _categories;
 		private static ListView _categoryListView;
 		private static FrameView _rightPane;
-		private static List<Type> _scenarios;
+		private static List<Scenario> _scenarios;
 		private static ListView _scenarioListView;
 		private static StatusBar _statusBar;
 		private static StatusItem _capslock;
 		private static StatusItem _numlock;
 		private static StatusItem _scrolllock;
-		private static int _categoryListViewItem;
-		private static int _scenarioListViewItem;
 
-		private static Scenario _runningScenario = null;
+		// If set, holds the scenario the user selected
+		private static Scenario _selectedScenario = null;
+		
 		private static bool _useSystemConsole = false;
 		private static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
 		private static bool _heightAsBuffer = false;
 		private static bool _isFirstRunning = true;
 
+		// When a scenario is run, the main app is killed. These items
+		// are therefore cached so that when the scenario exits the
+		// main app UI can be restored to previous state
+		private static int _cachedScenarioIndex = 0;
+		private static int _cachedCategoryIndex = 0;
+
+		private static StringBuilder _aboutMessage;
+
 		static void Main (string [] args)
 		{
 			Console.OutputEncoding = Encoding.Default;
 
-			if (Debugger.IsAttached)
+			if (Debugger.IsAttached) {
 				CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
+			}
 
-			_scenarios = Scenario.GetDerivedClasses<Scenario> ().OrderBy (t => Scenario.ScenarioMetadata.GetName (t)).ToList ();
+			_scenarios = Scenario.GetScenarios ();
 
 			if (args.Length > 0 && args.Contains ("-usc")) {
 				_useSystemConsole = true;
 				args = args.Where (val => val != "-usc").ToArray ();
 			}
 			if (args.Length > 0) {
-				var item = _scenarios.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals (args [0], StringComparison.OrdinalIgnoreCase));
-				_runningScenario = (Scenario)Activator.CreateInstance (_scenarios [item]);
+				var item = _scenarios.FindIndex (s => s.GetName ().Equals (args [0], StringComparison.OrdinalIgnoreCase));
+				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ());
 				Application.UseSystemConsole = _useSystemConsole;
 				Application.Init ();
-				_runningScenario.Init (Application.Top, _baseColorScheme);
-				_runningScenario.Setup ();
-				_runningScenario.Run ();
-				_runningScenario = null;
+				_selectedScenario.Init (Application.Top, _colorScheme);
+				_selectedScenario.Setup ();
+				_selectedScenario.Run ();
+				_selectedScenario = null;
 				Application.Shutdown ();
 				return;
 			}
 
+			_aboutMessage = new StringBuilder ();
+			_aboutMessage.AppendLine (@"A comprehensive sample library for");
+			_aboutMessage.AppendLine (@"");
+			_aboutMessage.AppendLine (@"  _______                  _             _   _____       _  ");
+			_aboutMessage.AppendLine (@" |__   __|                (_)           | | / ____|     (_) ");
+			_aboutMessage.AppendLine (@"    | | ___ _ __ _ __ ___  _ _ __   __ _| || |  __ _   _ _  ");
+			_aboutMessage.AppendLine (@"    | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | | ");
+			_aboutMessage.AppendLine (@"    | |  __/ |  | | | | | | | | | | (_| | || |__| | |_| | | ");
+			_aboutMessage.AppendLine (@"    |_|\___|_|  |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_| ");
+			_aboutMessage.AppendLine (@"");
+			_aboutMessage.AppendLine (@"https://github.com/gui-cs/Terminal.Gui");
+
 			Scenario scenario;
-			while ((scenario = GetScenarioToRun ()) != null) {
+			while ((scenario = SelectScenario ()) != null) {
 #if DEBUG_IDISPOSABLE
 				// Validate there are no outstanding Responder-based instances 
 				// after a scenario was selected to run. This proves the main UI Catalog
@@ -106,18 +124,12 @@ namespace UICatalog {
 				Responder.Instances.Clear ();
 #endif
 
-				scenario.Init (Application.Top, _baseColorScheme);
+				scenario.Init (Application.Top, _colorScheme);
 				scenario.Setup ();
 				scenario.Run ();
 
-				//static void LoadedHandler ()
-				//{
-				//	_rightPane.SetFocus ();
-				//	_top.Loaded -= LoadedHandler;
-				//}
-
-				//_top.Loaded += LoadedHandler;
-
+				// This call to Application.Shutdown brackets the Application.Init call
+				// made by Scenario.Init()
 				Application.Shutdown ();
 
 #if DEBUG_IDISPOSABLE
@@ -130,8 +142,6 @@ namespace UICatalog {
 #endif
 			}
 
-			Application.Shutdown ();
-
 #if DEBUG_IDISPOSABLE
 			// This proves that when the user exited the UI Catalog app
 			// it cleaned up properly.
@@ -143,31 +153,24 @@ namespace UICatalog {
 		}
 
 		/// <summary>
-		/// This shows the selection UI. Each time it is run, it calls Application.Init to reset everything.
+		/// Shows the UI Catalog selection UI. When the user selects a Scenario to run, the
+		/// UI Catalog main app UI is killed and the Scenario is run as though it were Application.Top. 
+		/// When the Scenario exits, this function exits.
 		/// </summary>
 		/// <returns></returns>
-		private static Scenario GetScenarioToRun ()
+		private static Scenario SelectScenario ()
 		{
 			Application.UseSystemConsole = _useSystemConsole;
 			Application.Init ();
+			if (_colorScheme == null) {
+				// `Colors` is not initilized until the ConsoleDriver is loaded by 
+				// Application.Init. Set it only the first time though so it is
+				// preserved between running multiple Scenarios
+				_colorScheme = Colors.Base;
+			}
 			Application.HeightAsBuffer = _heightAsBuffer;
 
-			// Set this here because not initialized until driver is loaded
-			_baseColorScheme = Colors.Base;
-
-			StringBuilder aboutMessage = new StringBuilder ();
-			aboutMessage.AppendLine (@"A comprehensive sample library for");
-			aboutMessage.AppendLine (@"");
-			aboutMessage.AppendLine (@"  _______                  _             _   _____       _  ");
-			aboutMessage.AppendLine (@" |__   __|                (_)           | | / ____|     (_) ");
-			aboutMessage.AppendLine (@"    | | ___ _ __ _ __ ___  _ _ __   __ _| || |  __ _   _ _  ");
-			aboutMessage.AppendLine (@"    | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | | ");
-			aboutMessage.AppendLine (@"    | |  __/ |  | | | | | | | | | | (_| | || |__| | |_| | | ");
-			aboutMessage.AppendLine (@"    |_|\___|_|  |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_| ");
-			aboutMessage.AppendLine (@"");
-			aboutMessage.AppendLine (@"https://github.com/gui-cs/Terminal.Gui");
-
-			_menu = new MenuBar (new MenuBarItem [] {
+			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Quit", "Quit UI Catalog", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
 				}),
@@ -177,7 +180,7 @@ namespace UICatalog {
 					new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://gui-cs.github.io/Terminal.Gui/articles/overview.html"), null, null, Key.F1),
 					new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
 					new MenuItem ("_About...",
-						"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
+						"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
 				}),
 			});
 
@@ -186,7 +189,7 @@ namespace UICatalog {
 				Y = 1, // for menu
 				Width = 25,
 				Height = Dim.Fill (1),
-				CanFocus = false,
+				CanFocus = true,
 				Shortcut = Key.CtrlMask | Key.C
 			};
 			_leftPane.Title = $"{_leftPane.Title} ({_leftPane.ShortcutTag})";
@@ -218,7 +221,7 @@ namespace UICatalog {
 			_rightPane.Title = $"{_rightPane.Title} ({_rightPane.ShortcutTag})";
 			_rightPane.ShortcutAction = () => _rightPane.SetFocus ();
 
-			_nameColumnWidth = Scenario.ScenarioMetadata.GetName (_scenarios.OrderByDescending (t => Scenario.ScenarioMetadata.GetName (t).Length).FirstOrDefault ()).Length;
+			_nameColumnWidth = _scenarios.OrderByDescending (s => s.GetName ().Length).FirstOrDefault ().GetName ().Length;
 
 			_scenarioListView = new ListView () {
 				X = 0,
@@ -232,9 +235,6 @@ namespace UICatalog {
 			_scenarioListView.OpenSelectedItem += _scenarioListView_OpenSelectedItem;
 			_rightPane.Add (_scenarioListView);
 
-			_categoryListView.SelectedItem = _categoryListViewItem;
-			_categoryListView.OnSelectedChanged ();
-
 			_capslock = new StatusItem (Key.CharMask, "Caps", null);
 			_numlock = new StatusItem (Key.CharMask, "Num", null);
 			_scrolllock = new StatusItem (Key.CharMask, "Scroll", null);
@@ -247,60 +247,73 @@ namespace UICatalog {
 				_numlock,
 				_scrolllock,
 				new StatusItem(Key.Q | Key.CtrlMask, "~CTRL-Q~ Quit", () => {
-					if (_runningScenario is null){
+					if (_selectedScenario is null){
 						// This causes GetScenarioToRun to return null
-						_runningScenario = null;
+						_selectedScenario = null;
 						Application.RequestStop();
 					} else {
-						_runningScenario.RequestStop();
+						_selectedScenario.RequestStop();
 					}
 				}),
 				new StatusItem(Key.F10, "~F10~ Hide/Show Status Bar", () => {
 					_statusBar.Visible = !_statusBar.Visible;
 					_leftPane.Height = Dim.Fill(_statusBar.Visible ? 1 : 0);
 					_rightPane.Height = Dim.Fill(_statusBar.Visible ? 1 : 0);
-					_top.LayoutSubviews();
-					_top.SetChildNeedsDisplay();
+					Application.Top.LayoutSubviews();
+					Application.Top.SetChildNeedsDisplay();
 				}),
 				new StatusItem (Key.CharMask, Application.Driver.GetType ().Name, null),
 			};
 
-			SetColorScheme ();
-			_top = Application.Top;
-			_top.KeyDown += KeyDownHandler;
-			_top.Add (_menu);
-			_top.Add (_leftPane);
-			_top.Add (_rightPane);
-			_top.Add (_statusBar);
-
-			void TopHandler () {
-				if (_runningScenario != null) {
-					_runningScenario = null;
+			Application.Top.ColorScheme = _colorScheme;
+			Application.Top.KeyDown += KeyDownHandler;
+			Application.Top.Add (menu);
+			Application.Top.Add (_leftPane);
+			Application.Top.Add (_rightPane);
+			Application.Top.Add (_statusBar);
+
+			void TopHandler ()
+			{
+				if (_selectedScenario != null) {
+					_selectedScenario = null;
 					_isFirstRunning = false;
 				}
 				if (!_isFirstRunning) {
 					_rightPane.SetFocus ();
 				}
-				_top.Loaded -= TopHandler;
+				Application.Top.Loaded -= TopHandler;
+			}
+			Application.Top.Loaded += TopHandler;
+
+			// Restore previous selections
+			_categoryListView.SelectedItem = _cachedCategoryIndex;
+			_scenarioListView.SelectedItem = _cachedScenarioIndex;
+
+			// Run UI Catalog UI. When it exits, if _selectedScenario is != null then
+			// a Scenario was selected. Otherwise, the user wants to exit UI Catalog.
+			Application.Run (Application.Top);
+			Application.Shutdown ();
+
+			return _selectedScenario;
+		}
+
+
+		/// <summary>
+		/// Launches the selected scenario, setting the global _selectedScenario
+		/// </summary>
+		/// <param name="e"></param>
+		private static void _scenarioListView_OpenSelectedItem (EventArgs e)
+		{
+			if (_selectedScenario is null) {
+				// Save selected item state
+				_cachedCategoryIndex = _categoryListView.SelectedItem;
+				_cachedScenarioIndex = _scenarioListView.SelectedItem;
+				// Create new instance of scenario (even though Scenarios contains instances)
+				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarioListView.Source.ToList () [_scenarioListView.SelectedItem].GetType ());
+
+				// Tell the main app to stop
+				Application.RequestStop ();
 			}
-			_top.Loaded += TopHandler;
-			// The following code was moved to the TopHandler event
-			//  because in the MainLoop.EventsPending (wait)
-			//  from the Application.RunLoop with the WindowsDriver
-			//  the OnReady event is triggered due the Focus event.
-			//  On CursesDriver and NetDriver the focus event won't be triggered
-			//  and if it's possible I don't know how to do it.
-			//void ReadyHandler ()
-			//{
-			//	if (!_isFirstRunning) {
-			//		_rightPane.SetFocus ();
-			//	}
-			//	_top.Ready -= ReadyHandler;
-			//}
-			//_top.Ready += ReadyHandler;
-
-			Application.Run (_top);
-			return _runningScenario;
 		}
 
 		static List<MenuItem []> CreateDiagnosticMenuItems ()
@@ -329,7 +342,7 @@ namespace UICatalog {
 
 			return menuItems.ToArray ();
 		}
-		private static MenuItem[] CreateKeybindings()
+		private static MenuItem [] CreateKeybindings ()
 		{
 
 			List<MenuItem> menuItems = new List<MenuItem> ();
@@ -410,7 +423,7 @@ namespace UICatalog {
 						}
 					}
 					ConsoleDriver.Diagnostics = _diagnosticFlags;
-					_top.SetNeedsDisplay ();
+					Application.Top.SetNeedsDisplay ();
 				};
 				menuItems.Add (item);
 			}
@@ -462,52 +475,9 @@ namespace UICatalog {
 					break;
 				}
 			}
-
-			//MenuItem CheckedMenuMenuItem (ustring menuItem, Action action, Func<bool> checkFunction)
-			//{
-			//	var mi = new MenuItem ();
-			//	mi.Title = menuItem;
-			//	mi.Shortcut = Key.AltMask + index.ToString () [0];
-			//	index++;
-			//	mi.CheckType |= MenuItemCheckStyle.Checked;
-			//	mi.Checked = checkFunction ();
-			//	mi.Action = () => {
-			//		action?.Invoke ();
-			//		mi.Title = menuItem;
-			//		mi.Checked = checkFunction ();
-			//	};
-			//	return mi;
-			//}
-
-			//return new MenuItem [] {
-			//	CheckedMenuMenuItem ("Use _System Console",
-			//		() => {
-			//			_useSystemConsole = !_useSystemConsole;
-			//		},
-			//		() => _useSystemConsole),
-			//	CheckedMenuMenuItem ("Diagnostics: _Frame Padding",
-			//		() => {
-			//			ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding;
-			//			_top.SetNeedsDisplay ();
-			//		},
-			//		() => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FramePadding) == ConsoleDriver.DiagnosticFlags.FramePadding),
-			//	CheckedMenuMenuItem ("Diagnostics: Frame _Ruler",
-			//		() => {
-			//			ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler;
-			//			_top.SetNeedsDisplay ();
-			//		},
-			//		() => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler),
-			//};
-		}
-
-		static void SetColorScheme ()
-		{
-			_leftPane.ColorScheme = _baseColorScheme;
-			_rightPane.ColorScheme = _baseColorScheme;
-			_top?.SetNeedsDisplay ();
 		}
 
-		static ColorScheme _baseColorScheme;
+		static ColorScheme _colorScheme;
 		static MenuItem [] CreateColorSchemeMenuItems ()
 		{
 			List<MenuItem> menuItems = new List<MenuItem> ();
@@ -516,12 +486,12 @@ namespace UICatalog {
 				item.Title = $"_{sc.Key}";
 				item.Shortcut = Key.AltMask | (Key)sc.Key.Substring (0, 1) [0];
 				item.CheckType |= MenuItemCheckStyle.Radio;
-				item.Checked = sc.Value == _baseColorScheme;
+				item.Checked = sc.Value == _colorScheme;
 				item.Action += () => {
-					_baseColorScheme = sc.Value;
-					SetColorScheme ();
+					Application.Top.ColorScheme = _colorScheme = sc.Value;
+					Application.Top?.SetNeedsDisplay ();
 					foreach (var menuItem in menuItems) {
-						menuItem.Checked = menuItem.Title.Equals ($"_{sc.Key}") && sc.Value == _baseColorScheme;
+						menuItem.Checked = menuItem.Title.Equals ($"_{sc.Key}") && sc.Value == _colorScheme;
 					}
 				};
 				menuItems.Add (item);
@@ -529,106 +499,8 @@ namespace UICatalog {
 			return menuItems.ToArray ();
 		}
 
-		private static void _scenarioListView_OpenSelectedItem (EventArgs e)
-		{
-			if (_runningScenario is null) {
-				_scenarioListViewItem = _scenarioListView.SelectedItem;
-				var source = _scenarioListView.Source as ScenarioListDataSource;
-				_runningScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]);
-				Application.RequestStop ();
-			}
-		}
-
-		internal class ScenarioListDataSource : IListDataSource {
-			private readonly int len;
-
-			public List<Type> Scenarios { get; set; }
-
-			public bool IsMarked (int item) => false;
-
-			public int Count => Scenarios.Count;
-
-			public int Length => len;
-
-			public ScenarioListDataSource (List<Type> itemList)
-			{
-				Scenarios = itemList;
-				len = GetMaxLengthItem ();
-			}
-
-			public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0)
-			{
-				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, start);
-			}
-
-			public void SetMark (int item, bool value)
-			{
-			}
-
-			int GetMaxLengthItem ()
-			{
-				if (Scenarios?.Count == 0) {
-					return 0;
-				}
-
-				int maxLength = 0;
-				for (int i = 0; i < Scenarios.Count; i++) {
-					var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [i]));
-					var sc = $"{s}  {Scenario.ScenarioMetadata.GetDescription (Scenarios [i])}";
-					var l = sc.Length;
-					if (l > maxLength) {
-						maxLength = l;
-					}
-				}
-
-				return maxLength;
-			}
-
-			// A slightly adapted method from: https://github.com/gui-cs/Terminal.Gui/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461
-			private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width, int start = 0)
-			{
-				int used = 0;
-				int index = start;
-				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;
-			}
-		}
-
-		/// <summary>
-		/// 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.
-		/// </summary>
-		/// <param name="ke"></param>
 		private static void KeyDownHandler (View.KeyEventEventArgs a)
 		{
-			//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);
-			//}
-
 			if (a.KeyEvent.IsCapslock) {
 				_capslock.Title = "Caps: On";
 				_statusBar.SetNeedsDisplay ();
@@ -656,22 +528,16 @@ namespace UICatalog {
 
 		private static void CategoryListView_SelectedChanged (ListViewItemEventArgs e)
 		{
-			if (_categoryListViewItem != _categoryListView.SelectedItem) {
-				_scenarioListViewItem = 0;
-			}
-			_categoryListViewItem = _categoryListView.SelectedItem;
-			var item = _categories [_categoryListViewItem];
-			List<Type> newlist;
-			if (_categoryListViewItem == 0) {
+			var item = _categories [e.Item];
+			List<Scenario> newlist;
+			if (e.Item == 0) {
 				// First category is "All"
 				newlist = _scenarios;
 
 			} else {
-				newlist = _scenarios.Where (t => Scenario.ScenarioCategory.GetCategories (t).Contains (item)).ToList ();
+				newlist = _scenarios.Where (s => s.GetCategories ().Contains (item)).ToList ();
 			}
-			_scenarioListView.Source = new ScenarioListDataSource (newlist);
-			_scenarioListView.SelectedItem = _scenarioListViewItem;
-
+			_scenarioListView.SetSource (newlist.ToList ());
 		}
 
 		private static void OpenUrl (string url)

+ 11 - 13
UnitTests/ScenarioTests.cs

@@ -49,19 +49,18 @@ namespace Terminal.Gui {
 		[Fact]
 		public void Run_All_Scenarios ()
 		{
-			List<Type> scenarioClasses = Scenario.GetDerivedClasses<Scenario> ();
-			Assert.NotEmpty (scenarioClasses);
+			List<Scenario> scenarios = Scenario.GetScenarios ();
+			Assert.NotEmpty (scenarios);
 
-			foreach (var scenarioClass in scenarioClasses) {
+			foreach (var scenario in scenarios) {
 
-				output.WriteLine ($"Running Scenario '{scenarioClass.Name}'");
+				output.WriteLine ($"Running Scenario '{scenario}'");
 
 				Func<MainLoop, bool> closeCallback = (MainLoop loop) => {
 					Application.RequestStop ();
 					return false;
 				};
 
-				var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
 				Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
 
 				// Close after a short period of time
@@ -83,11 +82,11 @@ namespace Terminal.Gui {
 		[Fact]
 		public void Run_Generic ()
 		{
-			List<Type> scenarioClasses = Scenario.GetDerivedClasses<Scenario> ();
-			Assert.NotEmpty (scenarioClasses);
+			List<Scenario> scenarios = Scenario.GetScenarios ();
+			Assert.NotEmpty (scenarios);
 
-			var item = scenarioClasses.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals ("Generic", StringComparison.OrdinalIgnoreCase));
-			var scenarioClass = scenarioClasses [item];
+			var item = scenarios.FindIndex (s => s.GetName ().Equals ("Generic", StringComparison.OrdinalIgnoreCase));
+			var generic = scenarios [item];
 			// Setup some fake keypresses 
 			// Passing empty string will cause just a ctrl-q to be fired
 			int stackSize = CreateInput ("");
@@ -116,13 +115,12 @@ namespace Terminal.Gui {
 				Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key);
 			};
 
-			var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
-			scenario.Init (Application.Top, Colors.Base);
-			scenario.Setup ();
+			generic.Init (Application.Top, Colors.Base);
+			generic.Setup ();
 			// There is no need to call Application.Begin because Init already creates the Application.Top
 			// If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run.
 			//var rs = Application.Begin (Application.Top);
-			scenario.Run ();
+			generic.Run ();
 
 			//Application.End (rs);