From 045090999141b8ca6b0e40a5938932b798ab022e Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 22 Dec 2020 00:32:54 +0000 Subject: [PATCH 1/5] Fixes #93. Audit TextView just like we did TextField to ensure proper treatment of Unicode --- Terminal.Gui/Views/TextField.cs | 4 ++-- Terminal.Gui/Views/TextView.cs | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 2c77a87d4..9b902746c 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -241,7 +241,7 @@ namespace Terminal.Gui { PositionCursor (); } - static int SetCol (int col, int width, int cols) + internal static int SetCol (int col, int width, int cols) { if (col + cols <= width) { col += cols; @@ -805,7 +805,7 @@ namespace Terminal.Gui { return point; } - int GetPointFromX (List t, int start, int x) + internal static int GetPointFromX (List t, int start, int x) { if (x < 0) { return x; diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 76429274c..3b9f2c4fa 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -421,11 +421,18 @@ namespace Terminal.Gui { } var line = model.GetLine (currentRow); var retreat = 0; + var col = 0; if (line.Count > 0) { retreat = Math.Max ((SpecialRune (line [Math.Max (CurrentColumn - leftColumn - 1, 0)]) ? 1 : 0), 0); + for (int idx = leftColumn < 0 ? 0 : leftColumn; idx < line.Count; idx++) { + if (idx == CurrentColumn) + break; + var cols = Rune.ColumnWidth (line [idx]); + col += cols - 1; + } } - Move (CurrentColumn - leftColumn - retreat, CurrentRow - topRow); + Move (CurrentColumn - leftColumn - retreat + col, CurrentRow - topRow); } void ClearRegion (int left, int top, int right, int bottom) @@ -569,10 +576,12 @@ namespace Terminal.Gui { } Move (bounds.Left, row); - for (int col = bounds.Left; col < right; col++) { - var lineCol = leftColumn + col; + var col = 0; + for (int idx = bounds.Left; idx < right; idx++) { + var lineCol = leftColumn + idx; var rune = lineCol >= lineRuneCount ? ' ' : line [lineCol]; - if (selecting && PointInSelection (col, row)) { + var cols = Rune.ColumnWidth (rune); + if (selecting && PointInSelection (idx, row)) { ColorSelection (); } else { ColorNormal (); @@ -581,6 +590,7 @@ namespace Terminal.Gui { if (!SpecialRune (rune)) { AddRune (col, row, rune); } + col = TextField.SetCol (col, bounds.Right, cols); } } PositionCursor (); @@ -1265,10 +1275,12 @@ namespace Terminal.Gui { currentRow = ev.Y + topRow; } var r = GetCurrentLine (); - if (ev.X - leftColumn >= r.Count) + var idx = TextField.GetPointFromX (r, leftColumn, ev.X); + if (idx - leftColumn >= r.Count) { currentColumn = r.Count - leftColumn; - else - currentColumn = ev.X - leftColumn; + } else { + currentColumn = idx - leftColumn; + } } PositionCursor (); } else if (ev.Flags == MouseFlags.WheeledDown) { From fe311ea54bcab83438f1c3dd3998f30d3ca34f51 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 22 Dec 2020 12:02:07 +0000 Subject: [PATCH 2/5] Moving common methods to the TextModel class. --- Terminal.Gui/Views/TextField.cs | 34 ++++--------------------------- Terminal.Gui/Views/TextView.cs | 36 +++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 9b902746c..275eac475 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -195,7 +195,7 @@ namespace Terminal.Gui { if (idx == point) break; var cols = Rune.ColumnWidth (text [idx]); - col = SetCol (col, Frame.Width - 1, cols); + col = TextModel.SetCol (col, Frame.Width - 1, cols); } Move (col, 0); } @@ -227,7 +227,7 @@ namespace Terminal.Gui { if (col + cols <= width) { Driver.AddRune ((Rune)(Secret ? '*' : rune)); } - col = SetCol (col, width, cols); + col = TextModel.SetCol (col, width, cols); if (idx + 1 < tcount && col + Rune.ColumnWidth (text [idx + 1]) > width) { break; } @@ -241,15 +241,6 @@ namespace Terminal.Gui { PositionCursor (); } - internal static int SetCol (int col, int width, int cols) - { - if (col + cols <= width) { - col += cols; - } - - return col; - } - // Returns the size and length in a range of the string. (int size, int length) DisplaySize (List t, int start = -1, int end = -1, bool checkNextRune = true) { @@ -779,7 +770,7 @@ namespace Terminal.Gui { { // We could also set the cursor position. int x; - var pX = GetPointFromX (text, first, ev.X); + var pX = TextModel.GetColFromX (text, first, ev.X); if (text.Count == 0) { x = pX - ev.OfX; } else { @@ -792,7 +783,7 @@ namespace Terminal.Gui { { int pX = x; if (getX) { - pX = GetPointFromX (text, first, x); + pX = TextModel.GetColFromX (text, first, x); } if (first + pX > text.Count) { point = text.Count; @@ -805,23 +796,6 @@ namespace Terminal.Gui { return point; } - internal static int GetPointFromX (List t, int start, int x) - { - if (x < 0) { - return x; - } - int size = start; - var pX = x + start; - for (int i = start; i < t.Count; i++) { - var r = t [i]; - size += Rune.ColumnWidth (r); - if (i == pX || (size > pX)) { - return i - start; - } - } - return t.Count - start; - } - void PrepareSelection (int x, int direction = 0) { x = x + first < 0 ? 0 : x; diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 3b9f2c4fa..dc420b972 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -171,6 +171,34 @@ namespace Terminal.Gui { { lines.RemoveAt (pos); } + + internal static int SetCol (int col, int width, int cols) + { + if (col + cols <= width) { + col += cols; + } + + return col; + } + + internal static int GetColFromX (List t, int start, int x) + { + if (x < 0) { + return x; + } + int size = start; + var pX = x + start; + for (int i = start; i < t.Count; i++) { + var r = t [i]; + size += Rune.ColumnWidth (r); + if (i == pX || (size > pX)) { + return i - start; + } + } + return t.Count - start; + } + + } /// @@ -590,7 +618,7 @@ namespace Terminal.Gui { if (!SpecialRune (rune)) { AddRune (col, row, rune); } - col = TextField.SetCol (col, bounds.Right, cols); + col = TextModel.SetCol (col, bounds.Right, cols); } } PositionCursor (); @@ -658,7 +686,7 @@ namespace Terminal.Gui { var line = GetCurrentLine (); - // Optmize single line + // Optimize single line if (lines.Count == 1) { line.InsertRange (currentColumn, lines [0]); currentColumn += lines [0].Count; @@ -683,7 +711,7 @@ namespace Terminal.Gui { var lastp = last.Count; last.InsertRange (last.Count, rest); - // Now adjjust column and row positions + // Now adjust column and row positions currentRow += lines.Count - 1; currentColumn = lastp; if (currentRow - topRow > Frame.Height) { @@ -1275,7 +1303,7 @@ namespace Terminal.Gui { currentRow = ev.Y + topRow; } var r = GetCurrentLine (); - var idx = TextField.GetPointFromX (r, leftColumn, ev.X); + var idx = TextModel.GetColFromX (r, leftColumn, ev.X); if (idx - leftColumn >= r.Count) { currentColumn = r.Count - leftColumn; } else { From 9c4b3e0c6bff503b6d1b5426bde1786f1a3f57d8 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 24 Dec 2020 22:21:47 +0000 Subject: [PATCH 3/5] Fixing some keys and mouse navigation. Moving a few more methods from the TextField to the TextModel class. --- Terminal.Gui/Views/TextField.cs | 68 +++----------- Terminal.Gui/Views/TextView.cs | 152 ++++++++++++++++++-------------- 2 files changed, 97 insertions(+), 123 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 275eac475..613fc3392 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -157,7 +157,7 @@ namespace Terminal.Gui { TextChanged?.Invoke (oldText); if (point > text.Count) { - point = Math.Max (DisplaySize (text, 0).size - 1, 0); + point = Math.Max (TextModel.DisplaySize (text, 0).size - 1, 0); } Adjust (); @@ -241,36 +241,15 @@ namespace Terminal.Gui { PositionCursor (); } - // Returns the size and length in a range of the string. - (int size, int length) DisplaySize (List t, int start = -1, int end = -1, bool checkNextRune = true) - { - if (t == null || t.Count == 0) { - return (0, 0); - } - int size = 0; - int len = 0; - int tcount = end == -1 ? t.Count : end > t.Count ? t.Count : end; - int i = start == -1 ? 0 : start; - for (; i < tcount; i++) { - var rune = t [i]; - size += Rune.ColumnWidth (rune); - len += Rune.RuneLen (rune); - if (checkNextRune && i == tcount - 1 && t.Count > tcount && Rune.ColumnWidth (t [i + 1]) > 1) { - size += Rune.ColumnWidth (t [i + 1]); - len += Rune.RuneLen (t [i + 1]); - } - } - return (size, len); - } - void Adjust () { int offB = OffSetBackground (); if (point < first) { first = point; } else if (first + point - (Frame.Width + offB) == 0 || - DisplaySize (text, first, point).size >= Frame.Width + offB) { - first = Math.Max (CalculateFirst (text, first, point, Frame.Width - 1 + offB), 0); + TextModel.DisplaySize (text, first, point).size >= Frame.Width + offB) { + first = Math.Max (TextModel.CalculateLeftColumn (text, first, + point, Frame.Width - 1 + offB, point), 0); } SetNeedsDisplay (); } @@ -285,33 +264,6 @@ namespace Terminal.Gui { return offB; } - int CalculateFirst (List t, int start, int end, int width) - { - if (t == null) { - return 0; - } - (var dSize, _) = DisplaySize (t, start, end); - if (dSize < width) { - return start; - } - int size = 0; - int tcount = end > t.Count - 1 ? t.Count - 1 : end; - int col = 0; - for (int i = tcount; i > start; i--) { - var rune = t [i]; - var s = Rune.ColumnWidth (rune); - size += s; - if (size >= dSize - width) { - col = tcount - i + start; - if (start == 0 || col == start || (point == t.Count && (point - col > width))) { - col++; - } - break; - } - } - return col; - } - void SetText (List newText) { Text = ustring.Make (newText); @@ -871,9 +823,9 @@ namespace Terminal.Gui { ustring actualText = Text; int selStart = SelectedLength < 0 ? SelectedLength + SelectedStart : SelectedStart; int selLength = Math.Abs (SelectedLength); - (var _, var len) = DisplaySize (text, 0, selStart, false); - (var _, var len2) = DisplaySize (text, selStart, selStart + selLength, false); - (var _, var len3) = DisplaySize (text, selStart + selLength, actualText.RuneCount, false); + (var _, var len) = TextModel.DisplaySize (text, 0, selStart, false); + (var _, var len2) = TextModel.DisplaySize (text, selStart, selStart + selLength, false); + (var _, var len3) = TextModel.DisplaySize (text, selStart + selLength, actualText.RuneCount, false); Text = actualText[0, len] + actualText[len + len2, len + len2 + len3]; ClearAllSelection (); @@ -893,9 +845,9 @@ namespace Terminal.Gui { SetSelectedStartSelectedLength (); int selStart = start == -1 ? CursorPosition : start; ustring actualText = Text; - (int _, int len) = DisplaySize (text, 0, selStart, false); - (var _, var len2) = DisplaySize (text, selStart, selStart + length, false); - (var _, var len3) = DisplaySize (text, selStart + length, actualText.RuneCount, false); + (int _, int len) = TextModel.DisplaySize (text, 0, selStart, false); + (var _, var len2) = TextModel.DisplaySize (text, selStart, selStart + length, false); + (var _, var len3) = TextModel.DisplaySize (text, selStart + length, actualText.RuneCount, false); ustring cbTxt = Clipboard.Contents ?? ""; Text = actualText [0, len] + cbTxt + diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index dc420b972..88b000284 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -198,7 +198,55 @@ namespace Terminal.Gui { return t.Count - start; } + // Returns the size and length in a range of the string. + internal static (int size, int length) DisplaySize (List t, int start = -1, int end = -1, bool checkNextRune = true) + { + if (t == null || t.Count == 0) { + return (0, 0); + } + int size = 0; + int len = 0; + int tcount = end == -1 ? t.Count : end > t.Count ? t.Count : end; + int i = start == -1 ? 0 : start; + for (; i < tcount; i++) { + var rune = t [i]; + size += Rune.ColumnWidth (rune); + len += Rune.RuneLen (rune); + if (checkNextRune && i == tcount - 1 && t.Count > tcount && Rune.ColumnWidth (t [i + 1]) > 1) { + size += Rune.ColumnWidth (t [i + 1]); + len += Rune.RuneLen (t [i + 1]); + } + } + return (size, len); + } + // Returns the left column in a range of the string. + internal static int CalculateLeftColumn (List t, int start, int end, int width, int currentColumn) + { + if (t == null) { + return 0; + } + (var dSize, _) = TextModel.DisplaySize (t, start, end); + if (dSize < width) { + return start; + } + int size = 0; + int tcount = end > t.Count - 1 ? t.Count - 1 : end; + int col = 0; + for (int i = tcount; i > start; i--) { + var rune = t [i]; + var s = Rune.ColumnWidth (rune); + size += s; + if (size >= dSize - width) { + col = tcount - i + start; + if (start == 0 || col == start || (currentColumn == t.Count && (currentColumn - col > width))) { + col++; + } + break; + } + } + return col; + } } /// @@ -679,10 +727,15 @@ namespace Terminal.Gui { void InsertText (ustring text) { + if (ustring.IsNullOrEmpty (text)) { + return; + } + var lines = TextModel.StringToRunes (text); - if (lines.Count == 0) + if (lines.Count == 0) { return; + } var line = GetCurrentLine (); @@ -690,8 +743,9 @@ namespace Terminal.Gui { if (lines.Count == 1) { line.InsertRange (currentColumn, lines [0]); currentColumn += lines [0].Count; - if (currentColumn - leftColumn > Frame.Width) + if (currentColumn - leftColumn > Frame.Width) { leftColumn = currentColumn - Frame.Width + 1; + } SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, currentRow - topRow + 1)); return; } @@ -704,8 +758,9 @@ namespace Terminal.Gui { // First line is inserted at the current location, the rest is appended line.InsertRange (currentColumn, lines [0]); - for (int i = 1; i < lines.Count; i++) + for (int i = 1; i < lines.Count; i++) { model.AddLine (currentRow + i, lines [i]); + } var last = model.GetLine (currentRow + lines.Count - 1); var lastp = last.Count; @@ -714,16 +769,7 @@ namespace Terminal.Gui { // Now adjust column and row positions currentRow += lines.Count - 1; currentColumn = lastp; - if (currentRow - topRow > Frame.Height) { - topRow = currentRow - Frame.Height + 1; - if (topRow < 0) - topRow = 0; - } - if (currentColumn < leftColumn) - leftColumn = currentColumn; - if (currentColumn - leftColumn >= Frame.Width) - leftColumn = currentColumn - Frame.Width + 1; - SetNeedsDisplay (); + Adjust (); } // The column we are tracking, or -1 if we are not tracking any column @@ -745,27 +791,40 @@ namespace Terminal.Gui { void Adjust () { + int offB = OffSetBackground (); + var line = GetCurrentLine (); bool need = false; if (currentColumn < leftColumn) { - currentColumn = leftColumn; + leftColumn = currentColumn; need = true; - } - if (currentColumn - leftColumn > Frame.Width) { - leftColumn = currentColumn - Frame.Width + 1; + } else if (currentColumn - leftColumn > Frame.Width + offB || + TextModel.DisplaySize (line, leftColumn, currentColumn).size >= Frame.Width + offB) { + leftColumn = Math.Max (TextModel.CalculateLeftColumn (line, leftColumn, + currentColumn, Frame.Width - 1 + offB, currentColumn), 0); need = true; } if (currentRow < topRow) { topRow = currentRow; need = true; - } - if (currentRow - topRow > Frame.Height) { + } else if (currentRow - topRow > Frame.Height) { topRow = currentRow - Frame.Height + 1; need = true; } - if (need) + if (need) { SetNeedsDisplay (); - else + } else { PositionCursor (); + } + } + + int OffSetBackground () + { + int offB = 0; + if (SuperView?.Frame.Right - Frame.Right < 0) { + offB = SuperView.Frame.Right - Frame.Right - 1; + } + + return offB; } /// @@ -854,53 +913,33 @@ namespace Terminal.Gui { var currentLine = GetCurrentLine (); if (currentColumn < currentLine.Count) { currentColumn++; - if (currentColumn >= leftColumn + Frame.Width) { - leftColumn++; - SetNeedsDisplay (); - } - PositionCursor (); } else { if (currentRow + 1 < model.Count) { currentRow++; currentColumn = 0; - leftColumn = 0; if (currentRow >= topRow + Frame.Height) { topRow++; } - SetNeedsDisplay (); - PositionCursor (); } - break; } + Adjust (); break; case Key.B | Key.CtrlMask: case Key.CursorLeft: if (currentColumn > 0) { currentColumn--; - if (currentColumn < leftColumn) { - leftColumn--; - SetNeedsDisplay (); - } - PositionCursor (); } else { if (currentRow > 0) { currentRow--; if (currentRow < topRow) { topRow--; - SetNeedsDisplay (); } currentLine = GetCurrentLine (); currentColumn = currentLine.Count; - int prev = leftColumn; - leftColumn = currentColumn - Frame.Width + 1; - if (leftColumn < 0) - leftColumn = 0; - if (prev != leftColumn) - SetNeedsDisplay (); - PositionCursor (); } } + Adjust (); break; case Key.Delete: @@ -928,10 +967,7 @@ namespace Terminal.Gui { model.RemoveLine (currentRow); currentRow--; currentColumn = prevCount; - leftColumn = currentColumn - Frame.Width + 1; - if (leftColumn < 0) - leftColumn = 0; - SetNeedsDisplay (); + Adjust (); } break; @@ -939,11 +975,7 @@ namespace Terminal.Gui { case Key.Home: case Key.A | Key.CtrlMask: currentColumn = 0; - if (currentColumn < leftColumn) { - leftColumn = 0; - SetNeedsDisplay (); - } else - PositionCursor (); + Adjust (); break; case Key.DeleteChar: case Key.D | Key.CtrlMask: // Delete @@ -970,12 +1002,7 @@ namespace Terminal.Gui { currentLine = GetCurrentLine (); currentColumn = currentLine.Count; int pcol = leftColumn; - leftColumn = currentColumn - Frame.Width + 1; - if (leftColumn < 0) - leftColumn = 0; - if (pcol != leftColumn) - SetNeedsDisplay (); - PositionCursor (); + Adjust (); break; case Key.K | Key.CtrlMask: // kill-to-end @@ -1016,11 +1043,7 @@ namespace Terminal.Gui { selectionStartRow = currentRow; break; - case ((int)'w' + Key.AltMask): - SetClipboard (GetRegion ()); - selecting = false; - break; - + case ((int)'W' + Key.AltMask): case Key.W | Key.CtrlMask: SetClipboard (GetRegion ()); if (!isReadOnly) @@ -1050,7 +1073,6 @@ namespace Terminal.Gui { case Key.Enter: if (isReadOnly) break; - var orow = currentRow; currentLine = GetCurrentLine (); restCount = currentLine.Count - currentColumn; rest = currentLine.GetRange (currentColumn, restCount); @@ -1307,7 +1329,7 @@ namespace Terminal.Gui { if (idx - leftColumn >= r.Count) { currentColumn = r.Count - leftColumn; } else { - currentColumn = idx - leftColumn; + currentColumn = idx + leftColumn; } } PositionCursor (); From 56f2319227db6d955d20c4d5d4ea8430d3250fbd Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 25 Dec 2020 22:24:01 +0000 Subject: [PATCH 4/5] Added column scrolling feature and some more other improvements. --- Terminal.Gui/Views/TextField.cs | 2 - Terminal.Gui/Views/TextView.cs | 194 ++++++++++++++++++++++---------- 2 files changed, 133 insertions(+), 63 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 613fc3392..e0f85c27d 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -115,8 +115,6 @@ namespace Terminal.Gui { get => base.Frame; set { base.Frame = value; - var w = base.Frame.Width; - first = point > w ? point - w : 0; Adjust (); } } diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 88b000284..27eba5b2a 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -172,6 +172,25 @@ namespace Terminal.Gui { lines.RemoveAt (pos); } + /// + /// Returns the maximum line length of the visible lines. + /// + /// The first line. + /// The last line. + public int GetMaxVisibleLine (int first, int last) + { + int maxLength = 0; + last = last < lines.Count ? last : lines.Count; + for (int i = first; i < last; i++) { + var l = GetLine (i).Count; + if (l > maxLength) { + maxLength = l; + } + } + + return maxLength; + } + internal static int SetCol (int col, int width, int cols) { if (col + cols <= width) { @@ -432,6 +451,15 @@ namespace Terminal.Gui { } } + /// + public override Rect Frame { + get => base.Frame; + set { + base.Frame = value; + Adjust (); + } + } + /// /// Loads the contents of the file into the . /// @@ -508,7 +536,11 @@ namespace Terminal.Gui { col += cols - 1; } } - Move (CurrentColumn - leftColumn - retreat + col, CurrentRow - topRow); + var ccol = CurrentColumn - leftColumn - retreat + col; + if (leftColumn <= CurrentColumn && ccol < Frame.Width + && topRow <= CurrentRow && CurrentRow - topRow < Frame.Height) { + Move (ccol, CurrentRow - topRow); + } } void ClearRegion (int left, int top, int right, int bottom) @@ -791,23 +823,23 @@ namespace Terminal.Gui { void Adjust () { - int offB = OffSetBackground (); + var offB = OffSetBackground (); var line = GetCurrentLine (); bool need = false; if (currentColumn < leftColumn) { leftColumn = currentColumn; need = true; - } else if (currentColumn - leftColumn > Frame.Width + offB || - TextModel.DisplaySize (line, leftColumn, currentColumn).size >= Frame.Width + offB) { + } else if (currentColumn - leftColumn > Frame.Width + offB.width || + TextModel.DisplaySize (line, leftColumn, currentColumn).size >= Frame.Width + offB.width) { leftColumn = Math.Max (TextModel.CalculateLeftColumn (line, leftColumn, - currentColumn, Frame.Width - 1 + offB, currentColumn), 0); + currentColumn, Frame.Width - 1 + offB.width, currentColumn), 0); need = true; } if (currentRow < topRow) { topRow = currentRow; need = true; - } else if (currentRow - topRow > Frame.Height) { - topRow = currentRow - Frame.Height + 1; + } else if (currentRow - topRow >= Frame.Height + offB.height) { + topRow = Math.Min (Math.Max (currentRow - Frame.Height + 1, 0), currentRow); need = true; } if (need) { @@ -817,25 +849,43 @@ namespace Terminal.Gui { } } - int OffSetBackground () + (int width, int height) OffSetBackground () { - int offB = 0; + int w = 0; + int h = 0; if (SuperView?.Frame.Right - Frame.Right < 0) { - offB = SuperView.Frame.Right - Frame.Right - 1; + w = SuperView.Frame.Right - Frame.Right - 1; } - - return offB; + if (SuperView?.Frame.Bottom - Frame.Bottom < 0) { + h = SuperView.Frame.Bottom - Frame.Bottom - 1; + } + return (w, h); } /// /// Will scroll the to display the specified row at the top /// /// Row that should be displayed at the top, if the value is negative it will be reset to zero - public void ScrollTo (int row) + public void ScrollToRow (int row) { - if (row < 0) + if (row < 0) { row = 0; - topRow = row > model.Count ? model.Count - 1 : row; + } + topRow = row > model.Count - 1 ? model.Count - 1 : row; + SetNeedsDisplay (); + } + + /// + /// Will scroll the to display the specified column at the left + /// + /// Column that should be displayed at the left, if the value is negative it will be reset to zero + public void ScrollToCol (int col) + { + if (col < 0) { + col = 0; + } + var maxlength = model.GetMaxVisibleLine (topRow, topRow + Frame.Height); + leftColumn = col > maxlength - 1 ? maxlength - 1 : col; SetNeedsDisplay (); } @@ -883,7 +933,7 @@ namespace Terminal.Gui { break; case Key.PageUp: - case ((int)'v' + Key.AltMask): + case ((int)'V' + Key.AltMask): int nPageUpShift = Frame.Height - 1; if (currentRow > 0) { if (columnTrack == -1) @@ -1051,7 +1101,8 @@ namespace Terminal.Gui { selecting = false; break; - case (Key)((int)'b' + Key.AltMask): + case Key.CtrlMask | Key.CursorLeft: + case (Key)((int)'B' + Key.AltMask): var newPos = WordBackward (currentColumn, currentRow); if (newPos.HasValue) { currentColumn = newPos.Value.col; @@ -1061,7 +1112,8 @@ namespace Terminal.Gui { break; - case (Key)((int)'f' + Key.AltMask): + case Key.CtrlMask | Key.CursorRight: + case (Key)((int)'F' + Key.AltMask): newPos = WordForward (currentColumn, currentRow); if (newPos.HasValue) { currentColumn = newPos.Value.col; @@ -1123,11 +1175,12 @@ namespace Terminal.Gui { return true; } - private void MoveUp () + void MoveUp () { if (currentRow > 0) { - if (columnTrack == -1) + if (columnTrack == -1) { columnTrack = currentColumn; + } currentRow--; if (currentRow < topRow) { topRow--; @@ -1138,11 +1191,12 @@ namespace Terminal.Gui { } } - private void MoveDown () + void MoveDown () { if (currentRow + 1 < model.Count) { - if (columnTrack == -1) + if (columnTrack == -1) { columnTrack = currentColumn; + } currentRow++; if (currentRow >= topRow + Frame.Height) { topRow++; @@ -1150,6 +1204,8 @@ namespace Terminal.Gui { } TrackColumn (); PositionCursor (); + } else if (currentRow > Frame.Height) { + Adjust (); } } @@ -1246,28 +1302,31 @@ namespace Terminal.Gui { { var col = fromCol; var row = fromRow; - var line = GetCurrentLine (); - var rune = RuneAt (col, row); + try { + var rune = RuneAt (col, row); - var srow = row; - if (Rune.IsPunctuation (rune) || Rune.IsWhiteSpace (rune)) { - while (MoveNext (ref col, ref row, out rune)) { - if (Rune.IsLetterOrDigit (rune)) - break; - } - while (MoveNext (ref col, ref row, out rune)) { - if (!Rune.IsLetterOrDigit (rune)) - break; - } - } else { - while (MoveNext (ref col, ref row, out rune)) { - if (!Rune.IsLetterOrDigit (rune)) - break; + var srow = row; + if (Rune.IsPunctuation (rune) || Rune.IsWhiteSpace (rune)) { + while (MoveNext (ref col, ref row, out rune)) { + if (Rune.IsLetterOrDigit (rune)) + break; + } + while (MoveNext (ref col, ref row, out rune)) { + if (!Rune.IsLetterOrDigit (rune)) + break; + } + } else { + while (MoveNext (ref col, ref row, out rune)) { + if (!Rune.IsLetterOrDigit (rune)) + break; + } } + if (fromCol != col || fromRow != row) + return (col, row); + return null; + } catch (Exception) { + return null; } - if (fromCol != col || fromRow != row) - return (col, row); - return null; } (int col, int row)? WordBackward (int fromCol, int fromRow) @@ -1277,27 +1336,30 @@ namespace Terminal.Gui { var col = fromCol; var row = fromRow; - var line = GetCurrentLine (); - var rune = RuneAt (col, row); + try { + var rune = RuneAt (col, row); - if (Rune.IsPunctuation (rune) || Rune.IsSymbol (rune) || Rune.IsWhiteSpace (rune)) { - while (MovePrev (ref col, ref row, out rune)) { - if (Rune.IsLetterOrDigit (rune)) - break; - } - while (MovePrev (ref col, ref row, out rune)) { - if (!Rune.IsLetterOrDigit (rune)) - break; - } - } else { - while (MovePrev (ref col, ref row, out rune)) { - if (!Rune.IsLetterOrDigit (rune)) - break; + if (Rune.IsPunctuation (rune) || Rune.IsSymbol (rune) || Rune.IsWhiteSpace (rune)) { + while (MovePrev (ref col, ref row, out rune)) { + if (Rune.IsLetterOrDigit (rune)) + break; + } + while (MovePrev (ref col, ref row, out rune)) { + if (!Rune.IsLetterOrDigit (rune)) + break; + } + } else { + while (MovePrev (ref col, ref row, out rune)) { + if (!Rune.IsLetterOrDigit (rune)) + break; + } } + if (fromCol != col || fromRow != row) + return (col, row); + return null; + } catch (Exception) { + return null; } - if (fromCol != col || fromRow != row) - return (col, row); - return null; } /// @@ -1335,10 +1397,20 @@ namespace Terminal.Gui { PositionCursor (); } else if (ev.Flags == MouseFlags.WheeledDown) { lastWasKill = false; - MoveDown (); + columnTrack = currentColumn; + ScrollToRow (topRow + 1); } else if (ev.Flags == MouseFlags.WheeledUp) { lastWasKill = false; - MoveUp (); + columnTrack = currentColumn; + ScrollToRow (topRow - 1); + } else if (ev.Flags == MouseFlags.WheeledRight) { + lastWasKill = false; + columnTrack = currentColumn; + ScrollToCol (leftColumn + 1); + } else if (ev.Flags == MouseFlags.WheeledLeft) { + lastWasKill = false; + columnTrack = currentColumn; + ScrollToCol (leftColumn - 1); } return true; From fa57c20a48a457ed70ea412da81d0a25dcce90ff Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 27 Dec 2020 19:03:49 +0000 Subject: [PATCH 5/5] Allowing ScrollTo method to deal with rows and columns without breaking anything. --- Terminal.Gui/Views/TextView.cs | 40 +++++++++++++++------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 27eba5b2a..3284843f4 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -863,29 +863,23 @@ namespace Terminal.Gui { } /// - /// Will scroll the to display the specified row at the top + /// Will scroll the to display the specified row at the top if is true or + /// will scroll the to display the specified column at the left if is false. /// - /// Row that should be displayed at the top, if the value is negative it will be reset to zero - public void ScrollToRow (int row) + /// Row that should be displayed at the top or Column that should be displayed at the left, + /// if the value is negative it will be reset to zero + /// If true (default) the is a row, column otherwise. + public void ScrollTo (int idx, bool isRow = true) { - if (row < 0) { - row = 0; + if (idx < 0) { + idx = 0; } - topRow = row > model.Count - 1 ? model.Count - 1 : row; - SetNeedsDisplay (); - } - - /// - /// Will scroll the to display the specified column at the left - /// - /// Column that should be displayed at the left, if the value is negative it will be reset to zero - public void ScrollToCol (int col) - { - if (col < 0) { - col = 0; + if (isRow) { + topRow = idx > model.Count - 1 ? model.Count - 1 : idx; + } else { + var maxlength = model.GetMaxVisibleLine (topRow, topRow + Frame.Height); + leftColumn = idx > maxlength - 1 ? maxlength - 1 : idx; } - var maxlength = model.GetMaxVisibleLine (topRow, topRow + Frame.Height); - leftColumn = col > maxlength - 1 ? maxlength - 1 : col; SetNeedsDisplay (); } @@ -1398,19 +1392,19 @@ namespace Terminal.Gui { } else if (ev.Flags == MouseFlags.WheeledDown) { lastWasKill = false; columnTrack = currentColumn; - ScrollToRow (topRow + 1); + ScrollTo (topRow + 1); } else if (ev.Flags == MouseFlags.WheeledUp) { lastWasKill = false; columnTrack = currentColumn; - ScrollToRow (topRow - 1); + ScrollTo (topRow - 1); } else if (ev.Flags == MouseFlags.WheeledRight) { lastWasKill = false; columnTrack = currentColumn; - ScrollToCol (leftColumn + 1); + ScrollTo (leftColumn + 1, false); } else if (ev.Flags == MouseFlags.WheeledLeft) { lastWasKill = false; columnTrack = currentColumn; - ScrollToCol (leftColumn - 1); + ScrollTo (leftColumn - 1, false); } return true;