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); } } }