using NStack;
using System;
using System.Collections.Generic;
using System.Linq;
using Terminal.Gui;
namespace UICatalog {
///
/// Base class for each demo/scenario.
///
/// To define a new scenario:
///
/// - Create a new .cs file in the Scenarios directory that derives from .
/// - Annotate the derived class with a attribute specifying the scenario's name and description.
/// - Add one or more attributes to the class specifying which categories the scenario belongs to. If you don't specify a category the scenario will show up in "_All".
/// - Implement the override which will be called when a user selects the scenario to run.
/// - Optionally, implement the and/or overrides to provide a custom implementation.
///
///
///
/// The UI Catalog 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. /
///
///
///
/// The example below is provided in the `Scenarios` directory as a generic sample that can be copied and re-named:
///
/// using Terminal.Gui;
///
/// namespace UICatalog {
/// [ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")]
/// [ScenarioCategory ("Controls")]
/// class MyScenario : Scenario {
/// public override void Setup ()
/// {
/// // Put your scenario code here, e.g.
/// Win.Add (new Button ("Press me!") {
/// X = Pos.Center (),
/// Y = Pos.Center (),
/// Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", "Yes", "No")
/// });
/// }
/// }
/// }
///
///
public class Scenario : IDisposable {
private bool _disposedValue;
///
/// The Window for the . This should be set to in most cases.
///
public Window Win { get; set; }
///
/// Helper that provides the default implementation with a frame and
/// label showing the name of the and logic to exit back to
/// the Scenario picker UI.
/// Override to provide any behavior needed.
///
/// The colorscheme to use.
///
///
/// The base implementation calls and creates a for
/// and adds it to .
///
///
/// Overrides that do not call the base., must call
/// before creating any views or calling other Terminal.Gui APIs.
///
///
public virtual void Init (ColorScheme colorScheme)
{
Application.Init ();
Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
X = 0,
Y = 0,
Width = Dim.Fill (),
Height = Dim.Fill (),
ColorScheme = colorScheme,
};
Application.Top.Add (Win);
}
///
/// Defines the metadata (Name and Description) for a
///
[System.AttributeUsage (System.AttributeTargets.Class)]
public class ScenarioMetadata : System.Attribute {
///
/// Name
///
public string Name { get; set; }
///
/// Description
///
public string Description { get; set; }
public ScenarioMetadata (string Name, string Description)
{
this.Name = Name;
this.Description = Description;
}
///
/// Static helper function to get the Name given a Type
///
///
///
public static string GetName (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Name;
///
/// Static helper function to get the Description given a Type
///
///
///
public static string GetDescription (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Description;
}
///
/// Helper to get the Name (defined in )
///
///
public string GetName () => ScenarioMetadata.GetName (this.GetType ());
///
/// Helper to get the Description (defined in )
///
///
public string GetDescription () => ScenarioMetadata.GetDescription (this.GetType ());
///
/// Defines the category names used to catagorize a
///
[System.AttributeUsage (System.AttributeTargets.Class, AllowMultiple = true)]
public class ScenarioCategory : System.Attribute {
///
/// Category Name
///
public string Name { get; set; }
public ScenarioCategory (string Name) => this.Name = Name;
///
/// Static helper function to get the Name given a Type
///
///
/// Name of the category
public static string GetName (Type t) => ((ScenarioCategory)System.Attribute.GetCustomAttributes (t) [0]).Name;
///
/// Static helper function to get the Categories given a Type
///
///
/// list of category names
public static List GetCategories (Type t) => System.Attribute.GetCustomAttributes (t)
.ToList ()
.Where (a => a is ScenarioCategory)
.Select (a => ((ScenarioCategory)a).Name)
.ToList ();
}
///
/// Helper function to get the list of categories a belongs to (defined in )
///
/// list of category names
public List GetCategories () => ScenarioCategory.GetCategories (this.GetType ());
private static int _maxScenarioNameLen = 30;
///
/// Gets the Scenario Name + Description with the Description padded
/// based on the longest known Scenario name.
///
///
public override string ToString () => $"{GetName ().PadRight(_maxScenarioNameLen)}{GetDescription ()}";
///
/// Override this to implement the setup logic (create controls, etc...).
///
/// This is typically the best place to put scenario logic code.
public virtual void Setup ()
{
}
///
/// Runs the . Override to start the using a different than `Top`.
///
///
///
/// Overrides that do not call the base., must call before returning.
///
public virtual void Run ()
{
// Must explicit call Application.Shutdown method to shutdown.
Application.Run (Application.Top);
}
///
/// Stops the scenario. Override to change shutdown behavior for the .
///
public virtual void RequestStop ()
{
Application.RequestStop ();
}
///
/// Returns a list of all Categories set by all of the s defined in the project.
///
internal static List GetAllCategories ()
{
List categories = new List ();
foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
.Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
List attrs = System.Attribute.GetCustomAttributes (type).ToList ();
categories = categories.Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name)).ToList ();
}
// Sort
categories = categories.OrderBy (c => c).ToList ();
// Put "All" at the top
categories.Insert (0, "All Scenarios");
return categories;
}
///
/// Returns a list of all instanaces defined in the project, sorted by .
/// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class
///
public static List GetScenarios ()
{
List objects = new List ();
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.OrderBy (s => s.GetName ()).ToList ();
}
protected virtual void Dispose (bool disposing)
{
if (!_disposedValue) {
if (disposing) {
// TODO: dispose managed state (managed objects)
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
_disposedValue = true;
}
}
public void Dispose ()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose (disposing: true);
GC.SuppressFinalize (this);
}
}
}