TextView.Core.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. using System.Globalization;
  2. namespace Terminal.Gui.Views;
  3. /// <summary>Core functionality - Fields, Constructor, and fundamental properties</summary>
  4. public partial class TextView
  5. {
  6. #region Fields
  7. private readonly HistoryText _historyText = new ();
  8. private bool _allowsReturn = true;
  9. private bool _allowsTab = true;
  10. private bool _clickWithSelecting;
  11. // The column we are tracking, or -1 if we are not tracking any column
  12. private int _columnTrack = -1;
  13. private bool _continuousFind;
  14. private bool _copyWithoutSelection;
  15. private string? _currentCaller;
  16. private CultureInfo? _currentCulture;
  17. private bool _isButtonShift;
  18. private bool _isButtonReleased;
  19. private bool _isDrawing;
  20. private bool _isReadOnly;
  21. private bool _lastWasKill;
  22. private int _leftColumn;
  23. private TextModel _model = new ();
  24. private bool _multiline = true;
  25. private Dim? _savedHeight;
  26. private int _selectionStartColumn, _selectionStartRow;
  27. private bool _shiftSelecting;
  28. private int _tabWidth = 4;
  29. private int _topRow;
  30. private bool _wordWrap;
  31. private WordWrapManager? _wrapManager;
  32. private bool _wrapNeeded;
  33. private string? _copiedText;
  34. private List<List<Cell>> _copiedCellsList = [];
  35. #endregion
  36. #region Constructor
  37. /// <summary>
  38. /// Initializes a <see cref="TextView"/> on the specified area, with dimensions controlled with the X, Y, Width
  39. /// and Height properties.
  40. /// </summary>
  41. public TextView ()
  42. {
  43. CanFocus = true;
  44. CursorVisibility = CursorVisibility.Default;
  45. Used = true;
  46. // By default, disable hotkeys (in case someone sets Title)
  47. base.HotKeySpecifier = new ('\xffff');
  48. _model.LinesLoaded += Model_LinesLoaded!;
  49. _historyText.ChangeText += HistoryText_ChangeText!;
  50. Initialized += TextView_Initialized!;
  51. SuperViewChanged += TextView_SuperViewChanged!;
  52. SubViewsLaidOut += TextView_LayoutComplete;
  53. // Things this view knows how to do
  54. // Note - NewLine is only bound to Enter if Multiline is true
  55. AddCommand (Command.NewLine, ctx => ProcessEnterKey (ctx));
  56. AddCommand (
  57. Command.PageDown,
  58. () =>
  59. {
  60. ProcessPageDown ();
  61. return true;
  62. }
  63. );
  64. AddCommand (
  65. Command.PageDownExtend,
  66. () =>
  67. {
  68. ProcessPageDownExtend ();
  69. return true;
  70. }
  71. );
  72. AddCommand (
  73. Command.PageUp,
  74. () =>
  75. {
  76. ProcessPageUp ();
  77. return true;
  78. }
  79. );
  80. AddCommand (
  81. Command.PageUpExtend,
  82. () =>
  83. {
  84. ProcessPageUpExtend ();
  85. return true;
  86. }
  87. );
  88. AddCommand (Command.Down, () => ProcessMoveDown ());
  89. AddCommand (
  90. Command.DownExtend,
  91. () =>
  92. {
  93. ProcessMoveDownExtend ();
  94. return true;
  95. }
  96. );
  97. AddCommand (Command.Up, () => ProcessMoveUp ());
  98. AddCommand (
  99. Command.UpExtend,
  100. () =>
  101. {
  102. ProcessMoveUpExtend ();
  103. return true;
  104. }
  105. );
  106. AddCommand (Command.Right, () => ProcessMoveRight ());
  107. AddCommand (
  108. Command.RightExtend,
  109. () =>
  110. {
  111. ProcessMoveRightExtend ();
  112. return true;
  113. }
  114. );
  115. AddCommand (Command.Left, () => ProcessMoveLeft ());
  116. AddCommand (
  117. Command.LeftExtend,
  118. () =>
  119. {
  120. ProcessMoveLeftExtend ();
  121. return true;
  122. }
  123. );
  124. AddCommand (
  125. Command.DeleteCharLeft,
  126. () =>
  127. {
  128. ProcessDeleteCharLeft ();
  129. return true;
  130. }
  131. );
  132. AddCommand (
  133. Command.LeftStart,
  134. () =>
  135. {
  136. ProcessMoveLeftStart ();
  137. return true;
  138. }
  139. );
  140. AddCommand (
  141. Command.LeftStartExtend,
  142. () =>
  143. {
  144. ProcessMoveLeftStartExtend ();
  145. return true;
  146. }
  147. );
  148. AddCommand (
  149. Command.DeleteCharRight,
  150. () =>
  151. {
  152. ProcessDeleteCharRight ();
  153. return true;
  154. }
  155. );
  156. AddCommand (
  157. Command.RightEnd,
  158. () =>
  159. {
  160. ProcessMoveEndOfLine ();
  161. return true;
  162. }
  163. );
  164. AddCommand (
  165. Command.RightEndExtend,
  166. () =>
  167. {
  168. ProcessMoveRightEndExtend ();
  169. return true;
  170. }
  171. );
  172. AddCommand (
  173. Command.CutToEndLine,
  174. () =>
  175. {
  176. KillToEndOfLine ();
  177. return true;
  178. }
  179. );
  180. AddCommand (
  181. Command.CutToStartLine,
  182. () =>
  183. {
  184. KillToLeftStart ();
  185. return true;
  186. }
  187. );
  188. AddCommand (
  189. Command.Paste,
  190. () =>
  191. {
  192. ProcessPaste ();
  193. return true;
  194. }
  195. );
  196. AddCommand (
  197. Command.ToggleExtend,
  198. () =>
  199. {
  200. ToggleSelecting ();
  201. return true;
  202. }
  203. );
  204. AddCommand (
  205. Command.Copy,
  206. () =>
  207. {
  208. ProcessCopy ();
  209. return true;
  210. }
  211. );
  212. AddCommand (
  213. Command.Cut,
  214. () =>
  215. {
  216. ProcessCut ();
  217. return true;
  218. }
  219. );
  220. AddCommand (
  221. Command.WordLeft,
  222. () =>
  223. {
  224. ProcessMoveWordBackward ();
  225. return true;
  226. }
  227. );
  228. AddCommand (
  229. Command.WordLeftExtend,
  230. () =>
  231. {
  232. ProcessMoveWordBackwardExtend ();
  233. return true;
  234. }
  235. );
  236. AddCommand (
  237. Command.WordRight,
  238. () =>
  239. {
  240. ProcessMoveWordForward ();
  241. return true;
  242. }
  243. );
  244. AddCommand (
  245. Command.WordRightExtend,
  246. () =>
  247. {
  248. ProcessMoveWordForwardExtend ();
  249. return true;
  250. }
  251. );
  252. AddCommand (
  253. Command.KillWordForwards,
  254. () =>
  255. {
  256. ProcessKillWordForward ();
  257. return true;
  258. }
  259. );
  260. AddCommand (
  261. Command.KillWordBackwards,
  262. () =>
  263. {
  264. ProcessKillWordBackward ();
  265. return true;
  266. }
  267. );
  268. AddCommand (
  269. Command.End,
  270. () =>
  271. {
  272. MoveBottomEnd ();
  273. return true;
  274. }
  275. );
  276. AddCommand (
  277. Command.EndExtend,
  278. () =>
  279. {
  280. MoveBottomEndExtend ();
  281. return true;
  282. }
  283. );
  284. AddCommand (
  285. Command.Start,
  286. () =>
  287. {
  288. MoveTopHome ();
  289. return true;
  290. }
  291. );
  292. AddCommand (
  293. Command.StartExtend,
  294. () =>
  295. {
  296. MoveTopHomeExtend ();
  297. return true;
  298. }
  299. );
  300. AddCommand (
  301. Command.SelectAll,
  302. () =>
  303. {
  304. ProcessSelectAll ();
  305. return true;
  306. }
  307. );
  308. AddCommand (
  309. Command.ToggleOverwrite,
  310. () =>
  311. {
  312. ProcessSetOverwrite ();
  313. return true;
  314. }
  315. );
  316. AddCommand (
  317. Command.EnableOverwrite,
  318. () =>
  319. {
  320. SetOverwrite (true);
  321. return true;
  322. }
  323. );
  324. AddCommand (
  325. Command.DisableOverwrite,
  326. () =>
  327. {
  328. SetOverwrite (false);
  329. return true;
  330. }
  331. );
  332. AddCommand (Command.Tab, () => ProcessTab ());
  333. AddCommand (Command.BackTab, () => ProcessBackTab ());
  334. AddCommand (
  335. Command.Undo,
  336. () =>
  337. {
  338. Undo ();
  339. return true;
  340. }
  341. );
  342. AddCommand (
  343. Command.Redo,
  344. () =>
  345. {
  346. Redo ();
  347. return true;
  348. }
  349. );
  350. AddCommand (
  351. Command.DeleteAll,
  352. () =>
  353. {
  354. DeleteAll ();
  355. return true;
  356. }
  357. );
  358. AddCommand (
  359. Command.Context,
  360. () =>
  361. {
  362. ShowContextMenu (null);
  363. return true;
  364. }
  365. );
  366. AddCommand (
  367. Command.Open,
  368. () =>
  369. {
  370. PromptForColors ();
  371. return true;
  372. });
  373. // Default keybindings for this view
  374. KeyBindings.Remove (Key.Space);
  375. KeyBindings.Remove (Key.Enter);
  376. KeyBindings.Add (Key.Enter, Multiline ? Command.NewLine : Command.Accept);
  377. KeyBindings.Add (Key.PageDown, Command.PageDown);
  378. KeyBindings.Add (Key.V.WithCtrl, Command.PageDown);
  379. KeyBindings.Add (Key.PageDown.WithShift, Command.PageDownExtend);
  380. KeyBindings.Add (Key.PageUp, Command.PageUp);
  381. KeyBindings.Add (Key.PageUp.WithShift, Command.PageUpExtend);
  382. KeyBindings.Add (Key.N.WithCtrl, Command.Down);
  383. KeyBindings.Add (Key.CursorDown, Command.Down);
  384. KeyBindings.Add (Key.CursorDown.WithShift, Command.DownExtend);
  385. KeyBindings.Add (Key.P.WithCtrl, Command.Up);
  386. KeyBindings.Add (Key.CursorUp, Command.Up);
  387. KeyBindings.Add (Key.CursorUp.WithShift, Command.UpExtend);
  388. KeyBindings.Add (Key.F.WithCtrl, Command.Right);
  389. KeyBindings.Add (Key.CursorRight, Command.Right);
  390. KeyBindings.Add (Key.CursorRight.WithShift, Command.RightExtend);
  391. KeyBindings.Add (Key.B.WithCtrl, Command.Left);
  392. KeyBindings.Add (Key.CursorLeft, Command.Left);
  393. KeyBindings.Add (Key.CursorLeft.WithShift, Command.LeftExtend);
  394. KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
  395. KeyBindings.Add (Key.Home, Command.LeftStart);
  396. KeyBindings.Add (Key.Home.WithShift, Command.LeftStartExtend);
  397. KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
  398. KeyBindings.Add (Key.D.WithCtrl, Command.DeleteCharRight);
  399. KeyBindings.Add (Key.End, Command.RightEnd);
  400. KeyBindings.Add (Key.E.WithCtrl, Command.RightEnd);
  401. KeyBindings.Add (Key.End.WithShift, Command.RightEndExtend);
  402. KeyBindings.Add (Key.K.WithCtrl, Command.CutToEndLine); // kill-to-end
  403. KeyBindings.Add (Key.Delete.WithCtrl.WithShift, Command.CutToEndLine); // kill-to-end
  404. KeyBindings.Add (Key.Backspace.WithCtrl.WithShift, Command.CutToStartLine); // kill-to-start
  405. KeyBindings.Add (Key.Y.WithCtrl, Command.Paste); // Control-y, yank
  406. KeyBindings.Add (Key.Space.WithCtrl, Command.ToggleExtend);
  407. KeyBindings.Add (Key.C.WithCtrl, Command.Copy);
  408. KeyBindings.Add (Key.W.WithCtrl, Command.Cut); // Move to Unix?
  409. KeyBindings.Add (Key.X.WithCtrl, Command.Cut);
  410. KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.WordLeft);
  411. KeyBindings.Add (Key.CursorLeft.WithCtrl.WithShift, Command.WordLeftExtend);
  412. KeyBindings.Add (Key.CursorRight.WithCtrl, Command.WordRight);
  413. KeyBindings.Add (Key.CursorRight.WithCtrl.WithShift, Command.WordRightExtend);
  414. KeyBindings.Add (Key.Delete.WithCtrl, Command.KillWordForwards); // kill-word-forwards
  415. KeyBindings.Add (Key.Backspace.WithCtrl, Command.KillWordBackwards); // kill-word-backwards
  416. KeyBindings.Add (Key.End.WithCtrl, Command.End);
  417. KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.EndExtend);
  418. KeyBindings.Add (Key.Home.WithCtrl, Command.Start);
  419. KeyBindings.Add (Key.Home.WithCtrl.WithShift, Command.StartExtend);
  420. KeyBindings.Add (Key.A.WithCtrl, Command.SelectAll);
  421. KeyBindings.Add (Key.InsertChar, Command.ToggleOverwrite);
  422. KeyBindings.Add (Key.Tab, Command.Tab);
  423. KeyBindings.Add (Key.Tab.WithShift, Command.BackTab);
  424. KeyBindings.Add (Key.Z.WithCtrl, Command.Undo);
  425. KeyBindings.Add (Key.R.WithCtrl, Command.Redo);
  426. KeyBindings.Add (Key.G.WithCtrl, Command.DeleteAll);
  427. KeyBindings.Add (Key.D.WithCtrl.WithShift, Command.DeleteAll);
  428. KeyBindings.Add (Key.L.WithCtrl, Command.Open);
  429. #if UNIX_KEY_BINDINGS
  430. KeyBindings.Add (Key.C.WithAlt, Command.Copy);
  431. KeyBindings.Add (Key.B.WithAlt, Command.WordLeft);
  432. KeyBindings.Add (Key.W.WithAlt, Command.Cut);
  433. KeyBindings.Add (Key.V.WithAlt, Command.PageUp);
  434. KeyBindings.Add (Key.F.WithAlt, Command.WordRight);
  435. KeyBindings.Add (Key.K.WithAlt, Command.CutToStartLine); // kill-to-start
  436. #endif
  437. _currentCulture = Thread.CurrentThread.CurrentUICulture;
  438. }
  439. #endregion
  440. #region Initialization and Configuration
  441. private void TextView_Initialized (object sender, EventArgs e)
  442. {
  443. if (Autocomplete.HostControl is null)
  444. {
  445. Autocomplete.HostControl = this;
  446. }
  447. ContextMenu = CreateContextMenu ();
  448. App?.Popover?.Register (ContextMenu);
  449. KeyBindings.Add (ContextMenu.Key, Command.Context);
  450. // Configure ScrollBars to use modern View scrolling infrastructure
  451. ConfigureLayout ();
  452. OnContentsChanged ();
  453. }
  454. private void TextView_SuperViewChanged (object sender, SuperViewChangedEventArgs e)
  455. {
  456. if (e.SuperView is { })
  457. {
  458. if (Autocomplete.HostControl is null)
  459. {
  460. Autocomplete.HostControl = this;
  461. }
  462. }
  463. else
  464. {
  465. Autocomplete.HostControl = null;
  466. }
  467. }
  468. private void Model_LinesLoaded (object sender, EventArgs e)
  469. {
  470. // This call is not needed. Model_LinesLoaded gets invoked when
  471. // model.LoadString (value) is called. LoadString is called from one place
  472. // (Text.set) and historyText.Clear() is called immediately after.
  473. // If this call happens, HistoryText_ChangeText will get called multiple times
  474. // when Text is set, which is wrong.
  475. //historyText.Clear (Text);
  476. if (!_multiline && !IsInitialized)
  477. {
  478. CurrentColumn = Text.GetRuneCount ();
  479. _leftColumn = CurrentColumn > Viewport.Width + 1 ? CurrentColumn - Viewport.Width + 1 : 0;
  480. }
  481. }
  482. #endregion
  483. /// <summary>
  484. /// INTERNAL: Determines if a redraw is needed based on selection state, word wrap needs, and Used flag.
  485. /// If a redraw is needed, calls <see cref="AdjustScrollPosition"/>; otherwise positions the cursor and updates
  486. /// the unwrapped cursor position.
  487. /// </summary>
  488. private void DoNeededAction ()
  489. {
  490. if (!NeedsDraw && (IsSelecting || _wrapNeeded || !Used))
  491. {
  492. SetNeedsDraw ();
  493. }
  494. if (NeedsDraw)
  495. {
  496. AdjustScrollPosition ();
  497. }
  498. else
  499. {
  500. PositionCursor ();
  501. OnUnwrappedCursorPosition ();
  502. }
  503. }
  504. }