From: Jean-Philippe Bruyère Date: Thu, 27 Feb 2025 11:01:40 +0000 (+0100) Subject: editors Y scrolling and folding optimizations X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=c660ebe06335e60e36990edee0c281864a183dc1;p=jp%2Fcrowedit.git editors Y scrolling and folding optimizations --- diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index d0b2ec4..24f9068 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -36,14 +36,14 @@ namespace CrowEditBase if (!IsParsed || pos == 0 || Tokens.Length == 0) return default; int idx = Tokens.BinarySearch(new Token () {Start = pos}); - return idx == 0 ? 0 : idx < 0 ? ~idx - 1 : idx - 1; + return idx == 0 ? 0 : idx < 0 ? ~idx - 1 : idx; } /// /// if outermost is true, return oldest ancestor exept root node, useful for folding. /// - /*public SyntaxNode FindNodeIncludingPosition (int pos, bool outerMost = false) { + public SyntaxNode FindNodeIncludingPosition (int pos, bool outerMost = false) { if (root == null) return null; if (!root.Contains (pos)) @@ -55,7 +55,7 @@ namespace CrowEditBase } return sn; } - public T FindNodeIncludingPosition (int pos) { + /*public T FindNodeIncludingPosition (int pos) { if (root == null) return default; if (!root.Contains (pos)) diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNode.cs index c981973..07d5363 100644 --- a/CrowEditBase/src/Compiler/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNode.cs @@ -37,7 +37,7 @@ namespace CrowEditBase public int StartLine { get; private set; } public virtual int LineCount => lineCount; public virtual bool IsComplete => TokenCount.HasValue; - public virtual bool IsFoldable => IsComplete && Parent.StartLine != StartLine && lineCount > 1; + public virtual bool IsFoldable => IsComplete && !(Parent != Root && Parent.StartLine == StartLine) && lineCount > 1; public virtual SyntaxRootNode Root => Parent.Root; public virtual void UnfoldToTheTop () { isFolded = false; @@ -83,13 +83,16 @@ namespace CrowEditBase } public virtual SyntaxNode NextSiblingOrParentsNextSibling => NextSibling ?? Parent.NextSiblingOrParentsNextSibling; - public IEnumerable FoldableNodes { + public IEnumerable VisibleFoldableNodes { get { - if (IsFoldable) + if (IsFoldable) { yield return this; - foreach (SyntaxNode n in Children) { - foreach (SyntaxNode folds in n.FoldableNodes) - yield return folds; + } + if (!isFolded) { + foreach (SyntaxNode n in Children) { + foreach (SyntaxNode folds in n.VisibleFoldableNodes) + yield return folds; + } } } } @@ -211,5 +214,10 @@ namespace CrowEditBase } public bool IsSimilar (SyntaxNode other) => this.GetType() == other?.GetType(); - } + + public class CompareOnStartLine : IComparer + { + public int Compare(SyntaxNode x, SyntaxNode y) => x.StartLine - y.StartLine; + } + } } \ No newline at end of file diff --git a/CrowEditBase/src/Editor.cs b/CrowEditBase/src/Editor.cs index d3b7381..4767d0a 100644 --- a/CrowEditBase/src/Editor.cs +++ b/CrowEditBase/src/Editor.cs @@ -308,7 +308,7 @@ namespace Crow /// /// on screen visible line bounded by the client rectangle /// - protected int visibleLines => (int)((double)ClientRectangle.Height / lineHeight); + protected int visibleLines => (int)Math.Ceiling(ClientRectangle.Height / lineHeight); /// /// total line count /// @@ -333,6 +333,7 @@ namespace Crow else { gr.TextExtents (document.GetText (l), App.TabulationSize, out tmp); l.LengthInPixel = (int)Math.Ceiling (tmp.XAdvance); + document.SetLine(i, l); } } if (l.LengthInPixel > document.GetLine (longestLine).LengthInPixel) @@ -347,9 +348,7 @@ namespace Crow document.ExitReadLock (); } } - protected virtual void drawContent (IContext gr) { - gr.Translate (-ScrollX, -ScrollY); - + protected virtual void drawContent (IContext gr) { Rectangle cb = ClientRectangle; fe = gr.FontExtents; double lineHeight = fe.Ascent + fe.Descent; @@ -357,6 +356,8 @@ namespace Crow CharLocation selStart = default, selEnd = default; bool selectionNotEmpty = false; + gr.Translate (-ScrollX, 0); + document.EnterReadLock(); try { @@ -388,91 +389,97 @@ namespace Crow //} if (document.Length > 0) { + int skippedLines = (int)Math.Floor(ScrollY / lineHeight); + int curLine = skippedLines; + double y = -(ScrollY % lineHeight); + Foreground?.SetAsSource (IFace, gr); TextExtents extents; Span bytes = stackalloc byte[128]; - double y = 0; - - for (int i = 0; i < document.LinesCount; i++) { - if (!cancelLinePrint (lineHeight, y, cb.Height)) { - int encodedBytes = -1; - TextLine l = document.GetLine (i); - if (l.Length > 0) { - int size = l.Length * 4 + 1; - if (bytes.Length < size) - bytes = new byte[size]; - - encodedBytes = document.GetText (l).ToUtf8 (bytes); - bytes[encodedBytes++] = 0; - - if (l.LengthInPixel < 0) { - gr.TextExtents (bytes.Slice (0, encodedBytes), out extents); - l.LengthInPixel = (int)extents.XAdvance; - } + + while (curLine < document.LinesCount && curLine - skippedLines < visibleLines) { + + int encodedBytes = -1; + TextLine l = document.GetLine (curLine); + if (l.Length > 0) { + int size = l.Length * 4 + 1; + if (bytes.Length < size) + bytes = new byte[size]; + + encodedBytes = document.GetText (l).ToUtf8 (bytes); + bytes[encodedBytes++] = 0; + + if (l.LengthInPixel < 0) { + gr.TextExtents (bytes.Slice (0, encodedBytes), out extents); + l.LengthInPixel = (int)extents.XAdvance; + document.SetLine(curLine, l); } + } - RectangleD lineRect = new RectangleD ( - (int)cb.X, - y + cb.Top, l.LengthInPixel, lineHeight); + RectangleD lineRect = new RectangleD ( + (int)cb.X, + y + cb.Top, l.LengthInPixel, lineHeight); - if (encodedBytes > 0) { + if (encodedBytes > 0) { + gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent); + gr.ShowText (bytes.Slice (0, encodedBytes)); + } + /********** DEBUG TextLineCollection ************* + gr.SetSource (Colors.Red); + gr.SetFontSize (9); + gr.MoveTo (700, lineRect.Y + fe.Ascent); + gr.ShowText ($"({lines[i].Start}, {lines[i].End}, {lines[i].EndIncludingLineBreak})"); + gr.SetFontSize (Font.Size); + Foreground.SetAsSource (IFace, gr); + ********** DEBUG TextLineCollection *************/ + + if (selectionNotEmpty) { + RectangleD selRect = lineRect; + + if (curLine >= selStart.Line && curLine <= selEnd.Line) { + if (selStart.Line == selEnd.Line) { + selRect.X = selStart.VisualCharXPosition + cb.X; + selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition; + } else if (curLine == selStart.Line) { + double newX = selStart.VisualCharXPosition + cb.X; + selRect.Width -= (newX - selRect.X) - 10.0; + selRect.X = newX; + } else if (curLine == selEnd.Line) { + selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X; + } else + selRect.Width += 10.0; + } else { + y += lineHeight; + curLine++; + continue; + } + + 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 (); } - /********** DEBUG TextLineCollection ************* - gr.SetSource (Colors.Red); - gr.SetFontSize (9); - gr.MoveTo (700, lineRect.Y + fe.Ascent); - gr.ShowText ($"({lines[i].Start}, {lines[i].End}, {lines[i].EndIncludingLineBreak})"); - gr.SetFontSize (Font.Size); Foreground.SetAsSource (IFace, gr); - ********** DEBUG TextLineCollection *************/ - - if (selectionNotEmpty) { - RectangleD selRect = lineRect; - - if (i >= selStart.Line && i <= selEnd.Line) { - if (selStart.Line == selEnd.Line) { - selRect.X = selStart.VisualCharXPosition + cb.X; - selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition; - } else if (i == selStart.Line) { - double newX = selStart.VisualCharXPosition + cb.X; - selRect.Width -= (newX - selRect.X) - 10.0; - selRect.X = newX; - } else if (i == selEnd.Line) { - selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X; - } else - selRect.Width += 10.0; - } else { - y += lineHeight; - continue; - } - - 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); - } } + y += lineHeight; + curLine++; } } } finally { document.ExitReadLock (); } - gr.Translate (ScrollX, ScrollY); + gr.Translate (ScrollX, 0); } protected int getLineIndexFromMousePosition (Point mouseLocalPos) => (int)Math.Min (Math.Max (0, Math.Floor ((mouseLocalPos.Y + ScrollY)/ lineHeight)), visualLineCount - 1); @@ -490,7 +497,6 @@ namespace Crow updateLocation (ref newLoc); hoverLoc = newLoc; } - protected virtual bool cancelLinePrint (double lineHeght, double y, int clientHeight) => false; RectangleD? textCursor = null; public virtual bool DrawCursor (IContext ctx, out Rectangle rect) { diff --git a/CrowEditBase/src/SourceEditor.cs b/CrowEditBase/src/SourceEditor.cs index 9e87ebd..ce94796 100644 --- a/CrowEditBase/src/SourceEditor.cs +++ b/CrowEditBase/src/SourceEditor.cs @@ -11,6 +11,7 @@ using CrowEditBase; using static CrowEditBase.CrowEditBase; using System.Collections.Generic; using System.Linq; +using System.Collections.Frozen; namespace Crow { @@ -225,6 +226,7 @@ namespace Crow if (mouseIsInMargin) { Rectangle rFold = new Rectangle (leftMargin - foldMargin - leftMarginRightGap, (int)(lineHeight * getVisualLine(hoverLoc.Value.Line) + lineHeight / 2.0 - foldSize / 2.0) - ScrollY, foldSize, foldSize); + rFold.Inflate(2); mouseIsInFoldRect = rFold.ContainsOrIsEqual (mLoc); RegisterForRedraw(); return; @@ -357,7 +359,7 @@ namespace Crow SyntaxNode getFoldStartingAt (int line) { if (!(Document is SourceDocument doc)) return null; - IEnumerable folds = doc.Root.FoldableNodes; + IEnumerable folds = doc.Root.VisibleFoldableNodes; if (folds == null) return null; return folds.FirstOrDefault (n => n.StartLine == line); @@ -367,7 +369,7 @@ namespace Crow return null; doc.EnterReadLock(); try { - IEnumerable folds = doc.Root.FoldableNodes; + IEnumerable folds = doc.Root.VisibleFoldableNodes; if (folds == null) return null; return folds.LastOrDefault (n => n.StartLine <= line && n.EndLine >= line); @@ -382,7 +384,7 @@ namespace Crow doc.EnterReadLock(); try { int foldedLines = 0; - IEnumerator foldsEnum = doc.Root.FoldableNodes.GetEnumerator(); + IEnumerator foldsEnum = doc.Root.VisibleFoldableNodes.GetEnumerator(); bool notEndOfFolds = foldsEnum.MoveNext(); while (notEndOfFolds && foldsEnum.Current.StartLine < absoluteLine) { if (foldsEnum.Current.isFolded) { @@ -407,7 +409,7 @@ namespace Crow doc.EnterReadLock(); try { int foldedLines = 0; - IEnumerator nodeEnum = doc.Root.FoldableNodes.GetEnumerator (); + IEnumerator nodeEnum = doc.Root.VisibleFoldableNodes.GetEnumerator (); if (!nodeEnum.MoveNext()) return 0; @@ -499,7 +501,7 @@ namespace Crow } protected override void drawContent (IContext gr) { - if (!(Document is SourceDocument doc && doc.Root != null)) { + if (!(Document is SourceDocument doc)) { base.drawContent (gr); return; } @@ -513,21 +515,23 @@ namespace Crow Color marginBG = App.MarginBackground; Color marginFG = Colors.Ivory; Rectangle cb = ClientRectangle; - RectangleD marginRect = new RectangleD (cb.X + ScrollX, cb.Y, leftMargin - leftMarginRightGap, cb.Height); + RectangleD marginRect = new RectangleD (cb.X, cb.Y, leftMargin - leftMarginRightGap, cb.Height); gr.SetSource (marginBG); gr.Rectangle (marginRect); gr.Fill(); - if (!doc.IsParsed) { base.drawContent (gr); return; } + gr.Translate (-ScrollX, 0); + double lineNumWidth = gr.TextExtents (Document.LinesCount.ToString()).Width; marginRect.Height = lineHeight; + marginRect.Left += ScrollX; cb.Left += leftMargin; CharLocation selStart = default, selEnd = default; @@ -599,51 +603,46 @@ namespace Crow //double spacePixelWidth = gr.TextExtents (" ").XAdvance; - int x = 0; double pixX = cb.Left, pixY = cb.Top; Foreground.SetAsSource (IFace, gr); - gr.Translate (-ScrollX, -ScrollY); ReadOnlySpan sourceBytes = doc.source; Span bytes = stackalloc byte[128]; TextExtents extents; - int tokPtr = 0; - Token tok = doc.Tokens[tokPtr]; + + ReadOnlySpan buff = sourceBytes; - SyntaxNode curNode = null; + + int printedLines = 0; + int curLine = (int)Math.Floor(ScrollY / lineHeight); + pixY = -(ScrollY % lineHeight); - IEnumerator nodeEnum = doc.Root.FoldableNodes.GetEnumerator (); - bool notEndOfNodes = nodeEnum.MoveNext(); + + IEnumerator foldsEnum = doc.Root.VisibleFoldableNodes.GetEnumerator (); + bool notEndOfFolds = foldsEnum.MoveNext(); - gr.LineWidth = 1; - int l = 0; - while (l < Document.LinesCount) { - //if (!cancelLinePrint (lineHeight, lineHeight * y, cb.Height)) { - - bool foldable = false; - if (notEndOfNodes && nodeEnum.Current.StartLine == l) { - curNode = nodeEnum.Current; - notEndOfNodes = nodeEnum.MoveNext(); - if (curNode.isFolded) { - SyntaxNode nextNode = curNode.NextSiblingOrParentsNextSibling; - if (nextNode == null) - notEndOfNodes = false; - else { - while (notEndOfNodes && nodeEnum.Current.StartLine < nextNode.StartLine) - notEndOfNodes = nodeEnum.MoveNext(); - } - } - foldable = true; + while (curLine < doc.LinesCount && printedLines < Math.Min(visibleLines, doc.LinesCount)) { + + + while (notEndOfFolds && foldsEnum.Current.StartLine < curLine) { + if (foldsEnum.Current.isFolded && curLine <= foldsEnum.Current.EndLine) + curLine += foldsEnum.Current.LineCount - 1; + notEndOfFolds = foldsEnum.MoveNext(); } - //buff = sourceBytes.Slice (lines[l].Start, lines[l].Length); + if (curLine >= doc.LinesCount)//could it occurs? + break; + int encodedChar = 0; + TextLine curTxtLine = doc.GetLine (curLine); + int tokPtr = doc.FindTokenIndexIncludingPosition(curTxtLine.Start); + Token tok = doc.Tokens[tokPtr]; - while (tok.Start < Document.GetLine (l).End) { + while (tok.Start < curTxtLine.End) { buff = sourceBytes.Slice (tok.Start, tok.Length); gr.SetSource (doc.GetColorForToken (tok.Type)); @@ -662,17 +661,14 @@ namespace Crow } if (CurrentToken.Equals(tok)) { - /*CharLocation? tokloc = Document.GetLocation (tok.Start); - updateLocation (gr, cb.Width, ref tokloc);*/ Rectangle r = new RectangleD(pixX, pixY, extents.Width, lineHeight); r.Inflate(1); gr.Rectangle(r); - gr.SetSource(doc.GetColorForToken (tok.Type).AdjustAlpha(0.6)); + gr.SetSource(doc.GetColorForToken (tok.Type).AdjustAlpha(0.5)); gr.Stroke(); } pixX += extents.XAdvance; - x += buff.Length; } if (++tokPtr >= doc.Tokens.Length) @@ -681,41 +677,41 @@ namespace Crow } RectangleD lineRect = new RectangleD (cb.X, pixY, pixX - cb.X, lineHeight); - if (CurrentNode != null && l >= nodeStart.Value.Line && l <= nodeEnd.Value.Line) - fillHighlight (gr, l, nodeStart.Value, nodeEnd.Value, lineRect, new Color(0.0,0.1,0.0,0.04));; + if (CurrentNode != null && curLine >= nodeStart.Value.Line && curLine <= nodeEnd.Value.Line) + fillHighlight (gr, curLine, nodeStart.Value, nodeEnd.Value, lineRect, new Color(0.0,0.1,0.0,0.06));; #if DEBUG_NODES - if (doc.EditedNode != null && l >= editNodeStart.Value.Line && l <= editNodeEnd.Value.Line) - fillHighlight (gr, l, editNodeStart.Value, editNodeEnd.Value, lineRect, new Color(0,0.5,0,0.2));; - if (hoverNode != null && l >= hoverNodeStart.Value.Line && l <= hoverNodeEnd.Value.Line) - fillHighlight (gr, l, hoverNodeStart.Value, hoverNodeEnd.Value, lineRect, new Color(0,0,0.8,0.1));; + if (doc.EditedNode != null && curLine >= editNodeStart.Value.Line && l <= editNodeEnd.Value.Line) + fillHighlight (gr, curLine, editNodeStart.Value, editNodeEnd.Value, lineRect, new Color(0,0.5,0,0.2));; + if (hoverNode != null && curLine >= hoverNodeStart.Value.Line && curLine <= hoverNodeEnd.Value.Line) + fillHighlight (gr, curLine, hoverNodeStart.Value, hoverNodeEnd.Value, lineRect, new Color(0,0,0.8,0.1));; #endif - if (selectionNotEmpty && l >= selStart.Line && l <= selEnd.Line) - fillHighlight (gr, l, selStart, selEnd, lineRect, SelectionBackground); + if (selectionNotEmpty && curLine >= selStart.Line && curLine <= selEnd.Line) + fillHighlight (gr, curLine, selStart, selEnd, lineRect, SelectionBackground); //Draw line numbering if (printLineNumbers){ marginRect.Y = lineRect.Y; gr.SetSource (marginFG); - drawLineNumber (gr, l, marginRect.X + leftMarginGap + lineNumWidth, marginRect.Y + fe.Ascent); - if (tokPtr + 1 == doc.Tokens.Length && l < doc.LinesCount-1) - drawLineNumber (gr, l+1, marginRect.X + leftMarginGap + lineNumWidth, marginRect.Y + lineHeight + fe.Ascent); + drawLineNumber (gr, curLine, marginRect.X + leftMarginGap + lineNumWidth, marginRect.Y + fe.Ascent); + if (tokPtr + 1 == doc.Tokens.Length && curLine < doc.LinesCount-1) + drawLineNumber (gr, curLine+1, marginRect.X + leftMarginGap + lineNumWidth, marginRect.Y + lineHeight + fe.Ascent); } - + bool curFoldStart = notEndOfFolds && foldsEnum.Current.StartLine == curLine; //draw fold - if (foldable) { - Rectangle rFld = new Rectangle (cb.X - leftMarginGap - foldMargin, + if (curFoldStart) { + Rectangle rFld = new Rectangle ((int)marginRect.Right - leftMarginGap - foldSize, (int)(marginRect.Y + lineHeight / 2.0 - foldSize / 2.0), foldSize, foldSize); gr.Rectangle (rFld); - if (hoverLoc.HasValue && l == hoverLoc.Value.Line && mouseIsInFoldRect) + if (hoverLoc.HasValue && curLine == hoverLoc.Value.Line && mouseIsInFoldRect) gr.SetSource (Colors.LightBlue); else gr.SetSource (Colors.White); gr.Fill(); gr.SetSource (Colors.Black); gr.Rectangle (rFld, 1.0); - if (curNode.isFolded) { + if (foldsEnum.Current.isFolded) { gr.MoveTo (rFld.Center.X + 0.5, rFld.Y + 2); gr.LineTo (rFld.Center.X + 0.5, rFld.Bottom - 2); } @@ -725,49 +721,27 @@ namespace Crow gr.Stroke (); } - if (++tokPtr >= doc.Tokens.Length) { + if (++tokPtr >= doc.Tokens.Length) break; - } - tok = doc.Tokens[tokPtr]; - x = 0; pixX = cb.Left; pixY += lineHeight; - /*if (pixY > cb.Height) - break;*/ - - if (foldable && curNode.isFolded) { - TextSpan ns = curNode.Span; - l = curNode.StartLine + curNode.LineCount; - while (tok.End <= Document.GetLine (l).Start) { - if (++tokPtr >= doc.Tokens.Length) - break; - tok = doc.Tokens[tokPtr]; - } - //tokPtr = doc.FindTokenIndexIncludingPosition (lines[l].Start); - } else - l ++; - /* } else if (tok2.Type == TokenType.Tabulation) { - int spaceRounding = x % tabSize; - int spaces = spaceRounding == 0 ? - tabSize * tok2.Length : - spaceRounding + tabSize * (tok2.Length - 1); - x += spaces; - pixX += spacePixelWidth * spaces; - continue; - } else if (tok2.Type == TokenType.WhiteSpace) { - x += tok2.Length; - pixX += spacePixelWidth * tok2.Length;*/ + printedLines++; + + if (curFoldStart && foldsEnum.Current.isFolded) + curLine = foldsEnum.Current.EndLine + 1; + else + curLine++; + } - //gr.Translate (ScrollX, ScrollY); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); } finally { doc.ExitReadLock (); } - + gr.Translate (ScrollX, 0); } protected override RectangleD? computeTextCursor (Rectangle cursor) { Rectangle cb = ClientRectangle; @@ -819,8 +793,9 @@ namespace Crow } void updateCurrentTokAndNode() { if (currentLoc.HasValue && Document is SourceDocument srcdoc) { - currentTokenIndex = srcdoc.GetAbsolutePosition(currentLoc.Value); - Token tok = srcdoc.FindTokenIncludingPosition(currentTokenIndex); + int pos = srcdoc.GetAbsolutePosition(currentLoc.Value); + currentTokenIndex = srcdoc.FindTokenIndexIncludingPosition(pos); + Token tok = srcdoc.GetTokenByIndex(currentTokenIndex); CurrentNode = srcdoc.Root?.FindNodeIncludingSpan(tok.Span); NotifyValueChanged("CurrentToken",tok); diff --git a/CrowEditBase/src/TextBuffer.cs b/CrowEditBase/src/TextBuffer.cs index b4c6eb1..4d6981e 100644 --- a/CrowEditBase/src/TextBuffer.cs +++ b/CrowEditBase/src/TextBuffer.cs @@ -88,6 +88,7 @@ namespace CrowEditBase } public CharLocation GetLocation (int absolutePosition) => lines.GetLocation (absolutePosition); public TextLine GetLine (int index) => lines[index]; + public void SetLine (int index, TextLine line) => lines[index] = line; public ReadOnlySpan GetText (TextLine line) => GetText(line.Span); public ReadOnlySpan GetText (TextSpan textSpan) => buffer.Span.Slice(textSpan.Start, textSpan.Length); public int GetAbsolutePosition (CharLocation loc) => lines.GetAbsolutePosition (loc); diff --git a/CrowEditBase/src/TextDocument.cs b/CrowEditBase/src/TextDocument.cs index 1843dee..a7c91b1 100644 --- a/CrowEditBase/src/TextDocument.cs +++ b/CrowEditBase/src/TextDocument.cs @@ -250,6 +250,14 @@ namespace CrowEditBase documentRWLock.ExitReadLock(); } } + public void SetLine (int index, TextLine newValue) { + documentRWLock.EnterReadLock (); + try { + buffer.SetLine(index, newValue); + } finally { + documentRWLock.ExitReadLock(); + } + } public ReadOnlySpan GetLineText (int index) { documentRWLock.EnterReadLock (); try { diff --git a/CrowEditBase/ui/CrowEdit.style b/CrowEditBase/ui/CrowEdit.style index a48dd48..47b6598 100644 --- a/CrowEditBase/ui/CrowEdit.style +++ b/CrowEditBase/ui/CrowEdit.style @@ -31,7 +31,7 @@ Editor { Width="Stretched"; Background="White"; Foreground="Black"; - MouseWheelSpeed = "20"; + MouseWheelSpeed = "5"; BubbleEvents ="None"; ClipToClientRect = "true"; MouseCursor = "ibeam";