Scenario.cs 10 KB

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