TextAlignmentAndDirection.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace UICatalog.Scenarios;
  6. [ScenarioMetadata ("Text Alignment and Direction", "Demos horizontal and vertical text alignment and direction.")]
  7. [ScenarioCategory ("Text and Formatting")]
  8. public class TextAlignmentAndDirection : Scenario
  9. {
  10. internal class AlignmentAndDirectionView : View
  11. {
  12. public AlignmentAndDirectionView ()
  13. {
  14. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent;
  15. BorderStyle = LineStyle.Dotted;
  16. }
  17. }
  18. public override void Main ()
  19. {
  20. Application.Init ();
  21. Window app = new ()
  22. {
  23. Title = GetQuitKeyAndName ()
  24. };
  25. var txt = $"Hello World{Environment.NewLine}HELLO WORLD{Environment.NewLine}世界 您好";
  26. SchemeManager.AddScheme ("TextAlignmentAndDirection1", new Scheme { Normal = new (Color.Black, Color.Gray) });
  27. SchemeManager.AddScheme ("TextAlignmentAndDirection2", new Scheme { Normal = new (Color.Black, Color.DarkGray) });
  28. List<View> singleLineLabels = new (); // single line
  29. List<View> multiLineLabels = new (); // multi line
  30. // Horizontal Single-Line
  31. var labelHL = new Label
  32. {
  33. X = 0,
  34. Y = 0,
  35. Width = 6,
  36. Height = 1,
  37. TextAlignment = Alignment.End,
  38. SchemeName = "Dialog",
  39. Text = "Start",
  40. };
  41. var labelHC = new Label
  42. {
  43. X = 0,
  44. Y = 1,
  45. Width = 6,
  46. Height = 1,
  47. TextAlignment = Alignment.End,
  48. SchemeName = "Dialog",
  49. Text = "Center"
  50. };
  51. var labelHR = new Label
  52. {
  53. X = 0,
  54. Y = 2,
  55. Width = 6,
  56. Height = 1,
  57. TextAlignment = Alignment.End,
  58. SchemeName = "Dialog",
  59. Text = "End"
  60. };
  61. var labelHJ = new Label
  62. {
  63. X = 0,
  64. Y = 3,
  65. Width = 6,
  66. Height = 1,
  67. TextAlignment = Alignment.End,
  68. SchemeName = "Dialog",
  69. Text = "Fill"
  70. };
  71. var txtLabelHL = new View
  72. {
  73. X = Pos.Right (labelHL) + 1,
  74. Y = Pos.Y (labelHL),
  75. Width = Dim.Fill (9),
  76. Height = 1,
  77. SchemeName = "TextAlignmentAndDirection1",
  78. TextAlignment = Alignment.Start,
  79. Text = txt,
  80. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  81. };
  82. var txtLabelHC = new View
  83. {
  84. X = Pos.Right (labelHC) + 1,
  85. Y = Pos.Y (labelHC),
  86. Width = Dim.Fill (9),
  87. Height = 1,
  88. SchemeName = "TextAlignmentAndDirection2",
  89. TextAlignment = Alignment.Center,
  90. Text = txt,
  91. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  92. };
  93. var txtLabelHR = new View
  94. {
  95. X = Pos.Right (labelHR) + 1,
  96. Y = Pos.Y (labelHR),
  97. Width = Dim.Fill (9),
  98. Height = 1,
  99. SchemeName = "TextAlignmentAndDirection1",
  100. TextAlignment = Alignment.End,
  101. Text = txt,
  102. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  103. };
  104. var txtLabelHJ = new View
  105. {
  106. X = Pos.Right (labelHJ) + 1,
  107. Y = Pos.Y (labelHJ),
  108. Width = Dim.Fill (9),
  109. Height = 1,
  110. SchemeName = "TextAlignmentAndDirection2",
  111. TextAlignment = Alignment.Fill,
  112. Text = txt,
  113. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  114. };
  115. singleLineLabels.Add (txtLabelHL);
  116. singleLineLabels.Add (txtLabelHC);
  117. singleLineLabels.Add (txtLabelHR);
  118. singleLineLabels.Add (txtLabelHJ);
  119. app.Add (labelHL);
  120. app.Add (txtLabelHL);
  121. app.Add (labelHC);
  122. app.Add (txtLabelHC);
  123. app.Add (labelHR);
  124. app.Add (txtLabelHR);
  125. app.Add (labelHJ);
  126. app.Add (txtLabelHJ);
  127. // Vertical Single-Line
  128. var labelVT = new Label
  129. {
  130. X = Pos.AnchorEnd () - 6,
  131. Y = 0,
  132. Width = 2,
  133. Height = 6,
  134. SchemeName = "TextAlignmentAndDirection1",
  135. TextDirection = TextDirection.TopBottom_LeftRight,
  136. VerticalTextAlignment = Alignment.End,
  137. Text = "Start"
  138. };
  139. labelVT.TextFormatter.WordWrap = false;
  140. var labelVM = new Label
  141. {
  142. X = Pos.AnchorEnd () - 4,
  143. Y = 0,
  144. Width = 2,
  145. Height = 6,
  146. SchemeName = "TextAlignmentAndDirection1",
  147. TextDirection = TextDirection.TopBottom_LeftRight,
  148. VerticalTextAlignment = Alignment.End,
  149. Text = "Center"
  150. };
  151. labelVM.TextFormatter.WordWrap = false;
  152. var labelVB = new Label
  153. {
  154. X = Pos.AnchorEnd () - 2,
  155. Y = 0,
  156. Width = 2,
  157. Height = 6,
  158. SchemeName = "TextAlignmentAndDirection1",
  159. TextDirection = TextDirection.TopBottom_LeftRight,
  160. VerticalTextAlignment = Alignment.End,
  161. Text = "End"
  162. };
  163. labelVB.TextFormatter.WordWrap = false;
  164. var labelVJ = new Label
  165. {
  166. X = Pos.AnchorEnd (),
  167. Y = 0,
  168. Width = 2,
  169. Height = 6,
  170. SchemeName = "TextAlignmentAndDirection1",
  171. TextDirection = TextDirection.TopBottom_LeftRight,
  172. VerticalTextAlignment = Alignment.End,
  173. Text = "Fill"
  174. };
  175. labelVJ.TextFormatter.WordWrap = false;
  176. var txtLabelVT = new View
  177. {
  178. X = Pos.X (labelVT),
  179. Y = Pos.Bottom (labelVT) + 1,
  180. Width = 2,
  181. Height = Dim.Fill (),
  182. SchemeName = "TextAlignmentAndDirection1",
  183. TextDirection = TextDirection.TopBottom_LeftRight,
  184. VerticalTextAlignment = Alignment.Start,
  185. Text = txt,
  186. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  187. };
  188. txtLabelVT.TextFormatter.WordWrap = false;
  189. var txtLabelVM = new View
  190. {
  191. X = Pos.X (labelVM),
  192. Y = Pos.Bottom (labelVM) + 1,
  193. Width = 2,
  194. Height = Dim.Fill (),
  195. SchemeName = "TextAlignmentAndDirection2",
  196. TextDirection = TextDirection.TopBottom_LeftRight,
  197. VerticalTextAlignment = Alignment.Center,
  198. Text = txt,
  199. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  200. };
  201. txtLabelVM.TextFormatter.WordWrap = false;
  202. var txtLabelVB = new View
  203. {
  204. X = Pos.X (labelVB),
  205. Y = Pos.Bottom (labelVB) + 1,
  206. Width = 2,
  207. Height = Dim.Fill (),
  208. SchemeName = "TextAlignmentAndDirection1",
  209. TextDirection = TextDirection.TopBottom_LeftRight,
  210. VerticalTextAlignment = Alignment.End,
  211. Text = txt,
  212. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  213. };
  214. txtLabelVB.TextFormatter.WordWrap = false;
  215. var txtLabelVJ = new View
  216. {
  217. X = Pos.X (labelVJ),
  218. Y = Pos.Bottom (labelVJ) + 1,
  219. Width = 2,
  220. Height = Dim.Fill (),
  221. SchemeName = "TextAlignmentAndDirection2",
  222. TextDirection = TextDirection.TopBottom_LeftRight,
  223. VerticalTextAlignment = Alignment.Fill,
  224. Text = txt,
  225. ViewportSettings = Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent
  226. };
  227. txtLabelVJ.TextFormatter.WordWrap = false;
  228. singleLineLabels.Add (txtLabelVT);
  229. singleLineLabels.Add (txtLabelVM);
  230. singleLineLabels.Add (txtLabelVB);
  231. singleLineLabels.Add (txtLabelVJ);
  232. app.Add (labelVT);
  233. app.Add (txtLabelVT);
  234. app.Add (labelVM);
  235. app.Add (txtLabelVM);
  236. app.Add (labelVB);
  237. app.Add (txtLabelVB);
  238. app.Add (labelVJ);
  239. app.Add (txtLabelVJ);
  240. // Multi-Line
  241. var container = new View
  242. {
  243. X = 0,
  244. Y = Pos.Bottom (txtLabelHJ),
  245. Width = Dim.Fill (31),
  246. Height = Dim.Fill (4)
  247. //SchemeName = "TextAlignmentAndDirection2"
  248. };
  249. var txtLabelTL = new AlignmentAndDirectionView
  250. {
  251. X = 0,
  252. Y = 1,
  253. Width = Dim.Percent (100 / 3),
  254. Height = Dim.Percent (100 / 3),
  255. TextAlignment = Alignment.Start,
  256. VerticalTextAlignment = Alignment.Start,
  257. SchemeName = "TextAlignmentAndDirection1",
  258. Text = txt,
  259. };
  260. txtLabelTL.TextFormatter.MultiLine = true;
  261. var txtLabelTC = new AlignmentAndDirectionView
  262. {
  263. X = Pos.Right (txtLabelTL),
  264. Y = 1,
  265. Width = Dim.Percent (100 / 3),
  266. Height = Dim.Percent (100 / 3),
  267. TextAlignment = Alignment.Center,
  268. VerticalTextAlignment = Alignment.Start,
  269. SchemeName = "TextAlignmentAndDirection1",
  270. Text = txt,
  271. };
  272. txtLabelTC.TextFormatter.MultiLine = true;
  273. var txtLabelTR = new AlignmentAndDirectionView
  274. {
  275. X = Pos.Right (txtLabelTC),
  276. Y = 1,
  277. Width = Dim.Percent (100, DimPercentMode.Position),
  278. Height = Dim.Percent (100 / 3),
  279. TextAlignment = Alignment.End,
  280. VerticalTextAlignment = Alignment.Start,
  281. SchemeName = "TextAlignmentAndDirection1",
  282. Text = txt,
  283. };
  284. txtLabelTR.TextFormatter.MultiLine = true;
  285. var txtLabelML = new AlignmentAndDirectionView
  286. {
  287. X = Pos.X (txtLabelTL),
  288. Y = Pos.Bottom (txtLabelTL),
  289. Width = Dim.Width (txtLabelTL),
  290. Height = Dim.Percent (100 / 3),
  291. TextAlignment = Alignment.Start,
  292. VerticalTextAlignment = Alignment.Center,
  293. SchemeName = "TextAlignmentAndDirection1",
  294. Text = txt,
  295. };
  296. txtLabelML.TextFormatter.MultiLine = true;
  297. var txtLabelMC = new AlignmentAndDirectionView
  298. {
  299. X = Pos.X (txtLabelTC),
  300. Y = Pos.Bottom (txtLabelTC),
  301. Width = Dim.Width (txtLabelTC),
  302. Height = Dim.Percent (100 / 3),
  303. TextAlignment = Alignment.Center,
  304. VerticalTextAlignment = Alignment.Center,
  305. SchemeName = "TextAlignmentAndDirection1",
  306. Text = txt,
  307. };
  308. txtLabelMC.TextFormatter.MultiLine = true;
  309. var txtLabelMR = new AlignmentAndDirectionView
  310. {
  311. X = Pos.X (txtLabelTR),
  312. Y = Pos.Bottom (txtLabelTR),
  313. Width = Dim.Percent (100, DimPercentMode.Position),
  314. Height = Dim.Percent (100 / 3),
  315. TextAlignment = Alignment.End,
  316. VerticalTextAlignment = Alignment.Center,
  317. SchemeName = "TextAlignmentAndDirection1",
  318. Text = txt,
  319. };
  320. txtLabelMR.TextFormatter.MultiLine = true;
  321. var txtLabelBL = new AlignmentAndDirectionView
  322. {
  323. X = Pos.X (txtLabelML),
  324. Y = Pos.Bottom (txtLabelML),
  325. Width = Dim.Width (txtLabelML),
  326. Height = Dim.Percent (100, DimPercentMode.Position),
  327. TextAlignment = Alignment.Start,
  328. VerticalTextAlignment = Alignment.End,
  329. SchemeName = "TextAlignmentAndDirection1",
  330. Text = txt,
  331. };
  332. txtLabelBL.TextFormatter.MultiLine = true;
  333. var txtLabelBC = new AlignmentAndDirectionView
  334. {
  335. X = Pos.X (txtLabelMC),
  336. Y = Pos.Bottom (txtLabelMC),
  337. Width = Dim.Width (txtLabelMC),
  338. Height = Dim.Percent (100, DimPercentMode.Position),
  339. TextAlignment = Alignment.Center,
  340. VerticalTextAlignment = Alignment.End,
  341. SchemeName = "TextAlignmentAndDirection1",
  342. Text = txt,
  343. };
  344. txtLabelBC.TextFormatter.MultiLine = true;
  345. var txtLabelBR = new AlignmentAndDirectionView
  346. {
  347. X = Pos.X (txtLabelMR),
  348. Y = Pos.Bottom (txtLabelMR),
  349. Width = Dim.Percent (100, DimPercentMode.Position),
  350. Height = Dim.Percent (100, DimPercentMode.Position),
  351. TextAlignment = Alignment.End,
  352. VerticalTextAlignment = Alignment.End,
  353. SchemeName = "TextAlignmentAndDirection1",
  354. Text = txt,
  355. };
  356. txtLabelBR.TextFormatter.MultiLine = true;
  357. multiLineLabels.Add (txtLabelTL);
  358. multiLineLabels.Add (txtLabelTC);
  359. multiLineLabels.Add (txtLabelTR);
  360. multiLineLabels.Add (txtLabelML);
  361. multiLineLabels.Add (txtLabelMC);
  362. multiLineLabels.Add (txtLabelMR);
  363. multiLineLabels.Add (txtLabelBL);
  364. multiLineLabels.Add (txtLabelBC);
  365. multiLineLabels.Add (txtLabelBR);
  366. // Save Alignment in Data
  367. foreach (View t in multiLineLabels)
  368. {
  369. t.Data = new TextAlignmentData (t.TextAlignment, t.VerticalTextAlignment);
  370. }
  371. container.Add (txtLabelTL);
  372. container.Add (txtLabelTC);
  373. container.Add (txtLabelTR);
  374. container.Add (txtLabelML);
  375. container.Add (txtLabelMC);
  376. container.Add (txtLabelMR);
  377. container.Add (txtLabelBL);
  378. container.Add (txtLabelBC);
  379. container.Add (txtLabelBR);
  380. app.Add (container);
  381. // Edit Text
  382. var label = new Label
  383. {
  384. X = 1,
  385. Y = Pos.Bottom (container) + 1,
  386. Width = 10,
  387. Height = 1,
  388. Text = "Edit Text:"
  389. };
  390. var editText = new TextView
  391. {
  392. X = Pos.Right (label) + 1,
  393. Y = Pos.Top (label),
  394. Width = Dim.Fill (31),
  395. Height = 3,
  396. Text = txt
  397. };
  398. editText.MouseClick += (s, m) =>
  399. {
  400. foreach (View v in singleLineLabels)
  401. {
  402. v.Text = editText.Text;
  403. }
  404. foreach (View v in multiLineLabels)
  405. {
  406. v.Text = editText.Text;
  407. }
  408. };
  409. app.KeyUp += (s, m) =>
  410. {
  411. foreach (View v in singleLineLabels)
  412. {
  413. v.Text = editText.Text;
  414. }
  415. foreach (View v in multiLineLabels)
  416. {
  417. v.Text = editText.Text;
  418. }
  419. };
  420. editText.SetFocus ();
  421. app.Add (label, editText);
  422. // JUSTIFY CHECKBOX
  423. var justifyCheckbox = new CheckBox
  424. {
  425. X = Pos.Right (container) + 1,
  426. Y = Pos.Y (container) + 1,
  427. Width = Dim.Fill (10),
  428. Height = 1,
  429. Text = "Fill"
  430. };
  431. app.Add (justifyCheckbox);
  432. // JUSTIFY OPTIONS
  433. var justifyOptions = new OptionSelector
  434. {
  435. X = Pos.Left (justifyCheckbox) + 1,
  436. Y = Pos.Y (justifyCheckbox) + 1,
  437. Width = Dim.Fill (9),
  438. Labels = ["Current direction", "Opposite direction", "FIll Both"],
  439. Enabled = false
  440. };
  441. justifyCheckbox.CheckedStateChanging += (s, e) => ToggleJustify (e.Result != CheckState.Checked);
  442. justifyOptions.ValueChanged += (_, _) => { ToggleJustify (false, true); };
  443. app.Add (justifyOptions);
  444. // WRAP CHECKBOX
  445. var wrapCheckbox = new CheckBox
  446. {
  447. X = Pos.Right (container) + 1,
  448. Y = Pos.Bottom (justifyOptions),
  449. Width = Dim.Fill (10),
  450. Height = 1,
  451. Text = "Word Wrap"
  452. };
  453. wrapCheckbox.CheckedState = wrapCheckbox.TextFormatter.WordWrap ? CheckState.Checked : CheckState.UnChecked;
  454. wrapCheckbox.CheckedStateChanging += (s, e) =>
  455. {
  456. if (e.Result == CheckState.Checked)
  457. {
  458. foreach (View t in multiLineLabels)
  459. {
  460. t.TextFormatter.WordWrap = false;
  461. }
  462. }
  463. else
  464. {
  465. foreach (View t in multiLineLabels)
  466. {
  467. t.TextFormatter.WordWrap = true;
  468. }
  469. }
  470. };
  471. app.Add (wrapCheckbox);
  472. List<TextDirection> directionsEnum = Enum.GetValues (typeof (TextDirection)).Cast<TextDirection> ().ToList ();
  473. var directionOptions = new OptionSelector
  474. {
  475. X = Pos.Right (container) + 1,
  476. Y = Pos.Bottom (wrapCheckbox) + 1,
  477. Width = Dim.Fill (10),
  478. Height = Dim.Fill (1),
  479. HotKeySpecifier = (Rune)'\xffff',
  480. Labels = directionsEnum.Select (e => e.ToString ()).ToArray ()
  481. };
  482. directionOptions.ValueChanged += (s, ev) =>
  483. {
  484. bool justChecked = justifyCheckbox.CheckedState == CheckState.Checked;
  485. if (justChecked)
  486. {
  487. ToggleJustify (true);
  488. }
  489. foreach (View v in multiLineLabels.Where (v => ev.Value is { }))
  490. {
  491. v.TextDirection = (TextDirection)ev.Value!.Value;
  492. }
  493. if (justChecked)
  494. {
  495. ToggleJustify (false);
  496. }
  497. };
  498. app.Add (directionOptions);
  499. Application.Run (app);
  500. app.Dispose ();
  501. Application.Shutdown ();
  502. // Be a good citizen and remove the schemes we added
  503. SchemeManager.RemoveScheme ("TextAlignmentAndDirection1");
  504. SchemeManager.RemoveScheme ("TextAlignmentAndDirection2");
  505. return;
  506. void ToggleJustify (bool oldValue, bool wasJustOptions = false)
  507. {
  508. if (oldValue)
  509. {
  510. if (!wasJustOptions)
  511. {
  512. justifyOptions.Enabled = false;
  513. }
  514. foreach (View t in multiLineLabels)
  515. {
  516. var data = (TextAlignmentData)t.Data;
  517. t.TextAlignment = data!.h;
  518. t.VerticalTextAlignment = data.v;
  519. }
  520. }
  521. else
  522. {
  523. foreach (View t in multiLineLabels)
  524. {
  525. if (!wasJustOptions)
  526. {
  527. justifyOptions.Enabled = true;
  528. }
  529. var data = (TextAlignmentData)t.Data;
  530. if (TextFormatter.IsVerticalDirection (t.TextDirection))
  531. {
  532. switch (justifyOptions.Value)
  533. {
  534. case 0:
  535. t.VerticalTextAlignment = Alignment.Fill;
  536. t.TextAlignment = data!.h;
  537. break;
  538. case 1:
  539. t.VerticalTextAlignment = data!.v;
  540. t.TextAlignment = Alignment.Fill;
  541. break;
  542. case 2:
  543. t.VerticalTextAlignment = Alignment.Fill;
  544. t.TextAlignment = Alignment.Fill;
  545. break;
  546. }
  547. }
  548. else
  549. {
  550. switch (justifyOptions.Value)
  551. {
  552. case 0:
  553. t.TextAlignment = Alignment.Fill;
  554. t.VerticalTextAlignment = data!.v;
  555. break;
  556. case 1:
  557. t.TextAlignment = data!.h;
  558. t.VerticalTextAlignment = Alignment.Fill;
  559. break;
  560. case 2:
  561. t.TextAlignment = Alignment.Fill;
  562. t.VerticalTextAlignment = Alignment.Fill;
  563. break;
  564. }
  565. }
  566. }
  567. }
  568. }
  569. }
  570. private class TextAlignmentData (Alignment h, Alignment v)
  571. {
  572. public Alignment h { get; } = h;
  573. public Alignment v { get; } = v;
  574. }
  575. }