From 22df6328c7592cb42b459feb7dc8fc55bd8accbd Mon Sep 17 00:00:00 2001 From: Stefan Frank Date: Mon, 8 May 2023 11:47:33 +0200 Subject: [PATCH 1/3] Enable code folding in more than one TextViews. Define MinimumDistanceToViewBorder as an option to improve scroll speed during mouse selection --- .../Editing/SelectionMouseHandler.cs | 2 +- ICSharpCode.AvalonEdit/Folding/FoldingManager.cs | 8 ++++++++ ICSharpCode.AvalonEdit/Rendering/TextView.cs | 15 +++++++++++++++ ICSharpCode.AvalonEdit/TextEditorOptions.cs | 16 ++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs b/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs index e0287a38..857a7224 100644 --- a/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs +++ b/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs @@ -650,7 +650,7 @@ void ExtendSelectionToMouse(MouseEventArgs e) textArea.Caret.Offset = Math.Max(newWord.EndOffset, startWord.EndOffset); } } - textArea.Caret.BringCaretToView(5.0); + textArea.Caret.BringCaretToView(textArea.Options.MinimumDistanceToViewBorder); } #endregion diff --git a/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs b/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs index 27ae5bd4..46d325d2 100644 --- a/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs +++ b/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs @@ -55,6 +55,14 @@ public FoldingManager(TextDocument document) } #endregion + /// + /// Adds another text view to the same document + /// + public void AddTextView(TextView textView) + { + textViews.Add(textView); + } + #region ReceiveWeakEvent /// protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) diff --git a/ICSharpCode.AvalonEdit/Rendering/TextView.cs b/ICSharpCode.AvalonEdit/Rendering/TextView.cs index 9e1aa1f8..6d2fdc3b 100644 --- a/ICSharpCode.AvalonEdit/Rendering/TextView.cs +++ b/ICSharpCode.AvalonEdit/Rendering/TextView.cs @@ -994,6 +994,21 @@ double CreateAndMeasureVisualLines(Size availableSize) while (yPos < availableSize.Height && nextLine != null) { VisualLine visualLine = GetVisualLine(nextLine.LineNumber); if (visualLine == null) { + + // Don't build visual lines from collapsed lines + var bBreakAll = false; + while (heightTree.GetIsCollapsed(nextLine.LineNumber)) { + if (nextLine.NextLine != null) + nextLine = nextLine.NextLine; + else { + bBreakAll = true; + break; + } + } + + if (bBreakAll) + break; + visualLine = BuildVisualLine(nextLine, globalTextRunProperties, paragraphProperties, elementGeneratorsArray, lineTransformersArray, diff --git a/ICSharpCode.AvalonEdit/TextEditorOptions.cs b/ICSharpCode.AvalonEdit/TextEditorOptions.cs index 66ee37f9..db536737 100644 --- a/ICSharpCode.AvalonEdit/TextEditorOptions.cs +++ b/ICSharpCode.AvalonEdit/TextEditorOptions.cs @@ -492,5 +492,21 @@ public bool AllowToggleOverstrikeMode { } } } + + double minimumDistanceToViewBorder = 5.0; + + /// + /// Gets/Sets the minimum distance to the view border. + /// + [DefaultValue(5.0)] + public double MinimumDistanceToViewBorder { + get { return minimumDistanceToViewBorder; } + set { + if (Math.Abs(minimumDistanceToViewBorder - value) >= 1e-6) { + minimumDistanceToViewBorder = value; + OnPropertyChanged("MinimumDistanceToViewBorder"); + } + } + } } } From c4ff247d7a2072b7781cfdd5aaf72b40d4759367 Mon Sep 17 00:00:00 2001 From: Stefan Frank <104071518+SFrank1966@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:50:07 +0100 Subject: [PATCH 2/3] Update EditingCommandHandler.cs: Handling of Tab /Shift-Tab on block comments Behaviour of Tab/Shift-Tab on block comments is now like Visual Studio. --- .../Editing/EditingCommandHandler.cs | 75 +++++++++++++------ 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs index fcdda52c..968fb840 100644 --- a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs +++ b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs @@ -198,20 +198,29 @@ static void OnTab(object target, ExecutedRoutedEventArgs args) if (textArea != null && textArea.Document != null) { using (textArea.Document.RunUpdate()) { if (textArea.Selection.IsMultiline) { - var segment = textArea.Selection.SurroundingSegment; - DocumentLine start = textArea.Document.GetLineByOffset(segment.Offset); - DocumentLine end = textArea.Document.GetLineByOffset(segment.EndOffset); - // don't include the last line if no characters on it are selected - if (start != end && end.Offset == segment.EndOffset) - end = end.PreviousLine; - DocumentLine current = start; - while (true) { - int offset = current.Offset; - if (textArea.ReadOnlySectionProvider.CanInsert(offset)) - textArea.Document.Replace(offset, 0, textArea.Options.IndentationString, OffsetChangeMappingType.KeepAnchorBeforeInsertion); - if (current == end) - break; - current = current.NextLine; + if (!textArea.Selection.EnableVirtualSpace) { + var segment = textArea.Selection.SurroundingSegment; + DocumentLine start = textArea.Document.GetLineByOffset(segment.Offset); + DocumentLine end = textArea.Document.GetLineByOffset(segment.EndOffset); + // don't include the last line if no characters on it are selected + if (start != end && end.Offset == segment.EndOffset) + end = end.PreviousLine; + DocumentLine current = start; + while (true) { + int offset = current.Offset; + if (textArea.ReadOnlySectionProvider.CanInsert(offset)) + textArea.Document.Replace(offset, 0, textArea.Options.IndentationString, OffsetChangeMappingType.KeepAnchorBeforeInsertion); + if (current == end) + break; + current = current.NextLine; + } + } else { + IEnumerable segments = textArea.Selection.Segments; + foreach (var segment in segments.Reverse()) { + int offset = segment.StartOffset; + if (textArea.ReadOnlySectionProvider.CanInsert(offset)) + textArea.Document.Replace(offset, 0, textArea.Options.IndentationString, OffsetChangeMappingType.KeepAnchorBeforeInsertion); + } } } else { string indentationString = textArea.Options.GetIndentationString(textArea.Caret.Column); @@ -225,17 +234,37 @@ static void OnTab(object target, ExecutedRoutedEventArgs args) static void OnShiftTab(object target, ExecutedRoutedEventArgs args) { - TransformSelectedLines( - delegate (TextArea textArea, DocumentLine line) { - int offset = line.Offset; - ISegment s = TextUtilities.GetSingleIndentationSegment(textArea.Document, offset, textArea.Options.IndentationSize); - if (s.Length > 0) { - s = textArea.GetDeletableSegments(s).FirstOrDefault(); - if (s != null && s.Length > 0) { - textArea.Document.Remove(s.Offset, s.Length); + TextArea textArea = GetTextArea(target); + if (textArea != null && textArea.Document != null) { + if (textArea.Selection.IsEmpty || !textArea.Selection.EnableVirtualSpace) { + TransformSelectedLines( + delegate (TextArea textAreaIn, DocumentLine line) { + int offset = line.Offset; + ISegment s = TextUtilities.GetSingleIndentationSegment(textAreaIn.Document, offset, textAreaIn.Options.IndentationSize); + if (s.Length > 0) { + s = textAreaIn.GetDeletableSegments(s).FirstOrDefault(); + if (s != null && s.Length > 0) { + textAreaIn.Document.Remove(s.Offset, s.Length); + } + } + }, target, args, DefaultSegmentType.CurrentLine); + } else { + using (textArea.Document.RunUpdate()) { + IEnumerable segments = textArea.Selection.Segments; + foreach (var segment in segments.Reverse()) { + int offset = segment.StartOffset - 1; + ISegment s = TextUtilities.GetSingleIndentationSegment(textArea.Document, offset, + textArea.Options.IndentationSize); + if (s.Length > 0) { + s = textArea.GetDeletableSegments(s).FirstOrDefault(); + if (s != null && s.Length > 0) { + textArea.Document.Remove(s.Offset, s.Length); + } + } } } - }, target, args, DefaultSegmentType.CurrentLine); + } + } } #endregion From dfc77a28c1d4479419440a82182af2a6d0fe9577 Mon Sep 17 00:00:00 2001 From: Stefan Frank <104071518+SFrank1966@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:54:58 +0100 Subject: [PATCH 3/3] Update SelectionMouseHandler.cs: Double click on whitespaces should select the whitesspaces instead of the word left of them Double click on whitespaces should select the whitesspaces instead of the word left of them --- .../Editing/SelectionMouseHandler.cs | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs b/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs index 857a7224..6ec61e95 100644 --- a/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs +++ b/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs @@ -436,7 +436,7 @@ void textArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) startWord = GetLineAtMousePosition(e); } else { mode = MouseSelectionMode.WholeWord; - startWord = GetWordAtMousePosition(e); + startWord = GetWordOrWhitespacesAtMousePosition(e); } if (startWord == SimpleSegment.Invalid) { mode = MouseSelectionMode.None; @@ -511,6 +511,48 @@ SimpleSegment GetWordAtMousePosition(MouseEventArgs e) } } + SimpleSegment GetWordOrWhitespacesAtMousePosition(MouseEventArgs e) + { + TextView textView = textArea.TextView; + if (textView == null) return SimpleSegment.Invalid; + Point pos = e.GetPosition(textView); + if (pos.Y < 0) + pos.Y = 0; + if (pos.Y > textView.ActualHeight) + pos.Y = textView.ActualHeight; + pos += textView.ScrollOffset; + VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y); + if (line != null) { + int visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace); + var ch = textArea.Document.GetCharAt(line.StartOffset + visualColumn); + if (ch == ' ' || ch == '\t') { + int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordStartVC == -1) + wordStartVC = 0; + int wordEndVC = line.GetNextCaretPosition(visualColumn, LogicalDirection.Forward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordEndVC == -1) + wordEndVC = line.VisualLength; + int relOffset = line.FirstDocumentLine.Offset; + int wordStartOffset = line.GetRelativeOffset(wordStartVC) + relOffset; + int wordEndOffset = line.GetRelativeOffset(wordEndVC) + relOffset; + return new SimpleSegment(wordStartOffset, wordEndOffset - wordStartOffset); + } else { + int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordStartVC == -1) + wordStartVC = 0; + int wordEndVC = line.GetNextCaretPosition(wordStartVC, LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordEndVC == -1) + wordEndVC = line.VisualLength; + int relOffset = line.FirstDocumentLine.Offset; + int wordStartOffset = line.GetRelativeOffset(wordStartVC) + relOffset; + int wordEndOffset = line.GetRelativeOffset(wordEndVC) + relOffset; + return new SimpleSegment(wordStartOffset, wordEndOffset - wordStartOffset); + } + } else { + return SimpleSegment.Invalid; + } + } + SimpleSegment GetLineAtMousePosition(MouseEventArgs e) { TextView textView = textArea.TextView; @@ -638,7 +680,7 @@ void ExtendSelectionToMouse(MouseEventArgs e) else textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position); } else if (mode == MouseSelectionMode.WholeWord || mode == MouseSelectionMode.WholeLine) { - var newWord = (mode == MouseSelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordAtMousePosition(e); + var newWord = (mode == MouseSelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordOrWhitespacesAtMousePosition(e); if (newWord != SimpleSegment.Invalid) { textArea.Selection = Selection.Create(textArea, Math.Min(newWord.Offset, startWord.Offset),