AllViewsTester.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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 readonly List<string> _dimNames = new () { "Auto", "Percent", "Fill", "Absolute" };
  17. // TODO: This is missing some
  18. private readonly List<string> _posNames = new () { "Percent", "AnchorEnd", "Center", "Absolute" };
  19. private ListView _classListView;
  20. private View _curView;
  21. private FrameView _hostPane;
  22. private AdornmentsEditor _adornmentsEditor;
  23. private RadioGroup _hRadioGroup;
  24. private TextField _hText;
  25. private int _hVal;
  26. private FrameView _leftPane;
  27. private FrameView _locationFrame;
  28. // Settings
  29. private FrameView _settingsPane;
  30. private FrameView _sizeFrame;
  31. private Dictionary<string, Type> _viewClasses;
  32. private RadioGroup _wRadioGroup;
  33. private TextField _wText;
  34. private int _wVal;
  35. private RadioGroup _xRadioGroup;
  36. private TextField _xText;
  37. private int _xVal;
  38. private RadioGroup _yRadioGroup;
  39. private TextField _yText;
  40. private int _yVal;
  41. private RadioGroup _orientation;
  42. private string _demoText = "This, that, and the other thing.";
  43. private TextView _demoTextView;
  44. public override void Main ()
  45. {
  46. // Don't create a sub-win (Scenario.Win); just use Application.Top
  47. Application.Init ();
  48. // ConfigurationManager.Apply ();
  49. var app = new Window
  50. {
  51. Title = GetQuitKeyAndName (),
  52. ColorScheme = Colors.ColorSchemes ["TopLevel"]
  53. };
  54. _viewClasses = GetAllViewClassesCollection ()
  55. .OrderBy (t => t.Name)
  56. .Select (t => new KeyValuePair<string, Type> (t.Name, t))
  57. .ToDictionary (t => t.Key, t => t.Value);
  58. _leftPane = new ()
  59. {
  60. X = 0,
  61. Y = 0,
  62. Width = Dim.Auto (DimAutoStyle.Content),
  63. Height = Dim.Fill (),
  64. CanFocus = true,
  65. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  66. Title = "Classes"
  67. };
  68. _classListView = new ()
  69. {
  70. X = 0,
  71. Y = 0,
  72. Width = Dim.Auto (),
  73. Height = Dim.Fill (),
  74. AllowsMarking = false,
  75. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  76. SelectedItem = 0,
  77. Source = new ListWrapper<string> (new (_viewClasses.Keys.ToList ()))
  78. };
  79. _classListView.SelectedItemChanged += (s, args) =>
  80. {
  81. // Dispose existing current View, if any
  82. DisposeCurrentView ();
  83. CreateCurrentView (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
  84. // Force ViewToEdit to be the view and not a subview
  85. if (_adornmentsEditor is { })
  86. {
  87. _adornmentsEditor.AutoSelectSuperView = _curView;
  88. _adornmentsEditor.ViewToEdit = _curView;
  89. }
  90. };
  91. _leftPane.Add (_classListView);
  92. _adornmentsEditor = new ()
  93. {
  94. X = Pos.Right (_leftPane),
  95. Y = 0,
  96. Width = Dim.Auto (),
  97. Height = Dim.Fill (),
  98. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  99. BorderStyle = LineStyle.Single,
  100. AutoSelectViewToEdit = true,
  101. AutoSelectAdornments = false,
  102. };
  103. var expandButton = new ExpanderButton
  104. {
  105. CanFocus = false,
  106. Orientation = Orientation.Horizontal
  107. };
  108. _adornmentsEditor.Border.Add (expandButton);
  109. _settingsPane = new ()
  110. {
  111. X = Pos.Right (_adornmentsEditor),
  112. Y = 0, // for menu
  113. Width = Dim.Fill (),
  114. Height = Dim.Auto (),
  115. CanFocus = true,
  116. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  117. Title = "Settings"
  118. };
  119. string [] radioItems = { "_Percent(x)", "_AnchorEnd", "_Center", "A_bsolute(x)" };
  120. _locationFrame = new ()
  121. {
  122. X = 0,
  123. Y = 0,
  124. Height = Dim.Auto (),
  125. Width = Dim.Auto (),
  126. Title = "Location (Pos)",
  127. TabStop = TabBehavior.TabStop,
  128. };
  129. _settingsPane.Add (_locationFrame);
  130. var label = new Label { X = 0, Y = 0, Text = "X:" };
  131. _locationFrame.Add (label);
  132. _xRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
  133. _xRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
  134. _xText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
  135. _xText.Accepting += (s, args) =>
  136. {
  137. try
  138. {
  139. _xVal = int.Parse (_xText.Text);
  140. DimPosChanged (_curView);
  141. }
  142. catch
  143. { }
  144. };
  145. _locationFrame.Add (_xText);
  146. _locationFrame.Add (_xRadioGroup);
  147. radioItems = new [] { "P_ercent(y)", "A_nchorEnd", "C_enter", "Absolute(_y)" };
  148. label = new () { X = Pos.Right (_xRadioGroup) + 1, Y = 0, Text = "Y:" };
  149. _locationFrame.Add (label);
  150. _yText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_yVal}" };
  151. _yText.Accepting += (s, args) =>
  152. {
  153. try
  154. {
  155. _yVal = int.Parse (_yText.Text);
  156. DimPosChanged (_curView);
  157. }
  158. catch
  159. { }
  160. };
  161. _locationFrame.Add (_yText);
  162. _yRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
  163. _yRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
  164. _locationFrame.Add (_yRadioGroup);
  165. _sizeFrame = new ()
  166. {
  167. X = Pos.Right (_locationFrame),
  168. Y = Pos.Y (_locationFrame),
  169. Height = Dim.Auto (),
  170. Width = Dim.Auto (),
  171. Title = "Size (Dim)",
  172. TabStop = TabBehavior.TabStop,
  173. };
  174. radioItems = new [] { "Auto", "_Percent(width)", "_Fill(width)", "A_bsolute(width)" };
  175. label = new () { X = 0, Y = 0, Text = "Width:" };
  176. _sizeFrame.Add (label);
  177. _wRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
  178. _wRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
  179. _wText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
  180. _wText.Accepting += (s, args) =>
  181. {
  182. try
  183. {
  184. switch (_wRadioGroup.SelectedItem)
  185. {
  186. case 1:
  187. _wVal = Math.Min (int.Parse (_wText.Text), 100);
  188. break;
  189. case 0:
  190. case 2:
  191. case 3:
  192. _wVal = int.Parse (_wText.Text);
  193. break;
  194. }
  195. DimPosChanged (_curView);
  196. }
  197. catch
  198. { }
  199. };
  200. _sizeFrame.Add (_wText);
  201. _sizeFrame.Add (_wRadioGroup);
  202. radioItems = new [] { "_Auto", "P_ercent(height)", "F_ill(height)", "Ab_solute(height)" };
  203. label = new () { X = Pos.Right (_wRadioGroup) + 1, Y = 0, Text = "Height:" };
  204. _sizeFrame.Add (label);
  205. _hText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_hVal}" };
  206. _hText.Accepting += (s, args) =>
  207. {
  208. try
  209. {
  210. switch (_hRadioGroup.SelectedItem)
  211. {
  212. case 1:
  213. _hVal = Math.Min (int.Parse (_hText.Text), 100);
  214. break;
  215. case 0:
  216. case 2:
  217. case 3:
  218. _hVal = int.Parse (_hText.Text);
  219. break;
  220. }
  221. DimPosChanged (_curView);
  222. }
  223. catch
  224. { }
  225. };
  226. _sizeFrame.Add (_hText);
  227. _hRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
  228. _hRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
  229. _sizeFrame.Add (_hRadioGroup);
  230. _settingsPane.Add (_sizeFrame);
  231. label = new () { X = 0, Y = Pos.Bottom (_sizeFrame), Text = "_Orientation:" };
  232. _orientation = new ()
  233. {
  234. X = Pos.Right (label) + 1,
  235. Y = Pos.Top (label),
  236. RadioLabels = new [] { "Horizontal", "Vertical" },
  237. Orientation = Orientation.Horizontal
  238. };
  239. _orientation.SelectedItemChanged += (s, selected) =>
  240. {
  241. if (_curView is IOrientation orientatedView)
  242. {
  243. orientatedView.Orientation = (Orientation)_orientation.SelectedItem;
  244. }
  245. };
  246. _settingsPane.Add (label, _orientation);
  247. label = new () { X = 0, Y = Pos.Bottom (_orientation), Text = "_Text:" };
  248. _demoTextView = new ()
  249. {
  250. X = Pos.Right (label) + 1,
  251. Y = Pos.Top (label),
  252. Width = Dim.Fill (),
  253. Height = Dim.Auto (minimumContentDim: 2),
  254. Text = _demoText
  255. };
  256. _demoTextView.ContentsChanged += (s, e) =>
  257. {
  258. _demoText = _demoTextView.Text;
  259. if (_curView is { })
  260. {
  261. _curView.Text = _demoText;
  262. }
  263. };
  264. _settingsPane.Add (label, _demoTextView);
  265. _hostPane = new ()
  266. {
  267. X = Pos.Right (_adornmentsEditor),
  268. Y = Pos.Bottom (_settingsPane),
  269. Width = Dim.Fill (),
  270. Height = Dim.Fill (), // + 1 for status bar
  271. CanFocus = true,
  272. TabStop = TabBehavior.TabGroup,
  273. ColorScheme = Colors.ColorSchemes ["Dialog"]
  274. };
  275. _hostPane.LayoutStarted += (sender, args) =>
  276. {
  277. };
  278. app.Add (_leftPane, _adornmentsEditor, _settingsPane, _hostPane);
  279. _classListView.SelectedItem = 0;
  280. _leftPane.SetFocus ();
  281. Application.Run (app);
  282. app.Dispose ();
  283. Application.Shutdown ();
  284. }
  285. private void OnRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
  286. // TODO: Add Command.HotKey handler (pop a message box?)
  287. private void CreateCurrentView (Type type)
  288. {
  289. Debug.Assert(_curView is null);
  290. // If we are to create a generic Type
  291. if (type.IsGenericType)
  292. {
  293. // For each of the <T> arguments
  294. List<Type> typeArguments = new ();
  295. // use <object>
  296. foreach (Type arg in type.GetGenericArguments ())
  297. {
  298. typeArguments.Add (typeof (object));
  299. }
  300. // And change what type we are instantiating from MyClass<T> to MyClass<object>
  301. type = type.MakeGenericType (typeArguments.ToArray ());
  302. }
  303. // Instantiate view
  304. var view = (View)Activator.CreateInstance (type);
  305. if (view is IDesignable designable)
  306. {
  307. designable.EnableForDesign (ref _demoText);
  308. }
  309. else
  310. {
  311. view.Text = _demoText;
  312. view.Title = "_Test Title";
  313. }
  314. if (view is IOrientation orientatedView)
  315. {
  316. _orientation.SelectedItem = (int)orientatedView.Orientation;
  317. _orientation.Enabled = true;
  318. }
  319. else
  320. {
  321. _orientation.Enabled = false;
  322. }
  323. view.Initialized += CurrentView_Initialized;
  324. view.LayoutComplete += CurrentView_LayoutComplete;
  325. _curView = view;
  326. _hostPane.Add (_curView);
  327. }
  328. private void DisposeCurrentView ()
  329. {
  330. if (_curView != null)
  331. {
  332. _curView.Initialized -= CurrentView_Initialized;
  333. _curView.LayoutComplete -= CurrentView_LayoutComplete;
  334. _hostPane.Remove (_curView);
  335. _curView.Dispose ();
  336. _curView = null;
  337. }
  338. }
  339. private void DimPosChanged (View view)
  340. {
  341. if (view == null || _updatingSettings)
  342. {
  343. return;
  344. }
  345. try
  346. {
  347. view.X = _xRadioGroup.SelectedItem switch
  348. {
  349. 0 => Pos.Percent (_xVal),
  350. 1 => Pos.AnchorEnd (),
  351. 2 => Pos.Center (),
  352. 3 => Pos.Absolute (_xVal),
  353. _ => view.X
  354. };
  355. view.Y = _yRadioGroup.SelectedItem switch
  356. {
  357. 0 => Pos.Percent (_yVal),
  358. 1 => Pos.AnchorEnd (),
  359. 2 => Pos.Center (),
  360. 3 => Pos.Absolute (_yVal),
  361. _ => view.Y
  362. };
  363. view.Width = _wRadioGroup.SelectedItem switch
  364. {
  365. 0 => Dim.Auto (),
  366. 1 => Dim.Percent (_wVal),
  367. 2 => Dim.Fill (_wVal),
  368. 3 => Dim.Absolute (_wVal),
  369. _ => view.Width
  370. };
  371. view.Height = _hRadioGroup.SelectedItem switch
  372. {
  373. 0 => Dim.Auto (),
  374. 1 => Dim.Percent (_hVal),
  375. 2 => Dim.Fill (_hVal),
  376. 3 => Dim.Absolute (_hVal),
  377. _ => view.Height
  378. };
  379. }
  380. catch (Exception e)
  381. {
  382. MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
  383. }
  384. if (view.Width is DimAuto)
  385. {
  386. _wText.Text = "Auto";
  387. _wText.Enabled = false;
  388. }
  389. else
  390. {
  391. _wText.Text = $"{_wVal}";
  392. _wText.Enabled = true;
  393. }
  394. if (view.Height is DimAuto)
  395. {
  396. _hText.Text = "Auto";
  397. _hText.Enabled = false;
  398. }
  399. else
  400. {
  401. _hText.Text = $"{_hVal}";
  402. _hText.Enabled = true;
  403. }
  404. UpdateHostTitle (view);
  405. }
  406. private List<Type> GetAllViewClassesCollection ()
  407. {
  408. List<Type> types = new ();
  409. foreach (Type type in typeof (View).Assembly.GetTypes ()
  410. .Where (
  411. myType =>
  412. myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View))
  413. ))
  414. {
  415. types.Add (type);
  416. }
  417. types.Add (typeof (View));
  418. return types;
  419. }
  420. private void CurrentView_LayoutComplete (object sender, LayoutEventArgs args)
  421. {
  422. UpdateSettings (_curView);
  423. UpdateHostTitle (_curView);
  424. }
  425. private bool _updatingSettings = false;
  426. private void UpdateSettings (View view)
  427. {
  428. _updatingSettings = true;
  429. var x = view.X.ToString ();
  430. var y = view.Y.ToString ();
  431. try
  432. {
  433. _xRadioGroup.SelectedItem = _posNames.IndexOf (_posNames.First (s => x.Contains (s)));
  434. _yRadioGroup.SelectedItem = _posNames.IndexOf (_posNames.First (s => y.Contains (s)));
  435. }
  436. catch (InvalidOperationException e)
  437. {
  438. // This is a hack to work around the fact that the Pos enum doesn't have an "Align" value yet
  439. Debug.WriteLine ($"{e}");
  440. }
  441. _xText.Text = $"{view.Frame.X}";
  442. _yText.Text = $"{view.Frame.Y}";
  443. var w = view.Width.ToString ();
  444. var h = view.Height.ToString ();
  445. _wRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.First (s => w.Contains (s)));
  446. _hRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.First (s => h.Contains (s)));
  447. if (view.Width.Has<DimAuto> (out _))
  448. {
  449. _wText.Text = "Auto";
  450. _wText.Enabled = false;
  451. }
  452. else
  453. {
  454. _wText.Text = $"{view.Frame.Width}";
  455. _wText.Enabled = true;
  456. }
  457. if (view.Height.Has<DimAuto> (out _))
  458. {
  459. _hText.Text = "Auto";
  460. _hText.Enabled = false;
  461. }
  462. else
  463. {
  464. _hText.Text = $"{view.Frame.Height}";
  465. _hText.Enabled = true;
  466. }
  467. _updatingSettings = false;
  468. }
  469. private void UpdateHostTitle (View view) { _hostPane.Title = $"_Demo of {view.GetType ().Name}"; }
  470. private void CurrentView_Initialized (object sender, EventArgs e)
  471. {
  472. if (sender is not View view)
  473. {
  474. return;
  475. }
  476. if (!view.Width!.Has<DimAuto> (out _) || (view.Width is null))
  477. {
  478. view.Width = Dim.Fill ();
  479. }
  480. if (!view.Height!.Has<DimAuto> (out _) || (view.Height is null))
  481. {
  482. view.Height = Dim.Fill ();
  483. }
  484. UpdateSettings (view);
  485. UpdateHostTitle (view);
  486. }
  487. }