<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591;1587;1570;1572;1573;1574</NoWarn>
- <DefineConstants>DESIGN_MODE;MEASURE_TIME</DefineConstants>
+ <DefineConstants>_DESIGN_MODE;_MEASURE_TIME</DefineConstants>
<EnableDefaultItems>false</EnableDefaultItems>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<AppConfig>App.config</AppConfig>
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+using Crow.Text;
using System;
namespace Crow
{
public class TextChangeEventArgs: EventArgs
{
- public String Text;
+ public TextChange Change;
- public TextChangeEventArgs (string _newValue) : base()
+ public TextChangeEventArgs (TextChange _newValue) : base()
{
- Text = _newValue;
+ Change = _newValue;
}
}
}
//
// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using Crow.Text;
using System;
using System.IO;
using System.Linq;
internal static bool IsAnyLineBreakCharacter (this char c)
=> c == '\n' || c == '\r' || c == '\u0085' || c == '\u2028' || c == '\u2029';
- internal static ReadOnlySpan<char> GetLine (this string str, LineSpan ls) {
+ internal static ReadOnlySpan<char> GetLine (this string str, TextLine ls) {
if (ls.Start >= str.Length)
return "".AsSpan ();
return str.AsSpan ().Slice (ls.Start, ls.Length);
}
- internal static ReadOnlySpan<char> GetLine (this string str, LineSpan ls, int offset) {
+ internal static ReadOnlySpan<char> GetLine (this string str, TextLine ls, int offset) {
int start = ls.Start + offset;
if (start >= str.Length)
return "".AsSpan ();
return str.AsSpan ().Slice (start, ls.Length);
}
- internal static ReadOnlySpan<char> GetLineIncludingLineBreak (this string str, LineSpan ls) {
+ internal static ReadOnlySpan<char> GetLineIncludingLineBreak (this string str, TextLine ls) {
if (ls.Start >= str.Length)
return "".AsSpan ();
return str.AsSpan ().Slice (ls.Start, ls.LengthIncludingLineBreak);
}
- internal static ReadOnlySpan<char> GetLineIncludingLineBreak (this string str, LineSpan ls, int offset) {
+ internal static ReadOnlySpan<char> GetLineIncludingLineBreak (this string str, TextLine ls, int offset) {
int start = ls.Start + offset;
if (start >= str.Length)
return "".AsSpan ();
ctx.curLine += li.LineNumber - 1;
#endif
- string tmpXml = reader.ReadOuterXml ();
-
-
- XmlTextReader r = reader as XmlTextReader;
-
+ string tmpXml = reader.ReadOuterXml ();
if (ctx.nodesStack.Peek().HasTemplate)
emitTemplateLoad (ctx, tmpXml);
#endregion
- public string WindowTitle {
+ public string WindowTitle {
set => Glfw3.SetWindowTitle (hWin, value);
}
}
public virtual void InterfaceThread ()
{
-
while (!Glfw3.WindowShouldClose (hWin)) {
Update ();
Thread.Sleep (UPDATE_INTERVAL);
DiscardQueue = new Queue<LayoutingQueueItem> (LayoutingQueue.Count);
//Debug.WriteLine ("======= Layouting queue start =======");
- LayoutingQueueItem lqi;
+
while (LayoutingQueue.Count > 0) {
- lqi = LayoutingQueue.Dequeue ();
+ LayoutingQueueItem lqi = LayoutingQueue.Dequeue ();
lqi.ProcessLayouting ();
}
LayoutingQueue = DiscardQueue;
--- /dev/null
+using System;
+using System.Diagnostics;
+
+namespace Crow.Text
+{
+ [DebuggerDisplay ("{Line}, {Column}, {VisualCharXPosition}")]
+ public struct CharLocation : IEquatable<CharLocation>
+ {
+ public readonly int Line;
+ public int Column;
+ public double VisualCharXPosition;
+ public CharLocation (int line, int column, double visualX = -1) {
+ Line = line;
+ Column = column;
+ VisualCharXPosition = visualX;
+ }
+ public bool HasVisualX => Column >= 0 && VisualCharXPosition >= 0;
+
+ public static bool operator == (CharLocation a, CharLocation b)
+ => a.Equals (b);
+ public static bool operator != (CharLocation a, CharLocation b)
+ => !a.Equals (b);
+ public bool Equals (CharLocation other) {
+ return Column < 0 ?
+ Line == other.Line && VisualCharXPosition == other.VisualCharXPosition :
+ Line == other.Line && Column == other.Column;
+ }
+ public override bool Equals (object obj) => obj is CharLocation loc ? Equals (loc) : false;
+ public override int GetHashCode () {
+ return Column < 0 ?
+ HashCode.Combine (Line, VisualCharXPosition) :
+ HashCode.Combine (Line, Column);
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Crow.Text
+{
+ public class Text
+ {
+ char[] buffer;
+ int length;
+ TextLine[] lines;
+
+ public Text (string text, int capacity = -1) {
+ if (string.IsNullOrEmpty (text)) {
+ buffer = new char[capacity > 0 ? capacity : 0];
+ length = 0;
+ } else {
+ if (capacity >= text.Length) {
+ buffer = new char[capacity];
+ text.AsSpan ().CopyTo (buffer.AsSpan ());
+ } else
+ buffer = text.ToCharArray ();
+
+ length = text.Length;
+ updateLines ();
+ }
+ }
+ public Text (int capacity) {
+ buffer = new char[capacity];
+ length = 0;
+ }
+
+ void updateLines () {
+ if (length == 0) {
+ lines = new TextLine[] { new TextLine (0, 0, 0) };
+ return;
+ }
+
+ List<TextLine> _lines = new List<TextLine> ();
+ int start = 0, i = 0;
+ while (i < length) {
+ char c = buffer[i];
+ if (c == '\r') {
+ if (++i < length) {
+ if (buffer[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 < length) {
+ if (buffer[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, length, length));
+ else
+ _lines.Add (new TextLine (length, length, length));
+
+ lines = _lines.ToArray ();
+ }
+
+ public void Append (ReadOnlySpan<char> str) {
+
+ if (length + str.Length > buffer.Length) {
+ char[] newbuff = new char[length + str.Length];
+ Span<char> tmp = newbuff.AsSpan ();
+ buffer.AsSpan ().CopyTo (tmp);
+ str.CopyTo (tmp.Slice (length));
+ buffer = newbuff;
+ } else {
+ str.CopyTo (buffer.AsSpan ().Slice (length));
+ }
+ length = length + str.Length;
+ }
+ public void Insert (ReadOnlySpan<char> str, int start) {
+ if (length + str.Length > buffer.Length) {
+ char[] newbuff = new char[length + str.Length];
+ Span<char> tmp = newbuff.AsSpan ();
+ buffer.AsSpan ().Slice (0, start).CopyTo (tmp);
+ tmp = tmp.Slice (start);
+ str.CopyTo (tmp);
+ tmp = tmp.Slice (str.Length);
+ buffer.AsSpan ().Slice (start, length - start).CopyTo (tmp);
+ buffer = newbuff;
+ } else {
+ buffer.AsSpan ().Slice (start, length - start).CopyTo (buffer.AsSpan ().Slice (start + str.Length));
+ str.CopyTo (buffer.AsSpan ().Slice (start, str.Length));
+ }
+ length = length + str.Length;
+ }
+ public void Remove (int start, int length) {
+ Span<char> tmp = buffer.AsSpan ();
+ int end = Math.Min (this.length, start + length);
+ if (end < this.length)
+ tmp.Slice (end, this.length - end).CopyTo (tmp.Slice (start, this.length - end));
+ this.length = start + (this.length - end);
+ }
+
+ public void Update (TextChange change) {
+
+ }
+
+ public override string ToString () => new string (buffer, 0, length);
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Crow.Text
+{
+ public struct TextChange
+ {
+ public readonly int Start;
+ public readonly int Length;
+ public readonly string ChangedText;
+
+ public int End => Start + Length;
+ public TextChange (int position, int length, string changedText) {
+ Start = position;
+ Length = length;
+ ChangedText = changedText;
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace Crow.Text
+{
+ [DebuggerDisplay ("{Start}, {Length}, {LengthInPixel}")]
+ public struct TextLine : IComparable<TextLine>
+ {
+ public int Start;
+ public int Length;
+ public int LengthIncludingLineBreak;
+ public int LengthInPixel;
+ public int End => Start + Length;
+ public int EndIncludingLineBreak => Start + LengthIncludingLineBreak;
+ public int LineBreakLength => LengthIncludingLineBreak - Length;
+ public bool HasLineBreak => LineBreakLength > 0;
+ public TextLine (int start, int end, int endIncludingLineBreak) {
+ Start = start;
+ Length = end - start;
+ LengthIncludingLineBreak = endIncludingLineBreak - start;
+ LengthInPixel = -1;
+ }
+ public TextLine (int start) {
+ Start = start;
+ Length = 0;
+ LengthIncludingLineBreak = 0;
+ LengthInPixel = -1;
+ }
+ public TextLine WithStartOffset (int start) => new TextLine (Start + start, End, EndIncludingLineBreak);
+
+ public int CompareTo (TextLine other) => Start - other.Start;
+ /*public ReadOnlySpan<char> GetSubString (string str) {
+ if (Start >= str.Length)
+ return "".AsSpan();
+ return str.Length - Start < Length ?
+ str.AsSpan().Slice (Start, Length) :
+ str.AsSpan().Slice (Start);
+}
+public ReadOnlySpan<char> GetSubStringIncludingLineBreak (string str) {
+ if (Start >= str.Length)
+ return "".AsSpan ();
+ return (str.Length - Start < LengthIncludingLineBreak) ?
+ str.AsSpan ().Slice (Start, LengthIncludingLineBreak) :
+ str.AsSpan ().Slice (Start);
+}*/
+ }
+}
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Crow.Text
+{
+ public class LineCollection : IList<TextLine>
+ {
+ TextLine[] lines;
+ int length;
+
+ #region CTOR
+ public LineCollection (int capacity) {
+ lines = new TextLine[capacity];
+ length = 0;
+ }
+ public LineCollection (TextLine[] _lines, int capacity = -1) {
+ if (capacity >= _lines.Length) {
+ lines = new TextLine[capacity];
+ _lines.AsSpan ().CopyTo (lines);
+ } else
+ lines =_lines;
+
+ length = _lines.Length;
+ }
+ #endregion
+
+ public int GetAbsolutePosition (CharLocation loc) => lines[loc.Line].Start + loc.Column;
+ public CharLocation GetLocation (int absolutePosition) {
+ TextLine tl = new TextLine (absolutePosition);
+ int result = lines.AsSpan (0, length).BinarySearch (tl);
+ if (result < 0) {
+ result = ~result;
+ return result == 0 ?
+ new CharLocation (0, absolutePosition) :
+ new CharLocation (result - 1, absolutePosition - lines[result - 1].Start);
+ }
+ return new CharLocation (result, absolutePosition - lines[result].Start);
+ }
+ public void UpdateLineLengthInPixel (int index, int lengthInPixel) {
+ lines[index].LengthInPixel = lengthInPixel;
+ }
+ public int Count => length;
+ public bool IsReadOnly => false;
+
+ public TextLine this[int index] { get => lines[index]; set => lines[index] = value; }
+
+ public void Add (TextLine item) {
+ if (lines.Length < length + 1) {
+ TextLine[] tmp = new TextLine[length + 4];
+ lines.AsSpan ().CopyTo (tmp);
+ lines = tmp;
+ }
+ lines[length] = item;
+ length++;
+ }
+
+ public void Clear () {
+ length = 0;
+ }
+
+ public bool Contains (TextLine item) => Array.IndexOf<TextLine> (lines, item) >= 0;
+
+ public void CopyTo (TextLine[] array, int arrayIndex) {
+ lines.AsSpan (0, length).CopyTo (array.AsSpan (arrayIndex));
+ }
+
+ public bool Remove (TextLine item) {
+ int idx = Array.IndexOf<TextLine> (lines, item);
+ if (idx < 0)
+ return false;
+ if (idx + 1 < length)
+ lines.AsSpan (idx + 1, length - idx - 1).CopyTo (lines.AsSpan (idx));
+ length--;
+ return true;
+ }
+ public IEnumerator<TextLine> GetEnumerator () => new LineEnumerator (this);
+ IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
+
+ public int IndexOf (TextLine item) {
+ throw new NotImplementedException ();
+ }
+
+ public void Insert (int index, TextLine item) {
+ throw new NotImplementedException ();
+ }
+
+ public void RemoveAt (int index) {
+ throw new NotImplementedException ();
+ }
+
+ public class LineEnumerator : IEnumerator<TextLine>
+ {
+ TextLine[] lines;
+ int length, position = -1;
+ public LineEnumerator (LineCollection coll) {
+ lines = coll.lines;
+ length = coll.length;
+ }
+ public TextLine Current => lines[position];
+ object IEnumerator.Current => Current;
+ public void Dispose () { }
+ public bool MoveNext () => ++position < length;
+ public void Reset () {
+ position = -1;
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Crow.Text
+{
+ public struct TextSpan
+ {
+ public readonly int Start;
+ public readonly int End;
+ public TextSpan (int start, int end) {
+ Start = start;
+ End = end;
+ }
+
+ public bool IsEmpty => Start == End;
+ public int Length => End - Start;
+ }
+}
using System.Diagnostics;
using Glfw;
using System.Collections;
+using Crow.Text;
namespace Crow {
- internal struct TextSpan
- {
- public readonly int Start;
- public readonly int End;
- public TextSpan (int start, int end) {
- Start = start;
- End = end;
- }
- }
-
- [DebuggerDisplay ("{Line}, {Column}, {VisualCharXPosition}")]
- internal struct CharLocation : IEquatable<CharLocation>
- {
- public readonly int Line;
- public int Column;
- public double VisualCharXPosition;
- public CharLocation (int line, int column, double visualX = -1) {
- Line = line;
- Column = column;
- VisualCharXPosition = visualX;
- }
- public bool HasVisualX => Column >= 0 && VisualCharXPosition >= 0;
-
- public static bool operator ==(CharLocation a, CharLocation b)
- => a.Equals (b);
- public static bool operator != (CharLocation a, CharLocation b)
- => !a.Equals (b);
- public bool Equals (CharLocation other) {
- return Column < 0 ?
- Line == other.Line && VisualCharXPosition == other.VisualCharXPosition :
- Line == other.Line && Column == other.Column;
- }
- public override bool Equals (object obj) => obj is CharLocation loc ? Equals(loc) : false;
- public override int GetHashCode () {
- return Column < 0 ?
- HashCode.Combine (Line, VisualCharXPosition) :
- HashCode.Combine (Line, Column);
- }
- }
- internal class LineCollection : ICollection<LineSpan>
- {
- LineSpan[] lines;
-
- public int Count => throw new NotImplementedException ();
-
- public bool IsReadOnly => throw new NotImplementedException ();
-
- public void Add (LineSpan item) {
- throw new NotImplementedException ();
- }
-
- public void Clear () {
- throw new NotImplementedException ();
- }
-
- public bool Contains (LineSpan item) {
- throw new NotImplementedException ();
- }
-
- public void CopyTo (LineSpan[] array, int arrayIndex) {
- throw new NotImplementedException ();
- }
-
- public IEnumerator<LineSpan> GetEnumerator () {
- throw new NotImplementedException ();
- }
-
- public bool Remove (LineSpan item) {
- throw new NotImplementedException ();
- }
-
- IEnumerator IEnumerable.GetEnumerator () {
- throw new NotImplementedException ();
- }
-
- /*public LineSpan this[int index] {
- get => lines[index];
- set => lines[index] = value;
- }
- public int Count => lines.Length;
- public bool IsReadOnly => false;
-
- public void Add (LineSpan item) {
-
- }
-
- public void Clear () {
- throw new NotImplementedException ();
- }
-
- public bool Contains (LineSpan item) {
- throw new NotImplementedException ();
- }
-
- public void CopyTo (LineSpan[] array, int arrayIndex) {
- throw new NotImplementedException ();
- }
-
- public IEnumerator<LineSpan> GetEnumerator () {
- throw new NotImplementedException ();
- }
-
- public int IndexOf (LineSpan item) {
- throw new NotImplementedException ();
- }
-
- public void Insert (int index, LineSpan item) {
- throw new NotImplementedException ();
- }
-
- public bool Remove (LineSpan item) {
- throw new NotImplementedException ();
- }
-
- public void RemoveAt (int index) {
- throw new NotImplementedException ();
- }
-
- IEnumerator IEnumerable.GetEnumerator () {
- throw new NotImplementedException ();
- }*/
- }
- internal struct LineSpan
- {
- public int Start;
- public int Length;
- public int LengthIncludingLineBreak;
- public int LengthInPixel;
- public int End => Start + Length;
- public int EndIncludingLineBreak => Start + LengthIncludingLineBreak;
- public int LineBreakLength => LengthIncludingLineBreak - Length;
- public bool HasLineBreak => LineBreakLength > 0;
- public LineSpan (int start, int end, int endIncludingLineBreak) {
- Start = start;
- Length = end - start;
- LengthIncludingLineBreak = endIncludingLineBreak - start;
- LengthInPixel = -1;
- }
- public LineSpan WithStartOffset (int start) => new LineSpan (Start + start, End, EndIncludingLineBreak);
- /*public ReadOnlySpan<char> GetSubString (string str) {
- if (Start >= str.Length)
- return "".AsSpan();
- return str.Length - Start < Length ?
- str.AsSpan().Slice (Start, Length) :
- str.AsSpan().Slice (Start);
- }
- public ReadOnlySpan<char> GetSubStringIncludingLineBreak (string str) {
- if (Start >= str.Length)
- return "".AsSpan ();
- return (str.Length - Start < LengthIncludingLineBreak) ?
- str.AsSpan ().Slice (Start, LengthIncludingLineBreak) :
- str.AsSpan ().Slice (Start);
- }*/
- }
/// <summary>
/// Simple label widget possibly multiline but without tabulation handling.
/// </summary>
public event EventHandler<TextChangeEventArgs> TextChanged;
public virtual void OnTextChanged(Object sender, TextChangeEventArgs e)
- {
- textMeasureIsUpToDate = false;
- NotifyValueChanged ("Text", Text);
+ {
TextChanged.Raise (this, e);
}
//TODO:change protected to private
#region private and protected fields
- string _text;
+ protected string _text;
TextAlignment _textAlignment;
bool horizontalStretch;
bool verticalStretch;
//Point mouseLocalPos = -1;//mouse coord in widget space, filled only when clicked
- CharLocation? hoverLoc = null;
- CharLocation? currentLoc = null;
- CharLocation? selectionStart = null; //selection start (row,column)
+ protected CharLocation? hoverLoc = null;
+ protected CharLocation? currentLoc = null;
+ protected CharLocation? selectionStart = null; //selection start (row,column)
protected FontExtents fe;
protected TextExtents te;
RegisterForRedraw ();
}
}
- void resetLocationXs () {
- if (currentLoc.HasValue) {
- CharLocation cl = currentLoc.Value;
- cl.VisualCharXPosition = -1;
- currentLoc = cl;
- }
- if (selectionStart.HasValue) {
- CharLocation cl = selectionStart.Value;
- cl.VisualCharXPosition = -1;
- selectionStart = cl;
- }
- }
/// <summary>
/// If measure is not 'Fit', align text inside the bounds of this label.
/// </summary>
if (_text.AsSpan ().SequenceEqual (value.AsSpan ()))
return;
+ int oldTextLength = string.IsNullOrEmpty (_text) ? 0 : _text.Length;
_text = value;
getLines ();
- OnTextChanged (this, new TextChangeEventArgs (Text));
+ textMeasureIsUpToDate = false;
+ NotifyValueChanged ("Text", Text);
+ OnTextChanged (this, new TextChangeEventArgs (new TextChange (0, oldTextLength, _text)));
RegisterForGraphicUpdate ();
}
}
/// <summary>
/// If 'true', linebreaks will be interpreted. If 'false', linebreaks are threated as unprintable
- /// unicode characters.
+ /// unicode characters. Default value is 'False'.
/// </summary>
[DefaultValue(false)]
public bool Multiline
targetColumn = -1;
CharLocation loc = currentLoc.Value;
if (loc.Column == lines[loc.Line].Length) {
- if (loc.Line == lines.Length - 1)
+ if (loc.Line == lines.Count - 1)
return false;
currentLoc = new CharLocation (loc.Line + 1, 0);
} else
}
public bool MoveDown () {
CharLocation loc = currentLoc.Value;
- if (loc.Line == lines.Length - 1)
+ if (loc.Line == lines.Count - 1)
return false;
if (loc.Column > lines[loc.Line + 1].Length) {
return true;
}
+
/*
- /// <summary>
- /// Moves cursor one char to the right.
- /// </summary>
- /// <returns><c>true</c> if move succeed</returns>
- public bool MoveRight(){
- int tmp = _currentCol + 1;
- if (tmp > lines [_currentLine].Length){
- if (CurrentLine == lines.Length - 1)
- return false;
- CurrentLine++;
- CurrentColumn = 0;
- } else
- CurrentColumn = tmp;
- return true;
- }
public void GotoWordStart(){
CurrentColumn--;
//skip white spaces
*/
bool textMeasureIsUpToDate = false;
Size cachedTextSize = default(Size);
- LineSpan[] lines;
- void getLines () {
+ protected LineCollection lines;
+ protected void getLines () {
if (string.IsNullOrEmpty (_text)) {
- lines = new LineSpan[] { new LineSpan (0, 0, 0) };
+ lines = new LineCollection(new TextLine[] { new TextLine (0, 0, 0) });
return;
}
if (!_multiline) {
- lines = new LineSpan[] { new LineSpan (0, _text.Length, _text.Length) };
+ lines = new LineCollection (new TextLine[] { new TextLine (0, _text.Length, _text.Length) });
return;
}
- List<LineSpan> _lines = new List<LineSpan> ();
+ List<TextLine> _lines = new List<TextLine> ();
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 LineSpan (start, i - 1, ++i));
+ _lines.Add (new TextLine (start, i - 1, ++i));
else
- _lines.Add (new LineSpan (start, i - 1, i));
+ _lines.Add (new TextLine (start, i - 1, i));
} else
- _lines.Add (new LineSpan (start, i - 1, i));
+ _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 LineSpan (start, i - 1, ++i));
+ _lines.Add (new TextLine (start, i - 1, ++i));
else
- _lines.Add (new LineSpan (start, i - 1, i));
+ _lines.Add (new TextLine (start, i - 1, i));
} else
- _lines.Add (new LineSpan (start, i - 1, i));
+ _lines.Add (new TextLine (start, i - 1, i));
start = i;
} else if (c == '\u0085' || c == '\u2028' || c == '\u2029')
- _lines.Add (new LineSpan (start, i - 1, i));
+ _lines.Add (new TextLine (start, i - 1, i));
else
i++;
}
if (start < i)
- _lines.Add (new LineSpan (start, _text.Length, _text.Length));
+ _lines.Add (new TextLine (start, _text.Length, _text.Length));
else
- _lines.Add (new LineSpan (_text.Length, _text.Length, _text.Length));
+ _lines.Add (new TextLine (_text.Length, _text.Length, _text.Length));
- lines = _lines.ToArray ();
+ lines = new LineCollection (_lines.ToArray ());
+ }
+ void resetLocationXs () {
+ if (currentLoc.HasValue) {
+ CharLocation cl = currentLoc.Value;
+ cl.VisualCharXPosition = -1;
+ currentLoc = cl;
+ }
+ if (selectionStart.HasValue) {
+ CharLocation cl = selectionStart.Value;
+ cl.VisualCharXPosition = -1;
+ selectionStart = cl;
+ }
+ }
+ double getX (int clientWidth, TextLine ls) {
+ switch (TextAlignment) {
+ case TextAlignment.Right:
+ return clientWidth - ls.LengthInPixel;
+ case TextAlignment.Center:
+ return clientWidth / 2 - ls.LengthInPixel / 2;
+ }
+ return 0;
}
+ protected TextSpan Selection {
+ get {
+ CharLocation selStart = currentLoc.Value, selEnd = currentLoc.Value;
+ if (selectionStart.HasValue) {
+ if (currentLoc.Value.Line < selectionStart.Value.Line) {
+ selStart = currentLoc.Value;
+ selEnd = selectionStart.Value;
+ } else if (currentLoc.Value.Line > selectionStart.Value.Line) {
+ selStart = selectionStart.Value;
+ selEnd = currentLoc.Value;
+ } else if (currentLoc.Value.Column < selectionStart.Value.Column) {
+ selStart = currentLoc.Value;
+ selEnd = selectionStart.Value;
+ } else {
+ selStart = selectionStart.Value;
+ selEnd = currentLoc.Value;
+ }
+ }
+ return new TextSpan (lines.GetAbsolutePosition (selStart), lines.GetAbsolutePosition (selEnd));
+ }
+ }
#region GraphicObject overrides
public override int measureRawSize(LayoutingType lt)
fe = gr.FontExtents;
te = new TextExtents ();
- cachedTextSize.Height = (int)Math.Ceiling ((fe.Ascent+fe.Descent) * Math.Max (1, lines.Length));
+ cachedTextSize.Height = (int)Math.Ceiling ((fe.Ascent+fe.Descent) * Math.Max (1, lines.Count));
TextExtents tmp = default;
int longestLine = 0;
- for (int i = 0; i < lines.Length; i++) {
+ for (int i = 0; i < lines.Count; i++) {
if (lines[i].LengthInPixel < 0) {
if (lines[i].Length == 0)
- lines[i].LengthInPixel = 0;// (int)Math.Ceiling (fe.MaxXAdvance);
+ lines.UpdateLineLengthInPixel (i, 0);// (int)Math.Ceiling (fe.MaxXAdvance);
else {
gr.TextExtents (_text.GetLine (lines[i]), Interface.TAB_SIZE, out tmp);
- lines[i].LengthInPixel = (int)Math.Ceiling (tmp.XAdvance);
+ lines.UpdateLineLengthInPixel (i, (int)Math.Ceiling (tmp.XAdvance));
}
}
if (lines[i].LengthInPixel > lines[longestLine].LengthInPixel)
return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width);
}
- double getX (int clientWidth, ref LineSpan ls) {
- switch (TextAlignment) {
- case TextAlignment.Right:
- return clientWidth - ls.LengthInPixel;
- case TextAlignment.Center:
- return clientWidth / 2 - ls.LengthInPixel / 2;
- }
- return 0;
- }
protected override void onDraw (Context gr)
{
base.onDraw (gr);
TextExtents extents;
Span<byte> bytes = stackalloc byte[128];
- for (int i = 0; i < lines.Length; i++) {
+ for (int i = 0; i < lines.Count; i++) {
int encodedBytes = -1;
if (lines[i].Length > 0) {
if (lines[i].LengthInPixel < 0) {
gr.TextExtents (bytes.Slice (0, encodedBytes), out extents);
- lines[i].LengthInPixel = (int)extents.XAdvance;
+ lines.UpdateLineLengthInPixel (i, (int)extents.XAdvance);
}
}
Rectangle lineRect = new Rectangle (
- Width.IsFit && !Multiline ? cb.X : (int)getX (cb.Width, ref lines[i]) + cb.X,
+ Width.IsFit && !Multiline ? cb.X : (int)getX (cb.Width, lines[i]) + cb.X,
cb.Y + i * lineHeight,
lines[i].LengthInPixel,
lineHeight);
base.onFocused (sender, e);
if (currentLoc == null) {
selectionStart = new CharLocation (0, 0);
- currentLoc = new CharLocation (lines.Length - 1, lines[lines.Length - 1].Length);
+ currentLoc = new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length);
}
RegisterForRedraw ();
}
Point mouseLocalPos = e.Position - ScreenCoordinates (Slot).TopLeft - ClientRectangle.TopLeft;
int hoverLine = _multiline ?
- (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent))), lines.Length - 1) : 0;
+ (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent))), lines.Count - 1) : 0;
hoverLoc = new CharLocation (hoverLine, -1, mouseLocalPos.X);
if (HasFocus && IFace.IsDown (Glfw.MouseButton.Left)) {
break;
case Key.End:
checkShift ();
- int l = IFace.Ctrl ? lines.Length - 1 : currentLoc.Value.Line;
+ int l = IFace.Ctrl ? lines.Count - 1 : currentLoc.Value.Line;
currentLoc = new CharLocation (l, lines[l].Length);
RegisterForRedraw ();
break;
CharLocation loc = location.Value;
if (loc.HasVisualX)
return;
- LineSpan ls = lines[loc.Line];
- ReadOnlySpan<char> curLine = _text.GetLine (lines[loc.Line]);
- double cPos = Width.IsFit && !Multiline ? 0 : getX (clientWidth, ref ls);
+ TextLine ls = lines[loc.Line];
+ ReadOnlySpan<char> curLine = _text.GetLine (ls);
+ double cPos = Width.IsFit && !Multiline ? 0 : getX (clientWidth, ls);
if (loc.Column >= 0) {
loc.VisualCharXPosition = gr.TextExtents (curLine.Slice (0, loc.Column), Interface.TAB_SIZE).XAdvance + cPos;
lines = getLines;
- OnTextChanged (this, new TextChangeEventArgs (Text));
+ OnTextChanged (this, new TextChangeEventArgs (default));
RegisterForGraphicUpdate ();
}
}
lines [CurrentLine] += lines [CurrentLine + 1];
lines.RemoveAt (CurrentLine + 1);
- OnTextChanged (this, new TextChangeEventArgs (Text));
+ OnTextChanged (this, new TextChangeEventArgs (default));
return;
}
CurrentColumn--;
SelBegin = -1;
SelRelease = -1;
}
- OnTextChanged (this, new TextChangeEventArgs (Text));
+ OnTextChanged (this, new TextChangeEventArgs (default));
}
/// <summary>
/// Insert new string at caret position, should be sure no line break is inside.
lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, str);
CurrentColumn += str.Length;
}
- OnTextChanged (this, new TextChangeEventArgs (Text));
+ OnTextChanged (this, new TextChangeEventArgs (default));
}
/// <summary>
/// Insert a line break.
lines [CurrentLine] = lines [CurrentLine].Substring (0, CurrentColumn);
CurrentLine++;
CurrentColumn = 0;
- OnTextChanged (this, new TextChangeEventArgs (Text));
+ OnTextChanged (this, new TextChangeEventArgs (default));
}
bool textMeasureIsUpToDate = false;
Size cachedTextSize = default(Size);
if (Multiline)
InsertLineBreak ();
else
- OnTextChanged(this,new TextChangeEventArgs(Text));
+ OnTextChanged(this,new TextChangeEventArgs(default));
break;
case Key.Escape:
Text = "";
// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
using Crow.Cairo;
+using Crow.Text;
using Glfw;
+using System;
namespace Crow
{
public override void onKeyDown (object sender, KeyEventArgs e)
{
Key key = e.Key;
-
+ TextSpan selection = Selection;
switch (key)
{
- /*case Key.Backspace:
- if (CurrentPosition == 0)
- return;
- DeleteChar();
- break;
- case Key.Delete:
- if (selectionIsEmpty) {
- if (!MoveRight ())
+ case Key.Backspace:
+ if (selection.Length == 0) {
+ if (selection.Start == 0)
return;
- }else if (IFace.Shift)
- IFace.Clipboard = SelectedText;
- DeleteChar ();
- break;
- case Key.KeypadEnter:
- case Key.Enter:
- if (!selectionIsEmpty)
- DeleteChar ();
- if (Multiline)
- InsertLineBreak ();
- else
- OnTextChanged(this,new TextChangeEventArgs(Text));
+ update (new TextChange (selection.Start - 1, 1, ""));
+ } else
+ update (new TextChange (selection.Start, selection.Length, ""));
break;
- case Key.Escape:
- Text = "";
- CurrentColumn = 0;
- SelRelease = -1;
- break;
- case Key.Home:
- if (IFace.Shift) {
- if (selectionIsEmpty)
- SelBegin = new Point (CurrentColumn, CurrentLine);
- if (IFace.Ctrl)
- CurrentLine = 0;
- CurrentColumn = 0;
- SelRelease = new Point (CurrentColumn, CurrentLine);
- break;
- }
- SelRelease = -1;
- if (IFace.Ctrl)
- CurrentLine = 0;
- CurrentColumn = 0;
- break;
- case Key.End:
- if (IFace.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- if (IFace.Ctrl)
- CurrentLine = int.MaxValue;
- CurrentColumn = int.MaxValue;
- SelRelease = CurrentPosition;
- break;
+ case Key.Delete:
+ if (selection.Length == 0) {
+ if (selection.Start == Text.Length)
+ return;
+ update (new TextChange (selection.Start, 1, ""));
+ } else {
+ if (IFace.Shift)
+ IFace.Clipboard = Text.AsSpan(selection.Start, selection.End).ToString();
+ update (new TextChange (selection.Start, selection.Length, ""));
}
- SelRelease = -1;
- if (IFace.Ctrl)
- CurrentLine = int.MaxValue;
- CurrentColumn = int.MaxValue;
break;
case Key.Insert:
if (IFace.Shift)
- this.Insert (IFace.Clipboard);
- else if (IFace.Ctrl && !selectionIsEmpty)
- IFace.Clipboard = this.SelectedText;
- break;
- case Key.Left:
- if (IFace.Shift) {
- if (selectionIsEmpty)
- SelBegin = new Point(CurrentColumn, CurrentLine);
- if (IFace.Ctrl)
- GotoWordStart ();
- else if (!MoveLeft ())
- return;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- if (IFace.Ctrl)
- GotoWordStart ();
- else
- MoveLeft();
+ update (new TextChange (selection.Start, selection.Length, IFace.Clipboard));
+ else if (IFace.Ctrl && !selection.IsEmpty)
+ IFace.Clipboard = Text.AsSpan (selection.Start, selection.End).ToString ();
break;
- case Key.Right:
- if (IFace.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- if (IFace.Ctrl)
- GotoWordEnd ();
- else if (!MoveRight ())
- return;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- if (IFace.Ctrl)
- GotoWordEnd ();
+ case Key.KeypadEnter:
+ case Key.Enter:
+ if (Multiline)
+ update (new TextChange (selection.Start, selection.Length, "\n"));
else
- MoveRight ();
- break;
- case Key.Up:
- if (IFace.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- CurrentLine--;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- CurrentLine--;
+ OnTextChanged(this,new TextChangeEventArgs(default));
break;
- case Key.Down:
- if (IFace.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- CurrentLine++;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- CurrentLine++;
+ case Key.Escape:
+ selectionStart = null;
+ currentLoc = lines.GetLocation (selection.Start);
+ RegisterForRedraw ();
+ break;
+ case Key.Tab:
+ update (new TextChange (selection.Start, selection.Length, "\t"));
break;
- case Key.Tab:
- this.Insert ("\t");
- break;*/
default:
base.onKeyDown (sender, e);
break;
{
base.onKeyPress (sender, e);
+ TextSpan selection = Selection;
+ update (new TextChange (selection.Start, selection.Length, e.KeyChar.ToString ()));
+
/*Insert (e.KeyChar.ToString());
SelRelease = -1;
RegisterForGraphicUpdate();*/
}
#endregion
+
+ void update(TextChange change) {
+ Span<char> tmp = stackalloc char[Text.Length + (change.ChangedText.Length - change.Length)];
+ ReadOnlySpan<char> src = Text.AsSpan ();
+ 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 ();
+
+ selectionStart = null;
+ currentLoc = lines.GetLocation (change.Start + change.ChangedText.Length);
+ }
}
}
{
if (pi.GetSetMethod () == null)
return;
- XmlIgnoreAttribute xia = (XmlIgnoreAttribute)pi.GetCustomAttribute (typeof (XmlIgnoreAttribute));
+ XmlIgnoreAttribute xia = pi.GetCustomAttribute <XmlIgnoreAttribute>();
if (xia != null)
return;
System.Runtime.Loader.AssemblyLoadContext.Default.ResolvingUnmanagedDll+=resolveUnmanaged;
}
#endif
- readonly int count = 1, updateCycles = 0;
+ readonly int count = 10, updateCycles = 0;
readonly bool screenOutput = false;
readonly string inDirectory = null;//directory to test
readonly string outFilePath;
Console.WriteLine ("-i,--input:\n\tInput directory to search recursively for '.crow' file to test. If ommitted, builtin unit tests are performs");
Console.WriteLine ("-w,--width:\n\toutput surface width, not displayed on screen.");
Console.WriteLine ("-h,--height:\n\toutput surface height, not displayed on screen.");
- Console.WriteLine ("-c,--count:\n\trepeat each test 'c' times.");
+ Console.WriteLine ("-c,--count:\n\trepeat each test 'c' times. (default = 10, minimum = 5");
Console.WriteLine ("-b,--begin:\n\tStarting stage for measures, may be the stage name or stage index");
foreach (Stage s in Enum.GetValues(typeof(Stage))) {
}
Console.WriteLine ("-e,--end:\n\tEnding stage for measures, may be the stage name or stage index");
Console.WriteLine ("-r,--reset:\n\tenable clear iterators after each test file.");
- Console.WriteLine ("-u,--update:\n\tmeasure 'n' update cycle with DateTime.Now string notified.");
+ Console.WriteLine ("-u,--update:\n\tmeasure 'n' update cycle with elapsed ticks string notified. (default = 0)");
Console.WriteLine ("-s,--screen:\n\tenable output to screen.");
Console.WriteLine ("--help:\n\tthis help message.");
}
break;
case "-c":
case "--count":
- count = int.Parse (args[i++]);
+ count = Math.Max(5, int.Parse (args[i++]));
break;
case "-w":
case "--width":
public static void Main (string [] args)
{
- try {
+
+ try {
using (TestInterface iface = new TestInterface (args)) {
- iface.PerformUnitTests ();
- iface.PerformTests ();
+ if (string.IsNullOrEmpty(iface.inDirectory))
+ iface.PerformUnitTests ();
+ else
+ iface.PerformTests ();
}
} catch (Exception) {
}
"profiles": {
"PerfTests": {
"commandName": "Project",
- "commandLineArgs": "-c 20 -i Interfaces/PerfLabels"
+ "commandLineArgs": "-c 10 -b 0 -e 1 "
}
}
}
\ No newline at end of file