TextInputControls.cs 17 KB

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