Focusable = "True";
//Text = "TextBox";
Margin = "1";
- //MouseCursor = "IBeam";
}
Menu {
Margin = "0";
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;
}
}
length = _lines.Length;
}
+
+ public LineCollection (string _text, int capacity = 4) : this (capacity) {
+ Update (_text.AsSpan ());
+ }
#endregion
+ public void Update (ReadOnlySpan<char> _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);
length--;
return true;
}
- public IEnumerator<TextLine> GetEnumerator () => new LineEnumerator (this);
+ public IEnumerator<TextLine> 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<TextLine>
+ public class Enumerator : IEnumerator<TextLine>
{
TextLine[] lines;
int length, position = -1;
- public LineEnumerator (LineCollection coll) {
+ public Enumerator (LineCollection coll) {
lines = coll.lines;
length = coll.length;
}
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);
}
/// <summary>
/// Current Selected text span.
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;
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 {
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;
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);
--- /dev/null
+<?xml version="1.0"?>
+<HorizontalStack >
+ <VerticalStack Background="Jet" Height="Stretched" Width="50%" Margin="10" >
+ <TextBox Margin="0" Text="Hello World this is a test string" Focusable="true" Font="serif,14"/>
+ <TextBox Margin="15" Text="Hello World this is a test string" Focusable="true" Font="serif,14"
+ Background="Grey" Focused="{Background=White}" Unfocused="{Background=Grey}"/>
+ <TextBox Name="lab" TextAlignment="Center" Width="Fit" Height="50" Text="Hello World this is a test string" Focusable="true" Font="serif,14"
+ Background="Grey" Focused="{Background=White}" Unfocused="{Background=Grey}"/>
+ <TextBox TextAlignment="{TextAlignment}" Multiline="false" Width="300" Height="Fit" Text="Hello World this is a test string" Focusable="true" Font="serif,14"
+ Background="Grey" Focused="{Background=White}" Unfocused="{Background=Grey}"/>
+ </VerticalStack>
+ <VerticalStack Background="Jet" Height="Stretched" Margin="10">
+ <TextBox Multiline="true" TextAlignment="{TextAlignment}" Width="320" Height="Fit"
+ Text="{MultilineText}" Focusable="true" Font="consolas,12"
+ Background="Grey" Focused="{Background=White}" Unfocused="{Background=Grey}"/>
+ <VerticalStack Width="300" Height="100">
+ <HorizontalStack>
+ <TextBox Name="tb" Multiline="true" TextAlignment="{TextAlignment}" Width="Stretched" Height="Stretched"
+ ClipToClientRect="true"
+ Text="{MultilineText}" Focusable="true" Font="consolas,12" Margin="10"
+ Background="Grey" Focused="{Background=White}" Unfocused="{Background=Grey}"
+ />
+ <ScrollBar Value="{²../tb.ScrollY}"
+ LargeIncrement="{../tb.PageHeight}" SmallIncrement="1"
+ CursorRatio="{../tb.ChildHeightRatio}" Maximum="{../tb.MaxScrollY}" />
+ </HorizontalStack>
+ <ScrollBar Style="HScrollBar" Value="{²../tb.ScrollX}"
+ LargeIncrement="{../tb.PageWidth}" SmallIncrement="1"
+ CursorRatio="{../tb.ChildWidthRatio}" Maximum="{../tb.MaxScrollX}" />
+ </VerticalStack>
+ <EnumSelector RadioButtonStyle="CheckBox2" Caption="Text Alignment" EnumValue="{²TextAlignment}" >
+ <Template>
+ <GroupBox Caption="{./Caption}" CornerRadius="{./CornerRadius}" Foreground="{./Foreground}" Background="{./Background}">
+ <VerticalStack Name="Content" Width="100"/>
+ </GroupBox>
+ </Template>
+ </EnumSelector>
+ </VerticalStack>
+</HorizontalStack>
\ No newline at end of file