Scenario.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  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>
  13. /// <description>
  14. /// Create a new <c>.cs</c> file in the <cs>Scenarios</cs> directory that derives from
  15. /// <see cref="Scenario"/>.
  16. /// </description>
  17. /// </item>
  18. /// <item>
  19. /// <description>
  20. /// Annotate the <see cref="Scenario"/> derived class with a
  21. /// <see cref="Scenario.ScenarioMetadata"/> attribute specifying the scenario's name and 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 show up
  28. /// in "_All".
  29. /// </description>
  30. /// </item>
  31. /// <item>
  32. /// <description>
  33. /// Implement the <see cref="Main"/> override which will be called when a user selects the
  34. /// scenario to run.
  35. /// </description>
  36. /// </item>
  37. /// </list>
  38. /// </para>
  39. /// <para>
  40. /// The UI Catalog program uses reflection to find all scenarios and adds them to the ListViews. Press ENTER to
  41. /// run the selected scenario. Press the default quit key to quit.
  42. /// </para>
  43. /// </summary>
  44. /// <example>
  45. /// The example below is provided in the `Scenarios` directory as a generic sample that can be copied and re-named:
  46. /// <code>
  47. /// using Terminal.Gui;
  48. ///
  49. /// namespace UICatalog.Scenarios;
  50. ///
  51. /// [ScenarioMetadata ("Generic", "Generic sample - A template for creating new Scenarios")]
  52. /// [ScenarioCategory ("Controls")]
  53. /// public sealed class MyScenario : Scenario
  54. /// {
  55. /// public override void Main ()
  56. /// {
  57. /// // Init
  58. /// Application.Init ();
  59. ///
  60. /// // Setup - Create a top-level application window and configure it.
  61. /// Window appWindow = new ()
  62. /// {
  63. /// Title = GetQuitKeyAndName (),
  64. /// };
  65. ///
  66. /// var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
  67. /// button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
  68. /// appWindow.Add (button);
  69. ///
  70. /// // Run - Start the application.
  71. /// Application.Run (appWindow);
  72. /// appWindow.Dispose ();
  73. ///
  74. /// // Shutdown - Calling Application.Shutdown is required.
  75. /// Application.Shutdown ();
  76. /// }
  77. /// }
  78. /// </code>
  79. /// </example>
  80. public class Scenario : IDisposable
  81. {
  82. private static int _maxScenarioNameLen = 30;
  83. public string TopLevelColorScheme = "Base";
  84. private bool _disposedValue;
  85. /// <summary>
  86. /// Helper function to get the list of categories a <see cref="Scenario"/> belongs to (defined in
  87. /// <see cref="ScenarioCategory"/>)
  88. /// </summary>
  89. /// <returns>list of category names</returns>
  90. public List<string> GetCategories () { return ScenarioCategory.GetCategories (GetType ()); }
  91. /// <summary>Helper to get the <see cref="Scenario"/> Description (defined in <see cref="ScenarioMetadata"/>)</summary>
  92. /// <returns></returns>
  93. public string GetDescription () { return ScenarioMetadata.GetDescription (GetType ()); }
  94. /// <summary>Helper to get the <see cref="Scenario"/> Name (defined in <see cref="ScenarioMetadata"/>)</summary>
  95. /// <returns></returns>
  96. public string GetName () { return ScenarioMetadata.GetName (GetType ()); }
  97. /// <summary>
  98. /// Helper to get the <see cref="Application.QuitKey"/> and the <see cref="Scenario"/> Name (defined in
  99. /// <see cref="ScenarioMetadata"/>)
  100. /// </summary>
  101. /// <returns></returns>
  102. public string GetQuitKeyAndName () { return $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"; }
  103. /// <summary>
  104. /// Returns a list of all <see cref="Scenario"/> instanaces defined in the project, sorted by
  105. /// <see cref="ScenarioMetadata.Name"/>.
  106. /// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class
  107. /// </summary>
  108. public static ObservableCollection<Scenario> GetScenarios ()
  109. {
  110. List<Scenario> objects = new ();
  111. foreach (Type type in typeof (Scenario).Assembly.ExportedTypes
  112. .Where (
  113. myType => myType.IsClass
  114. && !myType.IsAbstract
  115. && myType.IsSubclassOf (typeof (Scenario))
  116. ))
  117. {
  118. var scenario = (Scenario)Activator.CreateInstance (type);
  119. objects.Add (scenario);
  120. _maxScenarioNameLen = Math.Max (_maxScenarioNameLen, scenario.GetName ().Length + 1);
  121. }
  122. return new (objects.OrderBy (s => s.GetName ()).ToList ());
  123. }
  124. /// <summary>
  125. /// Called by UI Catalog to run the <see cref="Scenario"/>. This is the main entry point for the <see cref="Scenario"/>
  126. /// .
  127. /// </summary>
  128. public virtual void Main () { }
  129. /// <summary>Gets the Scenario Name + Description with the Description padded based on the longest known Scenario name.</summary>
  130. /// <returns></returns>
  131. public override string ToString () { return $"{GetName ().PadRight (_maxScenarioNameLen)}{GetDescription ()}"; }
  132. #region IDispose
  133. public void Dispose ()
  134. {
  135. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  136. Dispose (true);
  137. GC.SuppressFinalize (this);
  138. }
  139. protected virtual void Dispose (bool disposing)
  140. {
  141. if (!_disposedValue)
  142. {
  143. if (disposing)
  144. { }
  145. _disposedValue = true;
  146. }
  147. }
  148. #endregion IDispose
  149. /// <summary>Returns a list of all Categories set by all of the <see cref="Scenario"/>s defined in the project.</summary>
  150. internal static ObservableCollection<string> GetAllCategories ()
  151. {
  152. List<string> aCategories = [];
  153. aCategories = typeof (Scenario).Assembly.GetTypes ()
  154. .Where (
  155. myType => myType.IsClass
  156. && !myType.IsAbstract
  157. && myType.IsSubclassOf (typeof (Scenario)))
  158. .Select (type => System.Attribute.GetCustomAttributes (type).ToList ())
  159. .Aggregate (
  160. aCategories,
  161. (current, attrs) => current
  162. .Union (
  163. attrs.Where (a => a is ScenarioCategory)
  164. .Select (a => ((ScenarioCategory)a).Name))
  165. .ToList ());
  166. // Sort
  167. ObservableCollection<string> categories = new (aCategories.OrderBy (c => c).ToList ());
  168. // Put "All" at the top
  169. categories.Insert (0, "All Scenarios");
  170. return categories;
  171. }
  172. /// <summary>Defines the category names used to categorize a <see cref="Scenario"/></summary>
  173. [AttributeUsage (AttributeTargets.Class, AllowMultiple = true)]
  174. public class ScenarioCategory (string name) : System.Attribute
  175. {
  176. /// <summary>Static helper function to get the <see cref="Scenario"/> Categories given a Type</summary>
  177. /// <param name="t"></param>
  178. /// <returns>list of category names</returns>
  179. public static List<string> GetCategories (Type t)
  180. {
  181. return GetCustomAttributes (t)
  182. .ToList ()
  183. .Where (a => a is ScenarioCategory)
  184. .Select (a => ((ScenarioCategory)a).Name)
  185. .ToList ();
  186. }
  187. /// <summary>Static helper function to get the <see cref="Scenario"/> Name given a Type</summary>
  188. /// <param name="t"></param>
  189. /// <returns>Name of the category</returns>
  190. public static string GetName (Type t) { return ((ScenarioCategory)GetCustomAttributes (t) [0]).Name; }
  191. /// <summary>Category Name</summary>
  192. public string Name { get; set; } = name;
  193. }
  194. /// <summary>Defines the metadata (Name and Description) for a <see cref="Scenario"/></summary>
  195. [AttributeUsage (AttributeTargets.Class)]
  196. public class ScenarioMetadata (string name, string description) : System.Attribute
  197. {
  198. /// <summary><see cref="Scenario"/> Description</summary>
  199. public string Description { get; set; } = description;
  200. /// <summary>Static helper function to get the <see cref="Scenario"/> Description given a Type</summary>
  201. /// <param name="t"></param>
  202. /// <returns></returns>
  203. public static string GetDescription (Type t) { return ((ScenarioMetadata)GetCustomAttributes (t) [0]).Description; }
  204. /// <summary>Static helper function to get the <see cref="Scenario"/> Name given a Type</summary>
  205. /// <param name="t"></param>
  206. /// <returns></returns>
  207. public static string GetName (Type t) { return ((ScenarioMetadata)GetCustomAttributes (t) [0]).Name; }
  208. /// <summary><see cref="Scenario"/> Name</summary>
  209. public string Name { get; set; } = name;
  210. }
  211. }