AllViewsTester.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using Terminal.Gui;
  7. namespace UICatalog.Scenarios;
  8. [ScenarioMetadata ("All Views Tester", "Provides a test UI for all classes derived from View.")]
  9. [ScenarioCategory ("Layout")]
  10. [ScenarioCategory ("Tests")]
  11. [ScenarioCategory ("Controls")]
  12. [ScenarioCategory ("Adornments")]
  13. [ScenarioCategory ("Arrangement")]
  14. public class AllViewsTester : Scenario
  15. {
  16. private Dictionary<string, Type>? _viewClasses;
  17. private ListView? _classListView;
  18. private AdornmentsEditor? _adornmentsEditor;
  19. private LayoutEditor? _layoutEditor;
  20. private FrameView? _settingsPane;
  21. private RadioGroup? _orientation;
  22. private string _demoText = "This, that, and the other thing.";
  23. private TextView? _demoTextView;
  24. private FrameView? _hostPane;
  25. private View? _curView;
  26. private EventLog? _eventLog;
  27. public override void Main ()
  28. {
  29. // Don't create a sub-win (Scenario.Win); just use Application.Top
  30. Application.Init ();
  31. var app = new Window
  32. {
  33. Title = GetQuitKeyAndName (),
  34. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  35. BorderStyle = LineStyle.None
  36. };
  37. _viewClasses = GetAllViewClassesCollection ()
  38. .OrderBy (t => t.Name)
  39. .Select (t => new KeyValuePair<string, Type> (t.Name, t))
  40. .ToDictionary (t => t.Key, t => t.Value);
  41. _classListView = new ()
  42. {
  43. Title = "Classes [_1]",
  44. X = 0,
  45. Y = 0,
  46. Width = Dim.Auto (),
  47. Height = Dim.Fill (),
  48. AllowsMarking = false,
  49. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  50. SelectedItem = 0,
  51. Source = new ListWrapper<string> (new (_viewClasses.Keys.ToList ())),
  52. BorderStyle = LineStyle.Rounded
  53. };
  54. _classListView.SelectedItemChanged += (s, args) =>
  55. {
  56. // Dispose existing current View, if any
  57. DisposeCurrentView ();
  58. CreateCurrentView (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
  59. // Force ViewToEdit to be the view and not a subview
  60. if (_adornmentsEditor is { })
  61. {
  62. _adornmentsEditor.AutoSelectSuperView = _curView;
  63. _adornmentsEditor.ViewToEdit = _curView;
  64. }
  65. };
  66. _classListView.Accepting += (sender, args) =>
  67. {
  68. _curView?.SetFocus ();
  69. args.Cancel = true;
  70. };
  71. _adornmentsEditor = new ()
  72. {
  73. Title = "Adornments [_2]",
  74. X = Pos.Right (_classListView),
  75. Y = 0,
  76. Width = Dim.Auto (),
  77. Height = Dim.Fill (),
  78. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  79. BorderStyle = LineStyle.Single,
  80. AutoSelectViewToEdit = false,
  81. AutoSelectAdornments = false
  82. };
  83. var expandButton = new ExpanderButton
  84. {
  85. CanFocus = false,
  86. Orientation = Orientation.Horizontal
  87. };
  88. _adornmentsEditor.Border.Add (expandButton);
  89. _layoutEditor = new ()
  90. {
  91. Title = "Layout [_3]",
  92. X = Pos.Right (_adornmentsEditor),
  93. Y = 0,
  94. //Width = Dim.Fill (), // set below
  95. Height = Dim.Auto (),
  96. CanFocus = true,
  97. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  98. BorderStyle = LineStyle.Rounded,
  99. AutoSelectViewToEdit = false,
  100. AutoSelectAdornments = false
  101. };
  102. _settingsPane = new ()
  103. {
  104. Title = "Settings [_4]",
  105. X = Pos.Right (_adornmentsEditor),
  106. Y = Pos.Bottom (_layoutEditor),
  107. Width = Dim.Width (_layoutEditor),
  108. Height = Dim.Auto (),
  109. CanFocus = true,
  110. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  111. BorderStyle = LineStyle.Rounded
  112. };
  113. Label label = new () { X = 0, Y = 0, Text = "_Orientation:" };
  114. _orientation = new ()
  115. {
  116. X = Pos.Right (label) + 1,
  117. Y = Pos.Top (label),
  118. RadioLabels = new [] { "Horizontal", "Vertical" },
  119. Orientation = Orientation.Horizontal
  120. };
  121. _orientation.SelectedItemChanged += (s, selected) =>
  122. {
  123. if (_curView is IOrientation orientatedView)
  124. {
  125. orientatedView.Orientation = (Orientation)_orientation.SelectedItem;
  126. }
  127. };
  128. _settingsPane.Add (label, _orientation);
  129. label = new () { X = 0, Y = Pos.Bottom (_orientation), Text = "_Text:" };
  130. _demoTextView = new ()
  131. {
  132. X = Pos.Right (label) + 1,
  133. Y = Pos.Top (label),
  134. Width = Dim.Fill (),
  135. Height = Dim.Auto (minimumContentDim: 2),
  136. Text = _demoText
  137. };
  138. _demoTextView.ContentsChanged += (s, e) =>
  139. {
  140. _demoText = _demoTextView.Text;
  141. if (_curView is { })
  142. {
  143. _curView.Text = _demoText;
  144. }
  145. };
  146. _settingsPane.Add (label, _demoTextView);
  147. _eventLog = new()
  148. {
  149. // X = Pos.Right(_layoutEditor)
  150. };
  151. _eventLog.X = Pos.AnchorEnd ();
  152. _eventLog.Y = 0;
  153. _eventLog.Height = Dim.Height (_classListView);
  154. //_eventLog.Width = 30;
  155. _layoutEditor.Width = Dim.Fill (
  156. Dim.Func (
  157. () =>
  158. {
  159. if (_eventLog.NeedsLayout)
  160. {
  161. // We have two choices:
  162. // 1) Call Layout explicitly
  163. // 2) Throw LayoutException so Layout tries again
  164. _eventLog.Layout ();
  165. //throw new LayoutException ("_eventLog");
  166. }
  167. return _eventLog.Frame.Width;
  168. }));
  169. _hostPane = new ()
  170. {
  171. Id = "_hostPane",
  172. X = Pos.Right (_adornmentsEditor),
  173. Y = Pos.Bottom (_settingsPane),
  174. Width = Dim.Width (_layoutEditor),
  175. Height = Dim.Fill (),
  176. CanFocus = true,
  177. TabStop = TabBehavior.TabStop,
  178. ColorScheme = Colors.ColorSchemes ["Base"],
  179. Arrangement = ViewArrangement.Resizable,
  180. BorderStyle = LineStyle.RoundedDotted
  181. };
  182. _hostPane.Border.ColorScheme = app.ColorScheme;
  183. _hostPane.Padding.Thickness = new (1);
  184. _hostPane.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
  185. _hostPane.Padding.ColorScheme = app.ColorScheme;
  186. app.Add (_classListView, _adornmentsEditor, _layoutEditor, _settingsPane, _eventLog, _hostPane);
  187. app.Initialized += App_Initialized;
  188. Application.Run (app);
  189. app.Dispose ();
  190. Application.Shutdown ();
  191. }
  192. private void App_Initialized (object? sender, EventArgs e)
  193. {
  194. _classListView!.SelectedItem = 0;
  195. _classListView.SetFocus ();
  196. }
  197. // TODO: Add Command.HotKey handler (pop a message box?)
  198. private void CreateCurrentView (Type type)
  199. {
  200. Debug.Assert (_curView is null);
  201. // If we are to create a generic Type
  202. if (type.IsGenericType)
  203. {
  204. // For each of the <T> arguments
  205. List<Type> typeArguments = new ();
  206. // use <object>
  207. foreach (Type arg in type.GetGenericArguments ())
  208. {
  209. typeArguments.Add (typeof (object));
  210. }
  211. // And change what type we are instantiating from MyClass<T> to MyClass<object>
  212. type = type.MakeGenericType (typeArguments.ToArray ());
  213. }
  214. // Instantiate view
  215. var view = (View)Activator.CreateInstance (type)!;
  216. _eventLog!.ViewToLog = _curView;
  217. if (view is IDesignable designable)
  218. {
  219. designable.EnableForDesign (ref _demoText);
  220. }
  221. else
  222. {
  223. view.Text = _demoText;
  224. view.Title = "_Test Title";
  225. }
  226. if (view is IOrientation orientatedView)
  227. {
  228. _orientation!.SelectedItem = (int)orientatedView.Orientation;
  229. _orientation.Enabled = true;
  230. }
  231. else
  232. {
  233. _orientation!.Enabled = false;
  234. }
  235. view.Initialized += CurrentView_Initialized;
  236. view.SubviewsLaidOut += CurrentView_LayoutComplete;
  237. view.Id = "_curView";
  238. _curView = view;
  239. _hostPane!.Add (_curView);
  240. _layoutEditor!.ViewToEdit = _curView;
  241. _curView.SetNeedsLayout ();
  242. }
  243. private void DisposeCurrentView ()
  244. {
  245. if (_curView != null)
  246. {
  247. _curView.Initialized -= CurrentView_Initialized;
  248. _curView.SubviewsLaidOut -= CurrentView_LayoutComplete;
  249. _hostPane!.Remove (_curView);
  250. _layoutEditor!.ViewToEdit = null;
  251. _curView.Dispose ();
  252. _curView = null;
  253. }
  254. }
  255. private static List<Type> GetAllViewClassesCollection ()
  256. {
  257. List<Type> types = typeof (View).Assembly.GetTypes ()
  258. .Where (
  259. myType => myType is { IsClass: true, IsAbstract: false, IsPublic: true }
  260. && myType.IsSubclassOf (typeof (View)))
  261. .ToList ();
  262. types.Add (typeof (View));
  263. return types;
  264. }
  265. private void CurrentView_LayoutComplete (object? sender, LayoutEventArgs args) { UpdateHostTitle (_curView); }
  266. private void UpdateHostTitle (View? view) { _hostPane!.Title = $"{view!.GetType ().Name} [_0]"; }
  267. private void CurrentView_Initialized (object? sender, EventArgs e)
  268. {
  269. if (sender is not View view)
  270. {
  271. return;
  272. }
  273. if (!view.Width!.Has<DimAuto> (out _) || view.Width is null)
  274. {
  275. view.Width = Dim.Fill ();
  276. }
  277. if (!view.Height!.Has<DimAuto> (out _) || view.Height is null)
  278. {
  279. view.Height = Dim.Fill ();
  280. }
  281. UpdateHostTitle (view);
  282. }
  283. public override List<Key> GetDemoKeyStrokes ()
  284. {
  285. var keys = new List<Key> ();
  286. for (int i = 0; i < GetAllViewClassesCollection ().Count; i++)
  287. {
  288. keys.Add (Key.CursorDown);
  289. }
  290. return keys;
  291. }
  292. }