From c7a3277469965f45c4fbd22ba1eb6f73c67ba278 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Mon, 28 Aug 2017 11:25:05 +0200 Subject: [PATCH] make currentInterface public, other minor changes --- Crow.OpenTK.nuspec | 13 +- src/CompilerServices/CompilerServices.cs | 2 +- src/GraphicObjects/GraphicObject.cs | 2 +- src/GraphicObjects/Group.cs | 3 - src/GraphicObjects/ScrollingObject.cs | 12 +- src/GraphicObjects/ScrollingTextBox.cs | 749 ++++++++++++++++++++++- 6 files changed, 767 insertions(+), 14 deletions(-) diff --git a/Crow.OpenTK.nuspec b/Crow.OpenTK.nuspec index e8ccb23b..21570980 100644 --- a/Crow.OpenTK.nuspec +++ b/Crow.OpenTK.nuspec @@ -2,7 +2,7 @@ Crow.OpenTK - 0.5.2 + 0.5.4 C# Rapid Open Widget Toolkit JP Bruyere Grand Tetras Software @@ -18,10 +18,19 @@ Crow.OpenTK is the OpenTK ready version. CROW is a pure C# widget toolkit with templates, styles, compositing, and bindings. Crow.OpenTK is the OpenTK ready version. +Native libraries dependencies: + - cairo + - librsvg (and its dependencies (glib2, libxml, fontconfig, etc) + +Precompiled binaries of those libs are available at http://ftp.gnome.org/pub/GNOME/binaries/ + For more information, please visit https://jpbruyere.github.io/Crow/. - - debug. + - embed Cairo bindings in Crow. + - use of CairoRegion to handle clipping + - IDisposable for GraphicObject + - Border style Raised/Sunken Copyright 2013-2017 diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 5409aa5e..752655f8 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -76,7 +76,7 @@ namespace Crow internal static MethodInfo miDSChangeEmitHelper = typeof(Instantiator).GetMethod("dataSourceChangedEmitHelper", BindingFlags.Instance | BindingFlags.NonPublic); internal static MethodInfo miDSReverseBinding = typeof(Instantiator).GetMethod("dataSourceReverseBinding", BindingFlags.Static | BindingFlags.NonPublic); - internal static FieldInfo miSetCurIface = typeof(GraphicObject).GetField ("currentInterface", BindingFlags.NonPublic | BindingFlags.Instance); + internal static FieldInfo miSetCurIface = typeof(GraphicObject).GetField ("currentInterface", BindingFlags.Public | BindingFlags.Instance); internal static MethodInfo miFindByName = typeof (GraphicObject).GetMethod ("FindByName"); internal static MethodInfo miGetGObjItem = typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) }); internal static MethodInfo miLoadDefaultVals = typeof (GraphicObject).GetMethod ("loadDefaultValues"); diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index 93e812de..7b9f0c56 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -91,7 +91,7 @@ namespace Crow internal static ulong currentUid = 0; internal ulong uid = 0; - internal Interface currentInterface = null; + public Interface currentInterface = null; public Region Clipping; diff --git a/src/GraphicObjects/Group.cs b/src/GraphicObjects/Group.cs index e7d7d4d4..917087cc 100644 --- a/src/GraphicObjects/Group.cs +++ b/src/GraphicObjects/Group.cs @@ -74,9 +74,6 @@ namespace Crow { child.LayoutChanged -= OnChildLayoutChanges; - //check if HoverWidget is removed from Tree - - lock (children) Children.Remove(child); child.Dispose (); diff --git a/src/GraphicObjects/ScrollingObject.cs b/src/GraphicObjects/ScrollingObject.cs index 8b9bc58a..42acdbf9 100644 --- a/src/GraphicObjects/ScrollingObject.cs +++ b/src/GraphicObjects/ScrollingObject.cs @@ -43,6 +43,11 @@ namespace Crow int scrollX, scrollY, maxScrollX, maxScrollY, mouseWheelSpeed; + /// + /// if true, key stroke are handled in derrived class + /// + protected bool KeyEventsOverrides = false; + /// Horizontal Scrolling Position [XmlAttributeAttribute][DefaultValue(0)] public virtual int ScrollX { @@ -142,15 +147,18 @@ namespace Crow { base.onMouseWheel (sender, e); if (currentInterface.Keyboard.IsKeyDown (Key.ShiftLeft)) - ScrollY += e.Delta * MouseWheelSpeed; - else ScrollX += e.Delta * MouseWheelSpeed; + else + ScrollY -= e.Delta * MouseWheelSpeed; } /// Process scrolling with arrow keys, home and end keys. public override void onKeyDown (object sender, KeyboardKeyEventArgs e) { base.onKeyDown (sender, e); + if (KeyEventsOverrides) + return; + switch (e.Key) { case Key.Up: ScrollY--; diff --git a/src/GraphicObjects/ScrollingTextBox.cs b/src/GraphicObjects/ScrollingTextBox.cs index f7971657..c409d022 100644 --- a/src/GraphicObjects/ScrollingTextBox.cs +++ b/src/GraphicObjects/ScrollingTextBox.cs @@ -30,23 +30,762 @@ using System.ComponentModel; using System.Collections; using Cairo; using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Linq; namespace Crow { + /// + /// Scrolling text box optimized for monospace fonts, for coding + /// public class ScrollingTextBox : ScrollingObject { #region CTOR public ScrollingTextBox ():base() { - + KeyEventsOverrides = true; + } + #endregion + public event EventHandler TextChanged; + + public virtual void OnTextChanged(Object sender, EventArgs e) + { + TextChanged.Raise (this, e); } + + #region private and protected fields + string lineBreak = Interface.LineBreak; + int visibleLines = 1; + List lines; + string _text = "label"; + Color selBackground; + Color selForeground; + Point mouseLocalPos = 0;//mouse coord in widget space + int _currentCol; //0 based cursor position in string + int _currentLine; + Point _selBegin = -1; //selection start (row,column) + Point _selRelease = -1; //selection end (row,column) + + protected Rectangle rText; + protected FontExtents fe; + protected TextExtents te; #endregion - struct test { - public T a; + [XmlAttributeAttribute][DefaultValue("label")] + public string Text + { + get { + return lines == null ? + _text : lines.Aggregate((i, j) => i + Interface.LineBreak + j); + } + set + { + if (string.Equals (value, _text, StringComparison.Ordinal)) + return; + + _text = value; + + if (string.IsNullOrEmpty(_text)) + _text = ""; + + lines = getLines; + MaxScrollY = Math.Max (0, lines.Count - visibleLines); + + OnTextChanged (this, null); + RegisterForGraphicUpdate (); + } } - } -} + [XmlAttributeAttribute][DefaultValue("BlueGray")] + public virtual Color SelectionBackground { + get { return selBackground; } + set { + if (value == selBackground) + return; + selBackground = value; + NotifyValueChanged ("SelectionBackground", selBackground); + RegisterForRedraw (); + } + } + [XmlAttributeAttribute][DefaultValue("White")] + public virtual Color SelectionForeground { + get { return selForeground; } + set { + if (value == selForeground) + return; + selForeground = value; + NotifyValueChanged ("SelectionForeground", selForeground); + RegisterForRedraw (); + } + } + [XmlAttributeAttribute][DefaultValue(0)] + public int CurrentColumn{ + get { return _currentCol; } + set { + if (value == _currentCol) + return; + if (value < 0) + _currentCol = 0; + else if (value > lines [_currentLine].Length) + _currentCol = lines [_currentLine].Length; + else + _currentCol = value; + NotifyValueChanged ("CurrentColumn", _currentCol); + } + } + [XmlAttributeAttribute][DefaultValue(0)] + public int CurrentLine{ + get { return _currentLine; } + set { + if (value == _currentLine) + return; + if (value >= lines.Count) + _currentLine = lines.Count-1; + else if (value < 0) + _currentLine = 0; + else + _currentLine = value; + //force recheck of currentCol for bounding + int cc = _currentCol; + _currentCol = 0; + CurrentColumn = cc; + NotifyValueChanged ("CurrentLine", _currentLine); + + if (CurrentLine < ScrollY) + ScrollY = CurrentLine; + else if (CurrentLine >= ScrollY + visibleLines) + ScrollY = Math.Max (0, CurrentLine - visibleLines + 1); + } + } + [XmlIgnore]public Point CurrentPosition { + get { return new Point(CurrentColumn, CurrentLine); } + } + //TODO:using HasFocus for drawing selection cause SelBegin and Release binding not to work + /// + /// Selection begin position in char units (line, column) + /// + [XmlAttributeAttribute][DefaultValue("-1")] + public Point SelBegin { + get { return _selBegin; } + set { + if (value == _selBegin) + return; + _selBegin = value; + System.Diagnostics.Debug.WriteLine ("SelBegin=" + _selBegin); + NotifyValueChanged ("SelBegin", _selBegin); + NotifyValueChanged ("SelectedText", SelectedText); + } + } + /// + /// Selection release position in char units (line, column) + /// + [XmlAttributeAttribute][DefaultValue("-1")] + public Point SelRelease { + get { + return _selRelease; + } + set { + if (value == _selRelease) + return; + _selRelease = value; + NotifyValueChanged ("SelRelease", _selRelease); + NotifyValueChanged ("SelectedText", SelectedText); + } + } + /// + /// return char at CurrentLine, CurrentColumn + /// + [XmlIgnore]protected Char CurrentChar + { + get { + return lines [CurrentLine] [CurrentColumn]; + } + } + /// + /// ordered selection start and end positions in char units + /// + [XmlIgnore]protected Point selectionStart + { + get { + return SelRelease < 0 || SelBegin.Y < SelRelease.Y ? SelBegin : + SelBegin.Y > SelRelease.Y ? SelRelease : + SelBegin.X < SelRelease.X ? SelBegin : SelRelease; + } + } + [XmlIgnore]public Point selectionEnd + { + get { + return SelRelease < 0 || SelBegin.Y > SelRelease.Y ? SelBegin : + SelBegin.Y < SelRelease.Y ? SelRelease : + SelBegin.X > SelRelease.X ? SelBegin : SelRelease; + } + } + [XmlIgnore]public string SelectedText + { + get { + + if (SelRelease < 0 || SelBegin < 0) + return ""; + if (selectionStart.Y == selectionEnd.Y) + return lines [selectionStart.Y].Substring (selectionStart.X, selectionEnd.X - selectionStart.X); + string tmp = ""; + tmp = lines [selectionStart.Y].Substring (selectionStart.X); + for (int l = selectionStart.Y + 1; l < selectionEnd.Y; l++) { + tmp += Interface.LineBreak + lines [l]; + } + tmp += Interface.LineBreak + lines [selectionEnd.Y].Substring (0, selectionEnd.X); + return tmp; + } + } + [XmlIgnore]public bool selectionIsEmpty + { get { return SelRelease == SelBegin; } } + + List getLines { + get { + return Regex.Split (_text, "\r\n|\r|\n|\\\\n").ToList(); + } + } + /// + /// Moves cursor one char to the left. + /// + /// true if move succeed + public bool MoveLeft(){ + int tmp = _currentCol - 1; + if (tmp < 0) { + if (_currentLine == 0) + return false; + CurrentLine--; + CurrentColumn = int.MaxValue; + } else + CurrentColumn = tmp; + return true; + } + /// + /// Moves cursor one char to the right. + /// + /// true if move succeed + public bool MoveRight(){ + int tmp = _currentCol + 1; + if (tmp > lines [_currentLine].Length){ + if (CurrentLine == lines.Count - 1) + return false; + CurrentLine++; + CurrentColumn = 0; + } else + CurrentColumn = tmp; + return true; + } + public void GotoWordStart(){ + if (lines[CurrentLine].Length == 0) + return; + CurrentColumn--; + //skip white spaces + while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) + CurrentColumn--; + while (char.IsLetterOrDigit (lines [CurrentLine] [CurrentColumn]) && CurrentColumn > 0) + CurrentColumn--; + if (!char.IsLetterOrDigit (this.CurrentChar)) + CurrentColumn++; + } + public void GotoWordEnd(){ + //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++; + } + public void DeleteChar() + { + if (selectionIsEmpty) { + if (CurrentColumn == 0) { + if (CurrentLine == 0 && lines.Count == 1) + return; + CurrentLine--; + CurrentColumn = lines [CurrentLine].Length; + lines [CurrentLine] += lines [CurrentLine + 1]; + lines.RemoveAt (CurrentLine + 1); + OnTextChanged (this, null); + return; + } + CurrentColumn--; + lines [CurrentLine] = lines [CurrentLine].Remove (CurrentColumn, 1); + } else { + int linesToRemove = selectionEnd.Y - selectionStart.Y + 1; + int l = selectionStart.Y; + + if (linesToRemove > 0) { + lines [l] = lines [l].Remove (selectionStart.X, lines [l].Length - selectionStart.X) + + lines [selectionEnd.Y].Substring (selectionEnd.X, lines [selectionEnd.Y].Length - selectionEnd.X); + l++; + for (int c = 0; c < linesToRemove-1; c++) + lines.RemoveAt (l); + CurrentLine = selectionStart.Y; + CurrentColumn = selectionStart.X; + } else + lines [l] = lines [l].Remove (selectionStart.X, selectionEnd.X - selectionStart.X); + CurrentColumn = selectionStart.X; + SelBegin = -1; + SelRelease = -1; + } + OnTextChanged (this, null); + } + + #region GraphicObject overrides + public override Font Font { + get { return base.Font; } + set { + base.Font = value; + + using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) { + using (Context gr = new Context (img)) { + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + + fe = gr.FontExtents; + } + } + MaxScrollY = 0; + RegisterForGraphicUpdate (); + } + } + protected override int measureRawSize(LayoutingType lt) + { + if (lt == LayoutingType.Height) + return (int)Math.Ceiling(fe.Height * lines.Count) + Margin * 2; + + string txt = _text.Replace("\t", new String (' ', Interface.TabSize)); + + + int maxChar = 0; + foreach (string s in Regex.Split (txt, "\r\n|\r|\n|\\\\n")) { + if (maxChar < s.Length) + maxChar = s.Length; + } + return (int)(fe.MaxXAdvance * maxChar) + Margin * 2; + } + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (layoutType == LayoutingType.Height) + updateVisibleLines (); + } + 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; + + Rectangle cb = ClientRectangle; + + Foreground.SetAsSource (gr); + + bool selectionInProgress = false; + + #region draw text cursor + if (SelBegin != SelRelease) + selectionInProgress = true; + else if (HasFocus){ + gr.SetSourceColor(Color.Red); + gr.LineWidth = 2.0; + double cursorX = cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance; + gr.MoveTo (0.5 + cursorX, cb.Y + (CurrentLine - ScrollY) * fe.Height); + gr.LineTo (0.5 + cursorX, cb.Y + (CurrentLine + 1 - ScrollY) * fe.Height); + gr.Stroke(); + } + #endregion + + Foreground.SetAsSource (gr); + + for (int i = 0; i < visibleLines; i++) { + int curL = i + ScrollY; + if (curL >= lines.Count) + break; + string lstr = lines[curL]; + + gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i); + gr.ShowText (lstr); + gr.Fill (); + + if (selectionInProgress && curL >= selectionStart.Y && curL <= selectionEnd.Y) { + + double rLineX = cb.X, + rLineY = cb.Y + i * fe.Height, + rLineW = lstr.Length * fe.MaxXAdvance; + + System.Diagnostics.Debug.WriteLine ("sel start: " + selectionStart + " sel end: " + selectionEnd); + if (curL == selectionStart.Y) { + rLineX += (selectionStart.X - ScrollX) * fe.MaxXAdvance; + rLineW -= selectionStart.X * fe.MaxXAdvance; + } + if (curL == selectionEnd.Y) + rLineW -= (lstr.Length - selectionEnd.X) * fe.MaxXAdvance; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); + gr.SetSourceColor (SelectionBackground); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSourceColor (SelectionForeground); + gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + } + } + #endregion + + #region Mouse handling + void updatemouseLocalPos(Point mpos){ + Point mouseLocalPos = mpos - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft; + if (mouseLocalPos.X < 0) + CurrentColumn--; + else + CurrentColumn = ScrollX + (int)Math.Round (mouseLocalPos.X / fe.MaxXAdvance); + + if (mouseLocalPos.Y < 0) + CurrentLine--; + else + CurrentLine = ScrollY + (int)Math.Floor (mouseLocalPos.Y / fe.Height); + } + public override void onMouseEnter (object sender, MouseMoveEventArgs e) + { + base.onMouseEnter (sender, e); + currentInterface.MouseCursor = XCursor.Text; + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + base.onMouseLeave (sender, e); + currentInterface.MouseCursor = XCursor.Default; + } + protected override void onFocused (object sender, EventArgs e) + { + base.onFocused (sender, e); + + // SelBegin = new Point(0,0); + // SelRelease = new Point (lines.LastOrDefault ().Length, lines.Count-1); + RegisterForRedraw (); + } + protected override void onUnfocused (object sender, EventArgs e) + { + base.onUnfocused (sender, e); + + // SelBegin = -1; + // SelRelease = -1; + RegisterForRedraw (); + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + + if (!e.Mouse.IsButtonDown (MouseButton.Left)) + return; + if (!HasFocus || SelBegin < 0) + return; + + updatemouseLocalPos (e.Position); + SelRelease = CurrentPosition; + + RegisterForRedraw(); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + if (this.HasFocus){ + updatemouseLocalPos (e.Position); + SelBegin = SelRelease = CurrentPosition; + RegisterForRedraw();//TODO:should put it in properties + } + + //done at the end to set 'hasFocus' value after testing it + base.onMouseDown (sender, e); + } + public override void onMouseUp (object sender, MouseButtonEventArgs e) + { + base.onMouseUp (sender, e); + + if (SelBegin == SelRelease) + SelBegin = SelRelease = -1; + + updatemouseLocalPos (e.Position); + RegisterForRedraw (); + } + public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e) + { + base.onMouseDoubleClick (sender, e); + + GotoWordStart (); + SelBegin = CurrentPosition; + GotoWordEnd (); + SelRelease = CurrentPosition; + RegisterForRedraw (); + } + #endregion + + #region Keyboard handling + public override void onKeyDown (object sender, KeyboardKeyEventArgs e) + { + base.onKeyDown (sender, e); + + Key key = e.Key; + + switch (key) + { + case Key.Back: + if (CurrentPosition == 0) + return; + this.DeleteChar(); + break; + case Key.Clear: + break; + case Key.Delete: + if (selectionIsEmpty) { + if (!MoveRight ()) + return; + }else if (e.Shift) + currentInterface.Clipboard = this.SelectedText; + this.DeleteChar (); + break; + case Key.Enter: + case Key.KeypadEnter: + if (!selectionIsEmpty) + this.DeleteChar (); + this.InsertLineBreak (); + break; + case Key.Escape: + Text = ""; + CurrentColumn = 0; + SelRelease = -1; + break; + case Key.Home: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = new Point (CurrentColumn, CurrentLine); + if (e.Control) + CurrentLine = 0; + CurrentColumn = 0; + SelRelease = new Point (CurrentColumn, CurrentLine); + break; + } + SelRelease = -1; + if (e.Control) + CurrentLine = 0; + CurrentColumn = 0; + break; + case Key.End: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = CurrentPosition; + if (e.Control) + CurrentLine = int.MaxValue; + CurrentColumn = int.MaxValue; + SelRelease = CurrentPosition; + break; + } + SelRelease = -1; + if (e.Control) + CurrentLine = int.MaxValue; + CurrentColumn = int.MaxValue; + break; + case Key.Insert: + if (e.Shift) + this.Insert (currentInterface.Clipboard); + else if (e.Control && !selectionIsEmpty) + currentInterface.Clipboard = this.SelectedText; + break; + case Key.Left: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = new Point(CurrentColumn, CurrentLine); + if (e.Control) + GotoWordStart (); + else if (!MoveLeft ()) + return; + SelRelease = CurrentPosition; + break; + } + SelRelease = -1; + if (e.Control) + GotoWordStart (); + else + MoveLeft(); + break; + case Key.Right: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = CurrentPosition; + if (e.Control) + GotoWordEnd (); + else if (!MoveRight ()) + return; + SelRelease = CurrentPosition; + break; + } + SelRelease = -1; + if (e.Control) + GotoWordEnd (); + else + MoveRight (); + break; + case Key.Up: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = CurrentPosition; + CurrentLine--; + SelRelease = CurrentPosition; + break; + } + SelRelease = -1; + CurrentLine--; + break; + case Key.Down: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = CurrentPosition; + CurrentLine++; + SelRelease = CurrentPosition; + break; + } + SelRelease = -1; + CurrentLine++; + break; + case Key.Menu: + break; + case Key.NumLock: + break; + case Key.PageDown: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = CurrentPosition; + CurrentLine += visibleLines; + SelRelease = CurrentPosition; + break; + } + SelRelease = -1; + CurrentLine += visibleLines; + break; + case Key.PageUp: + if (e.Shift) { + if (selectionIsEmpty) + SelBegin = CurrentPosition; + CurrentLine -= visibleLines; + SelRelease = CurrentPosition; + break; + } + CurrentLine -= visibleLines; + break; + case Key.RWin: + break; + case Key.Tab: + this.Insert ("\t"); + break; + default: + break; + } + RegisterForGraphicUpdate(); + } + public override void onKeyPress (object sender, KeyPressEventArgs e) + { + base.onKeyPress (sender, e); + + this.Insert (e.KeyChar.ToString()); + + SelRelease = -1; + SelBegin = -1; //new Point(CurrentColumn, SelBegin.Y); + + RegisterForGraphicUpdate(); + } + #endregion + + + /// Compute x offset in cairo unit from text position + double GetXFromTextPointer(Context gr, Point pos) + { + try { + string l = lines [pos.Y].Substring (0, pos.X). + Replace ("\t", new String (' ', Interface.TabSize)); + return gr.TextExtents (l).XAdvance; + } catch{ + return -1; + } + } + + /// line break could be '\r' or '\n' or '\r\n' + string detectLineBreakKind(){ + string strLB = ""; + + if (string.IsNullOrEmpty(_text)) + return Interface.LineBreak; + int i = 0; + while ( i < _text.Length) { + if (_text [i] == '\r') { + strLB += '\r'; + i++; + } + if (i < _text.Length) { + if (_text [i] == '\r') + return "\r"; + if (_text [i] == '\n') + strLB += '\n'; + } + if (!string.IsNullOrEmpty (strLB)) + return strLB; + i++; + } + return Interface.LineBreak; + } + + void updateVisibleLines(){ + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height); + MaxScrollY = Math.Max (0, lines.Count - visibleLines); + + System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines); + System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY); + } + + + /// + /// Insert new string at caret position, should be sure no line break is inside. + /// + /// String. + protected void Insert(string str) + { + if (!selectionIsEmpty) + this.DeleteChar (); + string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray(); + lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, strLines[0]); + CurrentColumn += strLines[0].Length; + for (int i = 1; i < strLines.Length; i++) { + InsertLineBreak (); + lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, strLines[i]); + CurrentColumn += strLines[i].Length; + } + OnTextChanged (this, null); + RegisterForGraphicUpdate(); + } + + /// + /// Insert a line break. + /// + protected void InsertLineBreak() + { + lines.Insert(CurrentLine + 1, lines[CurrentLine].Substring(CurrentColumn)); + lines [CurrentLine] = lines [CurrentLine].Substring (0, CurrentColumn); + CurrentLine++; + CurrentColumn = 0; + OnTextChanged (this, null); + } + } +} \ No newline at end of file -- 2.47.3