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 | |
| parent | a515e31998f64f537e29cd30fdb7e9bfc7d4a02f (diff) | |
ItemsScroller for list, layouting changes, editor suggestions
29 files changed, 1362 insertions, 537 deletions
diff --git a/Crow/Templates/ScrollingListBox.template b/Crow/Templates/ScrollingListBox.template index 1e7304f0..a5a9b629 100644 --- a/Crow/Templates/ScrollingListBox.template +++ b/Crow/Templates/ScrollingListBox.template @@ -1,14 +1,14 @@ <?xml version="1.0"?> <Border BorderWidth="1" Background="{./Background}"> <HorizontalStack Margin="1"> - <Scroller Name="scroller1" + <Scroller Name="ItemsScroller" Margin="2"> <VerticalStack Height="Fit" MinimumSize="10,10" Name="ItemsContainer" Margin="0" VerticalAlignment="Top"/> </Scroller> - <ScrollBar Name="scrollbar1" Value="{²../scroller1.ScrollY}" - LargeIncrement="{../scroller1.PageHeight}" SmallIncrement="30" CursorSize="{../scroller1.ChildHeightRatio}" - Maximum="{../scroller1.MaxScrollY}" Orientation="Vertical" - Width="14" /> + <ScrollBar Name="scrollbar1" Value="{²../ItemsScroller.ScrollY}" + LargeIncrement="{../ItemsScroller.PageHeight}" SmallIncrement="30" CursorSize="{../ItemsScroller.ChildHeightRatio}" + Maximum="{../ItemsScroller.MaxScrollY}" Orientation="Vertical" + Width="12" /> </HorizontalStack> </Border> diff --git a/Crow/src/DebugUtils/DebugLogger.cs b/Crow/src/DebugUtils/DebugLogger.cs index 058921aa..2a9b75bb 100644 --- a/Crow/src/DebugUtils/DebugLogger.cs +++ b/Crow/src/DebugUtils/DebugLogger.cs @@ -24,8 +24,7 @@ namespace Crow #if DEBUG_LOG static bool logevt (DbgEvtType evtType) //=> IncludeEvents != DbgEvtType.None && (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) == IncludeEvents; - /*=> IncludeEvents == DbgEvtType.All || - IncludeEvents != DbgEvtType.None && (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) == IncludeEvents;*/ + //=> IncludeEvents != DbgEvtType.None && (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) == IncludeEvents; => IncludeEvents == DbgEvtType.All || (IncludeEvents != DbgEvtType.None && (evtType & IncludeEvents) == IncludeEvents); diff --git a/Crow/src/IML/Instantiator.cs b/Crow/src/IML/Instantiator.cs index b29912bc..ee1b615f 100644 --- a/Crow/src/IML/Instantiator.cs +++ b/Crow/src/IML/Instantiator.cs @@ -214,7 +214,7 @@ namespace Crow.IML { while (reader.NodeType != XmlNodeType.Element) reader.Read (); root = reader.Name; - Type t = tryGetGOType (root); + Type t = GetWidgetTypeFromName (root); if (t == null) throw new Exception ("IML parsing error: undefined root type (" + root + ")"); return t; @@ -547,7 +547,7 @@ namespace Crow.IML { ctx.il.Emit (OpCodes.Ldloc_0); ctx.il.Emit (OpCodes.Ldloc_0); - Type t = tryGetGOType (reader.Name); + Type t = GetWidgetTypeFromName (reader.Name); if (t == null) throw new Exception (reader.Name + " type not found"); ConstructorInfo ci = t.GetConstructor ( @@ -1609,7 +1609,7 @@ namespace Crow.IML { /// </remarks> /// <returns>the corresponding type object</returns> /// <param name="typeName">graphic object type name without its namespace</param> - internal static Type tryGetGOType (string typeName){ + public static Type GetWidgetTypeFromName (string typeName){ if (knownGOTypes.ContainsKey (typeName)) return knownGOTypes [typeName]; Type t = Type.GetType ("Crow." + typeName); diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 60f0e221..3466b33b 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -523,15 +523,12 @@ namespace Crow public Dictionary<string, string> StylingConstants; /// <summary> parse all styling data's during application startup and build global Styling Dictionary </summary> protected virtual void loadStyling() { - //fetch styling info in this order, if member styling is alreadey referenced in previous - //assembly, it's ignored. - loadThemeStyle (); - loadStylingFromAssembly (Assembly.GetExecutingAssembly ()); foreach (Assembly a in crowAssemblies) { loadStylingFromAssembly (a); } loadStylingFromAssembly (Assembly.GetEntryAssembly ()); + loadThemeStyle (); } /// <summary> Search for .style resources in assembly </summary> protected void loadStylingFromAssembly (Assembly assembly) { diff --git a/Crow/src/Text/Encoding.cs b/Crow/src/Text/Encoding.cs index b02e4565..ac730650 100644 --- a/Crow/src/Text/Encoding.cs +++ b/Crow/src/Text/Encoding.cs @@ -44,6 +44,7 @@ namespace Crow.Text } throw new NotImplementedException(); } + buff[encodedBytes] = 0; return encodedBytes; } diff --git a/Crow/src/Widgets/Group.cs b/Crow/src/Widgets/Group.cs index 55cc6353..9e78f187 100644 --- a/Crow/src/Widgets/Group.cs +++ b/Crow/src/Widgets/Group.cs @@ -62,6 +62,7 @@ namespace Crow childrenRWLock.ExitWriteLock (); + //largestChild = tallestChild = null; if (g.LastSlots.Width > contentSize.Width) { largestChild = g; contentSize.Width = g.LastSlots.Width; @@ -70,9 +71,9 @@ namespace Crow tallestChild = g; contentSize.Height = g.LastSlots.Height; } - g.LayoutChanged += OnChildLayoutChanges; + g.RegisteredLayoutings = LayoutingType.None; g.RegisterForLayouting (LayoutingType.Sizing | LayoutingType.ArrangeChildren); } public override void ClearChildren() @@ -99,10 +100,10 @@ namespace Crow { if (Children.Count > 0) { if (lt == LayoutingType.Width) { - if (largestChild == null) + //if (largestChild == null) searchLargestChild (); } else { - if (tallestChild == null) + //if (tallestChild == null) searchTallestChild (); } } @@ -110,7 +111,9 @@ namespace Crow } public override void OnLayoutChanges (LayoutingType layoutType) - { + { + /*if (!IsVisible) + return;*/ base.OnLayoutChanges (layoutType); childrenRWLock.EnterReadLock (); @@ -194,7 +197,7 @@ namespace Crow int cw = 0; if (forceMeasure) cw = Children [i].measureRawSize (LayoutingType.Width); - else if (Children [i].RegisteredLayoutings.HasFlag (LayoutingType.Width)) + else if (Children[i].Width.IsRelativeToParent || Children [i].RegisteredLayoutings.HasFlag (LayoutingType.Width)) continue; else cw = Children [i].Slot.Width; @@ -224,7 +227,7 @@ namespace Crow int ch = 0; if (forceMeasure) ch = Children [i].measureRawSize (LayoutingType.Height); - else if (Children [i].RegisteredLayoutings.HasFlag (LayoutingType.Height)) + else if (Children[i].Height.IsRelativeToParent || Children [i].RegisteredLayoutings.HasFlag (LayoutingType.Height)) continue; else ch = Children [i].Slot.Height; diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index c8b4b238..47082698 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -485,6 +485,14 @@ namespace Crow int hoverLine = _multiline ? (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent))), lines.Count - 1) : 0; hoverLoc = new CharLocation (hoverLine, -1, mouseLocalPos.X); + using (Context gr = new Context (IFace.surf)) { + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + gr.FontOptions = Interface.FontRenderingOptions; + gr.Antialias = Interface.Antialias; + + updateLocation (gr, ClientRectangle.Width, ref hoverLoc); + } } protected virtual bool cancelLinePrint (double lineHeght, double y, int clientHeight) => false; RectangleD? textCursor = null; diff --git a/Crow/src/Widgets/ScrollingStack.cs b/Crow/src/Widgets/ScrollingStack.cs new file mode 100644 index 00000000..959a4550 --- /dev/null +++ b/Crow/src/Widgets/ScrollingStack.cs @@ -0,0 +1,210 @@ +// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.ComponentModel; +using Crow.Cairo; + +namespace Crow { + public class ScrollingStack : GenericStack { + int scroll, maxScroll, itemSize = -1, visibleItems; + + [DefaultValue (0)]public virtual int Scroll { + get { return scroll; } + set { + //cancelAdjustScroll = true; + + if (scroll == value) + return; + + int newS = value; + if (newS < 0) + newS = 0; + else if (newS > maxScroll) + newS = maxScroll; + + if (newS == scroll) + return; + + scroll = newS; + + NotifyValueChangedAuto (scroll); + RegisterForGraphicUpdate (); + } + } + [DefaultValue (0)]public virtual int MaxScroll { + get { return maxScroll; } + set { + if (maxScroll == value) + return; + + maxScroll = Math.Max (0, value); + + if (scroll > maxScroll) + Scroll = maxScroll; + + NotifyValueChangedAuto (maxScroll); + //RegisterForGraphicUpdate (); + } + } + + + + public override void RemoveChild(Widget child){ + base.RemoveChild (child); + updateMaxScroll (); + } + public override void InsertChild (int idx, Widget g) { + base.InsertChild (idx, g); + updateMaxScroll (); + } + public override void ClearChildren() { + MaxScroll = 0; + itemSize = -1; + visibleItems = 0; + base.ClearChildren (); + } + + public override void OnLayoutChanges(LayoutingType layoutType) + { + if (Orientation == Orientation.Horizontal) { + if (layoutType == LayoutingType.Width && itemSize > 0) { + visibleItems = (int)Math.Ceiling ((double)ClientRectangle.Width / itemSize); + updateMaxScroll (); + } + } else if (layoutType == LayoutingType.Height && itemSize > 0) { + visibleItems = (int)Math.Ceiling ((double)ClientRectangle.Height / itemSize); + updateMaxScroll (); + } + base.OnLayoutChanges(layoutType); + } + public override void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) { + DbgLogger.StartEvent(DbgEvtType.GOOnChildLayoutChange, this); + Widget go = sender as Widget; + + if (Orientation == Orientation.Horizontal) { + if (arg.LayoutType == LayoutingType.Width && itemSize < go.Slot.Width) + itemSize = go.Slot.Width; + } else if (arg.LayoutType == LayoutingType.Height && itemSize < go.Slot.Height) + itemSize = go.Slot.Height; + + base.OnChildLayoutChanges (sender, arg); + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); + } + + void updateMaxScroll () { + if (visibleItems <= 0) + return; + MaxScroll = Children.Count - visibleItems; + NotifyValueChanged ("PageSize", visibleItems); + NotifyValueChanged ("ChildRatio", Math.Min (1.0, (double)Children.Count / visibleItems)); + } + + protected override void onDraw (Context gr) + { + DbgLogger.StartEvent (DbgEvtType.GODraw, this); + + base.onDraw (gr); + + if (ClipToClientRect) { + gr.Save (); + //clip to client zone + CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius); + gr.Clip (); + } + + try + { + childrenRWLock.EnterReadLock (); + + for (int i = Scroll; i < Children.Count && i < Scroll + visibleItems; i++) { + if (!Children[i].IsVisible) + continue; + /*if (Clipping.Contains (c.Slot + ClientRectangle.Position) == RegionOverlap.Out) + continue;*/ + Children[i].Paint (gr); + } + + childrenRWLock.ExitReadLock (); + } + catch (System.Exception) + { + childrenRWLock.ExitReadLock (); + throw; + } + + if (ClipToClientRect) + gr.Restore (); + + DbgLogger.EndEvent (DbgEvtType.GODraw); + } + protected override void UpdateCache (Context ctx) + { + DbgLogger.StartEvent(DbgEvtType.GOUpdateCache, this); + if (!Clipping.IsEmpty) { + using (Context gr = new Context (bmp)) { + for (int i = 0; i < Clipping.NumRectangles; i++) + gr.Rectangle(Clipping.GetRectangle(i)); + gr.ClipPreserve(); + gr.Operator = Operator.Clear; + gr.Fill(); + gr.Operator = Operator.Over; + + base.onDraw (gr); + + if (ClipToClientRect) { + CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius); + gr.Clip (); + } + + childrenRWLock.EnterReadLock (); + + for (int i = Scroll; i < Children.Count && i < Scroll + visibleItems; i++) { + if (!Children[i].IsVisible) + continue; + /*if (Clipping.Contains (c.Slot + ClientRectangle.Position) == RegionOverlap.Out) + continue;*/ + Children[i].Paint (gr); + } + + childrenRWLock.ExitReadLock (); + + #if DEBUG_CLIP_RECTANGLE + /*gr.LineWidth = 1; + gr.SetSourceColor(Color.DarkMagenta.AdjustAlpha (0.8)); + for (int i = 0; i < Clipping.NumRectangles; i++) + gr.Rectangle(Clipping.GetRectangle(i)); + gr.Stroke ();*/ + #endif + } + DbgLogger.AddEvent (DbgEvtType.GOResetClip, this); + Clipping.Reset (); + }/*else + Console.WriteLine("GROUP REPAINT WITH EMPTY CLIPPING");*/ + paintCache (ctx, Slot + Parent.ClientRectangle.Position); + DbgLogger.EndEvent(DbgEvtType.GOUpdateCache); + } + + + public override void checkHoverWidget (MouseMoveEventArgs e) { + base.checkHoverWidget (e);//TODO:check if not possible to put it at beginning of meth to avoid doubled check to DropTarget. + if (!childrenRWLock.TryEnterReadLock (10)) + return; + for (int i = Children.Count - 1; i >= 0; i--) { + if (Children[i].MouseIsIn (e.Position)) { + Children[i].checkHoverWidget (e); + childrenRWLock.ExitReadLock (); + return; + } + } + childrenRWLock.ExitReadLock (); + } + /// <summary> Process scrolling vertically, or if shift is down, vertically </summary> + public override void onMouseWheel (object sender, MouseWheelEventArgs e) { + e.Handled = true; + Scroll += e.Delta * itemSize; + base.onMouseWheel (sender, e); + } + } +}
\ No newline at end of file diff --git a/Crow/src/Widgets/TemplatedGroup.cs b/Crow/src/Widgets/TemplatedGroup.cs index 67c1f0db..f489ca96 100644 --- a/Crow/src/Widgets/TemplatedGroup.cs +++ b/Crow/src/Widgets/TemplatedGroup.cs @@ -38,6 +38,8 @@ namespace Crow { #endregion protected Group itemsContainer; + //if scroller name 'ItemsScroller' is found in template, scroll will adapt to selected items change. + protected Scroller scroller; string _itemTemplate, dataTest; #region events @@ -55,6 +57,20 @@ namespace Crow { CrowThread loadingThread = null; bool isPaged = false; + bool useLoadingThread; + /// <summary> + /// Use anothred thread for loading items, default value is true. + /// </summary> + [DefaultValue(true)] + public bool UseLoadingThread { + get => useLoadingThread; + set { + if (useLoadingThread == value) + return; + useLoadingThread = value; + NotifyValueChangedAuto (useLoadingThread); + } + } #region Templating //TODO: dont instantiate ItemTemplates if not used @@ -92,6 +108,7 @@ namespace Crow { itemsContainer = this.child.FindByName ("ItemsContainer") as Group; if (itemsContainer == null) throw new Exception ("TemplatedGroup template Must contain a Group named 'ItemsContainer'"); + scroller = child.FindByName ("ItemsScroller") as Scroller; NotifyValueChanged ("Items", Items); if (itemsContainer.Children.Count == 0) NotifyValueChanged ("HasChildren", false); @@ -131,7 +148,7 @@ namespace Crow { [XmlIgnore]public virtual object SelectedItem{ get => selectedItem; set { - if (SelectedItem == value) + if (selectedItem == value) return; if (selectedItem is ISelectable oldItem) @@ -143,10 +160,24 @@ namespace Crow { newItem.IsSelected = true; NotifyValueChanged ("SelectedItem", SelectedItem); + NotifyValueChanged ("SelectedIndex", SelectedIndex); SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem)); } } + [XmlIgnore]public virtual int SelectedIndex{ + get => selectedItemContainer == null ? -1 : itemsContainer.Children.IndexOf (selectedItemContainer); + set { + if (SelectedIndex == value) + return; + if (value < 0 || itemsContainer.Children.Count == 0) + Li_Selected (null, null); + if (value < itemsContainer.Children.Count) + Li_Selected (itemsContainer.Children[value], null); + else + Li_Selected (itemsContainer.Children[itemsContainer.Children.Count - 1], null); + } + } [XmlIgnore]public bool HasItems { get { return Items.Count > 0; } } @@ -191,10 +222,15 @@ namespace Crow { return; } } - - loadingThread = new CrowThread (this, loading); - loadingThread.Finished += (object sender, EventArgs e) => (sender as TemplatedGroup).Loaded.Raise (sender, e); - loadingThread.Start (); + + if (useLoadingThread) { + loadingThread = new CrowThread (this, loading); + loadingThread.Finished += (object sender, EventArgs e) => (sender as TemplatedGroup).Loaded.Raise (sender, e); + loadingThread.Start (); + } else { + loading(); + Loaded.Raise (this, null); + } //NotifyValueChanged ("SelectedIndex", _selectedIndex); //NotifyValueChanged ("SelectedItem", SelectedItem); @@ -241,13 +277,9 @@ namespace Crow { } } - - protected void raiseSelectedItemChanged(){ SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem)); } - - public virtual void AddItem(Widget g){ itemsContainer.AddChild (g); @@ -334,7 +366,7 @@ namespace Crow { loadPage (data, itemsContainer, dataTest); } catch (Exception ex) { while (Monitor.IsEntered (IFace.UpdateMutex)) - Monitor.Exit (IFace.UpdateMutex); + Monitor.Exit (IFace.UpdateMutex); while (Monitor.IsEntered (IFace.LayoutMutex)) Monitor.Exit (IFace.LayoutMutex); System.Diagnostics.Debug.WriteLine ("loading thread aborted: " + ex.ToString()); @@ -412,7 +444,7 @@ namespace Crow { foreach (object d in _data) { loadItem (d, page, _dataTest); - if (loadingThread.cancelRequested) + if (loadingThread != null && loadingThread.cancelRequested) break; } @@ -504,10 +536,27 @@ namespace Crow { { if (sender == selectedItemContainer) return; - if (selectedItemContainer is ISelectable li) - li.IsSelected = false; + if (selectedItemContainer is ISelectable lo) + lo.IsSelected = false; selectedItemContainer = sender as Widget; - SelectedItem = selectedItemContainer.DataSource; + if (selectedItemContainer is ISelectable ln) + ln.IsSelected = true; + SelectedItem = selectedItemContainer?.DataSource; + + if (scroller != null && selectedItemContainer != null && itemsContainer is GenericStack gs) { + Rectangle scrollerCb = scroller.ClientRectangle; + Rectangle cb = gs.Slot; + Rectangle rItem = selectedItemContainer.Slot + new Point (gs.Margin); + if (gs.Orientation == Orientation.Vertical) { + if (rItem.Y - scroller.ScrollY < 0) + scroller.ScrollY = rItem.Y; + else if (rItem.Bottom - scroller.ScrollY > scrollerCb.Height) + scroller.ScrollY = rItem.Bottom - scrollerCb.Height; + } else if (rItem.X - scroller.ScrollX < 0) + scroller.ScrollX = rItem.X; + else if (rItem.Right - scroller.ScrollX > scrollerCb.Width) + scroller.ScrollX = rItem.Right - scrollerCb.Width; + } SelectedItemContainerChanged.Raise (this, new SelectionChangeEventArgs (sender)); } @@ -550,8 +599,7 @@ namespace Crow { selectedItemContainer = sender as Widget; if (selectedItemContainer == null) return; - SelectedItem = selectedItemContainer.DataSource; - //SelectedIndex = items.Children.IndexOf(sender as Widget); + SelectedItem = selectedItemContainer.DataSource; } bool emitHelperIsAlreadyExpanded (Widget go){ @@ -600,5 +648,22 @@ namespace Crow { void onDatasChanged (object sender, ValueChangeEventArgs e) { } + + public override void onKeyDown(object sender, KeyEventArgs e) + { + switch (e.Key) { + case Glfw.Key.Up: + if (SelectedIndex > 0) + SelectedIndex--; + break; + case Glfw.Key.Down: + if (SelectedIndex < Items.Count - 1) + SelectedIndex++; + break; + default: + base.onKeyDown(sender, e); + break; + } + } } } diff --git a/Crow/src/Widgets/TextBox.cs b/Crow/src/Widgets/TextBox.cs index ece9adc0..2564129e 100644 --- a/Crow/src/Widgets/TextBox.cs +++ b/Crow/src/Widgets/TextBox.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -10,21 +10,21 @@ using System.ComponentModel; namespace Crow { - public class TextBox : Label - { - #region CTOR - protected TextBox () { } - public TextBox (Interface iface, string style = null) : base (iface, style) { } - #endregion + public class TextBox : Label + { + #region CTOR + protected TextBox () { } + public TextBox (Interface iface, string style = null) : base (iface, style) { } + #endregion - /// <summary> - /// Validate content of the text box. Occurs in non multiline TextBox when 'Enter' key - /// is pressed. - /// </summary> - public event EventHandler<ValidateEventArgs> Validate; - public virtual void OnValidate (Object sender, ValidateEventArgs e) { - Validate.Raise (this, e); - } + /// <summary> + /// Validate content of the text box. Occurs in non multiline TextBox when 'Enter' key + /// is pressed. + /// </summary> + public event EventHandler<ValidateEventArgs> Validate; + public virtual void OnValidate (Object sender, ValidateEventArgs e) { + Validate.Raise (this, e); + } #region Scrolling int scrollX, scrollY, maxScrollX, maxScrollY, mouseWheelSpeed; @@ -33,13 +33,13 @@ namespace Crow /// if true, key stroke are handled in derrived class /// </summary> protected bool KeyEventsOverrides = false; - bool autoAdjustScroll = false;//if scrollXY is changed directly, dont try adjust scroll to cursor + bool autoAdjustScroll = false;//if scrollXY is changed directly, dont try adjust scroll to cursor /// <summary> Horizontal Scrolling Position </summary> [DefaultValue (0)] public virtual int ScrollX { get { return scrollX; } set { - //cancelAdjustScroll = true; + //cancelAdjustScroll = true; if (scrollX == value) return; @@ -64,9 +64,9 @@ namespace Crow public virtual int ScrollY { get { return scrollY; } set { - //cancelAdjustScroll = true; + //cancelAdjustScroll = true; - if (scrollY == value) + if (scrollY == value) return; int newS = value; @@ -144,205 +144,205 @@ namespace Crow base.onMouseWheel (sender, e); } - public override void onMouseMove (object sender, MouseMoveEventArgs e) { - if (!HasFocus || !IFace.IsDown (MouseButton.Left)) { - base.onMouseMove (sender, e); + public override void onMouseMove (object sender, MouseMoveEventArgs e) { + if (!HasFocus || !IFace.IsDown (MouseButton.Left)) { + base.onMouseMove (sender, e); return; } - Rectangle cb = ClientRectangle; + Rectangle cb = ClientRectangle; - if (CurrentLoc.Value.VisualCharXPosition < scrollX) - ScrollX = (int)CurrentLoc.Value.VisualCharXPosition; - else if (CurrentLoc.Value.VisualCharXPosition > cb.Width + scrollX) - ScrollX = (int)CurrentLoc.Value.VisualCharXPosition - cb.Width; + if (CurrentLoc.Value.VisualCharXPosition < scrollX) + ScrollX = (int)CurrentLoc.Value.VisualCharXPosition; + else if (CurrentLoc.Value.VisualCharXPosition > cb.Width + scrollX) + ScrollX = (int)CurrentLoc.Value.VisualCharXPosition - cb.Width; - double lineHeight = fe.Ascent + fe.Descent; - int firstLine = (int)Math.Ceiling((double)scrollY / lineHeight); - int lastLine = (int)Math.Floor((double)(scrollY + cb.Height) / lineHeight) - 1; - //Console.WriteLine ($"current: {currentLoc.Value.Line} first:{firstLine} last:{lastLine}"); + double lineHeight = fe.Ascent + fe.Descent; + int firstLine = (int)Math.Ceiling((double)scrollY / lineHeight); + int lastLine = (int)Math.Floor((double)(scrollY + cb.Height) / lineHeight) - 1; + //Console.WriteLine ($"current: {currentLoc.Value.Line} first:{firstLine} last:{lastLine}"); - if (CurrentLoc.Value.Line < firstLine) - ScrollY = (int)(lineHeight * CurrentLoc.Value.Line); - else if (CurrentLoc.Value.Line > lastLine) - ScrollY = (int)(lineHeight * (CurrentLoc.Value.Line + 1)) - cb.Height; + if (CurrentLoc.Value.Line < firstLine) + ScrollY = (int)(lineHeight * CurrentLoc.Value.Line); + else if (CurrentLoc.Value.Line > lastLine) + ScrollY = (int)(lineHeight * (CurrentLoc.Value.Line + 1)) - cb.Height; e.Handled = true; base.onMouseMove (sender, e); - } - #endregion - public override void OnLayoutChanges (LayoutingType layoutType) { - base.OnLayoutChanges (layoutType); - updateMaxScrolls (layoutType); - } - protected override void drawContent (Context gr) { - gr.Translate (-scrollX, -scrollY); - base.drawContent (gr); - gr.Translate (scrollX, scrollY); - } - protected override bool cancelLinePrint (double lineHeght, double y, int clientHeight) => - y + lineHeght < scrollY || y - lineHeght > clientHeight + scrollY; - protected override void updateHoverLocation (Point mouseLocalPos) { - base.updateHoverLocation (mouseLocalPos + new Point (ScrollX, ScrollY)); - } - protected override void measureTextBounds (Context gr) { - base.measureTextBounds (gr); - updateMaxScrolls (LayoutingType.Height); - updateMaxScrolls (LayoutingType.Width); - } - internal override RectangleD? computeTextCursor (Rectangle cursor) { - Rectangle cb = ClientRectangle; - cursor -= new Point (scrollX, scrollY); + } + #endregion + public override void OnLayoutChanges (LayoutingType layoutType) { + base.OnLayoutChanges (layoutType); + updateMaxScrolls (layoutType); + } + protected override void drawContent (Context gr) { + gr.Translate (-scrollX, -scrollY); + base.drawContent (gr); + gr.Translate (scrollX, scrollY); + } + protected override bool cancelLinePrint (double lineHeght, double y, int clientHeight) => + y + lineHeght < scrollY || y - lineHeght > clientHeight + scrollY; + protected override void updateHoverLocation (Point mouseLocalPos) { + base.updateHoverLocation (mouseLocalPos + new Point (ScrollX, ScrollY)); + } + protected override void measureTextBounds (Context gr) { + base.measureTextBounds (gr); + updateMaxScrolls (LayoutingType.Height); + updateMaxScrolls (LayoutingType.Width); + } + internal override RectangleD? computeTextCursor (Rectangle cursor) { + Rectangle cb = ClientRectangle; + cursor -= new Point (scrollX, scrollY); - if (autoAdjustScroll) { - autoAdjustScroll = false; - int goodMsrs = 0; - if (cursor.Right < 0) - ScrollX += cursor.Right; - else if (cursor.X > cb.Width) - ScrollX += cursor.X - cb.Width; - else - goodMsrs++; + if (autoAdjustScroll) { + autoAdjustScroll = false; + int goodMsrs = 0; + if (cursor.Right < 0) + ScrollX += cursor.Right; + else if (cursor.X > cb.Width) + ScrollX += cursor.X - cb.Width; + else + goodMsrs++; - if (cursor.Y < 0) - ScrollY += cursor.Y; - else if (cursor.Bottom > cb.Height) - ScrollY += cursor.Bottom - cb.Height; - else - goodMsrs++; + if (cursor.Y < 0) + ScrollY += cursor.Y; + else if (cursor.Bottom > cb.Height) + ScrollY += cursor.Bottom - cb.Height; + else + goodMsrs++; - if (goodMsrs < 2) - return null; - } else if (cursor.Right < 0 || cursor.X > cb.Width || cursor.Y < 0 || cursor.Bottom > cb.Height) - return null; - - return cursor; - } + if (goodMsrs < 2) + return null; + } else if (cursor.Right < 0 || cursor.X > cb.Width || cursor.Y < 0 || cursor.Bottom > cb.Height) + return null; + + return cursor; + } - void updateMaxScrolls (LayoutingType layout) { - Rectangle cb = ClientRectangle; - if (layout == LayoutingType.Width) { - MaxScrollX = cachedTextSize.Width - cb.Width; - NotifyValueChanged ("PageWidth", ClientRectangle.Width); - if (cachedTextSize.Width > 0) - NotifyValueChanged ("ChildWidthRatio", Math.Min (1.0, (double)cb.Width / cachedTextSize.Width)); - } else if (layout == LayoutingType.Height) { - MaxScrollY = cachedTextSize.Height - cb.Height; - NotifyValueChanged ("PageHeight", ClientRectangle.Height); - if (cachedTextSize.Height > 0) - NotifyValueChanged ("ChildHeightRatio", Math.Min (1.0, (double)cb.Height / cachedTextSize.Height)); - } - } + void updateMaxScrolls (LayoutingType layout) { + Rectangle cb = ClientRectangle; + if (layout == LayoutingType.Width) { + MaxScrollX = cachedTextSize.Width - cb.Width; + NotifyValueChanged ("PageWidth", ClientRectangle.Width); + if (cachedTextSize.Width > 0) + NotifyValueChanged ("ChildWidthRatio", Math.Min (1.0, (double)cb.Width / cachedTextSize.Width)); + } else if (layout == LayoutingType.Height) { + MaxScrollY = cachedTextSize.Height - cb.Height; + NotifyValueChanged ("PageHeight", ClientRectangle.Height); + if (cachedTextSize.Height > 0) + NotifyValueChanged ("ChildHeightRatio", Math.Min (1.0, (double)cb.Height / cachedTextSize.Height)); + } + } - #region Keyboard handling - public override void onKeyDown (object sender, KeyEventArgs e) { - Key key = e.Key; - TextSpan selection = Selection; - switch (key) { - case Key.Backspace: - if (selection.IsEmpty) { - if (selection.Start == 0) - return; - if (CurrentLoc.Value.Column == 0) { - int lbLength = lines[CurrentLoc.Value.Line - 1].LineBreakLength; - update (new TextChange (selection.Start - lbLength, lbLength, "")); - }else - update (new TextChange (selection.Start - 1, 1, "")); - } else - update (new TextChange (selection.Start, selection.Length, "")); - break; - case Key.Delete: - if (selection.IsEmpty) { - if (selection.Start == Text.Length) - return; - if (CurrentLoc.Value.Column >= lines[CurrentLoc.Value.Line].Length) - update (new TextChange (selection.Start, lines[CurrentLoc.Value.Line].LineBreakLength, "")); - else - update (new TextChange (selection.Start, 1, "")); - } else { - if (IFace.Shift) - IFace.Clipboard = SelectedText; - update (new TextChange (selection.Start, selection.Length, "")); - } - break; - case Key.Insert: - if (IFace.Shift) - update (new TextChange (selection.Start, selection.Length, IFace.Clipboard)); - else if (IFace.Ctrl && !selection.IsEmpty) - IFace.Clipboard = SelectedText; - break; - case Key.KeypadEnter: - case Key.Enter: - if (Multiline) { - if (string.IsNullOrEmpty (LineBreak)) - detectLineBreak (); - update (new TextChange (selection.Start, selection.Length, LineBreak)); - } else - OnValidate (this, new ValidateEventArgs (_text)); - break; - case Key.Escape: - selectionStart = null; - CurrentLoc = lines.GetLocation (selection.Start); - RegisterForRedraw (); - break; - case Key.Tab: - update (new TextChange (selection.Start, selection.Length, "\t")); - break; - case Key.PageUp: - checkShift (); - LineMove (-visibleLines); - RegisterForRedraw (); - break; - case Key.PageDown: - checkShift (); - LineMove (visibleLines); - RegisterForRedraw (); - break; - default: - base.onKeyDown (sender, e); - break; - } - autoAdjustScroll = true; - e.Handled = true; - } - public override void onKeyPress (object sender, KeyPressEventArgs e) { - base.onKeyPress (sender, e); + #region Keyboard handling + public override void onKeyDown (object sender, KeyEventArgs e) { + Key key = e.Key; + TextSpan selection = Selection; + switch (key) { + case Key.Backspace: + if (selection.IsEmpty) { + if (selection.Start == 0) + return; + if (CurrentLoc.Value.Column == 0) { + int lbLength = lines[CurrentLoc.Value.Line - 1].LineBreakLength; + update (new TextChange (selection.Start - lbLength, lbLength, "")); + }else + update (new TextChange (selection.Start - 1, 1, "")); + } else + update (new TextChange (selection.Start, selection.Length, "")); + break; + case Key.Delete: + if (selection.IsEmpty) { + if (selection.Start == Text.Length) + return; + if (CurrentLoc.Value.Column >= lines[CurrentLoc.Value.Line].Length) + update (new TextChange (selection.Start, lines[CurrentLoc.Value.Line].LineBreakLength, "")); + else + update (new TextChange (selection.Start, 1, "")); + } else { + if (IFace.Shift) + IFace.Clipboard = SelectedText; + update (new TextChange (selection.Start, selection.Length, "")); + } + break; + case Key.Insert: + if (IFace.Shift) + update (new TextChange (selection.Start, selection.Length, IFace.Clipboard)); + else if (IFace.Ctrl && !selection.IsEmpty) + IFace.Clipboard = SelectedText; + break; + case Key.KeypadEnter: + case Key.Enter: + if (Multiline) { + if (string.IsNullOrEmpty (LineBreak)) + detectLineBreak (); + update (new TextChange (selection.Start, selection.Length, LineBreak)); + } else + OnValidate (this, new ValidateEventArgs (_text)); + break; + case Key.Escape: + selectionStart = null; + CurrentLoc = lines.GetLocation (selection.Start); + RegisterForRedraw (); + break; + case Key.Tab: + update (new TextChange (selection.Start, selection.Length, "\t")); + break; + case Key.PageUp: + checkShift (); + LineMove (-visibleLines); + RegisterForRedraw (); + break; + case Key.PageDown: + checkShift (); + LineMove (visibleLines); + RegisterForRedraw (); + break; + default: + base.onKeyDown (sender, e); + break; + } + autoAdjustScroll = true; + e.Handled = true; + } + public override void onKeyPress (object sender, KeyPressEventArgs e) { + base.onKeyPress (sender, e); - TextSpan selection = Selection; - update (new TextChange (selection.Start, selection.Length, e.KeyChar.ToString ())); + TextSpan selection = Selection; + update (new TextChange (selection.Start, selection.Length, e.KeyChar.ToString ())); - /*Insert (e.KeyChar.ToString()); + /*Insert (e.KeyChar.ToString()); SelRelease = -1; SelBegin = new Point(CurrentColumn, SelBegin.Y); RegisterForGraphicUpdate();*/ - } - #endregion + } + #endregion - protected void update (TextChange change) { - lock (linesMutex) { - Span<char> tmp = stackalloc char[Text.Length + (change.ChangedText.Length - change.Length)]; - //Console.WriteLine ($"{Text.Length,-4} {change.Start,-4} {change.Length,-4} {change.ChangedText.Length,-4} tmp:{tmp.Length,-4}"); - ReadOnlySpan<char> src = Text.AsSpan (); - src.Slice (0, change.Start).CopyTo (tmp); - change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); - src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); - - _text = tmp.ToString (); - lines.Update (change); - //lines.Update (_text); - selectionStart = null; + protected void update (TextChange change) { + lock (linesMutex) { + Span<char> tmp = stackalloc char[Text.Length + (change.ChangedText.Length - change.Length)]; + //Console.WriteLine ($"{Text.Length,-4} {change.Start,-4} {change.Length,-4} {change.ChangedText.Length,-4} tmp:{tmp.Length,-4}"); + ReadOnlySpan<char> src = Text.AsSpan (); + src.Slice (0, change.Start).CopyTo (tmp); + change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); + src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); + + _text = tmp.ToString (); + lines.Update (change); + //lines.Update (_text); + selectionStart = null; - CurrentLoc = lines.GetLocation (change.Start + change.ChangedText.Length); - textMeasureIsUpToDate = false; - IFace.forceTextCursor = true; - } + CurrentLoc = lines.GetLocation (change.Start + change.ChangedText.Length); + textMeasureIsUpToDate = false; + IFace.forceTextCursor = true; + } - NotifyValueChanged ("Text", Text); - OnTextChanged (this, new TextChangeEventArgs (change)); - - RegisterForGraphicUpdate (); - } - } + NotifyValueChanged ("Text", Text); + OnTextChanged (this, new TextChangeEventArgs (change)); + + RegisterForGraphicUpdate (); + } + } } diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 6f0cd6c2..dbed07b7 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -922,8 +922,7 @@ namespace Crow } else { unshownPostActions (); } - RegisterForLayouting(LayoutingType.Sizing); - + RegisterForLayouting(LayoutingType.Sizing); NotifyValueChangedAuto (isVisible); } @@ -1629,9 +1628,17 @@ namespace Crow { switch (layoutType) { case LayoutingType.Width: + /*if (Parent is Widget p) { + if (p.Width.IsFit) + p.RegisterForLayouting (LayoutingType.Width); + }*/ RegisterForLayouting (LayoutingType.X); break; case LayoutingType.Height: + /*if (Parent is Widget pp) { + if (pp.Height.IsFit) + pp.RegisterForLayouting (LayoutingType.Height); + }*/ RegisterForLayouting (LayoutingType.Y); break; } diff --git a/Crow/src/styling/StyleReader.cs b/Crow/src/styling/StyleReader.cs index fb7c43cc..22be0c35 100644 --- a/Crow/src/styling/StyleReader.cs +++ b/Crow/src/styling/StyleReader.cs @@ -162,9 +162,8 @@ namespace Crow throw new ParserException (line, column, "Unexpected end of statement", resId); ReadChar (); if (targetsClasses.Count == 0) { - //style constants, only the first occurence is kept - if (!StylingConstants.ContainsKey (currentProperty)) - StylingConstants[currentProperty] = token.ToString (); + //style constants override previous values. + StylingConstants[currentProperty] = token.ToString (); curState = States.classNames; } else { foreach (string tc in targetsClasses) { 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> diff --git a/spaceToTabs.sh b/spaceToTabs.sh new file mode 100755 index 00000000..2fd7108f --- /dev/null +++ b/spaceToTabs.sh @@ -0,0 +1,3 @@ +#!/bin/bash +sed -i.bak ':a;s/^\(\t*\) \{4\}/\1\t/;/^\t* \{4\}/ba' $1 +rm $1.bak |