Scenario.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using NStack;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using Terminal.Gui;
  6. namespace UICatalog {
  7. /// <summary>
  8. /// <para>Base class for each demo/scenario.</para>
  9. /// <para>
  10. /// To define a new scenario:
  11. /// <list type="number">
  12. /// <item><description>Create a new <c>.cs</c> file in the <cs>Scenarios</cs> directory that derives from <see cref="Scenario"/>.</description></item>
  13. /// <item><description>Annotate the <see cref="Scenario"/> derived class with a <see cref="Scenario.ScenarioMetaData"/> attribute specifying the scenario's name and description.</description></item>
  14. /// <item><description>Add one or more <see cref="Scenario.ScenarioCategory"/> attributes to the class specifying which categories the sceanrio belongs to. If you don't specify a category the sceanrio will show up in "All".</description></item>
  15. /// <item><description>Implement the <see cref="Setup"/> override which will be called when a user selects the scenario to run.</description></item>
  16. /// <item><description>Optionally, implement the <see cref="Init(Toplevel)"/> and/or <see cref="Run"/> overrides to provide a custom implementation.</description></item>
  17. /// </list>
  18. /// </para>
  19. /// <para>
  20. /// The UI Catalog program uses reflection to find all scenarios and adds them to the
  21. /// ListViews. Press ENTER to run the selected scenario. Press CTRL-Q to exit it. /
  22. /// </para>
  23. /// </summary>
  24. /// <example>
  25. /// The example below is provided in the `Scenarios` directory as a generic sample that can be copied and re-named:
  26. /// <code>
  27. /// using Terminal.Gui;
  28. ///
  29. /// namespace UICatalog {
  30. /// [ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")]
  31. /// [ScenarioCategory ("Controls")]
  32. /// class MyScenario : Scenario {
  33. /// public override void Setup ()
  34. /// {
  35. /// // Put your scenario code here, e.g.
  36. /// Win.Add (new Button ("Press me!") {
  37. /// X = Pos.Center (),
  38. /// Y = Pos.Center (),
  39. /// Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", "Yes", "No")
  40. /// });
  41. /// }
  42. /// }
  43. /// }
  44. /// </code>
  45. /// </example>
  46. public class Scenario : IDisposable {
  47. private bool _disposedValue;
  48. /// <summary>
  49. /// The Top level for the <see cref="Scenario"/>. This should be set to <see cref="Terminal.Gui.Application.Top"/> in most cases.
  50. /// </summary>
  51. public Toplevel Top { get; set; }
  52. /// <summary>
  53. /// The Window for the <see cref="Scenario"/>. This should be set within the <see cref="Terminal.Gui.Application.Top"/> in most cases.
  54. /// </summary>
  55. public Window Win { get; set; }
  56. /// <summary>
  57. /// Helper that provides the default <see cref="Terminal.Gui.Window"/> implementation with a frame and
  58. /// label showing the name of the <see cref="Scenario"/> and logic to exit back to
  59. /// the Scenario picker UI.
  60. /// Override <see cref="Init(Toplevel)"/> to provide any <see cref="Terminal.Gui.Toplevel"/> behavior needed.
  61. /// </summary>
  62. /// <param name="top">The Toplevel created by the UI Catalog host.</param>
  63. /// <param name="colorScheme">The colorscheme to use.</param>
  64. /// <remarks>
  65. /// <para>
  66. /// 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"/>.
  67. /// </para>
  68. /// <para>
  69. /// 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.
  70. /// </para>
  71. /// </remarks>
  72. public virtual void Init(Toplevel top, ColorScheme colorScheme)
  73. {
  74. Application.Init ();
  75. Top = top;
  76. if (Top == null) {
  77. Top = Application.Top;
  78. }
  79. Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
  80. X = 0,
  81. Y = 0,
  82. Width = Dim.Fill (),
  83. Height = Dim.Fill (),
  84. ColorScheme = colorScheme,
  85. };
  86. Top.Add (Win);
  87. }
  88. /// <summary>
  89. /// Defines the metadata (Name and Description) for a <see cref="Scenario"/>
  90. /// </summary>
  91. [System.AttributeUsage (System.AttributeTargets.Class)]
  92. public class ScenarioMetadata : System.Attribute {
  93. /// <summary>
  94. /// <see cref="Scenario"/> Name
  95. /// </summary>
  96. public string Name { get; set; }
  97. /// <summary>
  98. /// <see cref="Scenario"/> Description
  99. /// </summary>
  100. public string Description { get; set; }
  101. public ScenarioMetadata (string Name, string Description)
  102. {
  103. this.Name = Name;
  104. this.Description = Description;
  105. }
  106. /// <summary>
  107. /// Static helper function to get the <see cref="Scenario"/> Name given a Type
  108. /// </summary>
  109. /// <param name="t"></param>
  110. /// <returns></returns>
  111. public static string GetName (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Name;
  112. /// <summary>
  113. /// Static helper function to get the <see cref="Scenario"/> Description given a Type
  114. /// </summary>
  115. /// <param name="t"></param>
  116. /// <returns></returns>
  117. public static string GetDescription (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Description;
  118. }
  119. /// <summary>
  120. /// Helper to get the <see cref="Scenario"/> Name (defined in <see cref="ScenarioMetadata"/>)
  121. /// </summary>
  122. /// <returns></returns>
  123. public string GetName () => ScenarioMetadata.GetName (this.GetType ());
  124. /// <summary>
  125. /// Helper to get the <see cref="Scenario"/> Description (defined in <see cref="ScenarioMetadata"/>)
  126. /// </summary>
  127. /// <returns></returns>
  128. public string GetDescription () => ScenarioMetadata.GetDescription (this.GetType ());
  129. /// <summary>
  130. /// Defines the category names used to catagorize a <see cref="Scenario"/>
  131. /// </summary>
  132. [System.AttributeUsage (System.AttributeTargets.Class, AllowMultiple = true)]
  133. public class ScenarioCategory : System.Attribute {
  134. /// <summary>
  135. /// Category Name
  136. /// </summary>
  137. public string Name { get; set; }
  138. public ScenarioCategory (string Name) => this.Name = Name;
  139. /// <summary>
  140. /// Static helper function to get the <see cref="Scenario"/> Name given a Type
  141. /// </summary>
  142. /// <param name="t"></param>
  143. /// <returns>Name of the catagory</returns>
  144. public static string GetName (Type t) => ((ScenarioCategory)System.Attribute.GetCustomAttributes (t) [0]).Name;
  145. /// <summary>
  146. /// Static helper function to get the <see cref="Scenario"/> Categories given a Type
  147. /// </summary>
  148. /// <param name="t"></param>
  149. /// <returns>list of catagory names</returns>
  150. public static List<string> GetCategories (Type t) => System.Attribute.GetCustomAttributes (t)
  151. .ToList ()
  152. .Where (a => a is ScenarioCategory)
  153. .Select (a => ((ScenarioCategory)a).Name)
  154. .ToList ();
  155. }
  156. /// <summary>
  157. /// Helper function to get the list of categories a <see cref="Scenario"/> belongs to (defined in <see cref="ScenarioCategory"/>)
  158. /// </summary>
  159. /// <returns>list of catagory names</returns>
  160. public List<string> GetCategories () => ScenarioCategory.GetCategories (this.GetType ());
  161. /// <inheritdoc/>
  162. public override string ToString () => $"{GetName (),-30}{GetDescription ()}";
  163. /// <summary>
  164. /// Override this to implement the <see cref="Scenario"/> setup logic (create controls, etc...).
  165. /// </summary>
  166. /// <remarks>This is typically the best place to put scenario logic code.</remarks>
  167. public virtual void Setup ()
  168. {
  169. }
  170. /// <summary>
  171. /// Runs the <see cref="Scenario"/>. Override to start the <see cref="Scenario"/> using a <see cref="Toplevel"/> different than `Top`.
  172. ///
  173. /// </summary>
  174. /// <remarks>
  175. /// Overrides that do not call the base.<see cref="Run"/>, must call <see cref="Application.Shutdown"/> before returning.
  176. /// </remarks>
  177. public virtual void Run ()
  178. {
  179. // This method already performs a later automatic shutdown.
  180. Application.Run (Top);
  181. }
  182. /// <summary>
  183. /// Stops the scenario. Override to change shutdown behavior for the <see cref="Scenario"/>.
  184. /// </summary>
  185. public virtual void RequestStop ()
  186. {
  187. Application.RequestStop ();
  188. }
  189. /// <summary>
  190. /// Returns a list of all Categories set by all of the <see cref="Scenario"/>s defined in the project.
  191. /// </summary>
  192. internal static List<string> GetAllCategories ()
  193. {
  194. List<string> categories = new List<string> () { "All" };
  195. foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
  196. .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) {
  197. List<System.Attribute> attrs = System.Attribute.GetCustomAttributes (type).ToList ();
  198. categories = categories.Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name)).ToList ();
  199. }
  200. return categories;
  201. }
  202. /// <summary>
  203. /// Returns an instance of each <see cref="Scenario"/> defined in the project.
  204. /// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class
  205. /// </summary>
  206. public static List<Type> GetDerivedClasses<T> ()
  207. {
  208. List<Type> objects = new List<Type> ();
  209. foreach (Type type in typeof (T).Assembly.GetTypes ()
  210. .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (T)))) {
  211. objects.Add (type);
  212. }
  213. return objects;
  214. }
  215. protected virtual void Dispose (bool disposing)
  216. {
  217. if (!_disposedValue) {
  218. if (disposing) {
  219. // TODO: dispose managed state (managed objects)
  220. }
  221. // TODO: free unmanaged resources (unmanaged objects) and override finalizer
  222. // TODO: set large fields to null
  223. _disposedValue = true;
  224. }
  225. }
  226. public void Dispose ()
  227. {
  228. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  229. Dispose (disposing: true);
  230. GC.SuppressFinalize (this);
  231. }
  232. }
  233. }