Scenario.cs 10 KB

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