Scenario.cs 12 KB

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