From: Jean-Philippe Bruyère Date: Tue, 2 Feb 2021 02:21:11 +0000 (+0100) Subject: blinking text cursor, goto word start/end in label, shift/ctrl/alt right, paint ref... X-Git-Tag: v0.9.5-beta~85 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=f84da1455960c0f17e0058007bc3f921915e8c81;p=jp%2Fcrow.git blinking text cursor, goto word start/end in label, shift/ctrl/alt right, paint ref ctx: ref removed --- diff --git a/Crow/Default.style b/Crow/Default.style index 104b303c..0077a382 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -82,6 +82,7 @@ OldLabel { TextBox { Background = "White"; Foreground = "Black"; + CursorColor = "Black"; Selectable = "True"; //Text = "TextBox"; Margin = "1"; diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index b17a8dc5..e1b9bc77 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -253,9 +253,12 @@ namespace Crow public virtual void Quit () => Glfw3.SetWindowShouldClose (hWin, 1); - public bool Shift => Glfw3.GetKey(hWin, Glfw.Key.LeftShift) == InputAction.Press; - public bool Ctrl => Glfw3.GetKey (hWin, Glfw.Key.LeftControl) == InputAction.Press; - public bool Alt => Glfw3.GetKey (hWin, Glfw.Key.LeftAlt) == InputAction.Press; + public bool Shift => Glfw3.GetKey(hWin, Key.LeftShift) == InputAction.Press || + Glfw3.GetKey (hWin, Key.RightShift) == InputAction.Press; + public bool Ctrl => Glfw3.GetKey (hWin, Key.LeftControl) == InputAction.Press || + Glfw3.GetKey (hWin, Key.RightControl) == InputAction.Press; + public bool Alt => Glfw3.GetKey (hWin, Key.LeftAlt) == InputAction.Press || + Glfw3.GetKey (hWin, Key.RightAlt) == InputAction.Press; #region IDisposable Support private bool disposedValue = false; // To detect redundant calls @@ -820,7 +823,7 @@ namespace Crow continue; ctx.Save (); - p.Paint (ref ctx); + p.Paint (ctx); ctx.Restore (); } @@ -842,7 +845,7 @@ namespace Crow #if DEBUG_CLIP_RECTANGLE ctx.LineWidth = 1; - ctx.SetSourceColor(Color.Magenta.AdjustAlpha (0.5)); + ctx.SetSource(1,0,0,0.5); for (int i = 0; i < clipping.NumRectangles; i++) ctx.Rectangle(clipping.GetRectangle(i)); ctx.Stroke (); @@ -867,11 +870,36 @@ namespace Crow PerformanceMeasure.End (PerformanceMeasure.Kind.Drawing); } + + if (forceTextCursor) { + if (FocusedWidget is Label lab && lab.SelectionIsEmpty) { + Rectangle c = lab.DrawCursor (ctx); + if (textCursor != null && c != textCursor.Value) + RegisterClip (textCursor.Value); + textCursor = c; + surf.Flush (); + } + blinkingCursor.Restart (); + forceTextCursor = false; + } else if (textCursor != null && blinkingCursor.ElapsedMilliseconds > blinkFrequency) { + RegisterClip (textCursor.Value); + textCursor = null; + blinkingCursor.Restart (); + } else if (FocusedWidget is Label lab && lab.SelectionIsEmpty) { + if (blinkingCursor.ElapsedMilliseconds > blinkFrequency) { + textCursor = lab.DrawCursor (ctx); + surf.Flush (); + blinkingCursor.Restart (); + } + } DbgLogger.EndEvent (DbgEvtType.Drawing, true); } #endregion - + const long blinkFrequency = 300; + internal Rectangle? textCursor = null; + internal bool forceTextCursor = true; + Stopwatch blinkingCursor = Stopwatch.StartNew (); #region GraphicTree handling /// Add widget to the Graphic tree of this interface and register it for layouting public Widget AddWidget(Widget g) diff --git a/Crow/src/Text/CharLocation.cs b/Crow/src/Text/CharLocation.cs index 8a8b9784..f55dc45a 100644 --- a/Crow/src/Text/CharLocation.cs +++ b/Crow/src/Text/CharLocation.cs @@ -15,7 +15,7 @@ namespace Crow.Text VisualCharXPosition = visualX; } public bool HasVisualX => Column >= 0 && VisualCharXPosition >= 0; - + public void ResetVisualX () => VisualCharXPosition = -1; public static bool operator == (CharLocation a, CharLocation b) => a.Equals (b); public static bool operator != (CharLocation a, CharLocation b) diff --git a/Crow/src/Widgets/Border.cs b/Crow/src/Widgets/Border.cs index d9de7f6a..b308f00f 100644 --- a/Crow/src/Widgets/Border.cs +++ b/Crow/src/Widgets/Border.cs @@ -111,7 +111,7 @@ namespace Crow } if (child != null) - child.Paint (ref gr); + child.Paint (gr); gr.Restore (); } void drawborder2(Context gr){ diff --git a/Crow/src/Widgets/DockStack.cs b/Crow/src/Widgets/DockStack.cs index 728a35e1..3b8594ea 100644 --- a/Crow/src/Widgets/DockStack.cs +++ b/Crow/src/Widgets/DockStack.cs @@ -186,7 +186,7 @@ namespace Crow childrenRWLock.EnterReadLock (); foreach (Widget g in Children) - g.Paint (ref gr); + g.Paint (gr); childrenRWLock.ExitReadLock (); diff --git a/Crow/src/Widgets/Group.cs b/Crow/src/Widgets/Group.cs index b2b438cd..a861eaf2 100644 --- a/Crow/src/Widgets/Group.cs +++ b/Crow/src/Widgets/Group.cs @@ -297,7 +297,7 @@ namespace Crow childrenRWLock.EnterReadLock (); for (int i = 0; i < Children.Count; i++) - Children[i].Paint (ref gr); + Children[i].Paint (gr); childrenRWLock.ExitReadLock (); gr.Restore (); @@ -331,7 +331,7 @@ namespace Crow continue; if (Clipping.Contains (c.Slot + ClientRectangle.Position) == RegionOverlap.Out) continue; - c.Paint (ref gr); + c.Paint (gr); } childrenRWLock.ExitReadLock (); diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index 701a1283..e7fe430f 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -36,6 +36,7 @@ namespace Crow protected string _text; TextAlignment _textAlignment; bool _multiline; + Color cursorColor; Color selBackground; Color selForeground; @@ -60,7 +61,22 @@ namespace Crow /// /// Background color for selected text inside this label. /// - [DefaultValue("SteelBlue")] + [DefaultValue ("White")] + public virtual Color CursorColor { + get { return cursorColor; } + set { + if (cursorColor == value) + return; + cursorColor = value; + NotifyValueChangedAuto (cursorColor); + RegisterForRedraw (); + } + } + + /// + /// Background color for selected text inside this label. + /// + [DefaultValue ("SteelBlue")] public virtual Color SelectionBackground { get { return selBackground; } set { @@ -96,7 +112,9 @@ namespace Crow if (value == _textAlignment) return; _textAlignment = value; - resetLocationXs (); + + currentLoc?.ResetVisualX (); + selectionStart?.ResetVisualX (); RegisterForRedraw (); NotifyValueChangedAuto (_textAlignment); @@ -209,29 +227,25 @@ namespace Crow return true; } -/* + public void GotoWordStart(){ - CurrentColumn--; + int pos = lines.GetAbsolutePosition (currentLoc.Value); //skip white spaces - while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) - CurrentColumn--; - while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) - CurrentColumn--; - if (!char.IsLetterOrDigit (this.CurrentChar)) - CurrentColumn++; + while (pos > 0 && !char.IsLetterOrDigit (_text[pos-1])) + pos--; + while (pos > 0 && char.IsLetterOrDigit (_text[pos-1])) + pos--; + currentLoc = lines.GetLocation (pos); } public void GotoWordEnd(){ + int pos = lines.GetAbsolutePosition (currentLoc.Value); //skip white spaces - if (CurrentColumn >= lines [CurrentLine].Length - 1) - return; - while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < lines [CurrentLine].Length-1) - CurrentColumn++; - while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < lines [CurrentLine].Length-1) - CurrentColumn++; - if (char.IsLetterOrDigit (this.CurrentChar)) - CurrentColumn++; - } - */ + while (pos < _text.Length -1 && !char.IsLetterOrDigit (_text[pos])) + pos++; + while (pos < _text.Length - 1 && char.IsLetterOrDigit (_text[pos])) + pos++; + currentLoc = lines.GetLocation (pos); + } protected void detectLineBreak () { if (!_multiline) @@ -301,29 +315,10 @@ namespace Crow else lines.Add (new TextLine (_text.Length, _text.Length, _text.Length)); } - void resetLocationXs () { - if (currentLoc.HasValue) { - CharLocation cl = currentLoc.Value; - cl.VisualCharXPosition = -1; - currentLoc = cl; - } - if (selectionStart.HasValue) { - CharLocation cl = selectionStart.Value; - cl.VisualCharXPosition = -1; - selectionStart = cl; - } - } - double getX (int clientWidth, TextLine ls) { - switch (TextAlignment) { - case TextAlignment.Right: - return clientWidth - ls.LengthInPixel; - case TextAlignment.Center: - return clientWidth / 2 - ls.LengthInPixel / 2; - } - return 0; - } - - protected TextSpan Selection { + /// + /// Current Selected text span. + /// + public TextSpan Selection { get { CharLocation selStart = currentLoc.Value, selEnd = currentLoc.Value; if (selectionStart.HasValue) { @@ -344,6 +339,13 @@ namespace Crow return new TextSpan (lines.GetAbsolutePosition (selStart), lines.GetAbsolutePosition (selEnd)); } } + public string SelectedText { + get { + TextSpan selection = Selection; + return selection.IsEmpty ? "" : Text.AsSpan (selection.Start, selection.Length).ToString (); + } + } + public bool SelectionIsEmpty => selectionStart.HasValue ? Selection.IsEmpty : true; public override bool UpdateLayout (LayoutingType layoutType) { if ((LayoutingType.Sizing | layoutType) != LayoutingType.None) { if (!System.Threading.Monitor.TryEnter (linesMutex)) @@ -393,6 +395,7 @@ namespace Crow } return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width); } + protected override void onDraw (Context gr) { base.onDraw (gr); @@ -417,8 +420,7 @@ 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) { @@ -434,13 +436,8 @@ namespace Crow selStart = selectionStart.Value; selEnd = currentLoc.Value; } - } else { - Foreground.SetAsSource (IFace, gr); - gr.LineWidth = 1.0; - gr.MoveTo (0.5 + currentLoc.Value.VisualCharXPosition + cb.X, cb.Y + currentLoc.Value.Line * lineHeight); - gr.LineTo (0.5 + currentLoc.Value.VisualCharXPosition + cb.X, cb.Y + (currentLoc.Value.Line + 1) * lineHeight); - gr.Stroke (); - } + }else + IFace.forceTextCursor = true; } if (string.IsNullOrEmpty (_text)) { @@ -573,7 +570,7 @@ namespace Crow else if (!selectionStart.HasValue) selectionStart = currentLoc; currentLoc = hoverLoc; - + IFace.forceTextCursor = true; RegisterForRedraw (); e.Handled = true; } @@ -629,26 +626,24 @@ namespace Crow currentLoc = new CharLocation (l, lines[l].Length); RegisterForRedraw (); break; -/* case Key.Insert: - if (IFace.Shift) - this.Insert (IFace.Clipboard); - else if (IFace.Ctrl && !selectionIsEmpty) - IFace.Clipboard = this.SelectedText; - break;*/ + case Key.Insert: + if (IFace.Ctrl && !SelectionIsEmpty) + IFace.Clipboard = SelectedText; + break; case Key.Left: checkShift (); - /*if (IFace.Ctrl) + if (IFace.Ctrl) GotoWordStart (); - else*/ - MoveLeft (); + else + MoveLeft (); RegisterForRedraw (); break; case Key.Right: checkShift (); - /*if (IFace.Ctrl) - GotoWordStart (); - else*/ - MoveRight (); + if (IFace.Ctrl) + GotoWordEnd (); + else + MoveRight (); RegisterForRedraw (); break; case Key.Up: @@ -665,8 +660,8 @@ namespace Crow base.onKeyDown (sender, e); return; } + IFace.forceTextCursor = true; e.Handled = true; - } void checkShift () { if (IFace.Shift) { @@ -723,7 +718,42 @@ namespace Crow loc.VisualCharXPosition = cPos; location = loc; } + double getX (int clientWidth, TextLine ls) { + switch (TextAlignment) { + case TextAlignment.Right: + return clientWidth - ls.LengthInPixel; + case TextAlignment.Center: + return clientWidth / 2 - ls.LengthInPixel / 2; + } + return 0; + } + RectangleD? textCursor = null; + internal Rectangle DrawCursor (Context ctx) { + 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); + textCursor = null; + } + //if (textCursor == null) { + Rectangle cb = ClientRectangle; + 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); + ctx.ResetClip (); + ctx.SetSource (cursorColor); + ctx.LineWidth = 1.0; + ctx.MoveTo (0.5 + c.X, c.Y); + ctx.LineTo (0.5 + c.X, c.Bottom); + ctx.Stroke (); + return c; + } - } + } } diff --git a/Crow/src/Widgets/PrivateContainer.cs b/Crow/src/Widgets/PrivateContainer.cs index ebca78e8..293323e4 100644 --- a/Crow/src/Widgets/PrivateContainer.cs +++ b/Crow/src/Widgets/PrivateContainer.cs @@ -194,7 +194,7 @@ namespace Crow if (child != null) { if (child.Visible) - child.Paint (ref gr); + child.Paint (gr); } gr.Restore (); } diff --git a/Crow/src/Widgets/Scroller.cs b/Crow/src/Widgets/Scroller.cs index a706d692..9065a51f 100644 --- a/Crow/src/Widgets/Scroller.cs +++ b/Crow/src/Widgets/Scroller.cs @@ -197,7 +197,7 @@ namespace Crow gr.Translate (-ScrollX, -ScrollY); if (child != null) - child.Paint (ref gr); + child.Paint (gr); gr.Restore (); } diff --git a/Crow/src/Widgets/TabView.cs b/Crow/src/Widgets/TabView.cs index 9a88d72f..b7c963d1 100644 --- a/Crow/src/Widgets/TabView.cs +++ b/Crow/src/Widgets/TabView.cs @@ -260,17 +260,17 @@ namespace Crow int i = 0; while (i < selTabViewIdx) { - tabItms [i].Paint (ref gr); + tabItms [i].Paint (gr); i++; } i = tabItms.Length - 1; while (i > selTabViewIdx) { - tabItms [i].Paint (ref gr); + tabItms [i].Paint (gr); i--; } if (selTabViewIdx >= 0) - tabItms [selTabViewIdx].Paint (ref gr); + tabItms [selTabViewIdx].Paint (gr); childrenRWLock.ExitReadLock (); diff --git a/Crow/src/Widgets/TemplatedControl.cs b/Crow/src/Widgets/TemplatedControl.cs index 74c71fe2..9f60e0a8 100644 --- a/Crow/src/Widgets/TemplatedControl.cs +++ b/Crow/src/Widgets/TemplatedControl.cs @@ -98,7 +98,7 @@ namespace Crow } if (child != null) - child.Paint (ref gr); + child.Paint (gr); gr.Restore (); } #endregion diff --git a/Crow/src/Widgets/TextBox.cs b/Crow/src/Widgets/TextBox.cs index a677795f..0c060c60 100644 --- a/Crow/src/Widgets/TextBox.cs +++ b/Crow/src/Widgets/TextBox.cs @@ -53,7 +53,7 @@ namespace Crow update (new TextChange (selection.Start, 1, "")); } else { if (IFace.Shift) - IFace.Clipboard = Text.AsSpan (selection.Start, selection.Length).ToString (); + IFace.Clipboard = SelectedText; update (new TextChange (selection.Start, selection.Length, "")); } break; @@ -61,7 +61,7 @@ namespace Crow if (IFace.Shift) update (new TextChange (selection.Start, selection.Length, IFace.Clipboard)); else if (IFace.Ctrl && !selection.IsEmpty) - IFace.Clipboard = Text.AsSpan (selection.Start, selection.Length).ToString (); + IFace.Clipboard = SelectedText; break; case Key.KeypadEnter: case Key.Enter: @@ -116,6 +116,7 @@ namespace Crow currentLoc = lines.GetLocation (change.Start + change.ChangedText.Length); textMeasureIsUpToDate = false; + IFace.forceTextCursor = true; NotifyValueChanged ("Text", Text); OnTextChanged (this, new TextChangeEventArgs (change)); diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 05e258ef..255e1756 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -1796,7 +1796,7 @@ namespace Crow } /// Chained painting routine on the parent context of the actual cached version /// of the widget - public virtual void Paint (ref Context ctx) + public virtual void Paint (Context ctx) { DbgLogger.StartEvent (DbgEvtType.GOPaint, this); diff --git a/Samples/ShowCase/ShowCase.cs b/Samples/ShowCase/ShowCase.cs index b504648e..db91b1cf 100644 --- a/Samples/ShowCase/ShowCase.cs +++ b/Samples/ShowCase/ShowCase.cs @@ -244,10 +244,10 @@ namespace ShowCase g.DataSource = this; } } catch (InstantiatorException itorex) { - Console.WriteLine (itorex); + //Console.WriteLine (itorex); showError (itorex.InnerException); } catch (Exception ex) { - Console.WriteLine (ex); + //Console.WriteLine (ex); showError (ex); } } diff --git a/Samples/common/ui/Interfaces/Divers/testFocus.crow b/Samples/common/ui/Interfaces/Divers/testFocus.crow index d8f914ee..2e2ebe7e 100644 --- a/Samples/common/ui/Interfaces/Divers/testFocus.crow +++ b/Samples/common/ui/Interfaces/Divers/testFocus.crow @@ -40,7 +40,7 @@