From 2513dbc0f9258769c8ad70ca73897cd237149430 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 28 Dec 2021 02:13:51 +0000 Subject: [PATCH 1/7] Fixes the CalculateLeftColumn method avoiding jump two columns on forward moving. --- Terminal.Gui/Views/TextField.cs | 2 +- Terminal.Gui/Views/TextView.cs | 5 ++++- UnitTests/TextViewTests.cs | 11 +++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 8b3d5f558..d88199359 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -90,7 +90,7 @@ namespace Terminal.Gui { this.text = TextModel.ToRunes (text.Split ("\n") [0]); point = text.RuneCount; - first = point > w ? point - w : 0; + first = point > w + 1 ? point - w + 1 : 0; CanFocus = true; Used = true; WantMousePositionReports = true; diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index a4d14e032..12cbdfa5c 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -303,7 +303,10 @@ namespace Terminal.Gui { if (rune == '\t') { size += tabWidth + 1; } - if (size >= width) { + if (size > width) { + if (col + width == end) { + col++; + } break; } else if (end < t.Count && col > 0 && start < end && col == start) { break; diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index 53866d7e2..981a891b9 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -1809,7 +1809,10 @@ namespace Terminal.Gui.Views { if (r == '\t') { sumLength += tabWidth + 1; } - if (sumLength >= width) { + if (sumLength > width) { + if (col + width == cCol) { + col++; + } break; } else if (cCol < line.Length && col > 0 && start < cCol && col == start) { break; @@ -1987,9 +1990,9 @@ line. Assert.Equal ((15, 15), TextModel.DisplaySize (txtRunes)); Assert.Equal ((6, 6), TextModel.DisplaySize (txtRunes, 1, 7)); - Assert.Equal (1, TextModel.CalculateLeftColumn (txtRunes, 0, 7, 8)); - Assert.Equal (2, TextModel.CalculateLeftColumn (txtRunes, 0, 8, 8)); - Assert.Equal (3, TextModel.CalculateLeftColumn (txtRunes, 0, 9, 8)); + Assert.Equal (0, TextModel.CalculateLeftColumn (txtRunes, 0, 7, 8)); + Assert.Equal (1, TextModel.CalculateLeftColumn (txtRunes, 0, 8, 8)); + Assert.Equal (2, TextModel.CalculateLeftColumn (txtRunes, 0, 9, 8)); var tm = new TextModel (); tm.AddLine (0, TextModel.ToRunes ("This is first line.")); From 3becde9c86db3a865f084cddd3514b26d9725e13 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 28 Dec 2021 16:17:39 +0000 Subject: [PATCH 2/7] Fixes #1525. Gives the same backspace behavior as TextView. --- Terminal.Gui/Views/TextField.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index d88199359..ba744c282 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -329,9 +329,8 @@ namespace Terminal.Gui { var oldCursorPos = point; switch (ShortcutHelper.GetModifiersKey (kb)) { - case Key.Delete: case Key.DeleteChar: - case Key.D | Key.CtrlMask: + case Key.D | Key.CtrlMask: // Delete if (ReadOnly) return true; @@ -346,6 +345,7 @@ namespace Terminal.Gui { } break; + case Key.Delete: case Key.Backspace: if (ReadOnly) return true; From 32db62044eaaa1a23e925e0085883e6b46908820 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Jan 2022 01:09:15 +0000 Subject: [PATCH 3/7] Changes kill-to-start key to work on Linux too. --- Terminal.Gui/Views/TextView.cs | 2 +- UnitTests/TextViewTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 12cbdfa5c..e62d1bd89 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2335,7 +2335,7 @@ namespace Terminal.Gui { lastWasKill = setLastWasKill; break; - case Key.Backspace | Key.CtrlMask | Key.ShiftMask: // kill-to-start + case Key.K | Key.AltMask: // kill-to-start if (isReadOnly) break; currentLine = GetCurrentLine (); diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index 981a891b9..fa29e5d85 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -974,7 +974,7 @@ namespace Terminal.Gui.Views { bool iterationsFinished = false; while (!iterationsFinished) { - _textView.ProcessKey (new KeyEvent (Key.Backspace | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ())); + _textView.ProcessKey (new KeyEvent (Key.K | Key.AltMask, new KeyModifiers ())); switch (iteration) { case 0: Assert.Equal (0, _textView.CursorPosition.X); From 3baf993b63d04d20f18c534330da27777d8e048e Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Jan 2022 01:12:47 +0000 Subject: [PATCH 4/7] Fixes SelectedStart, SelectedText and some cleaning. --- Terminal.Gui/Views/TextField.cs | 436 +++++++++++++++++++++----------- UnitTests/TextFieldTests.cs | 205 ++++++++++++++- 2 files changed, 491 insertions(+), 150 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index ba744c282..b0c23de4d 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -303,6 +303,8 @@ namespace Terminal.Gui { Clipboard.Contents = ustring.Make (text.ToList ()); } + int oldCursorPos; + /// /// Processes key presses for the . /// @@ -326,228 +328,117 @@ namespace Terminal.Gui { // remember current cursor position // because the new calculated cursor position is needed to be set BEFORE the change event is triggest // Needed for the Elmish Wrapper issue https://github.com/DieselMeister/Terminal.Gui.Elmish/issues/2 - var oldCursorPos = point; + oldCursorPos = point; switch (ShortcutHelper.GetModifiersKey (kb)) { case Key.DeleteChar: case Key.D | Key.CtrlMask: // Delete - if (ReadOnly) - return true; - - if (length == 0) { - if (text.Count == 0 || text.Count == point) - return true; - - SetText (text.GetRange (0, point).Concat (text.GetRange (point + 1, text.Count - (point + 1)))); - Adjust (); - } else { - DeleteSelectedText (); - } + DeleteCharRight (); break; case Key.Delete: case Key.Backspace: - if (ReadOnly) - return true; - - if (length == 0) { - if (point == 0) - return true; - - point--; - if (oldCursorPos < text.Count) { - SetText (text.GetRange (0, oldCursorPos - 1).Concat (text.GetRange (oldCursorPos, text.Count - oldCursorPos))); - } else { - SetText (text.GetRange (0, oldCursorPos - 1)); - } - Adjust (); - } else { - DeleteSelectedText (); - } + DeleteCharLeft (); break; case Key.Home | Key.ShiftMask: case Key.Home | Key.ShiftMask | Key.CtrlMask: case Key.A | Key.ShiftMask | Key.CtrlMask: - if (point > 0) { - int x = point; - point = 0; - PrepareSelection (x, point - x); - } + MoveHomeExtend (); break; case Key.End | Key.ShiftMask: case Key.End | Key.ShiftMask | Key.CtrlMask: case Key.E | Key.ShiftMask | Key.CtrlMask: - if (point <= text.Count) { - int x = point; - point = text.Count; - PrepareSelection (x, point - x); - } + MoveEndExtend (); break; // Home, C-A case Key.Home: case Key.Home | Key.CtrlMask: case Key.A | Key.CtrlMask: - ClearAllSelection (); - point = 0; - Adjust (); + MoveHome (); break; case Key.CursorLeft | Key.ShiftMask: case Key.CursorUp | Key.ShiftMask: - if (point > 0) { - PrepareSelection (point--, -1); - } + MoveLeftExtend (); break; case Key.CursorRight | Key.ShiftMask: case Key.CursorDown | Key.ShiftMask: - if (point < text.Count) { - PrepareSelection (point++, 1); - } + MoveRightExtend (); break; case Key.CursorLeft | Key.ShiftMask | Key.CtrlMask: case Key.CursorUp | Key.ShiftMask | Key.CtrlMask: case (Key)((int)'B' + Key.ShiftMask | Key.AltMask): - if (point > 0) { - int x = Math.Min (start > -1 && start > point ? start : point, text.Count); - if (x > 0) { - int sbw = WordBackward (x); - if (sbw != -1) - point = sbw; - PrepareSelection (x, sbw - x); - } - } + MoveWordLeftExtend (); break; case Key.CursorRight | Key.ShiftMask | Key.CtrlMask: case Key.CursorDown | Key.ShiftMask | Key.CtrlMask: case (Key)((int)'F' + Key.ShiftMask | Key.AltMask): - if (point < text.Count) { - int x = start > -1 && start > point ? start : point; - int sfw = WordForward (x); - if (sfw != -1) - point = sfw; - PrepareSelection (x, sfw - x); - } + MoveWordRightExtend (); break; case Key.CursorLeft: case Key.B | Key.CtrlMask: - ClearAllSelection (); - if (point > 0) { - point--; - Adjust (); - } + MoveLeft (); break; case Key.End: case Key.End | Key.CtrlMask: case Key.E | Key.CtrlMask: // End - ClearAllSelection (); - point = text.Count; - Adjust (); + MoveEnd (); break; case Key.CursorRight: case Key.F | Key.CtrlMask: - ClearAllSelection (); - if (point == text.Count) - break; - point++; - Adjust (); + MoveRight (); break; case Key.K | Key.CtrlMask: // kill-to-end - if (ReadOnly) - return true; + KillToEnd (); + break; - ClearAllSelection (); - if (point >= text.Count) - return true; - SetClipboard (text.GetRange (point, text.Count - point)); - SetText (text.GetRange (0, point)); - Adjust (); + case Key.K | Key.AltMask: // kill-to-start + KillToStart (); break; // Undo case Key.Z | Key.CtrlMask: - if (ReadOnly) - return true; - - if (historyText != null && historyText.Count > 0) { - isFromHistory = true; - if (idxhistoryText > 0) - idxhistoryText--; - if (idxhistoryText > -1) - Text = historyText [idxhistoryText]; - point = text.Count; - isFromHistory = false; - } + case Key.Backspace | Key.AltMask: + UndoChanges (); break; //Redo case Key.Y | Key.CtrlMask: // Control-y, yank - if (ReadOnly) - return true; - - if (historyText != null && historyText.Count > 0) { - isFromHistory = true; - if (idxhistoryText < historyText.Count - 1) { - idxhistoryText++; - if (idxhistoryText < historyText.Count) { - Text = historyText [idxhistoryText]; - } else if (idxhistoryText == historyText.Count - 1) { - Text = historyText [historyText.Count - 1]; - } - point = text.Count; - } - isFromHistory = false; - } - - //if (Clipboard.Contents == null) - // return true; - //var clip = TextModel.ToRunes (Clipboard.Contents); - //if (clip == null) - // return true; - - //if (point == text.Count) { - // point = text.Count; - // SetText(text.Concat(clip).ToList()); - //} else { - // point += clip.Count; - // SetText(text.GetRange(0, oldCursorPos).Concat(clip).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos))); - //} - //Adjust (); - + RedoChanges (); break; case Key.CursorLeft | Key.CtrlMask: case Key.CursorUp | Key.CtrlMask: case (Key)((int)'B' + Key.AltMask): - ClearAllSelection (); - int bw = WordBackward (point); - if (bw != -1) - point = bw; - Adjust (); + MoveWordLeft (); break; case Key.CursorRight | Key.CtrlMask: case Key.CursorDown | Key.CtrlMask: case (Key)((int)'F' + Key.AltMask): - ClearAllSelection (); - int fw = WordForward (point); - if (fw != -1) - point = fw; - Adjust (); + MoveWordRight (); + break; + + case Key.DeleteChar | Key.CtrlMask: // kill-word-forwards + KillWordForwards (); + break; + + case Key.Backspace | Key.CtrlMask: // kill-word-backwards + KillWordBackwards (); break; case Key.InsertChar: - Used = !Used; - SetNeedsDisplay (); + InsertChar (); break; case Key.C | Key.CtrlMask: @@ -555,9 +446,6 @@ namespace Terminal.Gui { break; case Key.X | Key.CtrlMask: - if (ReadOnly) - return true; - Cut (); break; @@ -603,6 +491,254 @@ namespace Terminal.Gui { return true; } + void InsertChar () + { + Used = !Used; + SetNeedsDisplay (); + } + + void KillWordBackwards () + { + ClearAllSelection (); + int bw = WordBackward (point); + if (bw != -1) { + SetText (text.GetRange (0, bw).Concat (text.GetRange (point, text.Count - point))); + point = bw; + } + Adjust (); + } + + void KillWordForwards () + { + ClearAllSelection (); + int fw = WordForward (point); + if (fw != -1) { + SetText (text.GetRange (0, point).Concat (text.GetRange (fw, text.Count - fw))); + } + Adjust (); + } + + void MoveWordRight () + { + ClearAllSelection (); + int fw = WordForward (point); + if (fw != -1) + point = fw; + Adjust (); + } + + void MoveWordLeft () + { + ClearAllSelection (); + int bw = WordBackward (point); + if (bw != -1) + point = bw; + Adjust (); + } + + void RedoChanges () + { + if (ReadOnly) + return; + + if (historyText != null && historyText.Count > 0) { + isFromHistory = true; + if (idxhistoryText < historyText.Count - 1) { + idxhistoryText++; + if (idxhistoryText < historyText.Count) { + Text = historyText [idxhistoryText]; + } else if (idxhistoryText == historyText.Count - 1) { + Text = historyText [historyText.Count - 1]; + } + point = text.Count; + } + isFromHistory = false; + } + + //if (Clipboard.Contents == null) + // return true; + //var clip = TextModel.ToRunes (Clipboard.Contents); + //if (clip == null) + // return true; + + //if (point == text.Count) { + // point = text.Count; + // SetText(text.Concat(clip).ToList()); + //} else { + // point += clip.Count; + // SetText(text.GetRange(0, oldCursorPos).Concat(clip).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos))); + //} + //Adjust (); + } + + void UndoChanges () + { + if (ReadOnly) + return; + + if (historyText != null && historyText.Count > 0) { + isFromHistory = true; + if (idxhistoryText > 0) + idxhistoryText--; + if (idxhistoryText > -1) + Text = historyText [idxhistoryText]; + point = text.Count; + isFromHistory = false; + } + } + + void KillToStart () + { + if (ReadOnly) + return; + + ClearAllSelection (); + if (point == 0) + return; + SetClipboard (text.GetRange (0, point)); + SetText (text.GetRange (point, text.Count - point)); + point = 0; + Adjust (); + } + + void KillToEnd () + { + if (ReadOnly) + return; + + ClearAllSelection (); + if (point >= text.Count) + return; + SetClipboard (text.GetRange (point, text.Count - point)); + SetText (text.GetRange (0, point)); + Adjust (); + } + + void MoveRight () + { + ClearAllSelection (); + if (point == text.Count) + return; + point++; + Adjust (); + } + + void MoveEnd () + { + ClearAllSelection (); + point = text.Count; + Adjust (); + } + + void MoveLeft () + { + ClearAllSelection (); + if (point > 0) { + point--; + Adjust (); + } + } + + void MoveWordRightExtend () + { + if (point < text.Count) { + int x = start > -1 && start > point ? start : point; + int sfw = WordForward (x); + if (sfw != -1) + point = sfw; + PrepareSelection (x, sfw - x); + } + } + + void MoveWordLeftExtend () + { + if (point > 0) { + int x = Math.Min (start > -1 && start > point ? start : point, text.Count); + if (x > 0) { + int sbw = WordBackward (x); + if (sbw != -1) + point = sbw; + PrepareSelection (x, sbw - x); + } + } + } + + void MoveRightExtend () + { + if (point < text.Count) { + PrepareSelection (point++, 1); + } + } + + void MoveLeftExtend () + { + if (point > 0) { + PrepareSelection (point--, -1); + } + } + + void MoveHome () + { + ClearAllSelection (); + point = 0; + Adjust (); + } + + void MoveEndExtend () + { + if (point <= text.Count) { + int x = point; + point = text.Count; + PrepareSelection (x, point - x); + } + } + + void MoveHomeExtend () + { + if (point > 0) { + int x = point; + point = 0; + PrepareSelection (x, point - x); + } + } + + void DeleteCharLeft () + { + if (ReadOnly) + return; + + if (length == 0) { + if (point == 0) + return; + + point--; + if (oldCursorPos < text.Count) { + SetText (text.GetRange (0, oldCursorPos - 1).Concat (text.GetRange (oldCursorPos, text.Count - oldCursorPos))); + } else { + SetText (text.GetRange (0, oldCursorPos - 1)); + } + Adjust (); + } else { + DeleteSelectedText (); + } + } + + void DeleteCharRight () + { + if (ReadOnly) + return; + + if (length == 0) { + if (text.Count == 0 || text.Count == point) + return; + + SetText (text.GetRange (0, point).Concat (text.GetRange (point + 1, text.Count - (point + 1)))); + Adjust (); + } else { + DeleteSelectedText (); + } + } + int WordForward (int p) { if (p >= text.Count) @@ -627,7 +763,7 @@ namespace Terminal.Gui { break; } for (; i < text.Count; i++) { - if (Rune.IsLetterOrDigit (text [i]) || + if (Rune.IsLetterOrDigit (text [i]) || (Rune.IsPunctuation (text [i]) && Rune.IsWhiteSpace (text [i - 1]))) break; } @@ -831,8 +967,10 @@ namespace Terminal.Gui { if (first > start) { first = start; } + } else if (start > -1 && length == 0) { + selectedText = null; } - } else if (length > 0) { + } else if (length > 0 || selectedText != null) { ClearAllSelection (); } Adjust (); @@ -843,7 +981,7 @@ namespace Terminal.Gui { /// public void ClearAllSelection () { - if (selectedStart == -1 && length == 0) + if (selectedStart == -1 && length == 0 && selectedText == "") return; selectedStart = -1; @@ -879,7 +1017,7 @@ namespace Terminal.Gui { /// public virtual void Cut () { - if (Secret || length == 0) + if (ReadOnly || Secret || length == 0) return; Clipboard.Contents = SelectedText; diff --git a/UnitTests/TextFieldTests.cs b/UnitTests/TextFieldTests.cs index 26c153f9a..54c20cdfc 100644 --- a/UnitTests/TextFieldTests.cs +++ b/UnitTests/TextFieldTests.cs @@ -886,5 +886,208 @@ namespace Terminal.Gui.Views { Assert.False (fv.CanFocus); Assert.False (fv.HasFocus); } + + [Fact] + [AutoInitShutdown] + public void KeyBindings_Command () + { + var tf = new TextField ("This is a test.") { Width = 20 }; + Assert.Equal (15, tf.Text.Length); + Assert.Equal (15, tf.CursorPosition); + Assert.False (tf.ReadOnly); + + Assert.True (tf.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ()))); + Assert.Equal ("This is a test.", tf.Text); + tf.CursorPosition = 0; + Assert.True (tf.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ()))); + Assert.Equal ("his is a test.", tf.Text); + tf.ReadOnly = true; + Assert.True (tf.ProcessKey (new KeyEvent (Key.D | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("his is a test.", tf.Text); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Delete, new KeyModifiers ()))); + Assert.Equal ("his is a test.", tf.Text); + tf.ReadOnly = false; + tf.CursorPosition = 1; + Assert.True (tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + tf.CursorPosition = 5; + Assert.True (tf.ProcessKey (new KeyEvent (Key.Home | Key.ShiftMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is", tf.SelectedText); + tf.CursorPosition = 5; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Home | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is", tf.SelectedText); + tf.CursorPosition = 5; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.A | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is", tf.SelectedText); + tf.CursorPosition = 5; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.End | Key.ShiftMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (" a test.", tf.SelectedText); + tf.CursorPosition = 5; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.End | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (" a test.", tf.SelectedText); + tf.CursorPosition = 5; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.E | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (" a test.", tf.SelectedText); + tf.CursorPosition = 5; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Home, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (0, tf.CursorPosition); + tf.CursorPosition = 5; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Home | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (0, tf.CursorPosition); + tf.CursorPosition = 5; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.A | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (0, tf.CursorPosition); + tf.CursorPosition = 5; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft | Key.ShiftMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("s", tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorUp | Key.ShiftMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is", tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight | Key.ShiftMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("s", tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorDown | Key.ShiftMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Null (tf.SelectedText); + tf.CursorPosition = 7; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("a", tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorUp | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is a", tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'B' + Key.ShiftMask | Key.AltMask), new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a", tf.SelectedText); + tf.CursorPosition = 3; + tf.SelectedStart = -1; + Assert.Null (tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is ", tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorDown | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is a ", tf.SelectedText); + Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'F' + Key.ShiftMask | Key.AltMask), new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is a test.", tf.SelectedText); + Assert.Equal (13, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Null (tf.SelectedText); + Assert.Equal (12, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (11, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.End, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (13, tf.CursorPosition); + tf.CursorPosition = 0; + Assert.True (tf.ProcessKey (new KeyEvent (Key.End | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (13, tf.CursorPosition); + tf.CursorPosition = 0; + Assert.True (tf.ProcessKey (new KeyEvent (Key.E | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (13, tf.CursorPosition); + tf.CursorPosition = 0; + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (1, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.F | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.Equal (2, tf.CursorPosition); + tf.CursorPosition = 9; + tf.ReadOnly = true; + Assert.True (tf.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + tf.ReadOnly = false; + Assert.True (tf.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("est.", Clipboard.Contents); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Z | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Backspace | Key.AltMask, new KeyModifiers ()))); + Assert.Equal ("is is a test.", tf.Text); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal (8, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorUp | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal (6, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'B' + Key.AltMask), new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal (3, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal (6, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorDown | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal (8, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'F' + Key.AltMask), new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal (9, tf.CursorPosition); + Assert.True (tf.Used); + Assert.True (tf.ProcessKey (new KeyEvent (Key.InsertChar, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal (9, tf.CursorPosition); + Assert.False (tf.Used); + tf.SelectedStart = 3; + tf.CursorPosition = 7; + Assert.Equal ("is a", tf.SelectedText); + Assert.Equal ("est.", Clipboard.Contents); + Assert.True (tf.ProcessKey (new KeyEvent (Key.C | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is a", Clipboard.Contents); + Assert.True (tf.ProcessKey (new KeyEvent (Key.X | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is t", tf.Text); + Assert.Equal ("is a", Clipboard.Contents); + Assert.True (tf.ProcessKey (new KeyEvent (Key.V | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is a", Clipboard.Contents); + Assert.Equal (7, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.K | Key.AltMask, new KeyModifiers ()))); + Assert.Equal (" t", tf.Text); + Assert.Equal ("is is a", Clipboard.Contents); + tf.Text = "TAB to jump between text fields."; + Assert.Equal (0, tf.CursorPosition); + Assert.True (tf.ProcessKey (new KeyEvent (Key.DeleteChar | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("to jump between text fields.", tf.Text); + tf.CursorPosition = tf.Text.Length; + Assert.True (tf.ProcessKey (new KeyEvent (Key.Backspace | Key.CtrlMask, new KeyModifiers ()))); + Assert.Equal ("to jump between text ", tf.Text); + } } -} +} \ No newline at end of file From eca55d7d4286376a92c70db70c0bbc3f26be75c4 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 15 Jan 2022 22:54:49 +0000 Subject: [PATCH 5/7] Allows navigation to outside a TextView if IsMdiContainer is true. --- Terminal.Gui/Views/TextView.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index e62d1bd89..e6f7597f2 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -1362,7 +1362,7 @@ namespace Terminal.Gui { } var posX = currentColumn - leftColumn; var posY = currentRow - topRow; - if ( posX > -1 && col >= posX && posX < Frame.Width - RightOffset + if (posX > -1 && col >= posX && posX < Frame.Width - RightOffset && topRow <= currentRow && posY < Frame.Height - BottomOffset) { ResetCursorVisibility (); Move (col, currentRow - topRow); @@ -2568,6 +2568,22 @@ namespace Terminal.Gui { } break; + case Key _ when ShortcutHelper.GetModifiersKey (kb) == (Key.Tab | Key.CtrlMask): + case Key _ when ShortcutHelper.GetModifiersKey (kb) == Application.AlternateForwardKey: + if (Application.MdiTop != null) { + return SuperView?.FocusNext () == true; + } + + return false; + + case Key _ when ShortcutHelper.GetModifiersKey (kb) == (Key.Tab | Key.CtrlMask | Key.ShiftMask): + case Key _ when ShortcutHelper.GetModifiersKey (kb) == Application.AlternateBackwardKey: + if (Application.MdiTop != null) { + return SuperView?.FocusPrev () == true; + } + + return false; + default: // Ignore control characters and other special keys if (kb.Key < Key.Space || kb.Key > Key.CharMask) From 4df5897318f84dee46593078ed13fd14ed311d04 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 23 Jan 2022 23:14:47 +0000 Subject: [PATCH 6/7] Fixes the cursor not being showing if the text length is equal to the view width. --- Terminal.Gui/Views/TextView.cs | 2 +- UnitTests/TextViewTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index e6f7597f2..5753c4d27 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -308,7 +308,7 @@ namespace Terminal.Gui { col++; } break; - } else if (end < t.Count && col > 0 && start < end && col == start) { + } else if ((end < t.Count && col > 0 && start < end && col == start) || (end - col == width - 1)) { break; } col = i; diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index fa29e5d85..8852d0b70 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -1814,7 +1814,7 @@ namespace Terminal.Gui.Views { col++; } break; - } else if (cCol < line.Length && col > 0 && start < cCol && col == start) { + } else if ((cCol < line.Length && col > 0 && start < cCol && col == start) || (cCol - col == width - 1)) { break; } col = i; From 11f29d45ea81510436131decbaf4339f751f287d Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 23 Jan 2022 23:37:46 +0000 Subject: [PATCH 7/7] A unit test to prove the 4df5897. --- UnitTests/TextViewTests.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index 8852d0b70..031b137f6 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -2262,5 +2262,26 @@ line. } } } + + [Fact] + public void LeftColumn_Add_One_If_Text_Length_Is_Equal_To_Width () + { + var tv = new TextView () { + Width = 10, + Text = "1234567890" + }; + + Assert.Equal (Point.Empty, tv.CursorPosition); + Assert.Equal (0, tv.LeftColumn); + + tv.CursorPosition = new Point (9, 0); + Assert.Equal (new Point (9, 0), tv.CursorPosition); + Assert.Equal (0, tv.LeftColumn); + + Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + tv.CursorPosition = new Point (10, 0); + Assert.Equal (new Point (10, 0), tv.CursorPosition); + Assert.Equal (1, tv.LeftColumn); + } } }