diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-06-05 16:52:53 +0200 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-06-05 16:52:53 +0200 |
| commit | f738cf0f78c57750cce8fd5744825b0591f24bb8 (patch) | |
| tree | 47c3313d26151e53cbef822ac602132fb77b88dd /Samples | |
| parent | a515e31998f64f537e29cd30fdb7e9bfc7d4a02f (diff) | |
ItemsScroller for list, layouting changes, editor suggestions
Diffstat (limited to 'Samples')
| -rw-r--r-- | Samples/DebugLogAnalyzer/ui/main.crow | 5 | ||||
| -rw-r--r-- | Samples/Directory.Build.props | 3 | ||||
| -rw-r--r-- | Samples/ShowCase/ShowCase.cs | 6 | ||||
| -rw-r--r-- | Samples/common/samples.style | 8 | ||||
| -rw-r--r-- | Samples/common/src/Editor.cs (renamed from Samples/common/Editor.cs) | 412 | ||||
| -rw-r--r-- | Samples/common/src/ImlParsing/Syntax.cs | 310 | ||||
| -rw-r--r-- | Samples/common/src/ImlParsing/Token.cs | 40 | ||||
| -rw-r--r-- | Samples/common/src/ImlParsing/TokenType.cs | 38 | ||||
| -rw-r--r-- | Samples/common/src/ImlParsing/XmlSource.cs | 260 | ||||
| -rw-r--r-- | Samples/common/src/SampleBase.cs (renamed from Samples/common/SampleBase.cs) | 0 | ||||
| -rw-r--r-- | Samples/common/ui/Interfaces/Divers/0.crow | 2 | ||||
| -rw-r--r-- | Samples/common/ui/Interfaces/Divers/fitListWithStretchedItems.crow | 10 | ||||
| -rw-r--r-- | Samples/common/ui/Interfaces/Divers/temp.crow | 4 | ||||
| -rw-r--r-- | Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow | 20 | ||||
| -rw-r--r-- | Samples/common/ui/templates/ColorPicker.template | 2 | ||||
| -rw-r--r-- | Samples/common/ui/templates/Suggestions.template | 13 |
16 files changed, 833 insertions, 300 deletions
diff --git a/Samples/DebugLogAnalyzer/ui/main.crow b/Samples/DebugLogAnalyzer/ui/main.crow index 16973e11..5afc6c6c 100644 --- a/Samples/DebugLogAnalyzer/ui/main.crow +++ b/Samples/DebugLogAnalyzer/ui/main.crow @@ -92,7 +92,10 @@ </Border> </Template> </CheckBox> - <Menu Data="{../../../../dbgIfaceWidget.LoggerCommands}" Height="Stretched" Width="Fit"> + <Menu Data="{../../../../dbgIfaceWidget.LoggerCommands}" Height="Fit" Width="Stretched"> + <Template> + <Wrapper Orientation="Vertical" Name="ItemsContainer" Margin="0" Background="{./Background}"/> + </Template> <ItemTemplate> <Button Command="{}" Height="Stretched" Width="Fit"/> </ItemTemplate> diff --git a/Samples/Directory.Build.props b/Samples/Directory.Build.props index 8536aaaa..ea8b31f3 100644 --- a/Samples/Directory.Build.props +++ b/Samples/Directory.Build.props @@ -1,6 +1,7 @@ <Project> <PropertyGroup> <TargetFrameworks>netcoreapp3.1</TargetFrameworks> + <!--<TargetFrameworks>net5</TargetFrameworks>--> <OutputType>Exe</OutputType> <SolutionDir>$(MSBuildThisFileDirectory)..\</SolutionDir> @@ -40,7 +41,7 @@ <EmbeddedResource Include="$(SamplesDir)common\samples.style" > <Link>common\%(Filename)%(Extension)</Link> </EmbeddedResource> - <Compile Include="$(SamplesDir)common\*.cs"> + <Compile Include="$(SamplesDir)common\src\**\*.cs"> <Link>common\%(Filename)%(Extension)</Link> </Compile> </ItemGroup> diff --git a/Samples/ShowCase/ShowCase.cs b/Samples/ShowCase/ShowCase.cs index 93c4b217..ed5d8575 100644 --- a/Samples/ShowCase/ShowCase.cs +++ b/Samples/ShowCase/ShowCase.cs @@ -20,9 +20,9 @@ namespace ShowCase { static void Main () { - DbgLogger.IncludeEvents = DbgEvtType.None; - DbgLogger.DiscardEvents = DbgEvtType.All; - DbgLogger.ConsoleOutput = !Configuration.Global.Get<bool> (nameof (DebugLogToFile)); + DbgLogger.IncludeEvents = DbgEvtType.Layouting; + //DbgLogger.DiscardEvents = DbgEvtType.All; + //DbgLogger.ConsoleOutput = !Configuration.Global.Get<bool> (nameof (DebugLogToFile)); Environment.SetEnvironmentVariable ("FONTCONFIG_PATH", @"C:\Users\Jean-Philippe\source\vcpkg\installed\x64-windows\tools\fontconfig\fonts"); diff --git a/Samples/common/samples.style b/Samples/common/samples.style index 7c33bd9d..8a43f3a0 100644 --- a/Samples/common/samples.style +++ b/Samples/common/samples.style @@ -80,3 +80,11 @@ valignStyle2 { smallLabel { Font="consolas, 8"; } +suggestionsListBox { + Template = "#ui.Suggestions.template"; + Width = "Fit"; + Height = "Fit"; + MaximumSize = "300, 120"; + Background = "Jet"; + UseLoadingThread = "false"; +} diff --git a/Samples/common/Editor.cs b/Samples/common/src/Editor.cs index 95de4569..b9b0ae90 100644 --- a/Samples/common/Editor.cs +++ b/Samples/common/src/Editor.cs @@ -1,6 +1,4 @@ -using System.Security.Principal; -using System.Threading; -// Copyright (c) 2013-2019 Bruyère Jean-Philippe <jp_bruyere@hotmail.com> +// 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) @@ -11,272 +9,11 @@ using System.Collections.Generic; using Crow.Cairo; using System.Threading.Tasks; using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; 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, - } - - public struct 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 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<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); - } 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(); @@ -292,18 +29,134 @@ namespace Crow base.onInitialized(sender, e); } - Widget overlay; + 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()); + //Task.Run(()=>parse()); + parse(); + tryGetSuggestions (); - /*if (overlay == null && HasFocus) - overlay = IFace.LoadIMLFragment(@"<Widget Width='50' Height='50' Background='Jet'/>");*/ + //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; @@ -332,8 +185,7 @@ namespace Crow return; } base.onKeyDown(sender, e); - } - int tabSize = 4; + } protected override void drawContent (Context gr) { try { @@ -357,11 +209,15 @@ namespace Crow } 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 (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); @@ -535,7 +391,7 @@ namespace Crow x += tok2.Length; pixX += spacePixelWidth * tok2.Length;*/ } - gr.Translate (ScrollX, ScrollY); + //gr.Translate (ScrollX, ScrollY); } } catch { 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/SampleBase.cs b/Samples/common/src/SampleBase.cs index 1e849d6f..1e849d6f 100644 --- a/Samples/common/SampleBase.cs +++ b/Samples/common/src/SampleBase.cs diff --git a/Samples/common/ui/Interfaces/Divers/0.crow b/Samples/common/ui/Interfaces/Divers/0.crow index 49cb6605..f84ad20a 100644 --- a/Samples/common/ui/Interfaces/Divers/0.crow +++ b/Samples/common/ui/Interfaces/Divers/0.crow @@ -92,7 +92,7 @@ </VerticalStack> <Splitter /> <VerticalStack Width="30%" Margin="5"> - <ListBox Name="colorList" Data="{TestList}" Margin="5" ItemTemplate="Interfaces/colorItem.crow" /> + <ListBox Focusable="true" Name="colorList" Data="{TestList}" Margin="5" ItemTemplate="Interfaces/colorItem.crow" /> </VerticalStack> </HorizontalStack> </Window>
\ No newline at end of file diff --git a/Samples/common/ui/Interfaces/Divers/fitListWithStretchedItems.crow b/Samples/common/ui/Interfaces/Divers/fitListWithStretchedItems.crow index c2342c6b..6fd59f14 100644 --- a/Samples/common/ui/Interfaces/Divers/fitListWithStretchedItems.crow +++ b/Samples/common/ui/Interfaces/Divers/fitListWithStretchedItems.crow @@ -1,14 +1,18 @@ <?xml version="1.0"?> -<ListBox Data="{TestList}" Width="Fit" > +<ListBox Data="{TestList}" Width="Fit" Focusable="true" > <Template> - <Scroller Height="Stretched" Background="DarkRed" Margin="1"> + <Scroller Name="ItemsScroller" Height="Stretched" Background="Onyx" Margin="10"> <VerticalStack Name="ItemsContainer" Orientation="Horizontal" VerticalAlignment="Top" Height="Fit" Background="Grey" Margin="20"/> </Scroller> </Template> <ItemTemplate> <!--<Label Text="{}" TextAlignment="Center" Font="mono, 9" Foreground="Black" Margin="10" Width="Stretched" Background="{}"/>--> - <Label Text="{}" TextAlignment="Center" Font="mono, 9" Foreground="Black" Margin="10" Background="{}" Width="Stretched" /> + <ListItem Width="Stretched" Margin="1" CornerRadius="5" + Selected="{Background=Yellow}" + Unselected="{Background=Transparent}"> + <Label Text="{}" TextAlignment="Center" Font="mono, 9" Foreground="Black" Margin="6" Background="{}" Width="Stretched" /> + </ListItem> </ItemTemplate> </ListBox> diff --git a/Samples/common/ui/Interfaces/Divers/temp.crow b/Samples/common/ui/Interfaces/Divers/temp.crow new file mode 100644 index 00000000..633bd600 --- /dev/null +++ b/Samples/common/ui/Interfaces/Divers/temp.crow @@ -0,0 +1,4 @@ +<VerticalStack> + <Label Text='Hello World' Background='MediumSeaGreen' Margin='10'/> + <CheckBox I +</VerticalStack>
\ No newline at end of file diff --git a/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow b/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow index 39721689..0d3c04e7 100644 --- a/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow +++ b/Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow @@ -3,18 +3,14 @@ <Template> <Popper Margin="0" Caption="{./CurrentColor}" Background="{./Background}" > <Template> - <CheckBox Margin="0" Caption="{./Caption}" IsChecked="{²./IsPopped}" Background="{./Background}"> - <Template> - <HorizontalStack Margin="3" Spacing="3" Background="{./Background}"> - <Border Width="18" Height="12" CornerRadius="3" - Background="{../../../../CurrentColor}"> - </Border> - <Label Width="Stretched" Text="{./Caption}" /> - </HorizontalStack> - </Template>/>{} - </CheckBox> - </Template> - <TabView MinimumSize="{../../MinimumPopupSize}" Width="Fit" Height="Fit" > + <HorizontalStack Margin="3" Spacing="3" Background="{./Background}"> + <Border Width="18" Height="12" CornerRadius="3" + Background="{../../../CurrentColor}"> + </Border> + <Label Width="Stretched" Text="{./Caption}" /> + </HorizontalStack> + </Template>/> + <TabView MinimumSize="{../MinimumPopupSize}" Width="200" Height="200" > <ColorPicker Name="HSV" CurrentColor="{²../../../CurrentColor}" Background="Onyx"/> <ColorPicker IsVisible="false" Name="Names" CurrentColor="{²../../../CurrentColor}" Height="Stretched" Background="Onyx"> <Template> diff --git a/Samples/common/ui/templates/ColorPicker.template b/Samples/common/ui/templates/ColorPicker.template index ca7ddda4..b7c0ce4b 100644 --- a/Samples/common/ui/templates/ColorPicker.template +++ b/Samples/common/ui/templates/ColorPicker.template @@ -8,7 +8,7 @@ <Label Width="Stretched" Text="{./Caption}" /> </HorizontalStack> </Template> - <TabView MinimumSize="{../../MinimumPopupSize}" Width="Fit" Height="Fit" > + <TabView MinimumSize="{../MinimumPopupSize}" Width="200" Height="200" > <ColorPicker Name="HSV" CurrentColor="{²../../../CurrentColor}" Background="Onyx"/> <ColorPicker IsVisible="false" Name="Names" CurrentColor="{²../../../CurrentColor}" Height="Stretched" Background="Onyx"> <Template> diff --git a/Samples/common/ui/templates/Suggestions.template b/Samples/common/ui/templates/Suggestions.template new file mode 100644 index 00000000..5a4349d7 --- /dev/null +++ b/Samples/common/ui/templates/Suggestions.template @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<Border BorderWidth="1" Background="{./Background}" Height="Stretched"> + <!--<HorizontalStack Margin="1">--> + <Scroller Name="ItemsScroller" Margin="2"> + <VerticalStack Height="Fit" MinimumSize="10,10" + Name="ItemsContainer" Margin="0" VerticalAlignment="Top"/> + </Scroller> + <!--<ScrollBar Name="scrollbar1" Value="{²../ItemsScroller.ScrollY}" + LargeIncrement="{../ItemsScroller.PageHeight}" SmallIncrement="30" CursorSize="{../ItemsScroller.ChildHeightRatio}" + Maximum="{../ItemsScroller.MaxScrollY}" Orientation="Vertical" + Width="12" /> + </HorizontalStack>--> +</Border> |