]> O.S.I.I.S - jp/crow.git/commitdiff
ItemsScroller for list, layouting changes, editor suggestions
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Sat, 5 Jun 2021 14:52:53 +0000 (16:52 +0200)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Sat, 5 Jun 2021 14:52:53 +0000 (16:52 +0200)
31 files changed:
Crow/Templates/ScrollingListBox.template
Crow/src/DebugUtils/DebugLogger.cs
Crow/src/IML/Instantiator.cs
Crow/src/Interface.cs
Crow/src/Text/Encoding.cs
Crow/src/Widgets/Group.cs
Crow/src/Widgets/Label.cs
Crow/src/Widgets/ScrollingStack.cs [new file with mode: 0644]
Crow/src/Widgets/TemplatedGroup.cs
Crow/src/Widgets/TextBox.cs
Crow/src/Widgets/Widget.cs
Crow/src/styling/StyleReader.cs
Samples/DebugLogAnalyzer/ui/main.crow
Samples/Directory.Build.props
Samples/ShowCase/ShowCase.cs
Samples/common/Editor.cs [deleted file]
Samples/common/SampleBase.cs [deleted file]
Samples/common/samples.style
Samples/common/src/Editor.cs [new file with mode: 0644]
Samples/common/src/ImlParsing/Syntax.cs [new file with mode: 0644]
Samples/common/src/ImlParsing/Token.cs [new file with mode: 0644]
Samples/common/src/ImlParsing/TokenType.cs [new file with mode: 0644]
Samples/common/src/ImlParsing/XmlSource.cs [new file with mode: 0644]
Samples/common/src/SampleBase.cs [new file with mode: 0644]
Samples/common/ui/Interfaces/Divers/0.crow
Samples/common/ui/Interfaces/Divers/fitListWithStretchedItems.crow
Samples/common/ui/Interfaces/Divers/temp.crow [new file with mode: 0644]
Samples/common/ui/Interfaces/Experimental/multiColorPick2.crow
Samples/common/ui/templates/ColorPicker.template
Samples/common/ui/templates/Suggestions.template [new file with mode: 0644]
spaceToTabs.sh [new file with mode: 0755]

index 1e7304f04f87689c0cf64e16dbe188c1764faa93..a5a9b629ce8c4a8d1e574166cacdeda3af345938 100644 (file)
@@ -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>
index 058921aafe44e595fd04f8802a190cf72d587244..2a9b75bb605dc6a10ac61776668ee790dc429516 100644 (file)
@@ -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);
                        
 
index b29912bc5f2f54657a2404c9e1a26a0808ab3f5b..ee1b615f5365961518b75450bd84e9cd9bf616cc 100644 (file)
@@ -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);
index 60f0e22149e51645a5930dd1c80e3da3357fdc96..3466b33b75c1779c97a11b43a3fd6a538de0bac5 100644 (file)
@@ -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) {
index b02e4565fddcbf2265f776d47094af58087d8d12..ac730650efcb8a2f09df04e8c58ebef4b447e493 100644 (file)
@@ -44,6 +44,7 @@ namespace Crow.Text
                 }
                 throw new NotImplementedException();
             }
+                       buff[encodedBytes] = 0;
             return encodedBytes;
         }
 
index 55cc635353d1ce4867e692c33e6410fce904561f..9e78f1877dc919f7c0778542b198c092d36234fd 100644 (file)
@@ -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;
index c8b4b2387b871db1dde7e9086e42c30e87d8f32f..47082698c9e1ab21fcbd33f51701b20278bcd903 100644 (file)
@@ -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 (file)
index 0000000..959a455
--- /dev/null
@@ -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
index 67c1f0dba6a877d5217f71a2d077529c43979802..f489ca961edb730cac4183e18b7e129f3a8061b7 100644 (file)
@@ -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;
+                       }
+               }
        }
 }
index ece9adc06d8f4636ee9f54e88c032b19e7306d27..2564129e162ea0547a8e681c6764f7aa2fb75c8e 100644 (file)
@@ -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
-
-        /// <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);
-        }
+       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);
+               }
 
                #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);
-
-            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 (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));
-            }
-        }
-
-        #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 ()));
-
-            /*Insert (e.KeyChar.ToString());
+               }
+               #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 (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;            
+               }
+
+               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);
+
+                       TextSpan selection = Selection;
+                       update (new TextChange (selection.Start, selection.Length, e.KeyChar.ToString ()));
+
+                       /*Insert (e.KeyChar.ToString());
 
                        SelRelease = -1;
                        SelBegin = new Point(CurrentColumn, SelBegin.Y);
 
                        RegisterForGraphicUpdate();*/
-        }
-        #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;
-
-                CurrentLoc = lines.GetLocation (change.Start + change.ChangedText.Length);
-                textMeasureIsUpToDate = false;
-                IFace.forceTextCursor = true;
-            }
-
-
-            NotifyValueChanged ("Text", Text);
-            OnTextChanged (this, new TextChangeEventArgs (change));
-            
-            RegisterForGraphicUpdate ();
-        }
-    }
+               }
+               #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;
+
+                               CurrentLoc = lines.GetLocation (change.Start + change.ChangedText.Length);
+                               textMeasureIsUpToDate = false;
+                               IFace.forceTextCursor = true;
+                       }
+
+
+                       NotifyValueChanged ("Text", Text);
+                       OnTextChanged (this, new TextChangeEventArgs (change));
+                       
+                       RegisterForGraphicUpdate ();
+               }
+       }
 }
index 6f0cd6c2a265f5b8e18e1b969f00b93c6104769b..dbed07b70d2465b281d5a210b6f459c8eed3b395 100644 (file)
@@ -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;
                        }
index fb7c43cc16c155e982d614ab8c58e9ce7a257a9c..22be0c35e49e7f536d47a8d11f062a69f6aafb4d 100644 (file)
@@ -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) {
index 16973e119fbb1a7e20a94bb3cbc777b3a4905660..5afc6c6c34711443e3f3fbdca89cb7989b7f093f 100644 (file)
                                                        </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>
index 8536aaaa0be56539eed16188e39694b07c478676..ea8b31f3783f8df11857c34d1eca247d9c7b815e 100644 (file)
@@ -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>
index 93c4b21739d4ec4e34f215a4069492319bb5bb55..ed5d857564b47f3a48a4ad448b5863056448b7d9 100644 (file)
@@ -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/Editor.cs b/Samples/common/Editor.cs
deleted file mode 100644 (file)
index 95de456..0000000
+++ /dev/null
@@ -1,545 +0,0 @@
-using System.Security.Principal;
-using System.Threading;
-// Copyright (c) 2013-2019  Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
-//
-// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
-
-using System;
-using Glfw;
-using Crow.Text;
-using System.Collections.Generic;
-using Crow.Cairo;
-using System.Threading.Tasks;
-using System.Linq;
-
-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();
-
-               void parse () {                 
-                       XmlSource tmp = new XmlSource(_text);
-                       lock(TokenMutex)
-                               source = tmp;
-                       RegisterForGraphicUpdate();
-               }
-               protected override void onInitialized(object sender, EventArgs e)
-               {
-                       base.onInitialized(sender, e);
-
-               }
-               Widget overlay;
-               public override void OnTextChanged(object sender, TextChangeEventArgs e)
-               {
-                       base.OnTextChanged(sender, e);
-                       //Task.Run(()=>parse());                        
-                       parse();
-
-                       /*if (overlay == null && HasFocus)
-                               overlay = IFace.LoadIMLFragment(@"<Widget Width='50' Height='50' Background='Jet'/>");*/
-               }
-               public override void onKeyDown(object sender, KeyEventArgs e)
-               {
-                       TextSpan selection = Selection;
-                       if (e.Key == Key.Tab && !selection.IsEmpty) {
-                               int lineStart = lines.GetLocation (selection.Start).Line;
-                               int lineEnd = lines.GetLocation (selection.End).Line;
-
-                               if (IFace.Shift) {
-                                       for (int l = lineStart; l <= lineEnd; l++) {                            
-                                               if (Text[lines[l].Start] == '\t')
-                                                       update (new TextChange (lines[l].Start, 1, ""));
-                                               else if (Char.IsWhiteSpace (Text[lines[l].Start])) {
-                                                       int i = 1;
-                                                       while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i]))
-                                                               i++;
-                                                       update (new TextChange (lines[l].Start, i, ""));
-                                               }
-                                       }
-
-                               }else{
-                                       for (int l = lineStart; l <= lineEnd; l++)              
-                                               update (new TextChange (lines[l].Start, 0, "\t"));                              
-                               }
-
-                selectionStart = new CharLocation (lineStart, 0);
-                CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length);
-
-                               return;
-                       }
-                       base.onKeyDown(sender, e);                      
-               }
-               int tabSize = 4;
-
-               protected override void drawContent (Context gr) {
-                       try {
-                               lock(TokenMutex) {
-                                       if (source == null || source.Tokens.Length == 0) {
-                                               base.drawContent (gr);
-                                               return;
-                                       }
-                               
-                                       Rectangle cb = ClientRectangle;
-                                       fe = gr.FontExtents;
-                                       double lineHeight = fe.Ascent + fe.Descent;
-
-                                       CharLocation selStart = default, selEnd = default;
-                                       bool selectionNotEmpty = false;
-
-                                       if (HasFocus) {
-                                               if (currentLoc?.Column < 0) {
-                                                       updateLocation (gr, cb.Width, ref currentLoc);
-                                                       NotifyValueChanged ("CurrentColumn", CurrentColumn);
-                                               } else
-                                                       updateLocation (gr, cb.Width, ref currentLoc);
-
-                                               if (overlay != null) {
-                                                       Point p = new Point((int)currentLoc.Value.VisualCharXPosition, (int)(lineHeight * (currentLoc.Value.Line + 1)));
-                                                       p += ScreenCoordinates (Slot).TopLeft;
-                                                       overlay.Left = p.X;
-                                                       overlay.Top = p.Y;
-                                               }
-                                               if (selectionStart.HasValue) {
-                                                       updateLocation (gr, cb.Width, ref selectionStart);
-                                                       if (CurrentLoc.Value != selectionStart.Value)
-                                                               selectionNotEmpty = true;
-                                               }
-                                               if (selectionNotEmpty) {
-                                                       if (CurrentLoc.Value.Line < selectionStart.Value.Line) {
-                                                               selStart = CurrentLoc.Value;
-                                                               selEnd = selectionStart.Value;
-                                                       } else if (CurrentLoc.Value.Line > selectionStart.Value.Line) {
-                                                               selStart = selectionStart.Value;
-                                                               selEnd = CurrentLoc.Value;
-                                                       } else if (CurrentLoc.Value.Column < selectionStart.Value.Column) {
-                                                               selStart = CurrentLoc.Value;
-                                                               selEnd = selectionStart.Value;
-                                                       } else {
-                                                               selStart = selectionStart.Value;
-                                                               selEnd = CurrentLoc.Value;
-                                                       }
-                                               } else
-                                                       IFace.forceTextCursor = true;
-                                       }
-
-                                       double spacePixelWidth = gr.TextExtents (" ").XAdvance;
-                                       int x = 0, y = 0;
-                                       double pixX = cb.Left;
-
-
-                                       Foreground.SetAsSource (IFace, gr);
-                                       gr.Translate (-ScrollX, -ScrollY);
-
-
-                                       ReadOnlySpan<char> sourceBytes = source.Source.AsSpan();
-                                       Span<byte> bytes = stackalloc byte[128];
-                                       TextExtents extents;
-                                       int tokPtr = 0;
-                                       Token tok = source.Tokens[tokPtr];
-                                       bool multilineToken = false;                            
-
-                                       ReadOnlySpan<char> buff = sourceBytes;
-
-
-                                       for (int i = 0; i < lines.Count; i++) {
-                                               //if (!cancelLinePrint (lineHeight, lineHeight * y, cb.Height)) {
-
-                                               if (multilineToken) {
-                                                       if (tok.End < lines[i].End) {//last incomplete line of multiline token
-                                                               buff = sourceBytes.Slice (lines[i].Start, tok.End - lines[i].Start);                                                            
-                                                       } else {//print full line
-                                                               buff = sourceBytes.Slice (lines[i].Start, lines[i].Length);
-                                                       }
-                                               }
-
-                                               while (tok.Start < lines[i].End) {
-                                                       if (!multilineToken) {
-                                                               if (tok.End > lines[i].End) {//first line of multiline
-                                                                       multilineToken = true;
-                                                                       buff = sourceBytes.Slice (tok.Start, lines[i].End - tok.Start);
-                                                               } else
-                                                                       buff = sourceBytes.Slice (tok.Start, tok.Length);
-
-                                                               if (tok.Type.HasFlag (TokenType.Punctuation))
-                                                                       gr.SetSource(Colors.DarkGrey);
-                                                               else if (tok.Type.HasFlag (TokenType.Trivia))
-                                                                       gr.SetSource(Colors.DimGrey);
-                                                               else if (tok.Type == TokenType.ElementName) {
-                                                                       gr.SetSource(Colors.Green);
-                                                               }else if (tok.Type == TokenType.AttributeName) {
-                                                                       gr.SetSource(Colors.Blue);
-                                                               }else if (tok.Type == TokenType.AttributeValue) {
-                                                                       gr.SetSource(Colors.OrangeRed);
-                                                               }else if (tok.Type == TokenType.EqualSign) {
-                                                                       gr.SetSource(Colors.Black);
-                                                               }else if (tok.Type == TokenType.PI_Target) {
-                                                                       gr.SetSource(Colors.DarkSlateBlue);
-                                                               }else {
-                                                                       gr.SetSource(Colors.Red);
-                                                               }                                                                       
-                                                       }
-
-                                                       int size = buff.Length * 4 + 1;
-                                                       if (bytes.Length < size)
-                                                               bytes = size > 512 ? new byte[size] : stackalloc byte[size];
-
-                                                       int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes);
-
-                                                       if (encodedBytes > 0) {
-                                                               bytes[encodedBytes++] = 0;
-                                                               gr.TextExtents (bytes.Slice (0, encodedBytes), out extents);
-                                                               gr.MoveTo (pixX, lineHeight * y + fe.Ascent);
-                                                               gr.ShowText (bytes.Slice (0, encodedBytes));
-                                                               pixX += extents.XAdvance;
-                                                               x += buff.Length;                                                               
-                                                       }
-
-                                                       if (multilineToken) {
-                                                               if (tok.End < lines[i].End)//last incomplete line of multiline token
-                                                                       multilineToken = false;
-                                                               else
-                                                                       break;
-                                                       }
-
-                                                       if (++tokPtr >= source.Tokens.Length)
-                                                               break;
-                                                       tok = source.Tokens[tokPtr];
-                                               }
-
-                                               if (HasFocus && selectionNotEmpty) {
-                                                       RectangleD lineRect = new RectangleD (cb.X,     lineHeight * y + cb.Top, pixX, lineHeight);
-                                                       RectangleD selRect = lineRect;
-                                                       
-                                                       if (i >= selStart.Line && i <= selEnd.Line) {
-                                                               if (selStart.Line == selEnd.Line) {
-                                                                       selRect.X = selStart.VisualCharXPosition + cb.X;
-                                                                       selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition;
-                                                               } else if (i == selStart.Line) {
-                                                                       double newX = selStart.VisualCharXPosition + cb.X;
-                                                                       selRect.Width -= (newX - selRect.X) - 10.0;
-                                                                       selRect.X = newX;
-                                                               } else if (i == selEnd.Line)
-                                                                       selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X;
-                                                               else
-                                                                       selRect.Width += 10.0;
-
-                                                               buff = sourceBytes.Slice(lines[i].Start, lines[i].Length);
-                                                               int size = buff.Length * 4 + 1;
-                                                               if (bytes.Length < size)
-                                                                       bytes = size > 512 ? new byte[size] : stackalloc byte[size];
-
-                                                               int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes);
-
-                                                               gr.SetSource (SelectionBackground);
-                                                               gr.Rectangle (selRect);
-                                                               if (encodedBytes < 0)
-                                                                       gr.Fill ();
-                                                               else {
-                                                                       gr.FillPreserve ();
-                                                                       gr.Save ();
-                                                                       gr.Clip ();
-                                                                       gr.SetSource (SelectionForeground);
-                                                                       gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent);
-                                                                       gr.ShowText (bytes.Slice (0, encodedBytes));
-                                                                       gr.Restore ();
-                                                               }
-                                                               Foreground.SetAsSource (IFace, gr);
-                                                       }
-                                               }
-
-                                               if (!multilineToken) {
-                                                       if (++tokPtr >= source.Tokens.Length)
-                                                               break;
-                                                       tok = source.Tokens[tokPtr];
-                                               }
-
-                                               x = 0;
-                                               pixX = 0;
-                       
-                                               y++;
-
-
-                                                       /*      } else if (tok2.Type == TokenType.Tabulation) {
-                                                                       int spaceRounding = x % tabSize;
-                                                                       int spaces = spaceRounding == 0 ?
-                                                                               tabSize * tok2.Length :
-                                                                               spaceRounding + tabSize * (tok2.Length - 1);
-                                                                       x += spaces;
-                                                                       pixX += spacePixelWidth * spaces;
-                                                                       continue;
-                                                               } else if (tok2.Type == TokenType.WhiteSpace) {
-                                                                       x += tok2.Length;
-                                                                       pixX += spacePixelWidth * tok2.Length;*/                                                                                                                                                                
-                                       }                                       
-                                       gr.Translate (ScrollX, ScrollY);
-                               }
-                       } catch {
-                               
-                       }
-               }                       
-       }
-}
\ No newline at end of file
diff --git a/Samples/common/SampleBase.cs b/Samples/common/SampleBase.cs
deleted file mode 100644 (file)
index 1e849d6..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-using Crow;
-using Glfw;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net.NetworkInformation;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Crow
-{
-       public class SampleBase : Interface
-       {
-#if NETCOREAPP
-               static IntPtr resolveUnmanaged(Assembly assembly, String libraryName)
-               {
-
-                       switch (libraryName)
-                       {
-                               case "glfw3":
-                                       return NativeLibrary.Load("glfw", assembly, null);
-                               case "rsvg-2.40":
-                                       return NativeLibrary.Load("rsvg-2", assembly, null);
-                       }
-                       Console.WriteLine($"[UNRESOLVE] {assembly} {libraryName}");
-                       return IntPtr.Zero;
-               }
-
-               static SampleBase()
-               {
-                       System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).ResolvingUnmanagedDll += resolveUnmanaged;
-               }
-#endif
-               public SampleBase(IntPtr hWin) : base(800, 600, hWin) { }
-               public SampleBase() : base(800, 600, true, true) { }
-               public SampleBase(int width, int height, bool startUIThread, bool createSurface) :
-                       base(width, height, startUIThread, createSurface)
-               {
-
-               }
-
-               public Version CrowVersion => Assembly.GetAssembly(typeof(Widget)).GetName().Version;
-
-               #region Test values for Binding
-               public CommandGroup Commands, AllCommands;
-               public CommandGroup EditCommands = new CommandGroup("Edit Commands",
-                       new Command("Edit command 1", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 1 clicked")),
-                       new Command("Edit command 2 a bit longer", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 2 clicked"), null, false),
-                       new Command("Edit command three", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 3 clicked"))
-               );
-               public CommandGroup FileCommands = new CommandGroup("File Commands",
-                       new Command("File command 1", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 1 clicked")),
-                       new Command("File command 2 a bit longer", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 2 clicked")),
-                       new Command("File command three", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 3 clicked"))
-               );
-
-               void initCommands()
-               {
-                       Commands = new CommandGroup("commands msg boxes",
-                               new Command("Action 1", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 1 clicked")),
-                               new Command("Action two", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 2 clicked"), null, false),
-                               new Command("Action three", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 3 clicked"))
-                       );
-                       AllCommands = new CommandGroup ("All Commands",
-                               FileCommands,
-                               EditCommands,
-                               new CommandGroup ("Combined commands", FileCommands, EditCommands),
-                               new Command("Action A", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu A clicked"))
-                       );
-               }
-               public int intValue = 500;
-               VerticalAlignment currentVAlign;
-
-               DirectoryInfo curDir = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
-               public FileSystemInfo[] CurDirectory => curDir.GetFileSystemInfos();
-               public string MultilineText =
-                       $"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Sed non risus.\n\nSuspendisse lectus tortor,\nLorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Sed non risus.\n\nSuspendisse lectus tortor,";
-               //public string MultilineText = $"a\n";
-               TextAlignment textAlignment = TextAlignment.Left;
-               public TextAlignment TextAlignment
-               {
-                       get => textAlignment;
-                       set
-                       {
-                               if (textAlignment == value)
-                                       return;
-                               textAlignment = value;
-                               NotifyValueChanged(textAlignment);
-                       }
-               }
-
-               public int IntValue
-               {
-                       get => intValue;
-                       set
-                       {
-                               if (IntValue == value)
-                                       return;
-                               intValue = value;
-                               NotifyValueChanged("IntValue", intValue);
-                       }
-               }
-               public VerticalAlignment CurrentVAlign
-               {
-                       get => currentVAlign;
-                       set
-                       {
-                               if (currentVAlign == value)
-                                       return;
-                               currentVAlign = value;
-                               NotifyValueChanged("CurrentVAlign", currentVAlign);
-                       }
-               }
-               void onSpinnerValueChange(object sender, ValueChangeEventArgs e)
-               {
-                       if (e.MemberName != "Value")
-                               return;
-                       intValue = Convert.ToInt32(e.NewValue);
-               }
-               void change_alignment(object sender, EventArgs e)
-               {
-                       RadioButton rb = sender as RadioButton;
-                       if (rb == null)
-                               return;
-                       NotifyValueChanged("alignment", Enum.Parse(typeof(Alignment), rb.Caption));
-               }
-               public IEnumerable<String> List2 = new List<string>(new string[]
-                       {
-                               "string1",
-                               "string2",
-                               "string3",
-                               "string4",
-                               "string5",
-                               "string6",
-                               "string7",
-                               "string8",
-                               "string8",
-                               "string8",
-                               "string8",
-                               "string8",
-                               "string8",
-                               "string9"
-                       }
-               );
-               public IEnumerable<String> TestList2
-               {
-                       set
-                       {
-                               List2 = value;
-                               NotifyValueChanged("TestList2", testList);
-                       }
-                       get { return List2; }
-               }
-               public class TestClass
-               {
-                       public string Prop1 { get; set; }
-                       public string Prop2 { get; set; }
-
-                       public override string ToString()
-                               => $"{Prop1}, {Prop2}";
-
-                       public void OnValidateCommand(Object sender, ValidateEventArgs e)
-                       {
-                               Console.WriteLine($"Validation: {e.ValidatedText}");
-                       }
-               }
-               public class TestClassVC : IValueChange
-               {
-                       public event EventHandler<ValueChangeEventArgs> ValueChanged;
-                       public void NotifyValueChanged(object _value, [CallerMemberName] string caller = null)
-                               => ValueChanged.Raise(this, new ValueChangeEventArgs(caller, _value));
-                       string prop1, prop2;
-                       public string Prop1
-                       {
-                               get => prop1;
-                               set
-                               {
-                                       if (prop1 == value)
-                                               return;
-                                       prop1 = value;
-                                       NotifyValueChanged(prop1);
-                               }
-                       }
-                       public string Prop2
-                       {
-                               get => prop2;
-                               set
-                               {
-                                       if (prop2 == value)
-                                               return;
-                                       prop2 = value;
-                                       NotifyValueChanged(prop2);
-                               }
-
-
-
-                       }
-
-                       public override string ToString()
-                                       => $"{Prop1}, {Prop2}";
-
-               }
-               TestClass tcInstance = new TestClass() { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" };
-               TestClassVC tcVCInstance;// = new TestClassVC () { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" };
-               TestClass tcInstance1 = new TestClass() { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" };
-               TestClassVC tcVCInstance1 = new TestClassVC() { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" };
-               TestClass tcInstance2 = new TestClass() { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
-               TestClassVC tcVCInstance2 = new TestClassVC() { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
-
-               public TestClass TcInstance
-               {
-                       get => tcInstance;
-                       set
-                       {
-                               if (tcInstance == value)
-                                       return;
-                               tcInstance = value;
-                               NotifyValueChanged(tcInstance);
-                       }
-               }
-               public TestClassVC TcVCInstance
-               {
-                       get => tcVCInstance;
-                       set
-                       {
-                               if (tcVCInstance == value)
-                                       return;
-                               tcVCInstance = value;
-                               NotifyValueChanged(tcVCInstance);
-                       }
-               }
-
-               void tcInstance_ChangeProperties_MouseClick(object sender, MouseButtonEventArgs e)
-               {
-               }
-               public void tcInstance_ChangeInstance_MouseClick(object sender, MouseButtonEventArgs e)
-               {
-                       if (TcInstance == tcInstance1)
-                               TcInstance = tcInstance2;
-                       else
-                               TcInstance = tcInstance1;
-               }
-               void tcVCInstance_ChangeInstance_MouseClick(object sender, MouseButtonEventArgs e)
-               {
-                       TcVCInstance = new TestClassVC() { Prop1 = "prop1 value changed", Prop2 = "prop2 value changed" };
-               }
-
-               public IEnumerable<TestClass> List3 = new List<TestClass>(new TestClass[]
-                       {
-                               new TestClass { Prop1 = "string1", Prop2="prop2-1" },
-                               new TestClass { Prop1 = "string2", Prop2="prop2-2" },
-                               new TestClass { Prop1 = "string3", Prop2="prop2-3" },
-                       }
-               );
-               public IEnumerable<string> TestList3Props1 => List3.Select(sc => sc.Prop1).ToList();
-               public IEnumerable<TestClass> TestList3
-               {
-                       set
-                       {
-                               List3 = value;
-                               NotifyValueChanged("TestList3", testList);
-                       }
-                       get { return List3; }
-               }
-               string testString;
-               public string TestString
-               {
-                       get => testString;
-                       set
-                       {
-                               if (testString == value)
-                                       return;
-                               testString = value;
-
-                               NotifyValueChanged(testString);
-                       }
-               }               
-               string prop1;
-               public string TestList3SelProp1
-               {
-                       get => prop1;
-                       set
-                       {
-                               if (prop1 == value)
-                                       return;
-                               prop1 = value;
-
-                               NotifyValueChanged(prop1);
-                       }
-               }
-
-               string selString;
-               public string TestList2SelectedString
-               {
-                       get => selString;
-                       set
-                       {
-                               if (selString == value)
-                                       return;
-                               selString = value;
-                               NotifyValueChanged(selString);
-                       }
-               }
-
-
-               IList<Colors> testList = (IList<Colors>)FastEnumUtility.FastEnum.GetValues<Colors>().ToList();//.ColorDic.Values//.OrderBy(c=>c.Hue)
-                                                                                                                                                                                                         //.ThenBy(c=>c.Value).ThenBy(c=>c.Saturation)
-                                                                                                                                                                                                         //.ToList ();
-               public IList<Colors> TestList
-               {
-                       set
-                       {
-                               testList = value;
-                               NotifyValueChanged("TestList", testList);
-                       }
-                       get { return testList; }
-               }
-               void OnClear(object sender, MouseButtonEventArgs e) => TestList = null;
-               void OnLoadList(object sender, MouseButtonEventArgs e) => TestList = (IList<Colors>)FastEnumUtility.FastEnum.GetValues<Colors>().ToList();
-
-
-               string curSources = "";
-               public string CurSources
-               {
-                       get { return curSources; }
-                       set
-                       {
-                               if (value == curSources)
-                                       return;
-                               curSources = value;
-                               NotifyValueChanged(curSources);
-                       }
-               }
-               bool boolVal = true;
-               public bool BoolVal
-               {
-                       get { return boolVal; }
-                       set
-                       {
-                               if (boolVal == value)
-                                       return;
-                               boolVal = value;
-                               NotifyValueChanged(boolVal);
-                       }
-               }
-
-
-
-               #endregion
-
-               protected override void OnInitialized()
-               {
-                       initCommands();
-                       base.OnInitialized();
-               }
-
-               public override bool OnKeyDown(Key key)
-               {
-
-                       switch (key)
-                       {
-                               case Key.F5:
-                                       Load("Interfaces/Divers/testFileDialog.crow").DataSource = this;
-                                       return true;
-                               case Key.F6:
-                                       Load("Interfaces/Divers/0.crow").DataSource = this;
-                                       return true;
-                               case Key.F7:
-                                       Load("Interfaces/Divers/perfMeasures.crow").DataSource = this;
-                                       return true;
-                               case Key.F2:
-                                       if (IsKeyDown(Key.LeftShift))
-                                               DbgLogger.Reset();
-                                       DbgLogger.Save(this);
-                                       return true;
-                       }
-                       return base.OnKeyDown(key);
-               }
-       }
-}
\ No newline at end of file
index 7c33bd9d602fb1629416ab690f7ecf734282dc82..8a43f3a01f6e7af8d84e6547b7865f2fea3babb3 100644 (file)
@@ -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/src/Editor.cs b/Samples/common/src/Editor.cs
new file mode 100644 (file)
index 0000000..b9b0ae9
--- /dev/null
@@ -0,0 +1,401 @@
+// Copyright (c) 2013-2021  Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Glfw;
+using Crow.Text;
+using System.Collections.Generic;
+using Crow.Cairo;
+using System.Threading.Tasks;
+using System.Linq;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+
+namespace Crow
+{
+       public class Editor : TextBox {
+               XmlSource source;
+               object TokenMutex = new object();
+
+               void parse () {                 
+                       XmlSource tmp = new XmlSource(_text);
+                       lock(TokenMutex)
+                               source = tmp;
+                       RegisterForGraphicUpdate();
+               }
+               protected override void onInitialized(object sender, EventArgs e)
+               {
+                       base.onInitialized(sender, e);
+
+               }
+               ListBox overlay;
+               List<String> suggestions;
+               public List<String> Suggestions {
+                       get => suggestions;
+                       set {
+                               suggestions = value;
+                               NotifyValueChangedAuto (suggestions);
+                               if (suggestions == null || suggestions.Count == 0)
+                                       hideOverlay ();
+                               else
+                                       showOverlay ();                         
+                       }
+               }
+               bool suggestionsActive => overlay != null && overlay.IsVisible;
+               Token currentToken;
+               SyntaxNode currentNode;
+               string[] allWidgetNames = typeof (Widget).Assembly.GetExportedTypes ().Where(t=>typeof(Widget).IsAssignableFrom (t))
+                                       .Select (s => s.Name).ToArray ();
+
+
+               string [] getAllCrowTypeMembers (string crowTypeName) {
+                       Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName);
+                       return crowType.GetMembers (BindingFlags.Public | BindingFlags.Instance).
+                               Where (m=>((m is PropertyInfo pi && pi.CanWrite) || (m is EventInfo)) &&
+                                               m.GetCustomAttribute<XmlIgnoreAttribute>() == null).Select (mb=>mb.Name).ToArray();
+               }
+               public override void OnTextChanged(object sender, TextChangeEventArgs e)
+               {
+                       base.OnTextChanged(sender, e);
+                       //Task.Run(()=>parse());
+
+                       parse();
+                       tryGetSuggestions ();
+
+                       //Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}");
+               }
+               void tryGetSuggestions () {
+                       if (!currentLoc.HasValue)
+                               return;
+                       int pos = lines.GetAbsolutePosition (CurrentLoc.Value); 
+                       currentToken = source.FindTokenIncludingPosition (pos);
+                       currentNode = source.FindNodeIncludingPosition (pos);
+
+                       Console.WriteLine ($"Current Token: {currentToken} Current Node: {currentNode}");
+
+                       if (currentToken.Type == TokenType.ElementOpen) {
+                               Suggestions = new List<string> (allWidgetNames);
+                       } else if (currentToken.Type == TokenType.ElementName) {
+                               Suggestions = allWidgetNames.Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+                       } else if (currentNode is AttributeSyntax attribNode) {
+                               if (currentNode.Parent is ElementTagSyntax eltTag) {
+                                       if (currentToken.Type == TokenType.AttributeName && eltTag.NameToken.HasValue) {
+                                               Suggestions = getAllCrowTypeMembers (eltTag.NameToken.Value.AsString (_text))
+                                                       .Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+                                       }
+                               }
+                       } else if (currentNode is ElementStartTagSyntax eltStartTag) {
+                               Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text)).ToList ();
+                       } else {
+                               /*SyntaxNode curNode = source.FindNodeIncludingPosition (pos);
+                               Console.WriteLine ($"Current Node: {curNode}");
+                               if (curNode is ElementStartTagSyntax eltStartTag &&
+                                       (currentToken.Type != TokenType.ClosingSign && currentToken.Type != TokenType.EmptyElementClosing && currentToken.Type != TokenType.Unknown)) {
+                                       Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text)).ToList ();
+                               } else*/
+                                       hideOverlay ();
+                       }
+               }
+               void showOverlay () {
+                       lock (IFace.UpdateMutex) {
+                               if (overlay == null) {
+                                       overlay = IFace.LoadIMLFragment<ListBox>(@"
+                                               <ListBox Style='suggestionsListBox' Data='{Suggestions}' />
+                                       ");
+                                       overlay.DataSource = this;
+                                       overlay.Loaded += (sender, arg) => (sender as ListBox).SelectedIndex = 0;                               
+                               } else
+                                       overlay.IsVisible = true;
+                               overlay.RegisterForLayouting(LayoutingType.Sizing);     
+                       }
+               }
+               void hideOverlay () {
+                       if (overlay == null)
+                               return;
+                       overlay.IsVisible = false;
+               }
+               void completeToken () {                 
+                       string selectedSugg = overlay.SelectedItem as string;
+                       if (selectedSugg == null)
+                               return;
+                       if (currentToken.Type == TokenType.ElementOpen || currentToken.Type == TokenType.WhiteSpace)
+                               update (new TextChange (currentToken.End, 0, selectedSugg));
+                       else
+                               update (new TextChange (currentToken.Start, currentToken.Length, selectedSugg));
+                       hideOverlay ();
+               }
+               public override void onMouseDown (object sender, MouseButtonEventArgs e) {
+                       hideOverlay ();
+                       base.onMouseDown (sender, e);
+               }
+               public override void onKeyDown(object sender, KeyEventArgs e)
+               {
+                       if (suggestionsActive) {
+                               switch (e.Key) {
+                               case Key.Escape:
+                                       hideOverlay ();
+                                       return;
+                               case Key.Left:
+                               case Key.Right:
+                                       hideOverlay ();
+                                       break;
+                               case Key.End:
+                               case Key.Home:
+                               case Key.Down:
+                               case Key.Up:
+                               case Key.PageDown:
+                               case Key.PageUp:
+                                       overlay.onKeyDown (this, e);
+                                       return;
+                               case Key.Tab:
+                                       completeToken ();
+                                       return;
+                               }
+                       } else if (e.Key == Key.Space && IFace.Ctrl) {
+                               tryGetSuggestions ();
+                               return;
+                       }
+
+                       TextSpan selection = Selection;
+                       if (e.Key == Key.Tab && !selection.IsEmpty) {
+                               int lineStart = lines.GetLocation (selection.Start).Line;
+                               int lineEnd = lines.GetLocation (selection.End).Line;
+
+                               if (IFace.Shift) {
+                                       for (int l = lineStart; l <= lineEnd; l++) {                            
+                                               if (Text[lines[l].Start] == '\t')
+                                                       update (new TextChange (lines[l].Start, 1, ""));
+                                               else if (Char.IsWhiteSpace (Text[lines[l].Start])) {
+                                                       int i = 1;
+                                                       while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i]))
+                                                               i++;
+                                                       update (new TextChange (lines[l].Start, i, ""));
+                                               }
+                                       }
+
+                               }else{
+                                       for (int l = lineStart; l <= lineEnd; l++)              
+                                               update (new TextChange (lines[l].Start, 0, "\t"));                              
+                               }
+
+                selectionStart = new CharLocation (lineStart, 0);
+                CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length);
+
+                               return;
+                       }
+                       base.onKeyDown(sender, e);                      
+               }               
+
+               protected override void drawContent (Context gr) {
+                       try {
+                               lock(TokenMutex) {
+                                       if (source == null || source.Tokens.Length == 0) {
+                                               base.drawContent (gr);
+                                               return;
+                                       }
+                               
+                                       Rectangle cb = ClientRectangle;
+                                       fe = gr.FontExtents;
+                                       double lineHeight = fe.Ascent + fe.Descent;
+
+                                       CharLocation selStart = default, selEnd = default;
+                                       bool selectionNotEmpty = false;
+
+                                       if (HasFocus) {
+                                               if (currentLoc?.Column < 0) {
+                                                       updateLocation (gr, cb.Width, ref currentLoc);
+                                                       NotifyValueChanged ("CurrentColumn", CurrentColumn);
+                                               } else
+                                                       updateLocation (gr, cb.Width, ref currentLoc);
+
+                                               if (overlay != null && overlay.IsVisible) {
+                                                       Point p = new Point((int)currentLoc.Value.VisualCharXPosition - ScrollX, (int)(lineHeight * (currentLoc.Value.Line + 1) - ScrollY));
+                                                       if (p.Y < 0 || p.X < 0)
+                                                               hideOverlay ();
+                                                       else {
+                                                               p += ScreenCoordinates (Slot).TopLeft;
+                                                               overlay.Left = p.X;
+                                                               overlay.Top = p.Y;
+                                                       }
+                                               }
+                                               if (selectionStart.HasValue) {
+                                                       updateLocation (gr, cb.Width, ref selectionStart);
+                                                       if (CurrentLoc.Value != selectionStart.Value)
+                                                               selectionNotEmpty = true;
+                                               }
+                                               if (selectionNotEmpty) {
+                                                       if (CurrentLoc.Value.Line < selectionStart.Value.Line) {
+                                                               selStart = CurrentLoc.Value;
+                                                               selEnd = selectionStart.Value;
+                                                       } else if (CurrentLoc.Value.Line > selectionStart.Value.Line) {
+                                                               selStart = selectionStart.Value;
+                                                               selEnd = CurrentLoc.Value;
+                                                       } else if (CurrentLoc.Value.Column < selectionStart.Value.Column) {
+                                                               selStart = CurrentLoc.Value;
+                                                               selEnd = selectionStart.Value;
+                                                       } else {
+                                                               selStart = selectionStart.Value;
+                                                               selEnd = CurrentLoc.Value;
+                                                       }
+                                               } else
+                                                       IFace.forceTextCursor = true;
+                                       }
+
+                                       double spacePixelWidth = gr.TextExtents (" ").XAdvance;
+                                       int x = 0, y = 0;
+                                       double pixX = cb.Left;
+
+
+                                       Foreground.SetAsSource (IFace, gr);
+                                       gr.Translate (-ScrollX, -ScrollY);
+
+
+                                       ReadOnlySpan<char> sourceBytes = source.Source.AsSpan();
+                                       Span<byte> bytes = stackalloc byte[128];
+                                       TextExtents extents;
+                                       int tokPtr = 0;
+                                       Token tok = source.Tokens[tokPtr];
+                                       bool multilineToken = false;                            
+
+                                       ReadOnlySpan<char> buff = sourceBytes;
+
+
+                                       for (int i = 0; i < lines.Count; i++) {
+                                               //if (!cancelLinePrint (lineHeight, lineHeight * y, cb.Height)) {
+
+                                               if (multilineToken) {
+                                                       if (tok.End < lines[i].End) {//last incomplete line of multiline token
+                                                               buff = sourceBytes.Slice (lines[i].Start, tok.End - lines[i].Start);                                                            
+                                                       } else {//print full line
+                                                               buff = sourceBytes.Slice (lines[i].Start, lines[i].Length);
+                                                       }
+                                               }
+
+                                               while (tok.Start < lines[i].End) {
+                                                       if (!multilineToken) {
+                                                               if (tok.End > lines[i].End) {//first line of multiline
+                                                                       multilineToken = true;
+                                                                       buff = sourceBytes.Slice (tok.Start, lines[i].End - tok.Start);
+                                                               } else
+                                                                       buff = sourceBytes.Slice (tok.Start, tok.Length);
+
+                                                               if (tok.Type.HasFlag (TokenType.Punctuation))
+                                                                       gr.SetSource(Colors.DarkGrey);
+                                                               else if (tok.Type.HasFlag (TokenType.Trivia))
+                                                                       gr.SetSource(Colors.DimGrey);
+                                                               else if (tok.Type == TokenType.ElementName) {
+                                                                       gr.SetSource(Colors.Green);
+                                                               }else if (tok.Type == TokenType.AttributeName) {
+                                                                       gr.SetSource(Colors.Blue);
+                                                               }else if (tok.Type == TokenType.AttributeValue) {
+                                                                       gr.SetSource(Colors.OrangeRed);
+                                                               }else if (tok.Type == TokenType.EqualSign) {
+                                                                       gr.SetSource(Colors.Black);
+                                                               }else if (tok.Type == TokenType.PI_Target) {
+                                                                       gr.SetSource(Colors.DarkSlateBlue);
+                                                               }else {
+                                                                       gr.SetSource(Colors.Red);
+                                                               }                                                                       
+                                                       }
+
+                                                       int size = buff.Length * 4 + 1;
+                                                       if (bytes.Length < size)
+                                                               bytes = size > 512 ? new byte[size] : stackalloc byte[size];
+
+                                                       int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes);
+
+                                                       if (encodedBytes > 0) {
+                                                               bytes[encodedBytes++] = 0;
+                                                               gr.TextExtents (bytes.Slice (0, encodedBytes), out extents);
+                                                               gr.MoveTo (pixX, lineHeight * y + fe.Ascent);
+                                                               gr.ShowText (bytes.Slice (0, encodedBytes));
+                                                               pixX += extents.XAdvance;
+                                                               x += buff.Length;                                                               
+                                                       }
+
+                                                       if (multilineToken) {
+                                                               if (tok.End < lines[i].End)//last incomplete line of multiline token
+                                                                       multilineToken = false;
+                                                               else
+                                                                       break;
+                                                       }
+
+                                                       if (++tokPtr >= source.Tokens.Length)
+                                                               break;
+                                                       tok = source.Tokens[tokPtr];
+                                               }
+
+                                               if (HasFocus && selectionNotEmpty) {
+                                                       RectangleD lineRect = new RectangleD (cb.X,     lineHeight * y + cb.Top, pixX, lineHeight);
+                                                       RectangleD selRect = lineRect;
+                                                       
+                                                       if (i >= selStart.Line && i <= selEnd.Line) {
+                                                               if (selStart.Line == selEnd.Line) {
+                                                                       selRect.X = selStart.VisualCharXPosition + cb.X;
+                                                                       selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition;
+                                                               } else if (i == selStart.Line) {
+                                                                       double newX = selStart.VisualCharXPosition + cb.X;
+                                                                       selRect.Width -= (newX - selRect.X) - 10.0;
+                                                                       selRect.X = newX;
+                                                               } else if (i == selEnd.Line)
+                                                                       selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X;
+                                                               else
+                                                                       selRect.Width += 10.0;
+
+                                                               buff = sourceBytes.Slice(lines[i].Start, lines[i].Length);
+                                                               int size = buff.Length * 4 + 1;
+                                                               if (bytes.Length < size)
+                                                                       bytes = size > 512 ? new byte[size] : stackalloc byte[size];
+
+                                                               int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes);
+
+                                                               gr.SetSource (SelectionBackground);
+                                                               gr.Rectangle (selRect);
+                                                               if (encodedBytes < 0)
+                                                                       gr.Fill ();
+                                                               else {
+                                                                       gr.FillPreserve ();
+                                                                       gr.Save ();
+                                                                       gr.Clip ();
+                                                                       gr.SetSource (SelectionForeground);
+                                                                       gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent);
+                                                                       gr.ShowText (bytes.Slice (0, encodedBytes));
+                                                                       gr.Restore ();
+                                                               }
+                                                               Foreground.SetAsSource (IFace, gr);
+                                                       }
+                                               }
+
+                                               if (!multilineToken) {
+                                                       if (++tokPtr >= source.Tokens.Length)
+                                                               break;
+                                                       tok = source.Tokens[tokPtr];
+                                               }
+
+                                               x = 0;
+                                               pixX = 0;
+                       
+                                               y++;
+
+
+                                                       /*      } else if (tok2.Type == TokenType.Tabulation) {
+                                                                       int spaceRounding = x % tabSize;
+                                                                       int spaces = spaceRounding == 0 ?
+                                                                               tabSize * tok2.Length :
+                                                                               spaceRounding + tabSize * (tok2.Length - 1);
+                                                                       x += spaces;
+                                                                       pixX += spacePixelWidth * spaces;
+                                                                       continue;
+                                                               } else if (tok2.Type == TokenType.WhiteSpace) {
+                                                                       x += tok2.Length;
+                                                                       pixX += spacePixelWidth * tok2.Length;*/                                                                                                                                                                
+                                       }                                       
+                                       //gr.Translate (ScrollX, ScrollY);
+                               }
+                       } catch {
+                               
+                       }
+               }                       
+       }
+}
\ No newline at end of file
diff --git a/Samples/common/src/ImlParsing/Syntax.cs b/Samples/common/src/ImlParsing/Syntax.cs
new file mode 100644 (file)
index 0000000..fd410ea
--- /dev/null
@@ -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 (file)
index 0000000..46389df
--- /dev/null
@@ -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 (file)
index 0000000..f9d7bcc
--- /dev/null
@@ -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 (file)
index 0000000..f520dcf
--- /dev/null
@@ -0,0 +1,260 @@
+// Copyright (c) 2013-2021  Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Crow.Text;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Crow
+{
+       public class XmlSource {
+               public Token[] Tokens;
+               public readonly string Source;
+
+               SyntaxNode RootNode;
+
+               public XmlSource (string _source) {
+                       Source = _source;
+                       Tokenizer tokenizer = new Tokenizer();
+                       Tokens = tokenizer.Tokenize (Source);
+
+                       SyntaxAnalyser syntaxAnalyser = new SyntaxAnalyser (this);
+                       Stopwatch sw = Stopwatch.StartNew ();
+                       syntaxAnalyser.Process ();
+                       sw.Stop();
+
+                       foreach (Token t in Tokens)
+                               Console.WriteLine ($"{t,-40} {Source.AsSpan(t.Start, t.Length).ToString()}");
+                       syntaxAnalyser.Root.Dump();
+                       Console.WriteLine ($"Syntax Analysis done in {sw.ElapsedMilliseconds}(ms) {sw.ElapsedTicks}(ticks)");
+                       foreach (SyntaxException ex in syntaxAnalyser.Exceptions)
+                               Console.WriteLine ($"{ex}");
+
+                       RootNode = syntaxAnalyser.Root;
+               }
+
+               public Token FindTokenIncludingPosition (int pos) {
+                       if (Tokens == null || Tokens.Length == 0)
+                               return default;
+                       int idx = Array.BinarySearch (Tokens, 0, Tokens.Length, new  Token () {Start = pos});
+
+                       return idx == 0 ? Tokens[0] : idx < 0 ? Tokens[~idx - 1] : Tokens[idx - 1];
+               }
+               public SyntaxNode FindNodeIncludingPosition (int pos) {
+                       if (RootNode == null)
+                               return null;
+                       if (!RootNode.Contains (pos))
+                               return null;
+                       return RootNode.FindNodeIncludingPosition (pos);
+               }
+               public T FindNodeIncludingPosition<T> (int pos) {
+                       if (RootNode == null)
+                               return default;
+                       if (!RootNode.Contains (pos))
+                               return default;
+                       return RootNode.FindNodeIncludingPosition<T> (pos);
+               }
+
+               public class TokenizerException : Exception {
+                       public readonly int Position;
+                       public TokenizerException(string message, int position, Exception innerException = null)
+                                       : base (message, innerException) {
+                               Position = position;
+                       }
+               }
+
+               class Tokenizer {
+                       enum States
+                       {
+                               Init,//first statement of prolog, xmldecl should only apear in this state
+                               prolog,//misc before doctypedecl
+                               ProcessingInstrucitons,
+                               DTD,
+                               DTDObject,//doctype finished                            
+                               Xml,
+                               StartTag,//inside start tag
+                               Content,//after start tag with no closing slash
+                               EndTag
+                       }
+
+                       States curState = States.Init;
+                       List<Token> Toks = new List<Token>(100);
+
+                       public Tokenizer  () {}
+
+                       void skipWhiteSpaces (ref SpanCharReader reader) {
+                               while(!reader.EndOfSpan) {
+                                       switch (reader.Peak) {
+                                               case '\x85':
+                                               case '\x2028':
+                                               case '\xA':
+                                                       reader.Read();
+                                                       addTok (ref reader, TokenType.LineBreak);
+                                                       break;
+                                               case '\xD':
+                                                       reader.Read();
+                                                       if (reader.IsNextCharIn ('\xA', '\x85'))
+                                                               reader.Read();
+                                                       addTok (ref reader, TokenType.LineBreak);                                                                                                               
+                                                       break;
+                                               case '\x20':
+                                               case '\x9':
+                                                       char c = reader.Read();                                                                 
+                                                       while (reader.TryPeak (c))
+                                                               reader.Read();
+                                                       addTok (ref reader, c == '\x20' ? TokenType.WhiteSpace : TokenType.Tabulation);
+                                                       break;
+                                               default:
+                                                       return;
+                                       }
+                               }
+                       }
+                       bool readName (ref SpanCharReader reader) {
+                               if (reader.EndOfSpan)
+                                       return false;
+                               char c = reader.Peak;                                   
+                               if (char.IsLetter(c) || c == '_' || c == ':') {
+                                       reader.Advance ();
+                                       while (reader.TryPeak (ref c)) {                                                                        
+                                               if (!(char.IsLetterOrDigit(c) || c == '.' || c == '-' || c == '\xB7'))
+                                                       return true;
+                                               reader.Advance ();
+                                       }
+                                       return true;
+                               }
+                               return false;
+                       }
+
+                       int startOfTok;
+                       void addTok (ref SpanCharReader reader, TokenType tokType) {
+                               if (reader.CurrentPosition == startOfTok)
+                                       return;
+                               Toks.Add (new Token(tokType, startOfTok, reader.CurrentPosition));
+                               startOfTok = reader.CurrentPosition;
+                       }
+                       public Token[] Tokenize (string source) {
+                               SpanCharReader reader = new SpanCharReader(source);
+                               
+                               startOfTok = 0;
+                               int curObjectLevel = 0;
+                               curState = States.Init;
+
+                               while(!reader.EndOfSpan) {
+
+                                       skipWhiteSpaces (ref reader);
+
+                                       if (reader.EndOfSpan)
+                                               break;
+
+                                       switch (reader.Peak) {                          
+                                       case '<':
+                                               reader.Advance ();
+                                               if (reader.TryPeak ('?')) {                                                             
+                                                       reader.Advance ();
+                                                       addTok (ref reader, TokenType.PI_Start);
+                                                       readName (ref reader);
+                                                       addTok (ref reader, TokenType.PI_Target);
+                                                       curState = States.ProcessingInstrucitons;
+                                               } else if (reader.TryPeak ('!')) {
+                                                       reader.Advance ();
+                                                       if (reader.TryPeak ("--")) {
+                                                               reader.Advance (2);
+                                                               addTok (ref reader, TokenType.BlockCommentStart);                                                                               
+                                                               if (reader.TryReadUntil ("-->")) {
+                                                                       addTok (ref reader, TokenType.BlockComment);
+                                                                       reader.Advance (3);                                                                                     
+                                                                       addTok (ref reader, TokenType.BlockCommentEnd);
+                                                               } else if (reader.TryPeak ("-->")) {
+                                                                       reader.Advance (3);                                                                                     
+                                                                       addTok (ref reader, TokenType.BlockCommentEnd);
+                                                               }
+                                                       } else {
+                                                               addTok (ref reader, TokenType.DTDObjectOpen);
+                                                               if (readName (ref reader)) {
+                                                                       addTok (ref reader, TokenType.Keyword);
+                                                                       curState = States.DTDObject;
+                                                               }                                                               
+                                                       }                                                               
+                                               } else if (reader.TryPeak('/')) {
+                                                       reader.Advance ();
+                                                       addTok (ref reader, TokenType.EndElementOpen);
+                                                       if (readName (ref reader)) {
+                                                               addTok (ref reader, TokenType.ElementName);
+                                                               if (reader.TryPeak('>')) {
+                                                                       reader.Advance ();
+                                                                       addTok (ref reader, TokenType.ClosingSign);
+
+                                                                       if (--curObjectLevel > 0)
+                                                                               curState = States.Content;
+                                                                       else
+                                                                               curState = States.Xml;
+                                                               } 
+                                                       }
+                                               }else{                                                  
+                                                       addTok (ref reader, TokenType.ElementOpen);                                                     
+                                                       if (readName (ref reader)) {
+                                                               addTok (ref reader, TokenType.ElementName);                                                             
+                                                               curState = States.StartTag;
+                                                       }
+                                               }
+                                               break;
+                                       case '?':
+                                               reader.Advance ();
+                                               if (reader.TryPeak ('>')){
+                                                       reader.Advance ();
+                                                       addTok (ref reader, TokenType.PI_End);
+                                               }else
+                                                       addTok (ref reader, TokenType.Unknown);                                         
+                                               curState = States.prolog;                                               
+                                               break;
+                                       case '\'':
+                                       case '"':
+                                               char q = reader.Read();
+                                               if (reader.TryReadUntil (q))
+                                                       reader.Advance ();
+                                               addTok (ref reader, TokenType.AttributeValue);                                          
+                                               break;
+                                       case '=':
+                                               reader.Advance();
+                                               addTok (ref reader, TokenType.EqualSign);
+                                               break;
+                                       case '>':
+                                               reader.Advance();
+                                               addTok (ref reader, TokenType.ClosingSign);
+                                               curObjectLevel++;
+                                               curState = States.Content;
+                                               break;
+                                       case '/':
+                                               reader.Advance();
+                                               if (reader.TryRead ('>')) {
+                                                       addTok (ref reader, TokenType.EmptyElementClosing);
+                                                       if (--curObjectLevel > 0)
+                                                               curState = States.Content;
+                                                       else
+                                                               curState = States.Xml;
+                                               }else
+                                                       addTok (ref reader, TokenType.Unknown);
+                                               break;
+                                       default:
+                                               if (curState == States.StartTag || curState == States.ProcessingInstrucitons) {
+                                                       if (readName(ref reader))
+                                                               addTok (ref reader, TokenType.AttributeName);
+                                                       else if (reader.TryAdvance())
+                                                               addTok (ref reader, TokenType.Unknown);
+                                               } else {
+                                                       reader.TryReadUntil ('<');
+                                                       addTok (ref reader, TokenType.Content);
+                                               }
+                                               break;
+                                       }
+                               }
+
+                               return Toks.ToArray();
+                       }
+                       
+               }
+
+       }       
+}
\ No newline at end of file
diff --git a/Samples/common/src/SampleBase.cs b/Samples/common/src/SampleBase.cs
new file mode 100644 (file)
index 0000000..1e849d6
--- /dev/null
@@ -0,0 +1,383 @@
+using Crow;
+using Glfw;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.NetworkInformation;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Crow
+{
+       public class SampleBase : Interface
+       {
+#if NETCOREAPP
+               static IntPtr resolveUnmanaged(Assembly assembly, String libraryName)
+               {
+
+                       switch (libraryName)
+                       {
+                               case "glfw3":
+                                       return NativeLibrary.Load("glfw", assembly, null);
+                               case "rsvg-2.40":
+                                       return NativeLibrary.Load("rsvg-2", assembly, null);
+                       }
+                       Console.WriteLine($"[UNRESOLVE] {assembly} {libraryName}");
+                       return IntPtr.Zero;
+               }
+
+               static SampleBase()
+               {
+                       System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).ResolvingUnmanagedDll += resolveUnmanaged;
+               }
+#endif
+               public SampleBase(IntPtr hWin) : base(800, 600, hWin) { }
+               public SampleBase() : base(800, 600, true, true) { }
+               public SampleBase(int width, int height, bool startUIThread, bool createSurface) :
+                       base(width, height, startUIThread, createSurface)
+               {
+
+               }
+
+               public Version CrowVersion => Assembly.GetAssembly(typeof(Widget)).GetName().Version;
+
+               #region Test values for Binding
+               public CommandGroup Commands, AllCommands;
+               public CommandGroup EditCommands = new CommandGroup("Edit Commands",
+                       new Command("Edit command 1", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 1 clicked")),
+                       new Command("Edit command 2 a bit longer", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 2 clicked"), null, false),
+                       new Command("Edit command three", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "Edit comand 3 clicked"))
+               );
+               public CommandGroup FileCommands = new CommandGroup("File Commands",
+                       new Command("File command 1", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 1 clicked")),
+                       new Command("File command 2 a bit longer", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 2 clicked")),
+                       new Command("File command three", (sender) => MessageBox.ShowModal((sender as Widget).IFace, MessageBox.Type.Information, "File comand 3 clicked"))
+               );
+
+               void initCommands()
+               {
+                       Commands = new CommandGroup("commands msg boxes",
+                               new Command("Action 1", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 1 clicked")),
+                               new Command("Action two", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 2 clicked"), null, false),
+                               new Command("Action three", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 3 clicked"))
+                       );
+                       AllCommands = new CommandGroup ("All Commands",
+                               FileCommands,
+                               EditCommands,
+                               new CommandGroup ("Combined commands", FileCommands, EditCommands),
+                               new Command("Action A", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu A clicked"))
+                       );
+               }
+               public int intValue = 500;
+               VerticalAlignment currentVAlign;
+
+               DirectoryInfo curDir = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
+               public FileSystemInfo[] CurDirectory => curDir.GetFileSystemInfos();
+               public string MultilineText =
+                       $"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Sed non risus.\n\nSuspendisse lectus tortor,\nLorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Sed non risus.\n\nSuspendisse lectus tortor,";
+               //public string MultilineText = $"a\n";
+               TextAlignment textAlignment = TextAlignment.Left;
+               public TextAlignment TextAlignment
+               {
+                       get => textAlignment;
+                       set
+                       {
+                               if (textAlignment == value)
+                                       return;
+                               textAlignment = value;
+                               NotifyValueChanged(textAlignment);
+                       }
+               }
+
+               public int IntValue
+               {
+                       get => intValue;
+                       set
+                       {
+                               if (IntValue == value)
+                                       return;
+                               intValue = value;
+                               NotifyValueChanged("IntValue", intValue);
+                       }
+               }
+               public VerticalAlignment CurrentVAlign
+               {
+                       get => currentVAlign;
+                       set
+                       {
+                               if (currentVAlign == value)
+                                       return;
+                               currentVAlign = value;
+                               NotifyValueChanged("CurrentVAlign", currentVAlign);
+                       }
+               }
+               void onSpinnerValueChange(object sender, ValueChangeEventArgs e)
+               {
+                       if (e.MemberName != "Value")
+                               return;
+                       intValue = Convert.ToInt32(e.NewValue);
+               }
+               void change_alignment(object sender, EventArgs e)
+               {
+                       RadioButton rb = sender as RadioButton;
+                       if (rb == null)
+                               return;
+                       NotifyValueChanged("alignment", Enum.Parse(typeof(Alignment), rb.Caption));
+               }
+               public IEnumerable<String> List2 = new List<string>(new string[]
+                       {
+                               "string1",
+                               "string2",
+                               "string3",
+                               "string4",
+                               "string5",
+                               "string6",
+                               "string7",
+                               "string8",
+                               "string8",
+                               "string8",
+                               "string8",
+                               "string8",
+                               "string8",
+                               "string9"
+                       }
+               );
+               public IEnumerable<String> TestList2
+               {
+                       set
+                       {
+                               List2 = value;
+                               NotifyValueChanged("TestList2", testList);
+                       }
+                       get { return List2; }
+               }
+               public class TestClass
+               {
+                       public string Prop1 { get; set; }
+                       public string Prop2 { get; set; }
+
+                       public override string ToString()
+                               => $"{Prop1}, {Prop2}";
+
+                       public void OnValidateCommand(Object sender, ValidateEventArgs e)
+                       {
+                               Console.WriteLine($"Validation: {e.ValidatedText}");
+                       }
+               }
+               public class TestClassVC : IValueChange
+               {
+                       public event EventHandler<ValueChangeEventArgs> ValueChanged;
+                       public void NotifyValueChanged(object _value, [CallerMemberName] string caller = null)
+                               => ValueChanged.Raise(this, new ValueChangeEventArgs(caller, _value));
+                       string prop1, prop2;
+                       public string Prop1
+                       {
+                               get => prop1;
+                               set
+                               {
+                                       if (prop1 == value)
+                                               return;
+                                       prop1 = value;
+                                       NotifyValueChanged(prop1);
+                               }
+                       }
+                       public string Prop2
+                       {
+                               get => prop2;
+                               set
+                               {
+                                       if (prop2 == value)
+                                               return;
+                                       prop2 = value;
+                                       NotifyValueChanged(prop2);
+                               }
+
+
+
+                       }
+
+                       public override string ToString()
+                                       => $"{Prop1}, {Prop2}";
+
+               }
+               TestClass tcInstance = new TestClass() { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" };
+               TestClassVC tcVCInstance;// = new TestClassVC () { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" };
+               TestClass tcInstance1 = new TestClass() { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" };
+               TestClassVC tcVCInstance1 = new TestClassVC() { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" };
+               TestClass tcInstance2 = new TestClass() { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
+               TestClassVC tcVCInstance2 = new TestClassVC() { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
+
+               public TestClass TcInstance
+               {
+                       get => tcInstance;
+                       set
+                       {
+                               if (tcInstance == value)
+                                       return;
+                               tcInstance = value;
+                               NotifyValueChanged(tcInstance);
+                       }
+               }
+               public TestClassVC TcVCInstance
+               {
+                       get => tcVCInstance;
+                       set
+                       {
+                               if (tcVCInstance == value)
+                                       return;
+                               tcVCInstance = value;
+                               NotifyValueChanged(tcVCInstance);
+                       }
+               }
+
+               void tcInstance_ChangeProperties_MouseClick(object sender, MouseButtonEventArgs e)
+               {
+               }
+               public void tcInstance_ChangeInstance_MouseClick(object sender, MouseButtonEventArgs e)
+               {
+                       if (TcInstance == tcInstance1)
+                               TcInstance = tcInstance2;
+                       else
+                               TcInstance = tcInstance1;
+               }
+               void tcVCInstance_ChangeInstance_MouseClick(object sender, MouseButtonEventArgs e)
+               {
+                       TcVCInstance = new TestClassVC() { Prop1 = "prop1 value changed", Prop2 = "prop2 value changed" };
+               }
+
+               public IEnumerable<TestClass> List3 = new List<TestClass>(new TestClass[]
+                       {
+                               new TestClass { Prop1 = "string1", Prop2="prop2-1" },
+                               new TestClass { Prop1 = "string2", Prop2="prop2-2" },
+                               new TestClass { Prop1 = "string3", Prop2="prop2-3" },
+                       }
+               );
+               public IEnumerable<string> TestList3Props1 => List3.Select(sc => sc.Prop1).ToList();
+               public IEnumerable<TestClass> TestList3
+               {
+                       set
+                       {
+                               List3 = value;
+                               NotifyValueChanged("TestList3", testList);
+                       }
+                       get { return List3; }
+               }
+               string testString;
+               public string TestString
+               {
+                       get => testString;
+                       set
+                       {
+                               if (testString == value)
+                                       return;
+                               testString = value;
+
+                               NotifyValueChanged(testString);
+                       }
+               }               
+               string prop1;
+               public string TestList3SelProp1
+               {
+                       get => prop1;
+                       set
+                       {
+                               if (prop1 == value)
+                                       return;
+                               prop1 = value;
+
+                               NotifyValueChanged(prop1);
+                       }
+               }
+
+               string selString;
+               public string TestList2SelectedString
+               {
+                       get => selString;
+                       set
+                       {
+                               if (selString == value)
+                                       return;
+                               selString = value;
+                               NotifyValueChanged(selString);
+                       }
+               }
+
+
+               IList<Colors> testList = (IList<Colors>)FastEnumUtility.FastEnum.GetValues<Colors>().ToList();//.ColorDic.Values//.OrderBy(c=>c.Hue)
+                                                                                                                                                                                                         //.ThenBy(c=>c.Value).ThenBy(c=>c.Saturation)
+                                                                                                                                                                                                         //.ToList ();
+               public IList<Colors> TestList
+               {
+                       set
+                       {
+                               testList = value;
+                               NotifyValueChanged("TestList", testList);
+                       }
+                       get { return testList; }
+               }
+               void OnClear(object sender, MouseButtonEventArgs e) => TestList = null;
+               void OnLoadList(object sender, MouseButtonEventArgs e) => TestList = (IList<Colors>)FastEnumUtility.FastEnum.GetValues<Colors>().ToList();
+
+
+               string curSources = "";
+               public string CurSources
+               {
+                       get { return curSources; }
+                       set
+                       {
+                               if (value == curSources)
+                                       return;
+                               curSources = value;
+                               NotifyValueChanged(curSources);
+                       }
+               }
+               bool boolVal = true;
+               public bool BoolVal
+               {
+                       get { return boolVal; }
+                       set
+                       {
+                               if (boolVal == value)
+                                       return;
+                               boolVal = value;
+                               NotifyValueChanged(boolVal);
+                       }
+               }
+
+
+
+               #endregion
+
+               protected override void OnInitialized()
+               {
+                       initCommands();
+                       base.OnInitialized();
+               }
+
+               public override bool OnKeyDown(Key key)
+               {
+
+                       switch (key)
+                       {
+                               case Key.F5:
+                                       Load("Interfaces/Divers/testFileDialog.crow").DataSource = this;
+                                       return true;
+                               case Key.F6:
+                                       Load("Interfaces/Divers/0.crow").DataSource = this;
+                                       return true;
+                               case Key.F7:
+                                       Load("Interfaces/Divers/perfMeasures.crow").DataSource = this;
+                                       return true;
+                               case Key.F2:
+                                       if (IsKeyDown(Key.LeftShift))
+                                               DbgLogger.Reset();
+                                       DbgLogger.Save(this);
+                                       return true;
+                       }
+                       return base.OnKeyDown(key);
+               }
+       }
+}
\ No newline at end of file
index 49cb66052a844ae14b67761cf3ee91896aba9436..f84ad20a69e5e2f544bade64721060f6a351dc66 100644 (file)
@@ -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
index c2342c6b84f68723da475568f0168394ee91d607..6fd59f14211c6a6b73b93d3e2be315d33e6a1157 100644 (file)
@@ -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 (file)
index 0000000..633bd60
--- /dev/null
@@ -0,0 +1,4 @@
+<VerticalStack>
+       <Label Text='Hello World' Background='MediumSeaGreen' Margin='10'/>
+       <CheckBox I
+</VerticalStack>
\ No newline at end of file
index 39721689e81a952120d9c5890310d979fee64223..0d3c04e76d397e02d61df3fd7bdeaca59c135125 100644 (file)
@@ -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>
index ca7ddda4d0c27a0ce05aa5fa71cf361737be8c77..b7c0ce4bdca180fb5eb0567b792416500b12236b 100644 (file)
@@ -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 (file)
index 0000000..5a4349d
--- /dev/null
@@ -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 (executable)
index 0000000..2fd7108
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+sed -i.bak ':a;s/^\(\t*\) \{4\}/\1\t/;/^\t* \{4\}/ba' $1
+rm $1.bak