aboutsummaryrefslogtreecommitdiffstatsci
path: root/Samples/common/src
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2021-06-05 16:52:53 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2021-06-05 16:52:53 +0200
commitf738cf0f78c57750cce8fd5744825b0591f24bb8 (patch)
tree47c3313d26151e53cbef822ac602132fb77b88dd /Samples/common/src
parenta515e31998f64f537e29cd30fdb7e9bfc7d4a02f (diff)
ItemsScroller for list, layouting changes, editor suggestions
Diffstat (limited to 'Samples/common/src')
-rw-r--r--Samples/common/src/Editor.cs401
-rw-r--r--Samples/common/src/ImlParsing/Syntax.cs310
-rw-r--r--Samples/common/src/ImlParsing/Token.cs40
-rw-r--r--Samples/common/src/ImlParsing/TokenType.cs38
-rw-r--r--Samples/common/src/ImlParsing/XmlSource.cs260
-rw-r--r--Samples/common/src/SampleBase.cs383
6 files changed, 1432 insertions, 0 deletions
diff --git a/Samples/common/src/Editor.cs b/Samples/common/src/Editor.cs
new file mode 100644
index 00000000..b9b0ae90
--- /dev/null
+++ b/Samples/common/src/Editor.cs
@@ -0,0 +1,401 @@
+// Copyright (c) 2013-2021 Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// 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;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+
+namespace Crow
+{
+ 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);
+
+ }
+ ListBox overlay;
+ List<String> suggestions;
+ public List<String> Suggestions {
+ get => suggestions;
+ set {
+ suggestions = value;
+ NotifyValueChangedAuto (suggestions);
+ if (suggestions == null || suggestions.Count == 0)
+ hideOverlay ();
+ else
+ showOverlay ();
+ }
+ }
+ bool suggestionsActive => overlay != null && overlay.IsVisible;
+ Token currentToken;
+ SyntaxNode currentNode;
+ string[] allWidgetNames = typeof (Widget).Assembly.GetExportedTypes ().Where(t=>typeof(Widget).IsAssignableFrom (t))
+ .Select (s => s.Name).ToArray ();
+
+
+ string [] getAllCrowTypeMembers (string crowTypeName) {
+ Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName);
+ return crowType.GetMembers (BindingFlags.Public | BindingFlags.Instance).
+ Where (m=>((m is PropertyInfo pi && pi.CanWrite) || (m is EventInfo)) &&
+ m.GetCustomAttribute<XmlIgnoreAttribute>() == null).Select (mb=>mb.Name).ToArray();
+ }
+ public override void OnTextChanged(object sender, TextChangeEventArgs e)
+ {
+ base.OnTextChanged(sender, e);
+ //Task.Run(()=>parse());
+
+ parse();
+ tryGetSuggestions ();
+
+ //Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}");
+ }
+ void tryGetSuggestions () {
+ if (!currentLoc.HasValue)
+ return;
+ int pos = lines.GetAbsolutePosition (CurrentLoc.Value);
+ currentToken = source.FindTokenIncludingPosition (pos);
+ currentNode = source.FindNodeIncludingPosition (pos);
+
+ Console.WriteLine ($"Current Token: {currentToken} Current Node: {currentNode}");
+
+ if (currentToken.Type == TokenType.ElementOpen) {
+ Suggestions = new List<string> (allWidgetNames);
+ } else if (currentToken.Type == TokenType.ElementName) {
+ Suggestions = allWidgetNames.Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ } else if (currentNode is AttributeSyntax attribNode) {
+ if (currentNode.Parent is ElementTagSyntax eltTag) {
+ if (currentToken.Type == TokenType.AttributeName && eltTag.NameToken.HasValue) {
+ Suggestions = getAllCrowTypeMembers (eltTag.NameToken.Value.AsString (_text))
+ .Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ }
+ }
+ } else if (currentNode is ElementStartTagSyntax eltStartTag) {
+ Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text)).ToList ();
+ } else {
+ /*SyntaxNode curNode = source.FindNodeIncludingPosition (pos);
+ Console.WriteLine ($"Current Node: {curNode}");
+ if (curNode is ElementStartTagSyntax eltStartTag &&
+ (currentToken.Type != TokenType.ClosingSign && currentToken.Type != TokenType.EmptyElementClosing && currentToken.Type != TokenType.Unknown)) {
+ Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text)).ToList ();
+ } else*/
+ hideOverlay ();
+ }
+ }
+ void showOverlay () {
+ lock (IFace.UpdateMutex) {
+ if (overlay == null) {
+ overlay = IFace.LoadIMLFragment<ListBox>(@"
+ <ListBox Style='suggestionsListBox' Data='{Suggestions}' />
+ ");
+ overlay.DataSource = this;
+ overlay.Loaded += (sender, arg) => (sender as ListBox).SelectedIndex = 0;
+ } else
+ overlay.IsVisible = true;
+ overlay.RegisterForLayouting(LayoutingType.Sizing);
+ }
+ }
+ void hideOverlay () {
+ if (overlay == null)
+ return;
+ overlay.IsVisible = false;
+ }
+ void completeToken () {
+ string selectedSugg = overlay.SelectedItem as string;
+ if (selectedSugg == null)
+ return;
+ if (currentToken.Type == TokenType.ElementOpen || currentToken.Type == TokenType.WhiteSpace)
+ update (new TextChange (currentToken.End, 0, selectedSugg));
+ else
+ update (new TextChange (currentToken.Start, currentToken.Length, selectedSugg));
+ hideOverlay ();
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e) {
+ hideOverlay ();
+ base.onMouseDown (sender, e);
+ }
+ public override void onKeyDown(object sender, KeyEventArgs e)
+ {
+ if (suggestionsActive) {
+ switch (e.Key) {
+ case Key.Escape:
+ hideOverlay ();
+ return;
+ case Key.Left:
+ case Key.Right:
+ hideOverlay ();
+ break;
+ case Key.End:
+ case Key.Home:
+ case Key.Down:
+ case Key.Up:
+ case Key.PageDown:
+ case Key.PageUp:
+ overlay.onKeyDown (this, e);
+ return;
+ case Key.Tab:
+ completeToken ();
+ return;
+ }
+ } else if (e.Key == Key.Space && IFace.Ctrl) {
+ tryGetSuggestions ();
+ return;
+ }
+
+ 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);
+ }
+
+ protected override void drawContent (Context gr) {
+ try {
+ 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 && overlay.IsVisible) {
+ Point p = new Point((int)currentLoc.Value.VisualCharXPosition - ScrollX, (int)(lineHeight * (currentLoc.Value.Line + 1) - ScrollY));
+ if (p.Y < 0 || p.X < 0)
+ hideOverlay ();
+ else {
+ 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<char> sourceBytes = source.Source.AsSpan();
+ Span<byte> bytes = stackalloc byte[128];
+ TextExtents extents;
+ int tokPtr = 0;
+ Token tok = source.Tokens[tokPtr];
+ bool multilineToken = false;
+
+ ReadOnlySpan<char> 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);
+ }
+ } catch {
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Samples/common/src/ImlParsing/Syntax.cs b/Samples/common/src/ImlParsing/Syntax.cs
new file mode 100644
index 00000000..fd410eaf
--- /dev/null
+++ b/Samples/common/src/ImlParsing/Syntax.cs
@@ -0,0 +1,310 @@
+// Copyright (c) 2013-2021 Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Crow
+{
+ public class SyntaxException : Exception {
+ public readonly Token Token;
+ public SyntaxException(string message, Token token = default, Exception innerException = null)
+ : base (message, innerException) {
+ Token = token;
+ }
+ }
+ public class SyntaxAnalyser {
+ XmlSource source;
+ IEnumerable<Token> tokens => source.Tokens;
+ public SyntaxNode Root => CurrentNode;
+ public SyntaxAnalyser (XmlSource source) {
+ this.source = source;
+ //tokens = source.Tokens.Where (t => !t.Type.HasFlag (TokenType.Trivia));
+ }
+
+
+ /*List<SyntaxNode> readAttributes (IEnumerator<Token> iter) {
+ List<SyntaxNode> attributes = new List<SyntaxNode> (10);
+ while (iter.MoveNext ()) {
+ switch (iter.Current.Type) {
+ case TokenType.EmptyElementClosing:
+ case TokenType.ClosingSign:
+ return attributes;
+ case TokenType.AttributeName:
+ Token attribName = iter.Current;
+ moveNextOrThrow (iter);
+ Token equalSign = accept (iter, TokenType.EqualSign);
+ moveNextOrThrow (iter);
+ Token attribValue = accept (iter, TokenType.AttributeValue);
+ attributes.Add (new AttributeSyntax (source, attribName, equalSign, attribValue));
+ break;
+ }
+ }
+ throw new SyntaxException ("Unexpected end of source");
+ }
+
+ Token accept (IEnumerator<Token> iter, TokenType acceptedTokenType) {
+ if (iter.Current.Type == acceptedTokenType)
+ return iter.Current;
+ else
+ throw new SyntaxException ("Unexpected token", iter.Current);
+ }
+ void moveNextOrThrow (IEnumerator<Token> iter) {
+ if (!iter.MoveNext ())
+ throw new SyntaxException ("Unexpected end of source");
+ }
+ ElementStartTagSyntax readElementStart (IEnumerator<Token> iter) {
+ Token eltOpen = iter.Current;
+ moveNextOrThrow (iter);
+ Token eltName = accept (iter, TokenType.ElementName);
+
+ List<SyntaxNode> attributes = readAttributes (iter);
+
+ if (iter.Current.Type == TokenType.EmptyElementClosing || iter.Current.Type == TokenType.ClosingSign)
+ return new ElementStartTagSyntax(source, eltOpen, eltName, iter.Current, attributes);
+
+ throw new SyntaxException ("Unexpected token", iter.Current);
+ }
+ ElementEndTagSyntax readElementEnd (IEnumerator<Token> iter) {
+ Token eltOpen = iter.Current;
+ moveNextOrThrow (iter);
+ Token eltName = accept (iter, TokenType.ElementName);
+ moveNextOrThrow (iter);
+ return new ElementEndTagSyntax(source, eltOpen, eltName, accept (iter, TokenType.ClosingSign));
+ }*/
+
+ SyntaxNode CurrentNode;
+ Token previousTok;
+ IEnumerator<Token> iter;
+ public List<SyntaxException> Exceptions { get; private set; }
+
+ void finishCurrentNode () {
+
+ }
+
+ public void Process () {
+ Exceptions = new List<SyntaxException> ();
+ CurrentNode = new IMLRootSyntax (source);
+ previousTok = default;
+ iter = tokens.GetEnumerator ();
+
+ bool notEndOfSource = iter.MoveNext ();
+ while (notEndOfSource) {
+ if (!iter.Current.Type.HasFlag (TokenType.Trivia)) {
+ if (CurrentNode is ElementStartTagSyntax tag) {
+ if (iter.Current.Type == TokenType.AttributeName) {
+ AttributeSyntax attribute = new AttributeSyntax (iter.Current);
+ attribute.NameToken = iter.Current;
+ CurrentNode = CurrentNode.AddChild (attribute);
+ } else if (iter.Current.Type == TokenType.ElementName)
+ tag.NameToken = iter.Current;
+ else if (iter.Current.Type == TokenType.ClosingSign) {
+ tag.EndToken = iter.Current;
+ CurrentNode = tag.Parent;
+ CurrentNode.RemoveChild (tag);
+ CurrentNode = CurrentNode.AddChild (new ElementSyntax (tag));
+ } else if (iter.Current.Type == TokenType.EmptyElementClosing) {
+ tag.EndToken = iter.Current;
+ CurrentNode = tag.Parent;
+ CurrentNode.RemoveChild (tag);
+ CurrentNode.AddChild (new EmptyElementSyntax (tag));
+ } else {
+ Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current));
+ CurrentNode.EndToken = previousTok;
+ CurrentNode = CurrentNode.Parent;
+ continue;
+ }
+ } else if (CurrentNode is ElementSyntax elt) {
+ if (iter.Current.Type == TokenType.ElementOpen)
+ CurrentNode = CurrentNode.AddChild (new ElementStartTagSyntax (iter.Current));
+ else if (iter.Current.Type == TokenType.EndElementOpen) {
+ elt.EndTag = new ElementEndTagSyntax (iter.Current);
+ CurrentNode = elt.AddChild (elt.EndTag);
+ }
+ } else if (CurrentNode is AttributeSyntax attrib) {
+ if (iter.Current.Type == TokenType.EqualSign)
+ if (attrib.EqualToken.HasValue)
+ Exceptions.Add (new SyntaxException ("Extra equal sign in attribute syntax", iter.Current));
+ else
+ attrib.EqualToken = iter.Current;
+ else if (iter.Current.Type == TokenType.AttributeValue) {
+ attrib.ValueToken = attrib.EndToken = iter.Current;
+ CurrentNode = CurrentNode.Parent;
+ } else {
+ Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current));
+ CurrentNode.EndToken = previousTok;
+ CurrentNode = CurrentNode.Parent;
+ continue;
+ }
+ } else if (CurrentNode is ElementEndTagSyntax eltEndTag) {
+ if (iter.Current.Type == TokenType.ElementName)
+ eltEndTag.NameToken = iter.Current;
+ else if (iter.Current.Type == TokenType.ClosingSign) {
+ eltEndTag.EndToken = eltEndTag.Parent.EndToken = iter.Current;
+ CurrentNode = eltEndTag.Parent.Parent;
+ } else {
+ Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current));
+ eltEndTag.EndToken = eltEndTag.Parent.EndToken = previousTok;
+ CurrentNode = CurrentNode.Parent.Parent;
+ continue;
+ }
+ } else if (CurrentNode is IMLRootSyntax) {
+ switch (iter.Current.Type) {
+ case TokenType.ElementOpen:
+ CurrentNode = CurrentNode.AddChild (new ElementStartTagSyntax (iter.Current));
+ break;
+ case TokenType.PI_Start:
+ CurrentNode = CurrentNode.AddChild (new ProcessingInstructionSyntax (iter.Current));
+ break;
+ default:
+ Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current));
+ break;
+ }
+ } else if (CurrentNode is ProcessingInstructionSyntax pi) {
+ if (iter.Current.Type == TokenType.PI_Target)
+ pi.NameToken = iter.Current;
+ else if (iter.Current.Type == TokenType.PI_End) {
+ pi.EndToken = iter.Current;
+ CurrentNode = CurrentNode.Parent;
+ } else if (iter.Current.Type == TokenType.AttributeName) {
+ AttributeSyntax attribute = new AttributeSyntax (iter.Current);
+ attribute.NameToken = iter.Current;
+ CurrentNode = CurrentNode.AddChild (attribute);
+ } else {
+ Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current));
+ pi.EndToken = previousTok;
+ CurrentNode = CurrentNode.Parent;
+ continue;
+ }
+ }
+ }
+
+ previousTok = iter.Current;
+ notEndOfSource = iter.MoveNext ();
+ }
+ while (CurrentNode.Parent != null) {
+ if (!CurrentNode.EndToken.HasValue)
+ CurrentNode.EndToken = previousTok;
+ CurrentNode = CurrentNode.Parent;
+ }
+ }
+ }
+ public class SyntaxNode {
+ public SyntaxNode Parent { get; private set; }
+ List<SyntaxNode> children = new List<SyntaxNode> ();
+
+ public readonly Token StartToken;
+ public Token? EndToken { get; internal set; }
+ public SyntaxNode (Token tokStart, Token? tokEnd = null) {
+ StartToken = tokStart;
+ EndToken = tokEnd;
+ }
+
+ public virtual bool IsComplete => EndToken.HasValue;
+
+ internal SyntaxNode AddChild (SyntaxNode child) {
+ children.Add (child);
+ child.Parent = this;
+ return child;
+ }
+ internal void RemoveChild (SyntaxNode child) {
+ children.Remove (child);
+ child.Parent = null;
+ }
+ public T GetChild<T> () => children.OfType<T> ().FirstOrDefault ();
+ public SyntaxNode FindNodeIncludingPosition (int pos) {
+ foreach (SyntaxNode node in children) {
+ if (node.Contains (pos))
+ return node.FindNodeIncludingPosition (pos);
+ }
+ return this;
+ }
+ public T FindNodeIncludingPosition<T> (int pos) {
+ foreach (SyntaxNode node in children) {
+ if (node.Contains (pos))
+ return node.FindNodeIncludingPosition<T> (pos);
+ }
+
+ return this is T tt ? tt : default;
+ }
+ public virtual IMLRootSyntax Root => Parent.Root;
+ public XmlSource Source => Root.source;
+ public bool Contains (int pos) =>
+ EndToken.HasValue ?
+ StartToken.Start <= pos && EndToken.Value.End >= pos : false;
+
+ public void Dump (int level = 0) {
+ Console.WriteLine ($"{new string('\t', level)}{this}");
+ foreach (SyntaxNode node in children)
+ node.Dump (level + 1);
+ }
+ public override string ToString() => $"{this.GetType().Name}: {StartToken} -> {EndToken}";
+ }
+ public class IMLRootSyntax : SyntaxNode {
+ internal readonly XmlSource source;
+ public override IMLRootSyntax Root => this;
+ public IMLRootSyntax (XmlSource source)
+ : base (source.Tokens.FirstOrDefault (), source.Tokens.LastOrDefault ()) {
+ this.source = source;
+ }
+ }
+ public class ProcessingInstructionSyntax : SyntaxNode {
+ public Token PIStartToken => StartToken;
+ public Token? PIEndToken => EndToken.HasValue && EndToken.Value.Type == TokenType.PI_End ? EndToken : null;
+ public Token? NameToken { get; internal set; }
+ public override bool IsComplete => base.IsComplete & NameToken.HasValue;
+ public ProcessingInstructionSyntax (Token startTok)
+ : base (startTok) {
+ }
+ }
+
+ public abstract class ElementTagSyntax : SyntaxNode {
+ public Token OpenToken => StartToken;
+ public Token? NameToken { get; internal set; }
+ public Token? CloseToken => EndToken.HasValue && EndToken.Value.Type == TokenType.ClosingSign ? EndToken : null;
+ public override bool IsComplete => base.IsComplete & NameToken.HasValue & CloseToken.HasValue;
+ protected ElementTagSyntax (Token startTok)
+ : base (startTok) {
+ }
+ }
+ public class ElementStartTagSyntax : ElementTagSyntax {
+ public ElementStartTagSyntax (Token startTok)
+ : base (startTok) {
+ }
+ }
+ public class ElementEndTagSyntax : ElementTagSyntax {
+ public ElementEndTagSyntax (Token startTok)
+ : base (startTok) {
+ }
+ }
+
+ public class EmptyElementSyntax : SyntaxNode {
+ public readonly ElementStartTagSyntax StartTag;
+ public EmptyElementSyntax (ElementStartTagSyntax startNode) : base (startNode.StartToken, startNode.EndToken) {
+ StartTag = startNode;
+ AddChild (StartTag);
+ }
+ }
+
+ public class ElementSyntax : SyntaxNode {
+ public readonly ElementStartTagSyntax StartTag;
+ public ElementEndTagSyntax EndTag { get; internal set; }
+
+ public override bool IsComplete => base.IsComplete & StartTag.IsComplete & (EndTag != null && EndTag.IsComplete);
+
+ public ElementSyntax (ElementStartTagSyntax startTag)
+ : base (startTag.StartToken) {
+ StartTag = startTag;
+ AddChild (StartTag);
+ }
+ }
+
+ public class AttributeSyntax : SyntaxNode {
+ public Token? NameToken { get; internal set; }
+ public Token? EqualToken { get; internal set; }
+ public Token? ValueToken { get; internal set; }
+ public AttributeSyntax (Token startTok) : base (startTok) {}
+ public override bool IsComplete => base.IsComplete & NameToken.HasValue & EqualToken.HasValue & ValueToken.HasValue;
+ }
+} \ No newline at end of file
diff --git a/Samples/common/src/ImlParsing/Token.cs b/Samples/common/src/ImlParsing/Token.cs
new file mode 100644
index 00000000..46389df4
--- /dev/null
+++ b/Samples/common/src/ImlParsing/Token.cs
@@ -0,0 +1,40 @@
+// Copyright (c) 2013-2021 Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Crow.Text;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Crow
+{
+ public struct Token : IComparable<Token>, IEquatable<Token> {
+ public readonly TokenType Type;
+ public int Start;
+ public readonly int Length;
+ public int End => Start + Length;
+ public TextSpan Span => new TextSpan (Start, End);
+ public string AsString (ReadOnlySpan<char> source)
+ => source.Slice (Start, Length).ToString();
+
+ 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 int CompareTo([AllowNull] Token other)
+ => Start - other.Start;
+ public bool Equals([AllowNull] Token other)
+ => Type == other.Type && Start == other.Start && Length == other.Length;
+ public override bool Equals(object obj)
+ => obj is Token other ? Equals (other) : false;
+ public override int GetHashCode() => HashCode.Combine (Type, Start, Length);
+ public override string ToString() => $"{Type}:{Start},{Length};";
+ }
+} \ No newline at end of file
diff --git a/Samples/common/src/ImlParsing/TokenType.cs b/Samples/common/src/ImlParsing/TokenType.cs
new file mode 100644
index 00000000..f9d7bcc6
--- /dev/null
+++ b/Samples/common/src/ImlParsing/TokenType.cs
@@ -0,0 +1,38 @@
+// Copyright (c) 2013-2021 Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+
+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,// '<?'
+ PI_End = 0x0402,// '?>'
+ Operator = 0x0800,
+ EqualSign = 0x0801,
+ AttributeValue = 0x2000,
+ Keyword = 0x1000,
+ ElementOpen = 0x0403,// '<'
+ EndElementOpen = 0x0404,// '</'
+ EmptyElementClosing = 0x0405,// '/>'
+ ClosingSign = 0x0406,// '>'
+ DTDObjectOpen = 0x04A0,// '<!'
+ Content,
+ }
+} \ No newline at end of file
diff --git a/Samples/common/src/ImlParsing/XmlSource.cs b/Samples/common/src/ImlParsing/XmlSource.cs
new file mode 100644
index 00000000..f520dcf3
--- /dev/null
+++ b/Samples/common/src/ImlParsing/XmlSource.cs
@@ -0,0 +1,260 @@
+// Copyright (c) 2013-2021 Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Crow.Text;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Crow
+{
+ public class XmlSource {
+ public Token[] Tokens;
+ public readonly string Source;
+
+ SyntaxNode RootNode;
+
+ public XmlSource (string _source) {
+ Source = _source;
+ Tokenizer tokenizer = new Tokenizer();
+ Tokens = tokenizer.Tokenize (Source);
+
+ SyntaxAnalyser syntaxAnalyser = new SyntaxAnalyser (this);
+ Stopwatch sw = Stopwatch.StartNew ();
+ syntaxAnalyser.Process ();
+ sw.Stop();
+
+ foreach (Token t in Tokens)
+ Console.WriteLine ($"{t,-40} {Source.AsSpan(t.Start, t.Length).ToString()}");
+ syntaxAnalyser.Root.Dump();
+ Console.WriteLine ($"Syntax Analysis done in {sw.ElapsedMilliseconds}(ms) {sw.ElapsedTicks}(ticks)");
+ foreach (SyntaxException ex in syntaxAnalyser.Exceptions)
+ Console.WriteLine ($"{ex}");
+
+ RootNode = syntaxAnalyser.Root;
+ }
+
+ public Token FindTokenIncludingPosition (int pos) {
+ if (Tokens == null || Tokens.Length == 0)
+ return default;
+ int idx = Array.BinarySearch (Tokens, 0, Tokens.Length, new Token () {Start = pos});
+
+ return idx == 0 ? Tokens[0] : idx < 0 ? Tokens[~idx - 1] : Tokens[idx - 1];
+ }
+ public SyntaxNode FindNodeIncludingPosition (int pos) {
+ if (RootNode == null)
+ return null;
+ if (!RootNode.Contains (pos))
+ return null;
+ return RootNode.FindNodeIncludingPosition (pos);
+ }
+ public T FindNodeIncludingPosition<T> (int pos) {
+ if (RootNode == null)
+ return default;
+ if (!RootNode.Contains (pos))
+ return default;
+ return RootNode.FindNodeIncludingPosition<T> (pos);
+ }
+
+ 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<Token> Toks = new List<Token>(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);
+ 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();
+ }
+
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Samples/common/src/SampleBase.cs b/Samples/common/src/SampleBase.cs
new file mode 100644
index 00000000..1e849d6f
--- /dev/null
+++ b/Samples/common/src/SampleBase.cs
@@ -0,0 +1,383 @@
+using Crow;
+using Glfw;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.NetworkInformation;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Crow
+{
+ public class SampleBase : Interface
+ {
+#if NETCOREAPP
+ static IntPtr resolveUnmanaged(Assembly assembly, String libraryName)
+ {
+
+ switch (libraryName)
+ {
+ case "glfw3":
+ return NativeLibrary.Load("glfw", assembly, null);
+ case "rsvg-2.40":
+ return NativeLibrary.Load("rsvg-2", assembly, null);
+ }
+ Console.WriteLine($"[UNRESOLVE] {assembly} {libraryName}");
+ return IntPtr.Zero;
+ }
+
+ static SampleBase()
+ {
+ System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).ResolvingUnmanagedDll += resolveUnmanaged;
+ }
+#endif
+ public SampleBase(IntPtr hWin) : base(800, 600, hWin) { }
+ public SampleBase() : base(800, 600, true, true) { }
+ public SampleBase(int width, int height, bool startUIThread, bool createSurface) :
+ base(width, height, startUIThread, createSurface)
+ {
+
+ }
+
+ public Version CrowVersion => Assembly.GetAssembly(typeof(Widget)).GetName().Version;
+
+ #region Test values for Binding
+ public CommandGroup Commands, AllCommands;
+ public CommandGroup EditCommands = new CommandGroup("Edit Commands",
+ new Command("Edit command 1", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 1 clicked")),
+ new Command("Edit command 2 a bit longer", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 2 clicked"), null, false),
+ new Command("Edit command three", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 3 clicked"))
+ );
+ public CommandGroup FileCommands = new CommandGroup("File Commands",
+ new Command("File command 1", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 1 clicked")),
+ new Command("File command 2 a bit longer", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 2 clicked")),
+ new Command("File command three", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 3 clicked"))
+ );
+
+ void initCommands()
+ {
+ Commands = new CommandGroup("commands msg boxes",
+ new Command("Action 1", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 1 clicked")),
+ new Command("Action two", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 2 clicked"), null, false),
+ new Command("Action three", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 3 clicked"))
+ );
+ AllCommands = new CommandGroup ("All Commands",
+ FileCommands,
+ EditCommands,
+ new CommandGroup ("Combined commands", FileCommands, EditCommands),
+ new Command("Action A", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu A clicked"))
+ );
+ }
+ public int intValue = 500;
+ VerticalAlignment currentVAlign;
+
+ DirectoryInfo curDir = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
+ public FileSystemInfo[] CurDirectory => curDir.GetFileSystemInfos();
+ public string MultilineText =
+ $"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Sed non risus.\n\nSuspendisse lectus tortor,\nLorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Sed non risus.\n\nSuspendisse lectus tortor,";
+ //public string MultilineText = $"a\n";
+ TextAlignment textAlignment = TextAlignment.Left;
+ public TextAlignment TextAlignment
+ {
+ get => textAlignment;
+ set
+ {
+ if (textAlignment == value)
+ return;
+ textAlignment = value;
+ NotifyValueChanged(textAlignment);
+ }
+ }
+
+ public int IntValue
+ {
+ get => intValue;
+ set
+ {
+ if (IntValue == value)
+ return;
+ intValue = value;
+ NotifyValueChanged("IntValue", intValue);
+ }
+ }
+ public VerticalAlignment CurrentVAlign
+ {
+ get => currentVAlign;
+ set
+ {
+ if (currentVAlign == value)
+ return;
+ currentVAlign = value;
+ NotifyValueChanged("CurrentVAlign", currentVAlign);
+ }
+ }
+ void onSpinnerValueChange(object sender, ValueChangeEventArgs e)
+ {
+ if (e.MemberName != "Value")
+ return;
+ intValue = Convert.ToInt32(e.NewValue);
+ }
+ void change_alignment(object sender, EventArgs e)
+ {
+ RadioButton rb = sender as RadioButton;
+ if (rb == null)
+ return;
+ NotifyValueChanged("alignment", Enum.Parse(typeof(Alignment), rb.Caption));
+ }
+ public IEnumerable<String> List2 = new List<string>(new string[]
+ {
+ "string1",
+ "string2",
+ "string3",
+ "string4",
+ "string5",
+ "string6",
+ "string7",
+ "string8",
+ "string8",
+ "string8",
+ "string8",
+ "string8",
+ "string8",
+ "string9"
+ }
+ );
+ public IEnumerable<String> TestList2
+ {
+ set
+ {
+ List2 = value;
+ NotifyValueChanged("TestList2", testList);
+ }
+ get { return List2; }
+ }
+ public class TestClass
+ {
+ public string Prop1 { get; set; }
+ public string Prop2 { get; set; }
+
+ public override string ToString()
+ => $"{Prop1}, {Prop2}";
+
+ public void OnValidateCommand(Object sender, ValidateEventArgs e)
+ {
+ Console.WriteLine($"Validation: {e.ValidatedText}");
+ }
+ }
+ public class TestClassVC : IValueChange
+ {
+ public event EventHandler<ValueChangeEventArgs> ValueChanged;
+ public void NotifyValueChanged(object _value, [CallerMemberName] string caller = null)
+ => ValueChanged.Raise(this, new ValueChangeEventArgs(caller, _value));
+ string prop1, prop2;
+ public string Prop1
+ {
+ get => prop1;
+ set
+ {
+ if (prop1 == value)
+ return;
+ prop1 = value;
+ NotifyValueChanged(prop1);
+ }
+ }
+ public string Prop2
+ {
+ get => prop2;
+ set
+ {
+ if (prop2 == value)
+ return;
+ prop2 = value;
+ NotifyValueChanged(prop2);
+ }
+
+
+
+ }
+
+ public override string ToString()
+ => $"{Prop1}, {Prop2}";
+
+ }
+ 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" };
+ TestClass tcInstance2 = new TestClass() { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
+ TestClassVC tcVCInstance2 = new TestClassVC() { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
+
+ public TestClass TcInstance
+ {
+ get => tcInstance;
+ set
+ {
+ if (tcInstance == value)
+ return;
+ tcInstance = value;
+ NotifyValueChanged(tcInstance);
+ }
+ }
+ public TestClassVC TcVCInstance
+ {
+ get => tcVCInstance;
+ set
+ {
+ if (tcVCInstance == value)
+ return;
+ tcVCInstance = value;
+ NotifyValueChanged(tcVCInstance);
+ }
+ }
+
+ void tcInstance_ChangeProperties_MouseClick(object sender, MouseButtonEventArgs e)
+ {
+ }
+ public void tcInstance_ChangeInstance_MouseClick(object sender, MouseButtonEventArgs e)
+ {
+ if (TcInstance == tcInstance1)
+ TcInstance = tcInstance2;
+ else
+ TcInstance = tcInstance1;
+ }
+ void tcVCInstance_ChangeInstance_MouseClick(object sender, MouseButtonEventArgs e)
+ {
+ TcVCInstance = new TestClassVC() { Prop1 = "prop1 value changed", Prop2 = "prop2 value changed" };
+ }
+
+ public IEnumerable<TestClass> List3 = new List<TestClass>(new TestClass[]
+ {
+ new TestClass { Prop1 = "string1", Prop2="prop2-1" },
+ new TestClass { Prop1 = "string2", Prop2="prop2-2" },
+ new TestClass { Prop1 = "string3", Prop2="prop2-3" },
+ }
+ );
+ public IEnumerable<string> TestList3Props1 => List3.Select(sc => sc.Prop1).ToList();
+ public IEnumerable<TestClass> TestList3
+ {
+ set
+ {
+ List3 = value;
+ NotifyValueChanged("TestList3", testList);
+ }
+ get { return List3; }
+ }
+ string testString;
+ public string TestString
+ {
+ get => testString;
+ set
+ {
+ if (testString == value)
+ return;
+ testString = value;
+
+ NotifyValueChanged(testString);
+ }
+ }
+ string prop1;
+ public string TestList3SelProp1
+ {
+ get => prop1;
+ set
+ {
+ if (prop1 == value)
+ return;
+ prop1 = value;
+
+ NotifyValueChanged(prop1);
+ }
+ }
+
+ string selString;
+ public string TestList2SelectedString
+ {
+ get => selString;
+ set
+ {
+ if (selString == value)
+ return;
+ selString = value;
+ NotifyValueChanged(selString);
+ }
+ }
+
+
+ IList<Colors> testList = (IList<Colors>)FastEnumUtility.FastEnum.GetValues<Colors>().ToList();//.ColorDic.Values//.OrderBy(c=>c.Hue)
+ //.ThenBy(c=>c.Value).ThenBy(c=>c.Saturation)
+ //.ToList ();
+ public IList<Colors> TestList
+ {
+ set
+ {
+ testList = value;
+ NotifyValueChanged("TestList", testList);
+ }
+ get { return testList; }
+ }
+ void OnClear(object sender, MouseButtonEventArgs e) => TestList = null;
+ void OnLoadList(object sender, MouseButtonEventArgs e) => TestList = (IList<Colors>)FastEnumUtility.FastEnum.GetValues<Colors>().ToList();
+
+
+ string curSources = "";
+ public string CurSources
+ {
+ get { return curSources; }
+ set
+ {
+ if (value == curSources)
+ return;
+ curSources = value;
+ NotifyValueChanged(curSources);
+ }
+ }
+ bool boolVal = true;
+ public bool BoolVal
+ {
+ get { return boolVal; }
+ set
+ {
+ if (boolVal == value)
+ return;
+ boolVal = value;
+ NotifyValueChanged(boolVal);
+ }
+ }
+
+
+
+ #endregion
+
+ protected override void OnInitialized()
+ {
+ initCommands();
+ base.OnInitialized();
+ }
+
+ public override bool OnKeyDown(Key key)
+ {
+
+ switch (key)
+ {
+ case Key.F5:
+ Load("Interfaces/Divers/testFileDialog.crow").DataSource = this;
+ return true;
+ case Key.F6:
+ Load("Interfaces/Divers/0.crow").DataSource = this;
+ return true;
+ case Key.F7:
+ Load("Interfaces/Divers/perfMeasures.crow").DataSource = this;
+ return true;
+ case Key.F2:
+ if (IsKeyDown(Key.LeftShift))
+ DbgLogger.Reset();
+ DbgLogger.Save(this);
+ return true;
+ }
+ return base.OnKeyDown(key);
+ }
+ }
+} \ No newline at end of file