]> O.S.I.I.S - jp/crow.git/commitdiff
LangVersion=>7.3, Scrolling TextBox, multi svg Part ids, remove some Focusable=true...
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Thu, 4 Feb 2021 14:40:39 +0000 (15:40 +0100)
committerj-p <jp_bruyere@hotmail.com>
Sat, 6 Feb 2021 19:28:02 +0000 (20:28 +0100)
Crow/Default.style
Crow/src/Fill/SvgPicture.cs
Crow/src/Interface.cs
Crow/src/Text/CharLocation.cs
Crow/src/Text/Encoding.cs
Crow/src/Widgets/Label.cs
Crow/src/Widgets/ScrollBar.cs
Crow/src/Widgets/Slider.cs
Crow/src/Widgets/TextBox.cs
Directory.Build.props
Samples/ShowCase/ui/showcase.crow

index db754c1b8d508e03bebe57178fb444a327d814bf..caf37bb4ab65142be1248e4fe0e03e7f9d6f11df 100644 (file)
@@ -24,7 +24,7 @@ MenuBackground = "Jet";
 
 Button, CheckBox, RadioButton, ComboBox, Expandable,
 MessageBox, Popper, Slider, Spinner, TextBox {
-       Focusable = "true";
+       //Focusable = "true";
        Height = "Fit";
        Background = "${ControlBackground}";
        CornerRadius = "${ControlCornerRadius}";
@@ -249,7 +249,7 @@ CheckBoxAlt {
 
 ArrowBut {
        MouseRepeat="true";     
-       Focusable="true";
+       //Focusable="true";
        
        Foreground="Grey";
        Background="Transparent";
index ee6ae1ea2308d1f4682546d58fb7d16456f70734..f461a54420114fb559fcdd1b3a88ace915796eba 100644 (file)
@@ -1,28 +1,6 @@
-//
-// SvgPicture.cs
+// Copyright (c) 2013-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
 //
-// Author:
-//       Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
 
 using System;
 using System.IO;
@@ -114,7 +92,7 @@ namespace Crow
                /// </summary>
                /// <param name="gr">drawing Backend context</param>
                /// <param name="rect">bounds of the target surface to paint</param>
-               /// <param name="subPart">limit rendering to svg part named 'subPart'</param>
+               /// <param name="subPart">limit rendering to this coma separated list of svg part identified with their svg 'id' attribute.</param>
                public override void Paint (Interface iFace, Context gr, Rectangle rect, string subPart = "")
                {
                        if (hSVG == null)
@@ -142,8 +120,11 @@ namespace Crow
 
                        if (string.IsNullOrEmpty (subPart))
                                hSVG.RenderCairo (gr);
-                       else
-                               hSVG.RenderCairoSub (gr, "#" + subPart);
+                       else {
+                               string[] parts = subPart.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+                foreach (string p in parts)                
+                                       hSVG.RenderCairoSub (gr, "#" + subPart);
+                       }
                        
                        gr.Restore ();                  
                }
index d22c58e9538ec4f4d20919a95464b204747917c8..895c2c4c73735d2368ed2f285ae9bf07923efa53 100644 (file)
@@ -899,15 +899,30 @@ namespace Crow
                                PerformanceMeasure.End (PerformanceMeasure.Kind.Drawing);
                        }
 
+                       drawTextCursor (ctx);
+                       
+                       DbgLogger.EndEvent (DbgEvtType.Drawing, true);
+               }
+               #endregion
+
+               #region Blinking text cursor
+               /// <summary>
+               /// Text cursor blinking frequency.
+               /// </summary>
+               public static long TEXT_CURSOR_BLINK_FREQUENCY = 400;
+               internal Rectangle? textCursor = null;//last printed cursor, used to clear it.
+               internal bool forceTextCursor = true;//when true, cursor is printed even if blinkingCursor.elapsed is not reached.
+               Stopwatch blinkingCursor = Stopwatch.StartNew ();
+               void drawTextCursor (Context ctx) {
                        if (forceTextCursor) {
-                               if (FocusedWidget is Label lab && lab.SelectionIsEmpty) {
+                               if (FocusedWidget is Label lab) {
                                        if (lab.DrawCursor (ctx, out Rectangle c)) {
                                                if (textCursor != null && c != textCursor.Value)
                                                        RegisterClip (textCursor.Value);
                                                textCursor = c;
                                                surf.Flush ();
-                    } else if (textCursor != null)
-                                               RegisterClip (textCursor.Value);                                        
+                                       } else if (textCursor != null)
+                                               RegisterClip (textCursor.Value);
                                }
                                blinkingCursor.Restart ();
                                forceTextCursor = false;
@@ -915,27 +930,18 @@ namespace Crow
                                RegisterClip (textCursor.Value);
                                textCursor = null;
                                blinkingCursor.Restart ();
-                       } else if (FocusedWidget is Label lab && lab.SelectionIsEmpty) {                                                                
+                       } else if (FocusedWidget is Label lab && lab.SelectionIsEmpty) {
                                if (blinkingCursor.ElapsedMilliseconds > TEXT_CURSOR_BLINK_FREQUENCY) {
                                        if (lab.DrawCursor (ctx, out Rectangle c)) {
                                                textCursor = c;
                                                surf.Flush ();
                                                blinkingCursor.Restart ();
                                        }
-                               }                               
-            }
-                       
-                       DbgLogger.EndEvent (DbgEvtType.Drawing, true);
+                               }
+                       }
                }
                #endregion
 
-               #region Blinking text cursor
-               public static long TEXT_CURSOR_BLINK_FREQUENCY = 300;
-               internal Rectangle? textCursor = null;
-               internal bool forceTextCursor = true;
-               Stopwatch blinkingCursor = Stopwatch.StartNew ();
-               #endregion
-
                #region GraphicTree handling
                /// <summary>Add widget to the Graphic tree of this interface and register it for layouting</summary>
                public Widget AddWidget(Widget g)
index f55dc45a63d0d9ce70a7031e679ad8332dd63db9..e5087222ad30a1b32bbd4490ac19ebd9b8255bf8 100644 (file)
@@ -31,5 +31,7 @@ namespace Crow.Text
                                HashCode.Combine (Line, VisualCharXPosition) :
                                HashCode.Combine (Line, Column);
                }
-       }
+
+               public override string ToString () => $"{Line}, {Column}";
+    }
 }
index f89ae0e4cf78a54dc28b3c5a48cba72e373ff904..7196e44c5548bcb76ff0ce9a40349833bcd542dc 100644 (file)
@@ -21,15 +21,6 @@ namespace Crow.Text
                         c++;
                         continue;
                     }
-                    /*unsafe {
-                        fixed (void* bp = &source.GetPinnableReference())
-                        fixed (byte* buffer = &buff.GetPinnableReference())
-                        {
-                            return Utf16toUtf8 ((byte*)bp, buffer, tabWidth);
-                            
-                        }
-                    }*/
-
 
                     if (source[c] < 0x80) { //1 byte
                         buff[encodedBytes++] = (byte)source[c++];
index 451e80164c0884a9ad72d609238f82e730774ed7..2011a7515ff4bc2ac83a06f530ac83f89bf1c225 100644 (file)
@@ -355,8 +355,31 @@ namespace Crow
                        System.Threading.Monitor.Exit (linesMutex);
                        return result;
                }
-        #region GraphicObject overrides
-        public override int measureRawSize(LayoutingType lt)
+               protected virtual void measureTextBounds (Context gr) {
+                       fe = gr.FontExtents;
+                       te = new TextExtents ();
+
+                       cachedTextSize.Height = (int)Math.Ceiling ((fe.Ascent + fe.Descent) * Math.Max (1, lines.Count));
+
+                       TextExtents tmp = default;
+                       int longestLine = 0;
+                       for (int i = 0; i < lines.Count; i++) {
+                               if (lines[i].LengthInPixel < 0) {
+                                       if (lines[i].Length == 0)
+                                               lines.UpdateLineLengthInPixel (i, 0);// (int)Math.Ceiling (fe.MaxXAdvance);
+                                       else {
+                                               gr.TextExtents (_text.GetLine (lines[i]), Interface.TAB_SIZE, out tmp);
+                                               lines.UpdateLineLengthInPixel (i, (int)Math.Ceiling (tmp.XAdvance));
+                                       }
+                               }
+                               if (lines[i].LengthInPixel > lines[longestLine].LengthInPixel)
+                                       longestLine = i;
+                       }
+                       cachedTextSize.Width = lines[longestLine].LengthInPixel;
+                       textMeasureIsUpToDate = true;
+               }
+               #region GraphicObject overrides
+               public override int measureRawSize(LayoutingType lt)
                {
                        if ((bool)lines?.IsEmpty)
                                getLines ();
@@ -370,48 +393,14 @@ namespace Crow
                                        gr.FontOptions = Interface.FontRenderingOptions;
                                        gr.Antialias = Interface.Antialias;
 
-                                       fe = gr.FontExtents;
-                                       te = new TextExtents ();
-
-                                       cachedTextSize.Height = (int)Math.Ceiling ((fe.Ascent+fe.Descent) * Math.Max (1, lines.Count));
-
-                                       TextExtents tmp = default;
-                                       int longestLine = 0;
-                                       for (int i = 0; i < lines.Count; i++) {                                                 
-                                               if (lines[i].LengthInPixel < 0) {
-                                                       if (lines[i].Length == 0)
-                                                               lines.UpdateLineLengthInPixel (i, 0);// (int)Math.Ceiling (fe.MaxXAdvance);
-                                                       else {
-                                                               gr.TextExtents (_text.GetLine (lines[i]), Interface.TAB_SIZE, out tmp);
-                                                               lines.UpdateLineLengthInPixel (i, (int)Math.Ceiling (tmp.XAdvance));
-                                                       }
-                                               }
-                                               if (lines[i].LengthInPixel > lines[longestLine].LengthInPixel)
-                                                       longestLine = i;
-                                       }
-                                       cachedTextSize.Width = lines[longestLine].LengthInPixel;
-                                       textMeasureIsUpToDate = true;                           
+                                       measureTextBounds (gr);
                                }
                        }
                        return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width);
                }
-               
-        protected override void onDraw (Context gr)
-               {
-                       base.onDraw (gr);
-
-                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
-                       gr.SetFontSize (Font.Size);
-                       gr.FontOptions = Interface.FontRenderingOptions;
-                       gr.Antialias = Interface.Antialias;                     
 
+               protected virtual void drawContent (Context gr) {
                        Rectangle cb = ClientRectangle;
-                       if (ClipToClientRect) {
-                               gr.Save ();
-                               CairoHelpers.CairoRectangle (gr, cb, CornerRadius);
-                               gr.Clip ();
-                       }
-
                        fe = gr.FontExtents;
                        int lineHeight = (int)(fe.Ascent + fe.Descent);
 
@@ -423,13 +412,13 @@ namespace Crow
                                if (selectionStart.HasValue) {
                                        updateLocation (gr, cb.Width, ref selectionStart);
                                        if (currentLoc.Value != selectionStart.Value)
-                                               selectionNotEmpty = true;                                       
+                                               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) {
+                                       } else if (currentLoc.Value.Line > selectionStart.Value.Line) {
                                                selStart = selectionStart.Value;
                                                selEnd = currentLoc.Value;
                                        } else if (currentLoc.Value.Column < selectionStart.Value.Column) {
@@ -439,7 +428,7 @@ namespace Crow
                                                selStart = selectionStart.Value;
                                                selEnd = currentLoc.Value;
                                        }
-                               }else
+                               } else
                                        IFace.forceTextCursor = true;
                        }
 
@@ -451,72 +440,104 @@ namespace Crow
                                int y = cb.Y;
 
                                for (int i = 0; i < lines.Count; i++) {
-                                       int encodedBytes = -1;
-                                       if (lines[i].Length > 0) {
-                                               int size = lines[i].Length * 4 + 1;
-                                               if (bytes.Length < size)
-                                                       bytes = size > 512 ? new byte[size] : stackalloc byte[size];
-
-                                               encodedBytes = Crow.Text.Encoding.ToUtf8 (_text.GetLine (lines[i]), bytes);
-                                               bytes[encodedBytes++] = 0;
-
-                                               if (lines[i].LengthInPixel < 0) {
-                                                       gr.TextExtents (bytes.Slice (0, encodedBytes), out extents);
-                                                       lines.UpdateLineLengthInPixel (i, (int)extents.XAdvance);
+                                       if (!cancelLinePrint (lineHeight, y, cb.Height)) {
+                                               int encodedBytes = -1;
+                                               if (lines[i].Length > 0) {
+                                                       int size = lines[i].Length * 4 + 1;
+                                                       if (bytes.Length < size)
+                                                               bytes = size > 512 ? new byte[size] : stackalloc byte[size];
+
+                                                       encodedBytes = Crow.Text.Encoding.ToUtf8 (_text.GetLine (lines[i]), bytes);
+                                                       bytes[encodedBytes++] = 0;
+
+                                                       if (lines[i].LengthInPixel < 0) {
+                                                               gr.TextExtents (bytes.Slice (0, encodedBytes), out extents);
+                                                               lines.UpdateLineLengthInPixel (i, (int)extents.XAdvance);
+                                                       }
                                                }
-                                       }
 
-                                       Rectangle lineRect = new Rectangle (
-                                               Width.IsFit && !Multiline ? cb.X : (int)getX (cb.Width, lines[i]) + cb.X,
-                                               y, lines[i].LengthInPixel, lineHeight);
+                                               Rectangle lineRect = new Rectangle (
+                                                       Width.IsFit && !Multiline ? cb.X : (int)getX (cb.Width, lines[i]) + cb.X,
+                                                       y, lines[i].LengthInPixel, lineHeight);
 
-                                       if (encodedBytes > 0) {
-                                               gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent);
-                                               gr.ShowText (bytes.Slice (0, encodedBytes));
-                                       }
+                                               if (encodedBytes > 0) {
+                                                       gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent);
+                                                       gr.ShowText (bytes.Slice (0, encodedBytes));
+                                               }
 
-                                       if (HasFocus && selectionNotEmpty) {
-                                               Rectangle selRect = lineRect;
-                                               if (_multiline) {
-                                                       if (i >= selStart.Line && i <= selEnd.Line) {
-                                                               if (selStart.Line == selEnd.Line) {
-                                                                       selRect.X = (int)selStart.VisualCharXPosition + cb.X;
-                                                                       selRect.Width = (int)(selEnd.VisualCharXPosition - selStart.VisualCharXPosition);
-                                                               } else if (i == selStart.Line) {
-                                                                       int newX = (int)selStart.VisualCharXPosition + cb.X;
-                                                                       selRect.Width -= (newX - selRect.X) - 10;
-                                                                       selRect.X = newX;
-                                                               } else if (i == selEnd.Line) {
-                                                                       selRect.Width = (int)selEnd.VisualCharXPosition - selRect.X;
-                                                               } else
-                                                                       selRect.Width += 10;
+                                               if (HasFocus && selectionNotEmpty) {
+                                                       Rectangle selRect = lineRect;
+                                                       if (_multiline) {
+                                                               if (i >= selStart.Line && i <= selEnd.Line) {
+                                                                       if (selStart.Line == selEnd.Line) {
+                                                                               selRect.X = (int)selStart.VisualCharXPosition + cb.X;
+                                                                               selRect.Width = (int)(selEnd.VisualCharXPosition - selStart.VisualCharXPosition);
+                                                                       } else if (i == selStart.Line) {
+                                                                               int newX = (int)selStart.VisualCharXPosition + cb.X;
+                                                                               selRect.Width -= (newX - selRect.X) - 10;
+                                                                               selRect.X = newX;
+                                                                       } else if (i == selEnd.Line) {
+                                                                               selRect.Width = (int)selEnd.VisualCharXPosition - selRect.X;
+                                                                       } else
+                                                                               selRect.Width += 10;
+                                                               } else {
+                                                                       y += lineHeight;
+                                                                       continue;
+                                                               }
                                                        } else {
-                                                               y += lineHeight;
-                                                               continue;
+                                                               selRect.X = (int)selStart.VisualCharXPosition + cb.X;
+                                                               selRect.Width = (int)(selEnd.VisualCharXPosition - selStart.VisualCharXPosition);
                                                        }
-                                               } else {
-                                                       selRect.X = (int)selStart.VisualCharXPosition + cb.X;
-                                                       selRect.Width = (int)(selEnd.VisualCharXPosition - selStart.VisualCharXPosition);
-                                               }
 
-                                               gr.SetSource (selBackground);
-                                               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 ();
+                                                       gr.SetSource (selBackground);
+                                                       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);
                                                }
-                                               Foreground.SetAsSource (IFace, gr);
                                        }
                                        y += lineHeight;
                                }
                        }
+               }
+               protected virtual void updateHoverLocation (Point mouseLocalPos) {
+                       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);
+               }
+
+               protected override void onDraw (Context gr)
+               {
+                       base.onDraw (gr);
+
+                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+                       gr.SetFontSize (Font.Size);
+                       gr.FontOptions = Interface.FontRenderingOptions;
+                       gr.Antialias = Interface.Antialias;
+                       
+                       if (!textMeasureIsUpToDate) {
+                               lock (linesMutex)
+                                       measureTextBounds (gr);
+            }
+                       
+                       if (ClipToClientRect) {
+                               gr.Save ();
+                               CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+                               gr.Clip ();
+                       }
+
+                       lock (linesMutex)
+                               drawContent (gr);
+                       
                        if (ClipToClientRect)
                                gr.Restore ();
                }
@@ -546,13 +567,10 @@ namespace Crow
                {
                        base.onMouseMove (sender, e);
 
-                       Point mouseLocalPos = e.Position - ScreenCoordinates (Slot).TopLeft - ClientRectangle.TopLeft;
-                       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);
+                       updateHoverLocation (e.Position - ScreenCoordinates (Slot).TopLeft - ClientRectangle.TopLeft);
 
-                       if (HasFocus && IFace.IsDown (Glfw.MouseButton.Left)) {
-                               currentLoc = hoverLoc;
+                       if (HasFocus && IFace.IsDown (MouseButton.Left)) {
+                               currentLoc = hoverLoc;                          
                                RegisterForRedraw ();                           
                        }
                }
@@ -579,24 +597,20 @@ namespace Crow
                public override void onMouseUp (object sender, MouseButtonEventArgs e)
                {
                        base.onMouseUp (sender, e);
-                       if (e.Button != Glfw.MouseButton.Left || !HasFocus || !selectionStart.HasValue)
+                       if (e.Button != MouseButton.Left || !HasFocus || !selectionStart.HasValue)
                                return;                 
-                       if (selectionStart.Value == currentLoc.Value) {
+                       if (selectionStart.Value == currentLoc.Value)
                                selectionStart = null;
-                               //RegisterForRedraw ();
-                       }                       
                }
                public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e)
                {
                        base.onMouseDoubleClick (sender, e);
-                       /*if (!(this.HasFocus || _selectable))
+                       if (e.Button != MouseButton.Left || !HasFocus)
                                return;
-                       
+
                        GotoWordStart ();
-                       SelBegin = CurrentPosition;
+                       selectionStart = currentLoc;                    
                        GotoWordEnd ();
-                       SelRelease = CurrentPosition;
-                       SelectionInProgress = false;*/
                        RegisterForRedraw ();
                }
                #endregion
@@ -668,6 +682,8 @@ namespace Crow
                                selectionStart = null;
                }
 
+               protected virtual bool cancelLinePrint (int lineHeght, int y, int clientHeight) => false;
+
                #endregion
                /// <summary>
                /// location column from 
@@ -677,6 +693,7 @@ namespace Crow
                        if (location == null)
                                return;
                        CharLocation loc = location.Value;
+                       //Console.WriteLine ($"updateLocation: {loc} text:{_text.Length}");
                        if (loc.HasVisualX)
                                return;
                        TextLine ls = lines[loc.Line];
@@ -726,28 +743,33 @@ namespace Crow
                }
 
                RectangleD? textCursor = null;
-               internal bool DrawCursor (Context ctx, out Rectangle rect) {
-                       if (!currentLoc.Value.HasVisualX) {
+               internal virtual RectangleD? computeTextCursor (Rectangle cursor) {
+                       Rectangle cb = ClientRectangle ;
+                       if (cursor.X > cb.Width && cursor.Y > cb.Height)                                
+                               return null;
+                       return cursor;
+               }
+               internal virtual bool DrawCursor (Context ctx, out Rectangle rect) {
+                       if (!currentLoc.Value.HasVisualX) {                             
                                ctx.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
                                ctx.SetFontSize (Font.Size);
                                ctx.FontOptions = Interface.FontRenderingOptions;
                                ctx.Antialias = Interface.Antialias;
-
-                               updateLocation (ctx, ClientRectangle.Width, ref currentLoc);
+                               lock(linesMutex)
+                                       updateLocation (ctx, ClientRectangle.Width, ref currentLoc);
                                textCursor = null;
             }
                        
-                       //if (textCursor == null) {
-                               Rectangle cb = ClientRectangle;
-                       if (currentLoc.Value.VisualCharXPosition > cb.Width) {
+
+                       int lineHeight = (int)(fe.Ascent + fe.Descent);
+                       textCursor = computeTextCursor (new RectangleD (currentLoc.Value.VisualCharXPosition, currentLoc.Value.Line * lineHeight, 1.0, lineHeight));
+
+                       if (textCursor == null) {
                                rect = default;
                                return false;
                        }
-                       int lineHeight = (int)(fe.Ascent + fe.Descent);
-                       textCursor = new RectangleD (currentLoc.Value.VisualCharXPosition + cb.X + Slot.X,
-                                               cb.Y + Slot.Y + currentLoc.Value.Line * lineHeight, 1.0, lineHeight);
                        //}
-                       Rectangle c = ScreenCoordinates (textCursor.Value);
+                       Rectangle c = ScreenCoordinates (textCursor.Value + Slot.Position + ClientRectangle.Position);
                        ctx.ResetClip ();
                        ctx.SetSource (cursorColor);                    
                        ctx.LineWidth = 1.0;
index ffbd023b7c2301a44dbdb46473d8b79f31f1d625..fd3dd2448e89de593feccf181b8aae46afca95d3 100644 (file)
@@ -1,6 +1,8 @@
-// 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)
+using System;
+using System.ComponentModel;
 
 namespace Crow
 {
@@ -14,5 +16,33 @@ namespace Crow
                public ScrollBar(Interface iface, string style = null) : base (iface, style) { }
                #endregion
 
-       }
+               double cursorRatio;
+               /// <summary>
+               /// Ratio of CusorSize / CursorContainerSize, -1 if not in use.
+               /// </summary>
+               [DefaultValue(-1.0)]
+               public double CursorRatio {
+                       get => cursorRatio;
+                       set {
+                               if (cursorRatio == value)
+                                       return;
+                               cursorRatio = value;
+                               updateCursor ();
+                       }
+        }
+
+               void updateCursor () {
+                       if (cursorRatio < 0)
+                               return;
+                       Rectangle r = cursor.Parent.ClientRectangle;
+                       if (Orientation == Orientation.Horizontal)
+                               CursorSize = (int)(cursorRatio * r.Width);
+                       else
+                               CursorSize = (int)(cursorRatio * r.Height);
+               }
+        protected override void HandleCursorContainerLayoutChanged (object sender, LayoutingEventArgs e) {
+            base.HandleCursorContainerLayoutChanged (sender, e);
+                       updateCursor ();
+        }
+    }
 }
index 222b06043e9d73b736bf58942fca749a8284f307..586180779088138f8ef0f171e5c7f3c670977c2e 100644 (file)
@@ -30,7 +30,7 @@ namespace Crow
                        updateCursorWidgetProps ();
                }
 
-               void HandleCursorContainerLayoutChanged (object sender, LayoutingEventArgs e)
+               protected virtual void HandleCursorContainerLayoutChanged (object sender, LayoutingEventArgs e)
                {
                        computeCursorPosition ();
                }
@@ -43,10 +43,10 @@ namespace Crow
                        => RegisterForLayouting (LayoutingType.ArrangeChildren);
 
                #region private fields
-               int cursorSize;
+               int cursorSize, minimumCursorSize;
                Orientation _orientation;
                bool holdCursor = false;
-               Widget cursor;
+               protected Widget cursor;
                #endregion
 
                protected double unity;
@@ -76,13 +76,26 @@ namespace Crow
                                updateCursorWidgetProps ();
                        }
                }
+               [DefaultValue (20)]
+               public virtual int MinimuCursorSize {
+                       get => minimumCursorSize;
+                       set {
+                               if (minimumCursorSize == value)
+                                       return;
+                               minimumCursorSize = value;
+                               CursorSize = cursorSize;//force recheck
+                               NotifyValueChangedAuto (minimumCursorSize);
+                       }
+               }
+
                [DefaultValue (20)]
                public virtual int CursorSize {
-                       get { return cursorSize; }
+                       get => cursorSize;
                        set {
-                               if (cursorSize == value)
+                               int newCursorSize = Math.Max (MinimuCursorSize, value);
+                               if (cursorSize == newCursorSize)
                                        return;
-                               cursorSize = value;
+                               cursorSize = newCursorSize;
                                RegisterForGraphicUpdate ();
                                NotifyValueChangedAuto (cursorSize);
                                updateCursorWidgetProps ();
index 0c060c60ccb2bd5373791f42a57af5357903e2ec..95d7958db5be8ef0e227641956c7003ac4a6fcc2 100644 (file)
@@ -6,6 +6,7 @@ using Crow.Cairo;
 using Crow.Text;
 using Glfw;
 using System;
+using System.ComponentModel;
 
 namespace Crow
 {
@@ -25,6 +26,211 @@ namespace Crow
             Validate.Raise (this, e);
         }
 
+               #region Scrolling
+               int scrollX, scrollY, maxScrollX, maxScrollY, mouseWheelSpeed;
+
+               /// <summary>
+               /// 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
+               /// <summary> Horizontal Scrolling Position </summary>
+               [DefaultValue (0)]
+               public virtual int ScrollX {
+                       get { return scrollX; }
+                       set {
+                //cancelAdjustScroll = true;
+
+                               if (scrollX == value)
+                                       return;
+
+                               int newS = value;
+                               if (newS < 0)
+                                       newS = 0;
+                               else if (newS > maxScrollX)
+                                       newS = maxScrollX;
+
+                               if (newS == scrollX)
+                                       return;
+
+                               scrollX = newS;
+
+                               NotifyValueChangedAuto (scrollX);
+                               RegisterForGraphicUpdate ();
+                       }
+               }
+               /// <summary> Vertical Scrolling Position </summary>
+               [DefaultValue (0)]
+               public virtual int ScrollY {
+                       get { return scrollY; }
+                       set {
+                //cancelAdjustScroll = true;
+
+                if (scrollY == value)
+                                       return;
+
+                               int newS = value;
+                               if (newS < 0)
+                                       newS = 0;
+                               else if (newS > maxScrollY)
+                                       newS = maxScrollY;
+
+                               if (newS == scrollY)
+                                       return;
+
+                               scrollY = newS;
+
+                               NotifyValueChangedAuto (scrollY);
+                               RegisterForGraphicUpdate ();
+                       }
+               }
+               /// <summary> Horizontal Scrolling maximum value </summary>
+               [DefaultValue (0)]
+               public virtual int MaxScrollX {
+                       get { return maxScrollX; }
+                       set {
+                               if (maxScrollX == value)
+                                       return;
+
+                               maxScrollX = Math.Max (0, value);
+
+                               if (scrollX > maxScrollX)
+                                       ScrollX = maxScrollX;
+
+                               NotifyValueChangedAuto (maxScrollX);
+                               //RegisterForGraphicUpdate ();
+                       }
+               }
+               /// <summary> Vertical Scrolling maximum value </summary>
+               [DefaultValue (0)]
+               public virtual int MaxScrollY {
+                       get { return maxScrollY; }
+                       set {
+                               if (maxScrollY == value)
+                                       return;
+
+                               maxScrollY = Math.Max (0, value);
+
+                               if (scrollY > maxScrollY)
+                                       ScrollY = maxScrollY;
+
+                               NotifyValueChangedAuto (maxScrollY);
+                               //RegisterForGraphicUpdate ();
+                       }
+               }
+               /// <summary> Mouse Wheel Scrolling multiplier </summary>
+               [DefaultValue (5)]
+               public virtual int MouseWheelSpeed {
+                       get { return mouseWheelSpeed; }
+                       set {
+                               if (mouseWheelSpeed == value)
+                                       return;
+
+                               mouseWheelSpeed = value;
+
+                               NotifyValueChangedAuto (mouseWheelSpeed);
+                       }
+               }
+
+               /// <summary> Process scrolling vertically, or if shift is down, vertically </summary>
+               public override void onMouseWheel (object sender, MouseWheelEventArgs e) {
+                       base.onMouseWheel (sender, e);
+                       if (IFace.Shift)
+                               ScrollX += e.Delta * MouseWheelSpeed;
+                       else
+                               ScrollY -= e.Delta * MouseWheelSpeed;
+               }
+        public override void onMouseMove (object sender, MouseMoveEventArgs e) {
+            base.onMouseMove (sender, e);
+            if (!HasFocus || !IFace.IsDown (MouseButton.Left))
+                return;
+            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;
+
+            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;
+
+        }
+        #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 (int lineHeght, int 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;            
+        }
+        /*internal override bool DrawCursor (Context ctx, out Rectangle rect) {            
+            ctx.Translate (-scrollX, -scrollY);
+            bool result = base.DrawCursor (ctx, out rect);
+            ctx.Translate (scrollX, scrollY);
+            return result;
+        }*/
+
+        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) {
@@ -84,6 +290,7 @@ namespace Crow
                 base.onKeyDown (sender, e);
                 break;
             }
+            autoAdjustScroll = true;
             e.Handled = true;
         }
         public override void onKeyPress (object sender, KeyPressEventArgs e) {
@@ -102,25 +309,28 @@ namespace Crow
         #endregion
 
         void update (TextChange change) {
-            Span<char> tmp = stackalloc char[Text.Length + (change.ChangedText.Length - change.Length)];
-            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));
-
             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 ();
                 getLines ();
                 selectionStart = null;
+
+                currentLoc = lines.GetLocation (change.Start + change.ChangedText.Length);
+                textMeasureIsUpToDate = false;
+                IFace.forceTextCursor = true;
             }
 
-            currentLoc = lines.GetLocation (change.Start + change.ChangedText.Length);
-            textMeasureIsUpToDate = false;
-            IFace.forceTextCursor = true;
 
             NotifyValueChanged ("Text", Text);
             OnTextChanged (this, new TextChangeEventArgs (change));
-
+            
             RegisterForGraphicUpdate ();
         }
     }
index c9936924d91a4532e7b82061bde7773050482f6e..fa3323c8c401060b918c8b3018df3dbc54e974e2 100644 (file)
@@ -5,7 +5,7 @@
                <IntermediateOutputPath>$(SolutionDir)build\obj\$(Configuration)\</IntermediateOutputPath>
                <License>MIT</License>
                <Authors>Jean-Philippe Bruyère</Authors>           
-               <LangVersion>7.2</LangVersion>
+               <LangVersion>7.3</LangVersion>
                
                <CrowVersion>0.9.3</CrowVersion>
                <CrowPackageVersion>$(CrowVersion)-beta</CrowPackageVersion>
index 2447ab67de1f937c3c33dba9147d2530432fdcf4..6924fb540851a0f7cb7dce1b07c8ca65a379d44a 100644 (file)
                                <Button Style="IcoButton" Command="{CMDSaveAs}" />
                        </HorizontalStack>
                        <HorizontalStack>
-                               <Scroller Name="scroller1" Background="White"
-                                               Margin="2" ScrollY="{../scrollbar1.Value}">
-                                       <TextBox VerticalAlignment="Top" 
-                                               Text="{²Source}" Multiline="true"
-                                               Font="consolas, 12"/>
-                               </Scroller>
-                               <!--<ScrollBar Name="scrollbar1" Value="{../scroller1.ScrollY}"
-                                       LargeIncrement="{../scroller1.PageHeight}" SmallIncrement="30"
-                                       CursorSize="{../scroller1.ChildHeightRatio}" Maximum="{../scroller1.MaxScrollY}" />-->
+                               <TextBox Name="tb" Text="{²Source}" Multiline="true" Font="consolas, 12" Focusable="true" Height="Stretched" Width="Stretched" />
+                               <ScrollBar Value="{²../tb.ScrollY}"
+                                               LargeIncrement="{../tb.PageHeight}" SmallIncrement="1"
+                                               CursorRatio="{../tb.ChildHeightRatio}" Maximum="{../tb.MaxScrollY}" />
                        </HorizontalStack>
+                       <ScrollBar Style="HScrollBar" Value="{²../tb.ScrollX}"
+                                       LargeIncrement="{../tb.PageWidth}" SmallIncrement="1"
+                                       CursorRatio="{../tb.ChildWidthRatio}" Maximum="{../tb.MaxScrollX}" />
                        <Label Text="{CurrentFile}" Width="Stretched"/>
                        <Label Visible="{ShowError}" Text="{ErrorMessage}" Background="Red" Foreground="White" Width="Stretched" Margin="2"
                                Multiline="true"/>