From: Jean-Philippe Bruyère Date: Fri, 9 Apr 2021 02:51:37 +0000 (+0200) Subject: TG recursive lock check, IObsList clear evt, sample Editor xml syntax, command IList... X-Git-Tag: v0.9.5-beta~34 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=2b6d20f81ec25698e39f2b1efac2574340aafcdd;p=jp%2Fcrow.git TG recursive lock check, IObsList clear evt, sample Editor xml syntax, command IList, CommandBase class --- diff --git a/Crow/src/Command.cs b/Crow/src/Command.cs index 51f0bc0a..a185e1ad 100644 --- a/Crow/src/Command.cs +++ b/Crow/src/Command.cs @@ -7,6 +7,7 @@ using System; using System.ComponentModel; using System.Threading.Tasks; using System.Collections; +using System.Collections.Generic; namespace Crow { public abstract class CommandBase : IValueChange { @@ -20,7 +21,7 @@ namespace Crow { #region CTOR protected CommandBase() {} - protected CommandBase (string _caption, string _icon) + protected CommandBase (string _caption, string _icon = null) { caption = _caption; icon = _icon; @@ -60,7 +61,7 @@ namespace Crow { NotifyValueChanged ("Caption", caption); } } - public class CommandGroup : CommandBase, IEnumerable + public class CommandGroup : CommandBase, IEnumerable, IList { public ObservableList Commands = new ObservableList(); @@ -69,11 +70,44 @@ namespace Crow { base (caption, icon) { Commands.AddRange (commands); } + public CommandGroup (string caption, params CommandBase[] commands) : + base (caption) { + Commands.AddRange (commands); + } public CommandGroup (params CommandBase[] commands) { Commands.AddRange (commands); } + + public int Count => Commands.Count; + + public bool IsReadOnly => false; + + public CommandBase this[int index] { get => Commands[index]; set => Commands[index] = value; } + public IEnumerator GetEnumerator() => Commands.GetEnumerator (); + + public int IndexOf(CommandBase item) => Commands.IndexOf (item); + + public void Insert(int index, CommandBase item) => Commands.Insert(index, item); + + public void RemoveAt(int index) => Commands.RemoveAt(index); + + public void Add(CommandBase item) => Commands.Add (item); + + public void Clear() => Commands.Clear(); + + public bool Contains(CommandBase item) => Commands.Contains (item); + + public void CopyTo(CommandBase[] array, int arrayIndex) => Commands.CopyTo (array, arrayIndex); + + public bool Remove(CommandBase item) { + Commands.Remove (item); + return true; + } + + IEnumerator IEnumerable.GetEnumerator() + => Commands.GetEnumerator(); } diff --git a/Crow/src/DebugUtils/DbgEvent.cs b/Crow/src/DebugUtils/DbgEvent.cs index a9969716..1c0b79af 100644 --- a/Crow/src/DebugUtils/DbgEvent.cs +++ b/Crow/src/DebugUtils/DbgEvent.cs @@ -102,8 +102,10 @@ namespace Crow.DebugLogger case DbgEvtType.Update: return Colors.Grey; case DbgEvtType.IFaceLoad: - return Colors.Teal; + return Colors.Teal; default: + if (type.HasFlag(DbgEvtType.Mouse)) + return Colors.DeepPink; return Colors.White; } } diff --git a/Crow/src/DebugUtils/DbgEvtType.cs b/Crow/src/DebugUtils/DbgEvtType.cs index 24fd99fa..99cf1539 100644 --- a/Crow/src/DebugUtils/DbgEvtType.cs +++ b/Crow/src/DebugUtils/DbgEvtType.cs @@ -25,6 +25,7 @@ namespace Crow Override = 0x020000, TemplatedGroup = 0x010000, Dispose = 0x008000, + Mouse = 0x004000, Update = IFace | 0x004000, ProcessLayouting = IFace | Update | Lock | Layouting, @@ -32,8 +33,8 @@ namespace Crow ProcessDrawing = IFace | Update | Lock | Drawing, IFaceLoad = IFace | 0x01, IFaceInit = IFace | 0x02, - CreateITor = IFace | 0x04, - IFaceReloadTheme = IFace | 0x08, + CreateITor = IFace | 0x03, + IFaceReloadTheme = IFace | 0x04, HoverWidget = Focus | Widget | 0x01, FocusedWidget = Focus | Widget | 0x02, @@ -43,33 +44,33 @@ namespace Crow //10 nth bit set for graphic obj GOClassCreation = Widget | 0x01, GOInitialization = Widget | 0x02, - GORegisterForGraphicUpdate = Widget | 0x04, - GOEnqueueForRepaint = Widget | 0x08, - GONewDataSource = Widget | 0x10, - GONewParent = Widget | 0x20, - GONewLogicalParent = Widget | 0x40, - GOAddChild = Widget | 0x80, + GORegisterForGraphicUpdate = Widget | 0x03, + GOEnqueueForRepaint = Widget | 0x04, + GONewDataSource = Widget | 0x05, + GONewParent = Widget | 0x06, + GONewLogicalParent = Widget | 0x07, + GOAddChild = Widget | 0x08, - GOSearchLargestChild = Widget | 0x09, - GOSearchTallestChild = Widget | 0x0A, - GORegisterForRedraw = Widget | 0x0B, - GOComputeChildrenPositions = Widget | 0x0C, - GOOnChildLayoutChange = Widget | 0x0D, + GOMeasure = Widget | 0x09, + GOSearchLargestChild = Widget | 0x0A, + GOSearchTallestChild = Widget | 0x0B, + GORegisterForRedraw = Widget | 0x0C, + GOComputeChildrenPositions = Widget | 0x0D, + GOOnChildLayoutChange = Widget | 0x0E, - AlreadyDisposed = Dispose | Widget | Error | 0x01, - DisposedByGC = Dispose | Widget | Error | 0x02, - Disposing = Dispose | Widget | 0x01, + AlreadyDisposed = Widget | Dispose | Error | 0x01, + DisposedByGC = Widget | Dispose | Error | 0x02, + Disposing = Widget | Dispose | 0x01, - GOClippingRegistration = Clipping | Widget | 0x01, - GORegisterClip = Clipping | Widget | 0x02, - GORegisterLayouting = Layouting | Widget | 0x01, - GOProcessLayouting = Layouting | Widget | 0x02, - GOProcessLayoutingWithNoParent = Layouting | Widget | Warning | 0x01, - GOMeasure = Widget | 0x03, - GODraw = Drawing | Widget | 0x01, - GORecreateCache = Drawing | Widget | 0x02, - GOUpdateCache = Drawing | Widget | 0x03, - GOPaint = Drawing | Widget | 0x04, + GOClippingRegistration = Widget | Clipping | 0x01, + GORegisterClip = Widget | Clipping | 0x02, + GORegisterLayouting = Widget | Layouting | 0x01, + GOProcessLayouting = Widget | Layouting | 0x02, + GOProcessLayoutingWithNoParent = Widget | Layouting | Warning | 0x01, + GODraw = Widget | Drawing | 0x01, + GORecreateCache = Widget | Drawing | 0x02, + GOUpdateCache = Widget | Drawing | 0x03, + GOPaint = Widget | Drawing | 0x04, GOLockUpdate = Widget | Lock | 0x01, GOLockClipping = Widget | Lock | 0x02, @@ -79,6 +80,13 @@ namespace Crow TGLoadingThread = Widget | TemplatedGroup | 0x01, TGCancelLoadingThread = Widget | TemplatedGroup | 0x02, + MouseDown = IFace | Mouse | 0x01, + MouseUp = IFace | Mouse | 0x02, + MouseMove = IFace | Mouse | 0x03, + GOMouseDown = Widget | Mouse | 0x01, + GOMouseUp = Widget | Mouse | 0x02, + GOMouseMove = Widget | Mouse | 0x03, + All = 0x7FFFFF00 } } \ No newline at end of file diff --git a/Crow/src/IML/CompilerServices.cs b/Crow/src/IML/CompilerServices.cs index 224338fb..11779999 100644 --- a/Crow/src/IML/CompilerServices.cs +++ b/Crow/src/IML/CompilerServices.cs @@ -136,6 +136,7 @@ namespace Crow.IML il.Emit (OpCodes.Stfld, miSetCurIface); } + //TODO: should be able to handle struct in default values. public static void EmitSetValue(ILGenerator il, PropertyInfo pi, object val){ il.Emit (OpCodes.Ldloc_0); diff --git a/Crow/src/IObservableList.cs b/Crow/src/IObservableList.cs index 311a21a3..4fab1e81 100644 --- a/Crow/src/IObservableList.cs +++ b/Crow/src/IObservableList.cs @@ -10,6 +10,7 @@ namespace Crow event EventHandler ListAdd; event EventHandler ListRemove; event EventHandler ListEdit; + event EventHandler ListClear; void Insert (); void Remove (); diff --git a/Crow/src/Text/SpanCharReader.cs b/Crow/src/Text/SpanCharReader.cs index bab2c4b7..6c6fcde0 100644 --- a/Crow/src/Text/SpanCharReader.cs +++ b/Crow/src/Text/SpanCharReader.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Text; -namespace Crow.src.Text +namespace Crow.Text { public ref struct SpanCharReader { @@ -21,9 +21,82 @@ namespace Crow.src.Text public void Seek (int position) => curPos = position; - public Char Peak () => buffer[curPos]; + public Char Peak => buffer[curPos]; public Char Read () => buffer[curPos++]; + public bool TryRead (out char c) { + if (EndOfSpan) { + c = default; + return false; + } + c = Read(); + return true; + } + public bool TryRead (char c) => EndOfSpan ? false : Read() == c; + + public ReadOnlySpan Read (int length) => buffer.Slice (curPos += length, length); + public void Advance (int increment = 1) => curPos += increment; + public bool TryAdvance (int increment = 1) { + curPos += increment; + return curPos < buffer.Length; + } + + public bool TryReadUntil (ReadOnlySpan str, StringComparison comparison = StringComparison.Ordinal) { + int startPos = curPos; + while (curPos < buffer.Length - str.Length) { + if (buffer[curPos] == str[0] && buffer.Slice(curPos + 1, str.Length - 1).Equals(str.Slice (1), comparison)) + return true; + curPos++; + } + return false; + } + public bool TryReadUntil (char c) { + int startPos = curPos; + while (curPos < buffer.Length && buffer[curPos] != c) + curPos++; + return curPos < buffer.Length; + } + public bool TryRead (int length, out ReadOnlySpan str) { + if (length < buffer.Length) { + str = buffer.Slice (curPos += length, length); + return true; + } + str = default; + return false; + } + + /// + /// Try read expected string and advance reader position in any case + /// + /// expected string + /// comparison type + /// true if expected string is found + public bool TryRead (ReadOnlySpan expectedString, StringComparison comparison = StringComparison.OrdinalIgnoreCase) { + if (buffer.Length < curPos + expectedString.Length) { + curPos = buffer.Length; + return false; + } + bool res = buffer.Slice(curPos, expectedString.Length).Equals (expectedString, comparison); + curPos += expectedString.Length; + return res; + } + public bool TryPeak (ReadOnlySpan expectedString, StringComparison comparison = StringComparison.Ordinal) => + (buffer.Length < curPos + expectedString.Length)? false : + buffer.Slice(curPos, expectedString.Length).Equals (expectedString, comparison); + public ReadOnlySpan Get (int fromPosition) => buffer.Slice (fromPosition, curPos - fromPosition); public bool EndOfSpan => curPos >= buffer.Length; + public bool TryPeak (char c) => !EndOfSpan && Peak == c; + public bool TryPeak (ref char c) { + if (EndOfSpan) + return false; + c = buffer[curPos]; + return true; + } + public bool IsNextCharIn (params char[] chars) { + for (int i = 0; i < chars.Length; i++) + if (chars[i] == buffer[curPos]) + return true; + return false; + } } } diff --git a/Crow/src/Widgets/Popper.cs b/Crow/src/Widgets/Popper.cs index e99bbcbc..3e2f37cf 100644 --- a/Crow/src/Widgets/Popper.cs +++ b/Crow/src/Widgets/Popper.cs @@ -166,8 +166,9 @@ namespace Crow #region GraphicObject overrides public override void onMouseLeave (object sender, MouseMoveEventArgs e) { - base.onMouseLeave (this, e); IsPopped = false; + e.Handled = true; + base.onMouseLeave (this, e); } public override bool MouseIsIn (Point m) { diff --git a/Crow/src/Widgets/ScrollingObject.cs b/Crow/src/Widgets/ScrollingObject.cs index 3173996d..07750bab 100644 --- a/Crow/src/Widgets/ScrollingObject.cs +++ b/Crow/src/Widgets/ScrollingObject.cs @@ -52,7 +52,7 @@ namespace Crow /// Vertical Scrolling Position [DefaultValue(0)] public virtual int ScrollY { - get { return scrollY; } + get => scrollY; set { if (scrollY == value) return; @@ -75,7 +75,7 @@ namespace Crow /// Horizontal Scrolling maximum value [DefaultValue(0)] public virtual int MaxScrollX { - get { return maxScrollX; } + get => maxScrollX; set { if (maxScrollX == value) return; @@ -92,7 +92,7 @@ namespace Crow /// Vertical Scrolling maximum value [DefaultValue(0)] public virtual int MaxScrollY { - get { return maxScrollY; } + get => maxScrollY; set { if (maxScrollY == value) return; @@ -109,7 +109,7 @@ namespace Crow /// Mouse Wheel Scrolling multiplier [DefaultValue(1)] public virtual int MouseWheelSpeed { - get { return mouseWheelSpeed; } + get => mouseWheelSpeed; set { if (mouseWheelSpeed == value) return; diff --git a/Crow/src/Widgets/Slider.cs b/Crow/src/Widgets/Slider.cs index ca4089ec..dd977128 100644 --- a/Crow/src/Widgets/Slider.cs +++ b/Crow/src/Widgets/Slider.cs @@ -190,9 +190,9 @@ namespace Crow double tmp = mouseDownInitValue + (double)m.Y * unit; tmp -= tmp % SmallIncrement; Value = tmp; - } - e.Handled = true; + } } + e.Handled = true; base.onMouseMove (sender, e); } diff --git a/Crow/src/Widgets/TemplatedGroup.cs b/Crow/src/Widgets/TemplatedGroup.cs index 37d560f3..d4132672 100644 --- a/Crow/src/Widgets/TemplatedGroup.cs +++ b/Crow/src/Widgets/TemplatedGroup.cs @@ -65,7 +65,7 @@ namespace Crow { /// Keep track of expanded subnodes and closed time to unload /// //Dictionary nodes = new Dictionary(); - internal List nodes = new List(); + internal List nodes = new List();//TODO:close time tracking /// /// Item templates file path, on disk or embedded. /// @@ -157,6 +157,8 @@ namespace Crow { ol.ListAdd -= Ol_ListAdd; ol.ListRemove -= Ol_ListRemove; ol.ListEdit -= Ol_ListEdit; + ol.ListClear -= Ol_ListClear; + } data = value; @@ -166,6 +168,7 @@ namespace Crow { ol.ListAdd += Ol_ListAdd; ol.ListRemove += Ol_ListRemove; ol.ListEdit += Ol_ListEdit; + ol.ListClear += Ol_ListClear; } NotifyValueChangedAuto (data); @@ -222,6 +225,16 @@ namespace Crow { itemsContainer.Children [e.Index].DataSource = e.Element; } + void Ol_ListClear (object sender, ListChangedEventArg e) { + cancelLoadingThread (); + if (this.isPaged) { + throw new NotImplementedException (); + } else { + lock (IFace.UpdateMutex) + itemsContainer.ClearChildren (); + } + + } protected void raiseSelectedItemChanged(){ @@ -334,24 +347,30 @@ namespace Crow { void cancelLoadingThread(){ if (loadingThread == null) return; - bool updateMx = Monitor.IsEntered (IFace.UpdateMutex); - bool layoutMx = Monitor.IsEntered (IFace.LayoutMutex); - DbgLogger.AddEvent (DbgEvtType.TGCancelLoadingThread, this); + DbgLogger.StartEvent (DbgEvtType.TGCancelLoadingThread, this); - if (layoutMx) - Monitor.Exit (IFace.LayoutMutex); - if (updateMx) + int updateMx = 0, layoutMx = 0; + + while (Monitor.IsEntered (IFace.UpdateMutex)) { Monitor.Exit (IFace.UpdateMutex); + updateMx++; + } + while (Monitor.IsEntered (IFace.LayoutMutex)) { + Monitor.Exit (IFace.LayoutMutex); + layoutMx++; + } loadingThread.Cancel (); - if (layoutMx) + for (int i = 0; i < layoutMx; i++) Monitor.Enter (IFace.LayoutMutex); - if (updateMx) + for (int i = 0; i < updateMx; i++) Monitor.Enter (IFace.UpdateMutex); loadingThread = null; + + DbgLogger.EndEvent (DbgEvtType.TGCancelLoadingThread); } void loadPage(IEnumerable _data, Group page, string _dataTest) { diff --git a/Samples/DebugLogAnalyzer/src/DebugInterface.cs b/Samples/DebugLogAnalyzer/src/DebugInterface.cs index c0615321..8300abb2 100644 --- a/Samples/DebugLogAnalyzer/src/DebugInterface.cs +++ b/Samples/DebugLogAnalyzer/src/DebugInterface.cs @@ -41,11 +41,11 @@ namespace Crow } catch (System.Exception ex) { - if (Monitor.IsEntered(LayoutMutex)) + while (Monitor.IsEntered(LayoutMutex)) Monitor.Exit (LayoutMutex); - if (Monitor.IsEntered(UpdateMutex)) + while (Monitor.IsEntered(UpdateMutex)) Monitor.Exit (UpdateMutex); - if (Monitor.IsEntered(ClippingMutex)) + while (Monitor.IsEntered(ClippingMutex)) Monitor.Exit (ClippingMutex); delSetCurrentException (ex); ClearInterface(); diff --git a/Samples/DebugLogAnalyzer/src/DebugInterfaceWidget.cs b/Samples/DebugLogAnalyzer/src/DebugInterfaceWidget.cs index 3f331cf2..ee30d5ed 100644 --- a/Samples/DebugLogAnalyzer/src/DebugInterfaceWidget.cs +++ b/Samples/DebugLogAnalyzer/src/DebugInterfaceWidget.cs @@ -216,9 +216,16 @@ namespace Crow public override void onMouseMove(object sender, MouseMoveEventArgs e) { - if (initialized) { - Point m = ScreenPointToLocal (e.Position); - delMouseMove (m.X, m.Y); + if (initialized) { + try + { + Point m = ScreenPointToLocal (e.Position); + delMouseMove (m.X, m.Y); + } + catch (System.Exception ex) + { + Console.WriteLine($"[Error][DebugIFace mouse move]{ex}"); + } e.Handled = true; } base.onMouseMove(sender, e); @@ -226,7 +233,14 @@ namespace Crow public override void onMouseDown(object sender, MouseButtonEventArgs e) { if (initialized) { - delMouseDown (e.Button); + try + { + delMouseDown (e.Button); + } + catch (System.Exception ex) + { + Console.WriteLine($"[Error][DebugIFace mouse down]{ex}"); + } e.Handled=true; } base.onMouseDown (sender, e); @@ -234,7 +248,14 @@ namespace Crow public override void onMouseUp(object sender, MouseButtonEventArgs e) { if (initialized) { - delMouseUp (e.Button); + try + { + delMouseUp (e.Button); + } + catch (System.Exception ex) + { + Console.WriteLine($"[Error][DebugIFace mouse up]{ex}"); + } e.Handled=true; } base.onMouseUp (sender, e); diff --git a/Samples/DebugLogAnalyzer/src/Editor.cs b/Samples/DebugLogAnalyzer/src/Editor.cs deleted file mode 100644 index 315a5a9d..00000000 --- a/Samples/DebugLogAnalyzer/src/Editor.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using Glfw; -using Crow.Text; - -namespace Crow -{ - public class Editor : TextBox { - public override void onKeyDown(object sender, KeyEventArgs e) - { - TextSpan selection = Selection; - if (e.Key == Key.Tab && !selection.IsEmpty) { - int lineStart = lines.GetLocation (selection.Start).Line; - int lineEnd = lines.GetLocation (selection.End).Line; - - if (IFace.Shift) { - for (int l = lineStart; l <= lineEnd; l++) { - if (Text[lines[l].Start] == '\t') - update (new TextChange (lines[l].Start, 1, "")); - else if (Char.IsWhiteSpace (Text[lines[l].Start])) { - int i = 1; - while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i])) - i++; - update (new TextChange (lines[l].Start, i, "")); - } - } - - }else{ - for (int l = lineStart; l <= lineEnd; l++) - update (new TextChange (lines[l].Start, 0, "\t")); - } - - selectionStart = new CharLocation (lineStart, 0); - CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length); - - return; - } - base.onKeyDown(sender, e); - } - } -} \ No newline at end of file diff --git a/Samples/Directory.Build.props b/Samples/Directory.Build.props index c80ed43a..8536aaaa 100644 --- a/Samples/Directory.Build.props +++ b/Samples/Directory.Build.props @@ -40,7 +40,7 @@ common\%(Filename)%(Extension) - + common\%(Filename)%(Extension) diff --git a/Samples/ShowCase/Editor.cs b/Samples/ShowCase/Editor.cs deleted file mode 100644 index 315a5a9d..00000000 --- a/Samples/ShowCase/Editor.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using Glfw; -using Crow.Text; - -namespace Crow -{ - public class Editor : TextBox { - public override void onKeyDown(object sender, KeyEventArgs e) - { - TextSpan selection = Selection; - if (e.Key == Key.Tab && !selection.IsEmpty) { - int lineStart = lines.GetLocation (selection.Start).Line; - int lineEnd = lines.GetLocation (selection.End).Line; - - if (IFace.Shift) { - for (int l = lineStart; l <= lineEnd; l++) { - if (Text[lines[l].Start] == '\t') - update (new TextChange (lines[l].Start, 1, "")); - else if (Char.IsWhiteSpace (Text[lines[l].Start])) { - int i = 1; - while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i])) - i++; - update (new TextChange (lines[l].Start, i, "")); - } - } - - }else{ - for (int l = lineStart; l <= lineEnd; l++) - update (new TextChange (lines[l].Start, 0, "\t")); - } - - selectionStart = new CharLocation (lineStart, 0); - CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length); - - return; - } - base.onKeyDown(sender, e); - } - } -} \ No newline at end of file diff --git a/Samples/ShowCase/ui/showcase.crow b/Samples/ShowCase/ui/showcase.crow index 3d6b0bad..915b6b10 100644 --- a/Samples/ShowCase/ui/showcase.crow +++ b/Samples/ShowCase/ui/showcase.crow @@ -84,7 +84,7 @@ + Foreground="DarkGrey" Background="White" MouseWheelSpeed="20"/> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Glfw; +using Crow.Text; +using System.Collections.Generic; +using Crow.Cairo; +using System.Threading.Tasks; +using System.Linq; + +namespace Crow +{ + [Flags] + public enum TokenType { + Unknown, + Trivia = 0x0100, + WhiteSpace = 0x4100, + Tabulation = 0x4101, + LineBreak = 0x4102, + LineComment = 0x0103, + BlockCommentStart = 0x0104, + BlockComment = 0x0105, + BlockCommentEnd = 0x0106, + Name = 0x0200, + ElementName = 0x0201, + AttributeName = 0x0202, + PI_Target = 0x0203, + Punctuation = 0x0400, + PI_Start = 0x0401,// '' + Operator = 0x0800, + EqualSign = 0x0801, + AttributeValue = 0x2000, + Keyword = 0x1000, + ElementOpen = 0x0403,// '<' + EndElementOpen = 0x0404,// '' + ClosingSign = 0x0406,// '>' + DTDObjectOpen = 0x04A0,// ' Start + Length; + public TextSpan Span => new TextSpan (Start, End); + + public Token (TokenType type, int pos) { + Type = type; + Start = pos; + Length = 1; + } + public Token (TokenType type, int start, int end) { + Type = type; + Start = start; + Length = end - start; + } + public override string ToString() => $"{Type},{Start} {Length}"; + } + public class XmlSource { + public Token[] Tokens; + public readonly string Source; + + public XmlSource (string _source) { + Source = _source; + Tokenizer tokenizer = new Tokenizer(); + Tokens = tokenizer.Tokenize (Source); + + foreach (Token t in Tokens) + Console.WriteLine ($"{t,-40} {Source.AsSpan(t.Start, t.Length).ToString()}"); + } + public class TokenizerException : Exception { + public readonly int Position; + public TokenizerException(string message, int position, Exception innerException = null) + : base (message, innerException) { + Position = position; + } + } + + class Tokenizer { + enum States + { + Init,//first statement of prolog, xmldecl should only apear in this state + prolog,//misc before doctypedecl + ProcessingInstrucitons, + DTD, + DTDObject,//doctype finished + Xml, + StartTag,//inside start tag + Content,//after start tag with no closing slash + EndTag + } + + States curState = States.Init; + List Toks = new List(100); + + public Tokenizer () {} + + void skipWhiteSpaces (ref SpanCharReader reader) { + while(!reader.EndOfSpan) { + switch (reader.Peak) { + case '\x85': + case '\x2028': + case '\xA': + reader.Read(); + addTok (ref reader, TokenType.LineBreak); + break; + case '\xD': + reader.Read(); + if (reader.IsNextCharIn ('\xA', '\x85')) + reader.Read(); + addTok (ref reader, TokenType.LineBreak); + break; + case '\x20': + case '\x9': + char c = reader.Read(); + while (reader.TryPeak (c)) + reader.Read(); + addTok (ref reader, c == '\x20' ? TokenType.WhiteSpace : TokenType.Tabulation); + break; + default: + return; + } + } + } + bool readName (ref SpanCharReader reader) { + if (reader.EndOfSpan) + return false; + char c = reader.Peak; + if (char.IsLetter(c) || c == '_' || c == ':') { + reader.Advance (); + while (reader.TryPeak (ref c)) { + if (!(char.IsLetterOrDigit(c) || c == '.' || c == '-' || c == '\xB7')) + return true; + reader.Advance (); + } + return true; + } + return false; + } + + int startOfTok; + void addTok (ref SpanCharReader reader, TokenType tokType) { + if (reader.CurrentPosition == startOfTok) + return; + Toks.Add (new Token(tokType, startOfTok, reader.CurrentPosition)); + startOfTok = reader.CurrentPosition; + } + public Token[] Tokenize (string source) { + SpanCharReader reader = new SpanCharReader(source); + + startOfTok = 0; + int curObjectLevel = 0; + curState = States.Init; + + while(!reader.EndOfSpan) { + + skipWhiteSpaces (ref reader); + + if (reader.EndOfSpan) + break; + + switch (reader.Peak) { + case '<': + reader.Advance (); + if (reader.TryPeak ('?')) { + reader.Advance (); + addTok (ref reader, TokenType.PI_Start); + readName (ref reader); + addTok (ref reader, TokenType.PI_Target); + curState = States.ProcessingInstrucitons; + } else if (reader.TryPeak ('!')) { + reader.Advance (); + if (reader.TryPeak ("--")) { + reader.Advance (2); + addTok (ref reader, TokenType.BlockCommentStart); + if (reader.TryReadUntil ("-->")) { + addTok (ref reader, TokenType.BlockComment); + reader.Advance (3); + addTok (ref reader, TokenType.BlockCommentEnd); + } else if (reader.TryPeak ("-->")) { + reader.Advance (3); + addTok (ref reader, TokenType.BlockCommentEnd); + } + } else { + addTok (ref reader, TokenType.DTDObjectOpen); + if (readName (ref reader)) { + addTok (ref reader, TokenType.Keyword); + curState = States.DTDObject; + } + } + } else if (reader.TryPeak('/')) { + reader.Advance (); + addTok (ref reader, TokenType.EndElementOpen); + if (readName (ref reader)) { + addTok (ref reader, TokenType.ElementName); + if (reader.TryPeak('>')) { + reader.Advance (); + addTok (ref reader, TokenType.ClosingSign); + + if (--curObjectLevel > 0) + curState = States.Content; + else + curState = States.Xml; + } + } + }else{ + addTok (ref reader, TokenType.ElementOpen); + if (readName (ref reader)) { + addTok (ref reader, TokenType.ElementName); + curState = States.StartTag; + } + } + break; + case '?': + reader.Advance (); + if (reader.TryPeak ('>')){ + reader.Advance (); + addTok (ref reader, TokenType.PI_End); + }else + addTok (ref reader, TokenType.Unknown); + curState = States.prolog; + break; + case '\'': + case '"': + char q = reader.Read(); + if (reader.TryReadUntil (q)) { + reader.Advance (); + addTok (ref reader, TokenType.AttributeValue); + } else + addTok (ref reader, TokenType.Unknown); + break; + case '=': + reader.Advance(); + addTok (ref reader, TokenType.EqualSign); + break; + case '>': + reader.Advance(); + addTok (ref reader, TokenType.ClosingSign); + curObjectLevel++; + curState = States.Content; + break; + case '/': + reader.Advance(); + if (reader.TryRead ('>')) { + addTok (ref reader, TokenType.EmptyElementClosing); + if (--curObjectLevel > 0) + curState = States.Content; + else + curState = States.Xml; + }else + addTok (ref reader, TokenType.Unknown); + break; + default: + if (curState == States.StartTag || curState == States.ProcessingInstrucitons) { + if (readName(ref reader)) + addTok (ref reader, TokenType.AttributeName); + else if (reader.TryAdvance()) + addTok (ref reader, TokenType.Unknown); + } else { + reader.TryReadUntil ('<'); + addTok (ref reader, TokenType.Content); + } + break; + } + } + + return Toks.ToArray(); + } + + } + + } + public class Editor : TextBox { + XmlSource source; + object TokenMutex = new object(); + + void parse () { + XmlSource tmp = new XmlSource(_text); + lock(TokenMutex) + source = tmp; + RegisterForGraphicUpdate(); + } + protected override void onInitialized(object sender, EventArgs e) + { + base.onInitialized(sender, e); + + } + Widget overlay; + public override void OnTextChanged(object sender, TextChangeEventArgs e) + { + base.OnTextChanged(sender, e); + //Task.Run(()=>parse()); + parse(); + + /*if (overlay == null && HasFocus) + overlay = IFace.LoadIMLFragment(@"");*/ + } + public override void onKeyDown(object sender, KeyEventArgs e) + { + TextSpan selection = Selection; + if (e.Key == Key.Tab && !selection.IsEmpty) { + int lineStart = lines.GetLocation (selection.Start).Line; + int lineEnd = lines.GetLocation (selection.End).Line; + + if (IFace.Shift) { + for (int l = lineStart; l <= lineEnd; l++) { + if (Text[lines[l].Start] == '\t') + update (new TextChange (lines[l].Start, 1, "")); + else if (Char.IsWhiteSpace (Text[lines[l].Start])) { + int i = 1; + while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i])) + i++; + update (new TextChange (lines[l].Start, i, "")); + } + } + + }else{ + for (int l = lineStart; l <= lineEnd; l++) + update (new TextChange (lines[l].Start, 0, "\t")); + } + + selectionStart = new CharLocation (lineStart, 0); + CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length); + + return; + } + base.onKeyDown(sender, e); + } + int tabSize = 4; + + protected override void drawContent (Context gr) { + lock(TokenMutex) { + if (source == null || source.Tokens.Length == 0) { + base.drawContent (gr); + return; + } + + Rectangle cb = ClientRectangle; + fe = gr.FontExtents; + double lineHeight = fe.Ascent + fe.Descent; + + CharLocation selStart = default, selEnd = default; + bool selectionNotEmpty = false; + + if (HasFocus) { + if (currentLoc?.Column < 0) { + updateLocation (gr, cb.Width, ref currentLoc); + NotifyValueChanged ("CurrentColumn", CurrentColumn); + } else + updateLocation (gr, cb.Width, ref currentLoc); + + if (overlay != null) { + Point p = new Point((int)currentLoc.Value.VisualCharXPosition, (int)(lineHeight * (currentLoc.Value.Line + 1))); + p += ScreenCoordinates (Slot).TopLeft; + overlay.Left = p.X; + overlay.Top = p.Y; + } + if (selectionStart.HasValue) { + updateLocation (gr, cb.Width, ref selectionStart); + if (CurrentLoc.Value != selectionStart.Value) + selectionNotEmpty = true; + } + if (selectionNotEmpty) { + 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; + } + } else + IFace.forceTextCursor = true; + } + + double spacePixelWidth = gr.TextExtents (" ").XAdvance; + int x = 0, y = 0; + double pixX = cb.Left; + + + Foreground.SetAsSource (IFace, gr); + gr.Translate (-ScrollX, -ScrollY); + + + ReadOnlySpan sourceBytes = source.Source.AsSpan(); + Span bytes = stackalloc byte[128]; + TextExtents extents; + int tokPtr = 0; + Token tok = source.Tokens[tokPtr]; + bool multilineToken = false; + + ReadOnlySpan buff = sourceBytes; + + + for (int i = 0; i < lines.Count; i++) { + //if (!cancelLinePrint (lineHeight, lineHeight * y, cb.Height)) { + + if (multilineToken) { + if (tok.End < lines[i].End) {//last incomplete line of multiline token + buff = sourceBytes.Slice (lines[i].Start, tok.End - lines[i].Start); + } else {//print full line + buff = sourceBytes.Slice (lines[i].Start, lines[i].Length); + } + } + + while (tok.Start < lines[i].End) { + if (!multilineToken) { + if (tok.End > lines[i].End) {//first line of multiline + multilineToken = true; + buff = sourceBytes.Slice (tok.Start, lines[i].End - tok.Start); + } else + buff = sourceBytes.Slice (tok.Start, tok.Length); + + if (tok.Type.HasFlag (TokenType.Punctuation)) + gr.SetSource(Colors.DarkGrey); + else if (tok.Type.HasFlag (TokenType.Trivia)) + gr.SetSource(Colors.DimGrey); + else if (tok.Type == TokenType.ElementName) { + gr.SetSource(Colors.Green); + }else if (tok.Type == TokenType.AttributeName) { + gr.SetSource(Colors.Blue); + }else if (tok.Type == TokenType.AttributeValue) { + gr.SetSource(Colors.OrangeRed); + }else if (tok.Type == TokenType.EqualSign) { + gr.SetSource(Colors.Black); + }else if (tok.Type == TokenType.PI_Target) { + gr.SetSource(Colors.DarkSlateBlue); + }else { + gr.SetSource(Colors.Red); + } + } + + int size = buff.Length * 4 + 1; + if (bytes.Length < size) + bytes = size > 512 ? new byte[size] : stackalloc byte[size]; + + int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes); + + if (encodedBytes > 0) { + bytes[encodedBytes++] = 0; + gr.TextExtents (bytes.Slice (0, encodedBytes), out extents); + gr.MoveTo (pixX, lineHeight * y + fe.Ascent); + gr.ShowText (bytes.Slice (0, encodedBytes)); + pixX += extents.XAdvance; + x += buff.Length; + } + + if (multilineToken) { + if (tok.End < lines[i].End)//last incomplete line of multiline token + multilineToken = false; + else + break; + } + + if (++tokPtr >= source.Tokens.Length) + break; + tok = source.Tokens[tokPtr]; + } + + if (HasFocus && selectionNotEmpty) { + RectangleD lineRect = new RectangleD (cb.X, lineHeight * y + cb.Top, pixX, lineHeight); + RectangleD selRect = lineRect; + + if (i >= selStart.Line && i <= selEnd.Line) { + if (selStart.Line == selEnd.Line) { + selRect.X = selStart.VisualCharXPosition + cb.X; + selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition; + } else if (i == selStart.Line) { + double newX = selStart.VisualCharXPosition + cb.X; + selRect.Width -= (newX - selRect.X) - 10.0; + selRect.X = newX; + } else if (i == selEnd.Line) + selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X; + else + selRect.Width += 10.0; + + buff = sourceBytes.Slice(lines[i].Start, lines[i].Length); + int size = buff.Length * 4 + 1; + if (bytes.Length < size) + bytes = size > 512 ? new byte[size] : stackalloc byte[size]; + + int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes); + + gr.SetSource (SelectionBackground); + gr.Rectangle (selRect); + if (encodedBytes < 0) + gr.Fill (); + else { + gr.FillPreserve (); + gr.Save (); + gr.Clip (); + gr.SetSource (SelectionForeground); + gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent); + gr.ShowText (bytes.Slice (0, encodedBytes)); + gr.Restore (); + } + Foreground.SetAsSource (IFace, gr); + } + } + + if (!multilineToken) { + if (++tokPtr >= source.Tokens.Length) + break; + tok = source.Tokens[tokPtr]; + } + + x = 0; + pixX = 0; + + y++; + + + /* } else if (tok2.Type == TokenType.Tabulation) { + int spaceRounding = x % tabSize; + int spaces = spaceRounding == 0 ? + tabSize * tok2.Length : + spaceRounding + tabSize * (tok2.Length - 1); + x += spaces; + pixX += spacePixelWidth * spaces; + continue; + } else if (tok2.Type == TokenType.WhiteSpace) { + x += tok2.Length; + pixX += spacePixelWidth * tok2.Length;*/ + } + gr.Translate (ScrollX, ScrollY); + } + } + } +} \ No newline at end of file diff --git a/Samples/common/SampleBase.cs b/Samples/common/SampleBase.cs index 59244f1b..a17eaf92 100644 --- a/Samples/common/SampleBase.cs +++ b/Samples/common/SampleBase.cs @@ -127,6 +127,9 @@ namespace Crow public override string ToString () => $"{Prop1}, {Prop2}"; + public void OnValidateCommand (Object sender, ValidateEventArgs e) { + Console.WriteLine ($"Validation: {e.ValidatedText}"); + } } public class TestClassVC : IValueChange { @@ -160,7 +163,7 @@ namespace Crow => $"{Prop1}, {Prop2}"; } - TestClass tcInstance;// = new TestClass () { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" }; + TestClass tcInstance = new TestClass () { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" }; TestClassVC tcVCInstance;// = new TestClassVC () { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" }; TestClass tcInstance1 = new TestClass () { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" }; TestClassVC tcVCInstance1 = new TestClassVC () { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" }; diff --git a/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow b/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow index 211937c4..39721689 100644 --- a/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow +++ b/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow @@ -1,5 +1,5 @@  - + - - - - - - - - - - + + + + + + - + \ No newline at end of file diff --git a/Samples/common/ui/templates/ColorPicker.template b/Samples/common/ui/templates/ColorPicker.template index 2d963fc4..81f1f7ca 100644 --- a/Samples/common/ui/templates/ColorPicker.template +++ b/Samples/common/ui/templates/ColorPicker.template @@ -1,60 +1,34 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + \ No newline at end of file