AllViewsTester.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Reflection;
  7. using Terminal.Gui;
  8. namespace UICatalog.Scenarios;
  9. [ScenarioMetadata ("All Views Tester", "Provides a test UI for all classes derived from View.")]
  10. [ScenarioCategory ("Layout")]
  11. [ScenarioCategory ("Tests")]
  12. [ScenarioCategory ("Controls")]
  13. [ScenarioCategory ("Adornments")]
  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. };
  100. _settingsPane = new ()
  101. {
  102. Title = "Settings [_4]",
  103. X = Pos.Right (_adornmentsEditor),
  104. Y = Pos.Bottom (_layoutEditor),
  105. Width = Dim.Width(_layoutEditor),
  106. Height = Dim.Auto (),
  107. CanFocus = true,
  108. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  109. BorderStyle = LineStyle.Rounded
  110. };
  111. Label label = new () { X = 0, Y = 0, Text = "_Orientation:" };
  112. _orientation = new ()
  113. {
  114. X = Pos.Right (label) + 1,
  115. Y = Pos.Top (label),
  116. RadioLabels = new [] { "Horizontal", "Vertical" },
  117. Orientation = Orientation.Horizontal
  118. };
  119. _orientation.SelectedItemChanged += (s, selected) =>
  120. {
  121. if (_curView is IOrientation orientatedView)
  122. {
  123. orientatedView.Orientation = (Orientation)_orientation.SelectedItem;
  124. }
  125. };
  126. _settingsPane.Add (label, _orientation);
  127. label = new () { X = 0, Y = Pos.Bottom (_orientation), Text = "_Text:" };
  128. _demoTextView = new ()
  129. {
  130. X = Pos.Right (label) + 1,
  131. Y = Pos.Top (label),
  132. Width = Dim.Fill (),
  133. Height = Dim.Auto (minimumContentDim: 2),
  134. Text = _demoText
  135. };
  136. _demoTextView.ContentsChanged += (s, e) =>
  137. {
  138. _demoText = _demoTextView.Text;
  139. if (_curView is { })
  140. {
  141. _curView.Text = _demoText;
  142. }
  143. };
  144. _settingsPane.Add (label, _demoTextView);
  145. _eventLog = new EventLog ()
  146. {
  147. // X = Pos.Right(_layoutEditor)
  148. };
  149. _eventLog.X = Pos.AnchorEnd ();
  150. _eventLog.Y = 0;
  151. _eventLog.Height = Dim.Height (_classListView);
  152. //_eventLog.Width = 30;
  153. _layoutEditor.Width = Dim.Fill (Dim.Func ((() =>
  154. {
  155. if (_eventLog.NeedsLayout)
  156. {
  157. // We have two choices:
  158. // 1) Call Layout explicitly
  159. // 2) Throw LayoutException so Layout tries again
  160. //_eventLog.Layout ();
  161. throw new LayoutException ("_eventLog");
  162. }
  163. return _eventLog.Frame.Width;
  164. })));
  165. _hostPane = new ()
  166. {
  167. X = Pos.Right (_adornmentsEditor),
  168. Y = Pos.Bottom (_settingsPane),
  169. Width = Dim.Width (_layoutEditor),
  170. Height = Dim.Fill (),
  171. CanFocus = true,
  172. TabStop = TabBehavior.TabStop,
  173. ColorScheme = Colors.ColorSchemes ["Base"],
  174. Arrangement = ViewArrangement.Resizable,
  175. BorderStyle = LineStyle.RoundedDotted
  176. };
  177. _hostPane.Border.ColorScheme = app.ColorScheme;
  178. _hostPane.Padding.Thickness = new (1);
  179. _hostPane.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
  180. _hostPane.Padding.ColorScheme = app.ColorScheme;
  181. app.Add (_classListView, _adornmentsEditor, _layoutEditor, _settingsPane, _eventLog, _hostPane);
  182. app.Initialized += App_Initialized;
  183. Application.Run (app);
  184. app.Dispose ();
  185. Application.Shutdown ();
  186. }
  187. private void App_Initialized (object sender, EventArgs e)
  188. {
  189. _classListView.SelectedItem = 0;
  190. _classListView.SetFocus ();
  191. }
  192. // TODO: Add Command.HotKey handler (pop a message box?)
  193. private void CreateCurrentView (Type type)
  194. {
  195. Debug.Assert (_curView is null);
  196. // If we are to create a generic Type
  197. if (type.IsGenericType)
  198. {
  199. // For each of the <T> arguments
  200. List<Type> typeArguments = new ();
  201. // use <object>
  202. foreach (Type arg in type.GetGenericArguments ())
  203. {
  204. typeArguments.Add (typeof (object));
  205. }
  206. // And change what type we are instantiating from MyClass<T> to MyClass<object>
  207. type = type.MakeGenericType (typeArguments.ToArray ());
  208. }
  209. // Instantiate view
  210. var view = (View)Activator.CreateInstance (type);
  211. if (view is IDesignable designable)
  212. {
  213. designable.EnableForDesign (ref _demoText);
  214. }
  215. else
  216. {
  217. view.Text = _demoText;
  218. view.Title = "_Test Title";
  219. }
  220. if (view is IOrientation orientatedView)
  221. {
  222. _orientation.SelectedItem = (int)orientatedView.Orientation;
  223. _orientation.Enabled = true;
  224. }
  225. else
  226. {
  227. _orientation.Enabled = false;
  228. }
  229. view.Initialized += CurrentView_Initialized;
  230. view.SubviewsLaidOut += CurrentView_LayoutComplete;
  231. view.Id = "_curView";
  232. _curView = view;
  233. _eventLog.ViewToLog = _curView;
  234. _hostPane.Add (_curView);
  235. _layoutEditor.ViewToEdit = _curView;
  236. _curView.SetNeedsLayout ();
  237. }
  238. private void DisposeCurrentView ()
  239. {
  240. if (_curView != null)
  241. {
  242. _curView.Initialized -= CurrentView_Initialized;
  243. _curView.SubviewsLaidOut -= CurrentView_LayoutComplete;
  244. _hostPane.Remove (_curView);
  245. _layoutEditor.ViewToEdit = null;
  246. _curView.Dispose ();
  247. _curView = null;
  248. }
  249. }
  250. private static List<Type> GetAllViewClassesCollection ()
  251. {
  252. List<Type> types = new ();
  253. foreach (Type type in typeof (View).Assembly.GetTypes ()
  254. .Where (
  255. myType =>
  256. myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View))
  257. ))
  258. {
  259. types.Add (type);
  260. }
  261. types.Add (typeof (View));
  262. return types;
  263. }
  264. private void CurrentView_LayoutComplete (object sender, LayoutEventArgs args)
  265. {
  266. UpdateHostTitle (_curView);
  267. }
  268. private void UpdateHostTitle (View view) { _hostPane.Title = $"{view.GetType ().Name} [_0]"; }
  269. private void CurrentView_Initialized (object sender, EventArgs e)
  270. {
  271. if (sender is not View view)
  272. {
  273. return;
  274. }
  275. if (!view.Width!.Has<DimAuto> (out _) || (view.Width is null))
  276. {
  277. view.Width = Dim.Fill ();
  278. }
  279. if (!view.Height!.Has<DimAuto> (out _) || (view.Height is null))
  280. {
  281. view.Height = Dim.Fill ();
  282. }
  283. UpdateHostTitle (view);
  284. }
  285. }