Merge branch 'uicatalog_tweaks' into docs

This commit is contained in:
Charlie Kindel
2020-05-23 18:44:55 -06:00
11 changed files with 276 additions and 81 deletions

View File

@@ -12,7 +12,7 @@ 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.
/// </summary>
class Program {
internal class Program {
private static Toplevel _top;
private static MenuBar _menu;
private static int _nameColumnWidth;
@@ -24,34 +24,33 @@ namespace UICatalog {
private static ListView _scenarioListView;
private static StatusBar _statusBar;
private static Scenario _runningScenario = null;
private static Scenario _selectedScenario = null;
static void Main (string [] args)
{
if (Debugger.IsAttached)
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
_scenarios = Scenario.GetDerivedClassesCollection ().ToList ();
_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));
_runningScenario = (Scenario)Activator.CreateInstance (_scenarios [item]);
Application.Init ();
_runningScenario.Init (Application.Top);
_runningScenario.Setup ();
_runningScenario.Run ();
_runningScenario = null;
_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item]);
_selectedScenario.Init (Application.Top);
_selectedScenario.Setup ();
_selectedScenario.Run ();
_selectedScenario = null;
return;
}
Scenario scenario = GetScenarioToRun ();
while (scenario != null) {
Application.Init ();
scenario.Init (Application.Top);
scenario.Setup ();
scenario.Run ();
scenario = GetScenarioToRun ();
}
}
/// <summary>
@@ -75,7 +74,7 @@ namespace UICatalog {
};
_categories = Scenario.GetAllCategories ();
_categories = Scenario.GetAllCategories ().OrderBy(c => c).ToList();
_categoryListView = new ListView (_categories) {
X = 1,
Y = 0,
@@ -125,12 +124,12 @@ namespace UICatalog {
_statusBar = new StatusBar (new StatusItem [] {
//new StatusItem(Key.F1, "~F1~ Help", () => Help()),
new StatusItem(Key.ControlQ, "~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();
}
}),
});
@@ -163,15 +162,16 @@ namespace UICatalog {
Application.Iteration += Application_Iteration;
#else
_top.Ready += (o, a) => {
if (_runningScenario != null) {
if (_selectedScenario != null) {
_top.SetFocus (_rightPane);
_runningScenario = null;
_selectedScenario = null;
}
};
#endif
Application.Run (_top);
return _runningScenario;
Application.Shutdown ();
return _selectedScenario;
}
#if false
@@ -183,9 +183,9 @@ namespace UICatalog {
#endif
private static void _scenarioListView_OpenSelectedItem (object sender, EventArgs e)
{
if (_runningScenario is null) {
if (_selectedScenario is null) {
var source = _scenarioListView.Source as ScenarioListDataSource;
_runningScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]);
_selectedScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]);
Application.RequestStop ();
}
}
@@ -246,7 +246,7 @@ namespace UICatalog {
/// <param name="ke"></param>
private static void KeyUpHandler (object sender, View.KeyEventEventArgs a)
{
if (_runningScenario != null) {
if (_selectedScenario != null) {
//switch (ke.Key) {
//case Key.Esc:
// //_runningScenario.RequestStop ();

View File

@@ -6,40 +6,54 @@ using Terminal.Gui;
namespace UICatalog {
/// <summary>
/// Base class for each demo/scenario. To define a new scenario simply
/// Base class for each demo/scenario. To define a new <see cref="Scenario"/> simply
///
/// 1) declare a class derived from Scenario,
/// 2) Set Name and Description as appropriate using [ScenarioMetadata] attribute
/// 3) Set one or more categories with the [ScenarioCategory] attribute
/// 1) declare a class derived from <see cref="Scenario"/>,
/// 2) Set Name and Description as appropriate using [<see cref="ScenarioMetadata"/>] attribute
/// 3) Set one or more categories with the [<see cref="ScenarioCatagory"/>] attribute
/// 4) Implement Setup.
/// 5) Optionally, implement Run.
/// 5) Optionally, implement <see cref="Init"/> and/or <see cref="Run"/>.
///
/// The Main program uses reflection to find all scenarios and adds them to the
/// ListViews. Press ENTER to run the selected scenario. Press CTRL-Q to exit it.
/// This program uses reflection to find all scenarios and adds them to the
/// ListViews. Press ENTER to run the selected <see cref="Scenario"/>. Press CTRL-Q to exit it.
/// </summary>
public class Scenario : IDisposable {
private bool _disposedValue;
/// <summary>
/// The Top level for the Scenario. This should be set to `Application.Top` in most cases.
/// The <see cref="Toplevel"/> for the <see cref="Scenario"/>. This should be set to <see cref="Application.Top"/> in most cases.
/// </summary>
public Toplevel Top { get; set; }
/// <summary>
/// The Window for the Scenario. This should be set within the `Application.Top` in most cases.
/// The <see cref="Window"/> for the <see cref="Scenario"/>. This should be set within <see cref="Application.Top"/>` in most cases.
/// </summary>
public Window Win { get; set; }
/// <summary>
/// Helper that provides the default Window implementation with a frame and
/// label showing the name of the Scenario and logic to exit back to
/// the Scenario picker UI.
/// Helper that provides the default <see cref="Window"/> implementation with a frame and
/// label showing the name of the <see cref="Scenario"/> and logic to exit back to
/// the <see cref="Scenario"/> picker UI.
/// Override Init to provide any `Toplevel` behavior needed.
/// </summary>
/// <param name="top"></param>
/// <remarks>
/// <para>
/// Thg base implementation calls <see cref="Application.Init"/>, sets <see cref="Top"/> to the passed in <see cref="Toplevel"/>, creates a <see cref="Window"/> for <see cref="Win"/> and adds it to <see cref="Top"/>.
/// </para>
/// <para>
/// 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)
{
Application.Init ();
Top = top;
if (Top == null) {
Top = Application.Top;
}
Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
X = 0,
Y = 0,
@@ -49,15 +63,18 @@ namespace UICatalog {
Top.Add (Win);
}
/// <summary>
/// Defines the metadata (Name and Description) for a <see cref="Scenario"/>
/// </summary>
[System.AttributeUsage (System.AttributeTargets.Class)]
public class ScenarioMetadata : System.Attribute {
/// <summary>
/// Scenario Name
/// <see cref="Scenario"/> Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Scenario Description
/// <see cref="Scenario"/> Description
/// </summary>
public string Description { get; set; }
@@ -68,14 +85,14 @@ namespace UICatalog {
}
/// <summary>
/// Static helper function to get the Scenario Name given a Type
/// Static helper function to get the <see cref="Scenario"/> Name given a Type
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public static string GetName (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Name;
/// <summary>
/// Static helper function to get the Scenario Description given a Type
/// Static helper function to get the <see cref="Scenario"/> Description given a Type
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
@@ -83,17 +100,20 @@ namespace UICatalog {
}
/// <summary>
/// Helper to get the Scenario Name
/// Helper to get the <see cref="Scenario"/> Name (defined in <see cref="ScenarioMetadata"/>)
/// </summary>
/// <returns></returns>
public string GetName () => ScenarioMetadata.GetName (this.GetType ());
/// <summary>
/// Helper to get the Scenario Description
/// Helper to get the <see cref="Scenario"/> Description (defined in <see cref="ScenarioMetadata"/>)
/// </summary>
/// <returns></returns>
public string GetDescription () => ScenarioMetadata.GetDescription (this.GetType ());
/// <summary>
/// Defines the category names used to catagorize a <see cref="Scenario"/>
/// </summary>
[System.AttributeUsage (System.AttributeTargets.Class, AllowMultiple = true)]
public class ScenarioCategory : System.Attribute {
/// <summary>
@@ -104,17 +124,17 @@ namespace UICatalog {
public ScenarioCategory (string Name) => this.Name = Name;
/// <summary>
/// Static helper function to get the Scenario Name given a Type
/// Static helper function to get the <see cref="Scenario"/> Name given a Type
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
/// <returns>Name of the catagory</returns>
public static string GetName (Type t) => ((ScenarioCategory)System.Attribute.GetCustomAttributes (t) [0]).Name;
/// <summary>
/// Static helper function to get the Scenario Categories given a Type
/// Static helper function to get the <see cref="Scenario"/> Categories given a Type
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
/// <returns>list of catagory names</returns>
public static List<string> GetCategories (Type t) => System.Attribute.GetCustomAttributes (t)
.ToList ()
.Where (a => a is ScenarioCategory)
@@ -123,30 +143,40 @@ namespace UICatalog {
}
/// <summary>
/// Helper function to get the Categories of a Scenario
/// Helper function to get the list of categories a <see cref="Scenario"/> belongs to (defined in <see cref="ScenarioCategory"/>)
/// </summary>
/// <returns></returns>
/// <returns>list of catagory names</returns>
public List<string> GetCategories () => ScenarioCategory.GetCategories (this.GetType ());
/// <inheritdoc cref="ToString"/>
public override string ToString () => $"{GetName (),-30}{GetDescription ()}";
/// <summary>
/// Override this to implement the Scenario setup logic (create controls, etc...).
/// Override this to implement the <see cref="Scenario"/> setup logic (create controls, etc...).
/// </summary>
/// <remarks>This is typically the best place to put scenario logic code.</remarks>
public virtual void Setup ()
{
}
/// <summary>
/// Runs the scenario. Override to start the scenario using a Top level different than `Top`.
/// Runs the <see cref="Scenario"/>. Override to start the <see cref="Scenario"/> using a <see cref="Toplevel"/> different than `Top`.
///
/// </summary>
/// <remarks>
/// Overrides that do not call the base.<see cref="Run"/>, must call <see cref="Application.Shutdown"/> before returning.
/// </remarks>
public virtual void Run ()
{
Application.Run (Top);
// Every call to Application.Init must be bound by a call to Shutdown
// or Init doesn't do anything
Application.Shutdown ();
}
/// <summary>
/// Stops the scenario. Override to implement shutdown behavior for the Scenario.
/// Stops the scenario. Override to change shutdown behavior for the <see cref="Scenario"/>.
/// </summary>
public virtual void RequestStop ()
{
@@ -154,13 +184,13 @@ namespace UICatalog {
}
/// <summary>
/// Returns a list of all Categories set by all of the scenarios defined in the project.
/// Returns a list of all Categories set by all of the <see cref="Scenario"/>s defined in the project.
/// </summary>
internal static List<string> GetAllCategories ()
{
List<string> categories = new List<string> () { "All" };
foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
.Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
.Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
List<System.Attribute> attrs = System.Attribute.GetCustomAttributes (type).ToList ();
categories = categories.Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name)).ToList ();
}
@@ -168,14 +198,14 @@ namespace UICatalog {
}
/// <summary>
/// Returns an instance of each Scenario defined in the project.
/// 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>
internal static List<Type> GetDerivedClassesCollection ()
{
List<Type> objects = new List<Type> ();
foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
.Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
.Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
objects.Add (type);
}
return objects;

View File

@@ -14,7 +14,7 @@ namespace UICatalog {
public override void Init (Toplevel top)
{
Top = top;
base.Init (top);
}
public override void Setup ()
@@ -144,7 +144,7 @@ namespace UICatalog {
public override void Run ()
{
Application.Run (Top);
base.Run ();
}
}
}

View File

@@ -12,12 +12,6 @@ namespace UICatalog {
private HexView _hexView;
private bool _saved = true;
public override void Init (Toplevel top)
{
Top = top;
}
public override void Setup ()
{
var menu = new MenuBar (new MenuBarItem [] {
@@ -145,10 +139,5 @@ namespace UICatalog {
sw.Write (sb.ToString ());
sw.Close ();
}
public override void Run ()
{
Application.Run (Top);
}
}
}

View File

@@ -50,6 +50,7 @@ namespace UICatalog {
public override void Init (Toplevel top)
{
Application.Init ();
Top = top;
Win = new TestWindow ($"CTRL-Q to Close - Scenario: {GetName ()}") {

View File

@@ -6,7 +6,7 @@ using Terminal.Gui;
using NStack;
namespace UICatalog.Scenarios {
[ScenarioMetadata (Name: "Lists", Description: "Demonstrates list selections")]
[ScenarioMetadata (Name: "ListView & ComboBox", Description: "Demonstrates a ListView populating a ComboBox that acts as a filter.")]
[ScenarioCategory ("Controls")]
class ListsAndCombos : Scenario {

View File

@@ -33,10 +33,5 @@ namespace UICatalog {
Top.Add (Win);
}
public override void Run ()
{
Application.Run (Top);
}
}
}

View File

@@ -6,8 +6,9 @@ namespace UICatalog {
//
// This would be a great scenario to show of threading (Issue #471)
//
[ScenarioMetadata (Name: "Progress", Description: "Shows off ProgressBar.")]
[ScenarioMetadata (Name: "Progress", Description: "Shows off ProgressBar and Threading")]
[ScenarioCategory ("Controls")]
[ScenarioCategory ("Threading")]
class Progress : Scenario {
private ProgressBar _activityProgressBar;
@@ -19,23 +20,28 @@ namespace UICatalog {
{
var pulseButton = new Button ("Pulse") {
X = Pos.Center (),
Y = Pos.Center () - 5,
Y = Pos.Center () - 3,
Clicked = () => Pulse ()
};
Win.Add (new Button ("Start Timer") {
X = Pos.Left(pulseButton) - 20,
var startButton = new Button ("Start Timer") {
Y = Pos.Y(pulseButton),
Clicked = () => Start ()
});
};
Win.Add (new Button ("Stop Timer") {
X = Pos.Right (pulseButton) + 20, // BUGBUG: Right is somehow adding additional width
var stopbutton = new Button ("Stop Timer") {
Y = Pos.Y (pulseButton),
Clicked = () => Stop()
});
};
// Center three buttons with 5 spaces between them
// TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
startButton.X = Pos.Left (pulseButton) - (Pos.Right (startButton) - Pos.Left (startButton)) - 5;
stopbutton.X = Pos.Right (pulseButton) + 5;
Win.Add (startButton);
Win.Add (pulseButton);
Win.Add (stopbutton);
_activityProgressBar = new ProgressBar () {
X = Pos.Center (),
@@ -84,9 +90,8 @@ namespace UICatalog {
_pulseProgressBar.Fraction = 0F;
_timer = new Timer ((o) => {
// BUGBUG: #409 - Invoke does not cause Wakeup as it should
Application.MainLoop.Invoke (() => Pulse ());
}, null, 0, 250);
}, null, 0, 20);
}
private void Stop ()

View File

@@ -0,0 +1,33 @@
using Terminal.Gui;
namespace UICatalog {
[ScenarioMetadata (Name: "System Console", Description: "Not working - #518 - Enables System Console and exercises things")]
[ScenarioCategory ("Bug Repro")]
[ScenarioCategory ("Console")]
class UseSystemConsole : Scenario {
public override void Init (Toplevel top)
{
Application.UseSystemConsole = true;
base.Init (top);
}
public override void RequestStop ()
{
base.RequestStop ();
}
public override void Run ()
{
base.Run ();
}
public override void Setup ()
{
Win.Add (new Button ("Press me!") {
X = Pos.Center (),
Y = Pos.Center (),
Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", "Yes", "No")
});
}
}
}

View File

@@ -34,7 +34,7 @@ namespace UICatalog {
};
ntop.Add (win);
Application.Run (ntop);
base.Run ();
}
}
}

View File

@@ -0,0 +1,142 @@
using System.Collections.Generic;
using System.Linq;
using Terminal.Gui;
namespace UICatalog {
[ScenarioMetadata (Name: "Windows & FrameViews", Description: "Shows Windows, sub-Windows, FrameViews, and how TAB doesn't work right (#434, #522)")]
[ScenarioCategory ("Views")]
class WindowExperiment : Scenario {
public override void Init (Toplevel top)
{
Application.Init ();
Top = top;
if (Top == null) {
Top = Application.Top;
}
}
public override void RequestStop ()
{
base.RequestStop ();
}
public override void Run ()
{
base.Run ();
}
public override void Setup ()
{
int margin = 3;
int padding = 1;
int height = 10;
var listWin = new List<View> ();
Win = new Window ($"{listWin.Count} - Scenario: {GetName ()}", padding) {
X = margin,
Y = margin,
Width = Dim.Fill (margin),
Height = height,
};
Win.ColorScheme = Colors.Dialog;
Win.Add (new Button ("Press me!") {
X = Pos.Center (),
Y = 0,
ColorScheme = Colors.Error,
Clicked = () => MessageBox.ErrorQuery (30, 10, Win.Title.ToString (), "Neat?", "Yes", "No")
});
Top.Add (Win);
listWin.Add (Win);
for (var i = 0; i < 2; i++) {
Window win = null;
win = new Window ($"{listWin.Count} - Scenario: {GetName ()}", padding) {
X = margin,
Y = Pos.Bottom(listWin.Last()) + (margin/2),
Width = Dim.Fill (margin),
Height = height,
};
win.ColorScheme = Colors.Dialog;
win.Add (new Button ("Press me!") {
X = Pos.Center (),
Y = 0,
ColorScheme = Colors.Error,
Clicked = () => MessageBox.ErrorQuery (30, 10, win.Title.ToString (), "Neat?", "Yes", "No")
});
var subWin = new Window("Sub Window") {
X = Pos.Percent (0),
Y = Pos.AnchorEnd() - 5,
Width = Dim.Percent (50),
Height = 5,
ColorScheme = Colors.Base,
};
subWin.Add (new TextField (win.Title.ToString ()));
win.Add (subWin);
var frameView = new FrameView ("This is a Sub-FrameView") {
X = Pos.Percent(50),
Y = Pos.AnchorEnd () - 5,
Width = Dim.Percent (100),
Height = 5,
ColorScheme = Colors.Base,
};
frameView.Add (new TextField ("Edit Me"));
win.Add (frameView);
Top.Add (win);
listWin.Add (win);
}
FrameView frame = null;
frame = new FrameView ($"This is a FrameView") {
X = margin,
Y = Pos.Bottom (listWin.Last ()) + (margin / 2),
Width = Dim.Fill (margin),
Height = height,
};
frame.ColorScheme = Colors.Dialog;
frame.Add (new Button ("Press me!") {
X = Pos.Center (),
Y = 0,
ColorScheme = Colors.Error,
Clicked = () => MessageBox.ErrorQuery (30, 10, frame.Title.ToString (), "Neat?", "Yes", "No")
});
var subWinFV = new Window ("this is a Sub-Window") {
X = Pos.Percent (0),
Y = Pos.AnchorEnd () - (height - 4),
Width = Dim.Percent (50),
Height = Dim.Fill () - 1,
ColorScheme = Colors.Base,
};
subWinFV.Add (new TextField (frame.Title.ToString ()));
frame.Add (subWinFV);
var frameViewFV = new FrameView ("this is a Sub-FrameView") {
X = Pos.Percent (50),
Y = Pos.AnchorEnd () - (height - 4),
Width = Dim.Percent (100),
Height = Dim.Fill() - 1,
ColorScheme = Colors.Base,
};
frameViewFV.Add (new TextField ("Edit Me"));
frameViewFV.Add (new CheckBox (0, 1, "Check me"));
// BUGBUG: This checkbox is not shown even though frameViewFV has 3 rows in
// it's client area. #522
frameViewFV.Add (new CheckBox (0, 2, "Or, Check me"));
frame.Add (new CheckBox ("No, Check me!") {
X = 0,
Y = Pos.AnchorEnd() - 1, // BUGBUG: #522 If I don't do the -1 it doesn't draw, but it should!
});
frame.Add (new CheckBox ("Really, Check me!") {
X = Pos.Percent(50),
Y = Pos.AnchorEnd () - 1, // BUGBUG: #522 If I don't do the -1 it doesn't draw, but it should!
});
frame.Add (frameViewFV);
Top.Add (frame);
listWin.Add (frame);
}
}
}