TextInputControls.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. using System.Text;
  2. using System.Text.RegularExpressions;
  3. using Terminal.Gui;
  4. namespace UICatalog.Scenarios;
  5. [ScenarioMetadata ("Text Input Controls", "Tests all text input controls")]
  6. [ScenarioCategory ("Controls")]
  7. [ScenarioCategory ("Mouse and Keyboard")]
  8. [ScenarioCategory ("Text and Formatting")]
  9. public class TextInputControls : Scenario
  10. {
  11. private Label _labelMirroringTimeField;
  12. private TimeField _timeField;
  13. public override void Main ()
  14. {
  15. Application.Init ();
  16. var win = new Window { Title = GetQuitKeyAndName () };
  17. // TextField is a simple, single-line text input control
  18. var label = new Label { Text = " _TextField:" };
  19. win.Add (label);
  20. var textField = new TextField
  21. {
  22. X = Pos.Right (label) + 1,
  23. Y = 0,
  24. Width = Dim.Percent (50) - 1,
  25. Text = "TextField with test text. Unicode shouldn't 𝔹Aℝ𝔽!"
  26. };
  27. var singleWordGenerator = new SingleWordSuggestionGenerator ();
  28. textField.Autocomplete.SuggestionGenerator = singleWordGenerator;
  29. textField.TextChanging += TextFieldTextChanging;
  30. void TextFieldTextChanging (object sender, CancelEventArgs<string> e)
  31. {
  32. singleWordGenerator.AllSuggestions = Regex.Matches (e.NewValue, "\\w+")
  33. .Select (s => s.Value)
  34. .Distinct ()
  35. .ToList ();
  36. }
  37. win.Add (textField);
  38. var labelMirroringTextField = new Label
  39. {
  40. X = Pos.Right (textField) + 1,
  41. Y = Pos.Top (textField),
  42. Width = Dim.Fill (1) - 1,
  43. Height = 1,
  44. Text = textField.Text
  45. };
  46. win.Add (labelMirroringTextField);
  47. textField.TextChanged += (s, prev) => { labelMirroringTextField.Text = textField.Text; };
  48. label = new Label
  49. {
  50. Text = "Te_xtField2:",
  51. X = 0,
  52. Y = Pos.Bottom (textField)
  53. };
  54. win.Add (label);
  55. textField = new TextField
  56. {
  57. X = Pos.Right (label) + 1,
  58. Y = Pos.Bottom (textField),
  59. Width = Dim.Percent (50) - 1,
  60. Caption = "TextField with caption"
  61. };
  62. win.Add (textField);
  63. // TextView is a rich (as in functionality, not formatting) text editing control
  64. label = new () { Text = "T_extView:", Y = Pos.Bottom (label) + 1 };
  65. win.Add (label);
  66. var textView = new TextView
  67. {
  68. X = Pos.Right (label) + 1,
  69. Y = Pos.Top (label),
  70. Width = Dim.Percent (50) - 1,
  71. Height = Dim.Percent (10)
  72. };
  73. textView.Text = "TextView with some more test text. Unicode shouldn't 𝔹Aℝ𝔽!";
  74. textView.DrawingContent += TextView_DrawContent;
  75. // This shows how to enable autocomplete in TextView.
  76. void TextView_DrawContent (object sender, DrawEventArgs e)
  77. {
  78. singleWordGenerator.AllSuggestions = Regex.Matches (textView.Text, "\\w+")
  79. .Select (s => s.Value)
  80. .Distinct ()
  81. .ToList ();
  82. }
  83. win.Add (textView);
  84. var labelMirroringTextView = new Label
  85. {
  86. X = Pos.Right (textView) + 1,
  87. Y = Pos.Top (textView),
  88. Width = Dim.Fill (1) - 1,
  89. Height = Dim.Height (textView) - 1
  90. };
  91. win.Add (labelMirroringTextView);
  92. // Use ContentChanged to detect if the user has typed something in a TextView.
  93. // The TextChanged property is only fired if the TextView.Text property is
  94. // explicitly set
  95. textView.ContentsChanged += (s, a) =>
  96. {
  97. labelMirroringTextView.Enabled = !labelMirroringTextView.Enabled;
  98. labelMirroringTextView.Text = textView.Text;
  99. };
  100. CheckBox chxReadOnly = new ()
  101. {
  102. X = Pos.Left (textView), Y = Pos.Bottom (textView), CheckedState = textView.ReadOnly ? CheckState.Checked : CheckState.UnChecked, Text = "Read_Only"
  103. };
  104. chxReadOnly.CheckedStateChanging += (sender, args) => textView.ReadOnly = args.NewValue == CheckState.Checked;
  105. win.Add (chxReadOnly);
  106. // By default TextView is a multi-line control. It can be forced to
  107. // single-line mode.
  108. var chxMultiline = new CheckBox
  109. {
  110. X = Pos.Right (chxReadOnly) + 2, Y = Pos.Bottom (textView), CheckedState = textView.Multiline ? CheckState.Checked : CheckState.UnChecked, Text = "_Multiline"
  111. };
  112. win.Add (chxMultiline);
  113. var chxWordWrap = new CheckBox
  114. {
  115. X = Pos.Right (chxMultiline) + 2,
  116. Y = Pos.Top (chxMultiline),
  117. CheckedState = textView.WordWrap ? CheckState.Checked : CheckState.UnChecked,
  118. Text = "_Word Wrap"
  119. };
  120. chxWordWrap.CheckedStateChanging += (s, e) => textView.WordWrap = e.NewValue == CheckState.Checked;
  121. win.Add (chxWordWrap);
  122. // TextView captures Tabs (so users can enter /t into text) by default;
  123. // This means using Tab to navigate doesn't work by default. This shows
  124. // how to turn tab capture off.
  125. var chxCaptureTabs = new CheckBox
  126. {
  127. X = Pos.Right (chxWordWrap) + 2,
  128. Y = Pos.Top (chxWordWrap),
  129. CheckedState = textView.AllowsTab ? CheckState.Checked : CheckState.UnChecked,
  130. Text = "_Capture Tabs"
  131. };
  132. chxMultiline.CheckedStateChanging += (s, e) =>
  133. {
  134. textView.Multiline = e.NewValue == CheckState.Checked;
  135. if (!textView.Multiline && chxWordWrap.CheckedState == CheckState.Checked)
  136. {
  137. chxWordWrap.CheckedState = CheckState.UnChecked;
  138. }
  139. if (!textView.Multiline && chxCaptureTabs.CheckedState == CheckState.Checked)
  140. {
  141. chxCaptureTabs.CheckedState = CheckState.UnChecked;
  142. }
  143. };
  144. Key keyTab = textView.KeyBindings.GetFirstFromCommands (Command.Tab);
  145. Key keyBackTab = textView.KeyBindings.GetFirstFromCommands (Command.BackTab);
  146. chxCaptureTabs.CheckedStateChanging += (s, e) =>
  147. {
  148. if (e.NewValue == CheckState.Checked)
  149. {
  150. textView.KeyBindings.Add (keyTab, Command.Tab);
  151. textView.KeyBindings.Add (keyBackTab, Command.BackTab);
  152. }
  153. else
  154. {
  155. textView.KeyBindings.Remove (keyTab);
  156. textView.KeyBindings.Remove (keyBackTab);
  157. }
  158. textView.AllowsTab = e.NewValue == CheckState.Checked;
  159. };
  160. win.Add (chxCaptureTabs);
  161. // Hex editor
  162. label = new () { Text = "_HexView:", Y = Pos.Bottom (chxMultiline) + 1 };
  163. win.Add (label);
  164. var hexEditor =
  165. new HexView (
  166. new MemoryStream (Encoding.UTF8.GetBytes ("HexEditor Unicode that shouldn't 𝔹Aℝ𝔽!"))
  167. )
  168. {
  169. X = Pos.Right (label) + 1, Y = Pos.Bottom (chxMultiline) + 1, Width = Dim.Percent (50) - 1, Height = Dim.Percent (30),
  170. };
  171. win.Add (hexEditor);
  172. var labelMirroringHexEditor = new Label
  173. {
  174. X = Pos.Right (hexEditor) + 1,
  175. Y = Pos.Top (hexEditor),
  176. Width = Dim.Fill (1) - 1,
  177. Height = Dim.Height (hexEditor) - 1
  178. };
  179. byte [] array = ((MemoryStream)hexEditor.Source).ToArray ();
  180. labelMirroringHexEditor.Text = Encoding.UTF8.GetString (array, 0, array.Length);
  181. hexEditor.Edited += (s, kv) =>
  182. {
  183. hexEditor.ApplyEdits ();
  184. byte [] array = ((MemoryStream)hexEditor.Source).ToArray ();
  185. labelMirroringHexEditor.Text = Encoding.UTF8.GetString (array, 0, array.Length);
  186. };
  187. win.Add (labelMirroringHexEditor);
  188. // DateField
  189. label = new () { Text = "_DateField:", Y = Pos.Bottom (hexEditor) + 1 };
  190. win.Add (label);
  191. var dateField = new DateField (DateTime.Now) { X = Pos.Right (label) + 1, Y = Pos.Bottom (hexEditor) + 1, Width = 20 };
  192. win.Add (dateField);
  193. var labelMirroringDateField = new Label
  194. {
  195. X = Pos.Right (dateField) + 1,
  196. Y = Pos.Top (dateField),
  197. Width = Dim.Width (dateField),
  198. Height = Dim.Height (dateField),
  199. Text = dateField.Text
  200. };
  201. win.Add (labelMirroringDateField);
  202. dateField.TextChanged += (s, prev) => { labelMirroringDateField.Text = dateField.Text; };
  203. // TimeField
  204. label = new () { Text = "T_imeField:", Y = Pos.Top (dateField), X = Pos.Right (labelMirroringDateField) + 5 };
  205. win.Add (label);
  206. _timeField = new ()
  207. {
  208. X = Pos.Right (label) + 1,
  209. Y = Pos.Top (dateField),
  210. Width = 20,
  211. IsShortFormat = false,
  212. Time = DateTime.Now.TimeOfDay
  213. };
  214. win.Add (_timeField);
  215. _labelMirroringTimeField = new ()
  216. {
  217. X = Pos.Right (_timeField) + 1,
  218. Y = Pos.Top (_timeField),
  219. Width = Dim.Width (_timeField),
  220. Height = Dim.Height (_timeField),
  221. Text = _timeField.Text
  222. };
  223. win.Add (_labelMirroringTimeField);
  224. _timeField.TimeChanged += TimeChanged;
  225. // MaskedTextProvider - uses .NET MaskedTextProvider
  226. var netProviderLabel = new Label
  227. {
  228. X = Pos.Left (dateField),
  229. Y = Pos.Bottom (dateField) + 1,
  230. Text = "_NetMaskedTextProvider [ 999 000 LLL >LLL |AAA aaa ]:"
  231. };
  232. win.Add (netProviderLabel);
  233. var netProvider = new NetMaskedTextProvider ("999 000 LLL >LLL |AAA aaa");
  234. var netProviderField = new TextValidateField
  235. {
  236. X = Pos.Right (netProviderLabel) + 1, Y = Pos.Y (netProviderLabel), Provider = netProvider
  237. };
  238. win.Add (netProviderField);
  239. var labelMirroringNetProviderField = new Label
  240. {
  241. X = Pos.Right (netProviderField) + 1,
  242. Y = Pos.Top (netProviderField),
  243. Width = Dim.Width (netProviderField),
  244. Height = Dim.Height (netProviderField),
  245. Text = netProviderField.Text
  246. };
  247. win.Add (labelMirroringNetProviderField);
  248. netProviderField.Provider.TextChanged += (s, prev) => { labelMirroringNetProviderField.Text = netProviderField.Text; };
  249. // TextRegexProvider - Regex provider implemented by Terminal.Gui
  250. var regexProvider = new Label
  251. {
  252. X = Pos.Left (netProviderLabel),
  253. Y = Pos.Bottom (netProviderLabel) + 1,
  254. Text = "Text_RegexProvider [ ^([0-9]?[0-9]?[0-9]|1000)$ ]:"
  255. };
  256. win.Add (regexProvider);
  257. var provider2 = new TextRegexProvider ("^([0-9]?[0-9]?[0-9]|1000)$") { ValidateOnInput = false };
  258. var regexProviderField = new TextValidateField
  259. {
  260. X = Pos.Right (regexProvider) + 1,
  261. Y = Pos.Y (regexProvider),
  262. Width = 30,
  263. TextAlignment = Alignment.Center,
  264. Provider = provider2
  265. };
  266. win.Add (regexProviderField);
  267. var labelMirroringRegexProviderField = new Label
  268. {
  269. X = Pos.Right (regexProviderField) + 1,
  270. Y = Pos.Top (regexProviderField),
  271. Width = Dim.Width (regexProviderField),
  272. Height = Dim.Height (regexProviderField),
  273. Text = regexProviderField.Text
  274. };
  275. win.Add (labelMirroringRegexProviderField);
  276. regexProviderField.Provider.TextChanged += (s, prev) => { labelMirroringRegexProviderField.Text = regexProviderField.Text; };
  277. var labelAppendAutocomplete = new Label
  278. {
  279. Y = Pos.Y (regexProviderField) + 2, X = 1, Text = "_Append Autocomplete:"
  280. };
  281. var appendAutocompleteTextField = new TextField
  282. {
  283. X = Pos.Right (labelAppendAutocomplete) + 1, Y = Pos.Top (labelAppendAutocomplete), Width = Dim.Fill ()
  284. };
  285. appendAutocompleteTextField.Autocomplete = new AppendAutocomplete (appendAutocompleteTextField);
  286. appendAutocompleteTextField.Autocomplete.SuggestionGenerator = new SingleWordSuggestionGenerator
  287. {
  288. AllSuggestions = new ()
  289. {
  290. "fish",
  291. "flipper",
  292. "fin",
  293. "fun",
  294. "the",
  295. "at",
  296. "there",
  297. "some",
  298. "my",
  299. "of",
  300. "be",
  301. "use",
  302. "her",
  303. "than",
  304. "and",
  305. "this",
  306. "an",
  307. "would",
  308. "first",
  309. "have",
  310. "each",
  311. "make",
  312. "water",
  313. "to",
  314. "from",
  315. "which",
  316. "like",
  317. "been",
  318. "in",
  319. "or",
  320. "she",
  321. "him",
  322. "call",
  323. "is",
  324. "one",
  325. "do",
  326. "into",
  327. "who",
  328. "you",
  329. "had",
  330. "how",
  331. "time",
  332. "oil",
  333. "that",
  334. "by",
  335. "their",
  336. "has",
  337. "its",
  338. "it",
  339. "word",
  340. "if",
  341. "look",
  342. "now",
  343. "he",
  344. "but",
  345. "will",
  346. "two",
  347. "find",
  348. "was",
  349. "not",
  350. "up",
  351. "more",
  352. "long",
  353. "for",
  354. "what",
  355. "other",
  356. "write",
  357. "down",
  358. "on",
  359. "all",
  360. "about",
  361. "go",
  362. "day",
  363. "are",
  364. "were",
  365. "out",
  366. "see",
  367. "did",
  368. "as",
  369. "we",
  370. "many",
  371. "number",
  372. "get",
  373. "with",
  374. "when",
  375. "then",
  376. "no",
  377. "come",
  378. "his",
  379. "your",
  380. "them",
  381. "way",
  382. "made",
  383. "they",
  384. "can",
  385. "these",
  386. "could",
  387. "may",
  388. "said",
  389. "so",
  390. "people",
  391. "part"
  392. }
  393. };
  394. win.Add (labelAppendAutocomplete);
  395. win.Add (appendAutocompleteTextField);
  396. Label acceptView = new ()
  397. {
  398. X = Pos.Center (),
  399. Y = Pos.AnchorEnd (),
  400. };
  401. win.Add (acceptView);
  402. win.Accepting += WinOnAccept;
  403. ConfigurationManager.Applied += ConfigurationManagerOnApplied;
  404. Application.Run (win);
  405. win.Dispose ();
  406. win = null;
  407. Application.Shutdown ();
  408. return;
  409. void WinOnAccept (object sender, CommandEventArgs e)
  410. {
  411. e.Handled = true; // Don't let it close
  412. acceptView.Text = $"Accept was Invoked via {win.Focused.GetType ().Name}";
  413. // Start a task that will set acceptView.Text to an empty string after 1 second
  414. System.Threading.Tasks.Task.Run (async () =>
  415. {
  416. await System.Threading.Tasks.Task.Delay (1000);
  417. Application.Invoke (() => acceptView.Text = "");
  418. });
  419. }
  420. void ConfigurationManagerOnApplied (object sender, ConfigurationManagerEventArgs e)
  421. {
  422. if (win is { })
  423. {
  424. win.SetNeedsDraw ();
  425. }
  426. }
  427. }
  428. private void TimeChanged (object sender, DateTimeEventArgs<TimeSpan> e) { _labelMirroringTimeField.Text = _timeField.Text; }
  429. }