Button, CheckBox, RadioButton, ComboBox, Expandable,
MessageBox, Popper, Slider, Spinner, TextBox {
- Focusable = "true";
+ //Focusable = "true";
Height = "Fit";
Background = "${ControlBackground}";
CornerRadius = "${ControlCornerRadius}";
ArrowBut {
MouseRepeat="true";
- Focusable="true";
+ //Focusable="true";
Foreground="Grey";
Background="Transparent";
-//
-// 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;
/// </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)
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 ();
}
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;
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)
HashCode.Combine (Line, VisualCharXPosition) :
HashCode.Combine (Line, Column);
}
- }
+
+ public override string ToString () => $"{Line}, {Column}";
+ }
}
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++];
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 ();
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);
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) {
selStart = selectionStart.Value;
selEnd = currentLoc.Value;
}
- }else
+ } else
IFace.forceTextCursor = true;
}
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 ();
}
{
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 ();
}
}
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
selectionStart = null;
}
+ protected virtual bool cancelLinePrint (int lineHeght, int y, int clientHeight) => false;
+
#endregion
/// <summary>
/// location column from
if (location == null)
return;
CharLocation loc = location.Value;
+ //Console.WriteLine ($"updateLocation: {loc} text:{_text.Length}");
if (loc.HasVisualX)
return;
TextLine ls = lines[loc.Line];
}
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;
-// 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
{
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 ();
+ }
+ }
}
updateCursorWidgetProps ();
}
- void HandleCursorContainerLayoutChanged (object sender, LayoutingEventArgs e)
+ protected virtual void HandleCursorContainerLayoutChanged (object sender, LayoutingEventArgs e)
{
computeCursorPosition ();
}
=> 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;
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 ();
using Crow.Text;
using Glfw;
using System;
+using System.ComponentModel;
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) {
base.onKeyDown (sender, e);
break;
}
+ autoAdjustScroll = true;
e.Handled = true;
}
public override void onKeyPress (object sender, KeyPressEventArgs e) {
#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 ();
}
}
<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>
<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"/>