From 6fc0cd340818ed04d237c03b64a5960db2e770e5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Sat, 6 Feb 2021 17:55:00 +0100 Subject: [PATCH] TextLineCollection.update(TextChange ...) --- Crow/Default.style | 1 - Crow/src/Text/TextLine.cs | 9 +- Crow/src/Text/TextLineCollection.cs | 112 ++++++++++++++++-- Crow/src/Widgets/Label.cs | 57 +++------ Crow/src/Widgets/TextBox.cs | 4 +- .../ui/Interfaces/GraphicObject/textBox.crow | 39 ++++++ 6 files changed, 168 insertions(+), 54 deletions(-) create mode 100644 Samples/common/ui/Interfaces/GraphicObject/textBox.crow diff --git a/Crow/Default.style b/Crow/Default.style index 0dc85763..456376ec 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -88,7 +88,6 @@ TextBox { Focusable = "True"; //Text = "TextBox"; Margin = "1"; - //MouseCursor = "IBeam"; } Menu { Margin = "0"; diff --git a/Crow/src/Text/TextLine.cs b/Crow/src/Text/TextLine.cs index f5798a31..14d2ba08 100644 --- a/Crow/src/Text/TextLine.cs +++ b/Crow/src/Text/TextLine.cs @@ -35,10 +35,11 @@ namespace Crow.Text LengthIncludingLineBreak = 0; LengthInPixel = -1; } - public TextLine WithStartOffset (int start) => new TextLine (Start + start, End, EndIncludingLineBreak); - - - + public void SetLength (int newLength) { + LengthInPixel = -1; + Length = newLength; + } + public TextLine WithStartOffset (int start) => new TextLine (Start + start, End, EndIncludingLineBreak); public int CompareTo (TextLine other) => Start - other.Start; } } diff --git a/Crow/src/Text/TextLineCollection.cs b/Crow/src/Text/TextLineCollection.cs index 586c0d8c..b64c75e2 100644 --- a/Crow/src/Text/TextLineCollection.cs +++ b/Crow/src/Text/TextLineCollection.cs @@ -24,8 +24,91 @@ namespace Crow.Text length = _lines.Length; } + + public LineCollection (string _text, int capacity = 4) : this (capacity) { + Update (_text.AsSpan ()); + } #endregion + public void Update (ReadOnlySpan _text) { + length = 0; + int start = 0, i = 0; + while (i < _text.Length) { + char c = _text[i]; + if (c == '\r') { + if (++i < _text.Length) { + if (_text[i] == '\n') + Add (new TextLine (start, i - 1, ++i)); + else + Add (new TextLine (start, i - 1, i)); + } else + Add (new TextLine (start, i - 1, i)); + start = i; + } else if (c == '\n') { + if (++i < _text.Length) { + if (_text[i] == '\r') + Add (new TextLine (start, i - 1, ++i)); + else + Add (new TextLine (start, i - 1, i)); + } else + Add (new TextLine (start, i - 1, i)); + start = i; + + } else if (c == '\u0085' || c == '\u2028' || c == '\u2029') + Add (new TextLine (start, i - 1, i)); + else + i++; + } + + if (start < i) + Add (new TextLine (start, _text.Length, _text.Length)); + else + Add (new TextLine (_text.Length, _text.Length, _text.Length)); + } + + public void Update (TextChange change) { + CharLocation locStart = GetLocation (change.Start); + int charsDiff = change.ChangedText.Length - change.Length; + int lineEnd = locStart.Line; + while (lineEnd < length - 1 && change.End >= lines[lineEnd + 1].Start) + lineEnd++; + int columnEnd = change.End - lines[lineEnd].Start; + int lineEndLineBreakLength = lines[lineEnd].LineBreakLength; + + LineCollection newLines = new LineCollection (change.ChangedText); + int linesDiff = newLines.length - 1 - (lineEnd - locStart.Line); + TextLine endTl = lines[lineEnd]; + + if (linesDiff < 0) + RemoveAt (locStart.Line + 1, -linesDiff); + else if (linesDiff > 0) { + for (int i = 0; i < linesDiff; i++) + Insert (locStart.Line + 1, default); + } + + int remainingColumns = endTl.Length - columnEnd; + lineEnd += linesDiff; + lines[lineEnd].SetLength (0); + lines[locStart.Line].SetLength (locStart.Column + newLines[0].Length); + lines[lineEnd].Length += remainingColumns; + if (newLines.Count > 1) { + lines[lineEnd].Length += newLines[newLines.Count - 1].Length; + lines[locStart.Line].LengthIncludingLineBreak = lines[locStart.Line].Length + newLines[0].LineBreakLength; + } + lines[lineEnd].LengthIncludingLineBreak = lines[lineEnd].Length + endTl.LineBreakLength; + + for (int i = 1; i < newLines.Count - 1; i++) { + int l = locStart.Line + i; + lines[l] = newLines[i]; + lines[l].Start = lines[l - 1].EndIncludingLineBreak; + } + if (lineEnd > 0) + lines[lineEnd].Start = lines[lineEnd - 1].EndIncludingLineBreak; + + //shift start for remaining lines + for (int i = lineEnd + 1; i < length; i++) + lines[i].Start += charsDiff; + } public int GetAbsolutePosition (CharLocation loc) => lines[loc.Line].Start + loc.Column; public CharLocation GetLocation (int absolutePosition) { TextLine tl = new TextLine (absolutePosition); @@ -76,26 +159,39 @@ namespace Crow.Text length--; return true; } - public IEnumerator GetEnumerator () => new LineEnumerator (this); + public IEnumerator GetEnumerator () => new Enumerator (this); IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); - public int IndexOf (TextLine item) { - throw new NotImplementedException (); - } + public int IndexOf (TextLine item) => Array.IndexOf (lines, item); public void Insert (int index, TextLine item) { - throw new NotImplementedException (); + if (lines.Length < length + 1) { + TextLine[] tmp = new TextLine[length * 2]; + lines.AsSpan (0, index).CopyTo (tmp); + lines.AsSpan (index).CopyTo (tmp.AsSpan (index + 1)); + lines = tmp; + }else + lines.AsSpan (index, length - index).CopyTo (lines.AsSpan (index + 1)); + lines[index] = item; + length++; } public void RemoveAt (int index) { - throw new NotImplementedException (); + if (index + 1 < length) + lines.AsSpan (index + 1, length - index - 1).CopyTo (lines.AsSpan (index)); + length--; + } + public void RemoveAt (int index, int count) { + if (index + count < length) + lines.AsSpan (index + count, length - index - count).CopyTo (lines.AsSpan (index)); + length -= count; } - public class LineEnumerator : IEnumerator + public class Enumerator : IEnumerator { TextLine[] lines; int length, position = -1; - public LineEnumerator (LineCollection coll) { + public Enumerator (LineCollection coll) { lines = coll.lines; length = coll.length; } diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index e176f836..6033a6f4 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -255,47 +255,12 @@ namespace Crow else lines.Clear (); - if (string.IsNullOrEmpty (_text)) { + if (string.IsNullOrEmpty (_text)) lines.Add (new TextLine (0, 0, 0)); - return; - } - if (!_multiline) { + else if (!_multiline) lines.Add (new TextLine (0, _text.Length, _text.Length)); - return; - } - - int start = 0, i = 0; - while (i < _text.Length) { - char c = _text[i]; - if (c == '\r') { - if (++i < _text.Length) { - if (_text[i] == '\n') - lines.Add (new TextLine (start, i - 1, ++i)); - else - lines.Add (new TextLine (start, i - 1, i)); - } else - lines.Add (new TextLine (start, i - 1, i)); - start = i; - } else if (c == '\n') { - if (++i < _text.Length) { - if (_text[i] == '\r') - lines.Add (new TextLine (start, i - 1, ++i)); - else - lines.Add (new TextLine (start, i - 1, i)); - } else - lines.Add (new TextLine (start, i - 1, i)); - start = i; - - } else if (c == '\u0085' || c == '\u2028' || c == '\u2029') - lines.Add (new TextLine (start, i - 1, i)); - else - i++; - } - - if (start < i) - lines.Add (new TextLine (start, _text.Length, _text.Length)); else - lines.Add (new TextLine (_text.Length, _text.Length, _text.Length)); + lines.Update (_text); } /// /// Current Selected text span. @@ -417,6 +382,14 @@ namespace Crow gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent); gr.ShowText (bytes.Slice (0, encodedBytes)); } + /********** DEBUG TextLineCollection ************* + gr.SetSource (Colors.Red); + gr.SetFontSize (9); + gr.MoveTo (700, lineRect.Y + fe.Ascent); + gr.ShowText ($"({lines[i].Start}, {lines[i].End}, {lines[i].EndIncludingLineBreak})"); + gr.SetFontSize (Font.Size); + Foreground.SetAsSource (IFace, gr); + ********** DEBUG TextLineCollection *************/ if (HasFocus && selectionNotEmpty) { RectangleD selRect = lineRect; @@ -430,7 +403,7 @@ namespace Crow selRect.Width -= (newX - selRect.X) - 10.0; selRect.X = newX; } else if (i == selEnd.Line) { - selRect.Width = selEnd.VisualCharXPosition - selRect.X; + selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X; } else selRect.Width += 10.0; } else { @@ -519,6 +492,12 @@ namespace Crow if (loc.Column >= 0) { //int encodedBytes = Crow.Text.Encoding2.ToUtf8 (curLine.Slice (0, loc.Column), bytes); + //DEBUG + if (loc.Column > curLine.Length) { + Console.WriteLine ($"loc.Column: {loc.Column} curLine.Length:{curLine.Length}"); + loc.Column = curLine.Length; + } + loc.VisualCharXPosition = gr.TextExtents (curLine.Slice (0, loc.Column), Interface.TAB_SIZE).XAdvance + cPos; location = loc; return; diff --git a/Crow/src/Widgets/TextBox.cs b/Crow/src/Widgets/TextBox.cs index fc49df2b..d297caae 100644 --- a/Crow/src/Widgets/TextBox.cs +++ b/Crow/src/Widgets/TextBox.cs @@ -326,10 +326,10 @@ namespace Crow src.Slice (0, change.Start).CopyTo (tmp); change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); - _text = tmp.ToString (); - getLines (); + lines.Update (change); + //lines.Update (_text); selectionStart = null; currentLoc = lines.GetLocation (change.Start + change.ChangedText.Length); diff --git a/Samples/common/ui/Interfaces/GraphicObject/textBox.crow b/Samples/common/ui/Interfaces/GraphicObject/textBox.crow new file mode 100644 index 00000000..2094de4b --- /dev/null +++ b/Samples/common/ui/Interfaces/GraphicObject/textBox.crow @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- 2.47.3