From ea4dd216fdcbda49cd9a9956eb95ab7ad4529ef1 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 7 Mar 2009 22:32:11 +0000 Subject: [PATCH] Made AvalonEdit work without 'DoEvents'. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3834 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Document/TextAnchorTree.cs | 4 +- .../Gui/FoldingMargin.cs | 4 +- .../Gui/LineNumberMargin.cs | 3 +- .../Gui/SelectionMouseHandler.cs | 39 +++++++++------ .../ICSharpCode.AvalonEdit/Gui/TextEditor.cs | 3 ++ .../ICSharpCode.AvalonEdit/Gui/TextView.cs | 49 +++++++++++++------ .../Highlighting/HighlightingColor.cs | 1 + .../Highlighting/Xshd/SaveXshdVisitor.cs | 1 + .../ICSharpCode.AvalonEdit.csproj | 2 +- .../Utils/ExtensionMethods.cs | 16 ------ 10 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorTree.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorTree.cs index f45d50ba50..e2b6f5f430 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorTree.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorTree.cs @@ -37,7 +37,7 @@ namespace ICSharpCode.AvalonEdit.Document #region Insert Text public void InsertText(int offset, int length) { - Log("InsertText(" + offset + ", " + length + ")"); + //Log("InsertText(" + offset + ", " + length + ")"); if (length == 0 || root == null || offset > root.totalLength) return; @@ -139,7 +139,7 @@ namespace ICSharpCode.AvalonEdit.Document #region Remove Text public void RemoveText(int offset, int length, DelayedEvents delayedEvents) { - Log("RemoveText(" + offset + ", " + length + ")"); + //Log("RemoveText(" + offset + ", " + length + ")"); if (length == 0 || root == null || offset >= root.totalLength) return; TextAnchorNode node = FindNode(ref offset); diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs index fa5db5d058..98832cbcde 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs @@ -75,7 +75,7 @@ namespace ICSharpCode.AvalonEdit.Gui } markers.Clear(); InvalidateVisual(); - if (TextView != null && FoldingManager != null) { + if (TextView != null && FoldingManager != null && TextView.VisualLinesValid) { foreach (VisualLine line in TextView.VisualLines) { FoldingSection fs = FoldingManager.GetNextFolding(line.FirstDocumentLine.Offset); if (fs == null) @@ -113,6 +113,8 @@ namespace ICSharpCode.AvalonEdit.Gui /// protected override void OnRender(DrawingContext drawingContext) { + if (!TextView.VisualLinesValid) + return; if (TextView.VisualLines.Count == 0 || FoldingManager == null) return; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs index 54d3dfc28c..b6c49ee39d 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs @@ -69,7 +69,7 @@ namespace ICSharpCode.AvalonEdit.Gui { TextView textView = this.TextView; Size renderSize = this.RenderSize; - if (textView != null) { + if (textView != null && textView.VisualLinesValid) { var foreground = (Brush)GetValue(Control.ForegroundProperty); foreach (VisualLine line in textView.VisualLines) { int lineNumber = line.FirstDocumentLine.LineNumber; @@ -170,7 +170,6 @@ namespace ICSharpCode.AvalonEdit.Gui SimpleSegment GetTextLineSegment(MouseEventArgs e) { - TextView.EnsureVisualLines(); Point pos = e.GetPosition(TextView); pos.X = 0; pos.Y += TextView.VerticalOffset; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs index 92ace40aa1..567d398f21 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs @@ -154,7 +154,10 @@ namespace ICSharpCode.AvalonEdit.Gui if (mode != SelectionMode.None || !AllowTextDragDrop) { e.Cursor = Cursors.IBeam; e.Handled = true; - } else { + } else if (textArea.TextView.VisualLinesValid) { + // Only query the cursor if the visual lines are valid. + // If they are invalid, the cursor will get re-queried when the visual lines + // get refreshed. Point p = e.GetPosition(textArea.TextView); if (p.X >= 0 && p.Y >= 0 && p.X <= textArea.TextView.ActualWidth && p.Y <= textArea.TextView.ActualHeight) { int visualColumn; @@ -281,7 +284,6 @@ namespace ICSharpCode.AvalonEdit.Gui { TextView textView = textArea.TextView; if (textView == null) return SimpleSegment.Invalid; - textView.EnsureVisualLines(); Point pos = e.GetPosition(textView); if (pos.Y < 0) pos.Y = 0; @@ -325,7 +327,6 @@ namespace ICSharpCode.AvalonEdit.Gui { visualColumn = 0; TextView textView = textArea.TextView; - textView.EnsureVisualLines(); Point pos = positionRelativeToTextView; if (pos.Y < 0) pos.Y = 0; @@ -348,17 +349,11 @@ namespace ICSharpCode.AvalonEdit.Gui return; if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord) { e.Handled = true; - int oldOffset = textArea.Caret.Offset; - SetCaretOffsetToMousePosition(e); - if (mode == SelectionMode.Normal) { - textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldOffset, textArea.Caret.Offset); - } else if (mode == SelectionMode.WholeWord) { - var newWord = GetWordAtMousePosition(e); - if (newWord != SimpleSegment.Invalid) { - textArea.Selection = new SimpleSelection( - Math.Min(newWord.Offset, startWord.Offset), - Math.Max(newWord.GetEndOffset(), startWord.GetEndOffset())); - } + if (textArea.TextView.VisualLinesValid) { + // If the visual lines are not valid, don't extend the selection. + // Extending the selection forces a VisualLine refresh, and it is sufficient + // to do that on MouseUp, we don't have to do it every MouseMove. + ExtendSelectionToMouse(e); } } else if (mode == SelectionMode.PossibleDragStart) { e.Handled = true; @@ -371,6 +366,20 @@ namespace ICSharpCode.AvalonEdit.Gui } } + void ExtendSelectionToMouse(MouseEventArgs e) + { + int oldOffset = textArea.Caret.Offset; + SetCaretOffsetToMousePosition(e); + if (mode == SelectionMode.Normal) { + textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldOffset, textArea.Caret.Offset); + } else if (mode == SelectionMode.WholeWord) { + var newWord = GetWordAtMousePosition(e); + if (newWord != SimpleSegment.Invalid) { + textArea.Selection = new SimpleSelection(Math.Min(newWord.Offset, startWord.Offset), Math.Max(newWord.GetEndOffset(), startWord.GetEndOffset())); + } + } + } + void textArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (mode == SelectionMode.None || e.Handled) @@ -380,6 +389,8 @@ namespace ICSharpCode.AvalonEdit.Gui // -> this was not a drag start (mouse didn't move after mousedown) SetCaretOffsetToMousePosition(e); textArea.Selection = Selection.Empty; + } else if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord) { + ExtendSelectionToMouse(e); } mode = SelectionMode.None; textArea.ReleaseMouseCapture(); diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs index 7400e4fb67..54ca9e2b30 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs @@ -99,6 +99,9 @@ namespace ICSharpCode.AvalonEdit value = string.Empty; TextDocument document = GetOrCreateDocument(); document.Text = value; + // after replacing the full text, the caret is positioned at the end of the document + // - reset it to the beginning. + this.CaretOffset = 0; document.UndoStack.ClearAll(); } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs index 4af9fe4666..898c57ac7b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs @@ -242,17 +242,15 @@ namespace ICSharpCode.AvalonEdit.Gui /// /// Waits for the visual lines to be built. - /// Warning: this methods runs all operations with priority >= Render /// - public void EnsureVisualLines() + private void EnsureVisualLines() { Dispatcher.VerifyAccess(); if (visibleVisualLines == null) { - if (invalidateMeasureOperation != null) { - // increase priority - InvalidateMeasure(DispatcherPriority.Normal); - } - Dispatcher.DoEvents(DispatcherPriority.Render); + // increase priority for real Redraw + InvalidateMeasure(DispatcherPriority.Normal); + // force immediate re-measure + UpdateLayout(); } } @@ -285,7 +283,7 @@ namespace ICSharpCode.AvalonEdit.Gui /// public VisualLine GetVisualLine(int documentLineNumber) { - VerifyAccess(); + // TODO: EnsureVisualLines() ? foreach (VisualLine visualLine in allVisualLines) { Debug.Assert(visualLine.IsDisposed == false); int start = visualLine.FirstDocumentLine.LineNumber; @@ -355,15 +353,23 @@ namespace ICSharpCode.AvalonEdit.Gui /// /// Gets the currently visible visual lines. - /// Is empty when the visual lines are not available - /// (when one or more of the visual lines need to be regenerated). /// public ReadOnlyCollection VisualLines { get { - return visibleVisualLines ?? Empty.ReadOnlyCollection; + EnsureVisualLines(); + return visibleVisualLines; } } + /// + /// Gets whether the visual lines are valid. + /// Will return false after a call to Redraw(). Accessing the visual lines property + /// will force immediate regeneration of valid lines. + /// + public bool VisualLinesValid { + get { return visibleVisualLines != null; } + } + /// /// Occurs when the TextView was measured and changed its visual lines. /// @@ -398,10 +404,21 @@ namespace ICSharpCode.AvalonEdit.Gui { if (!canHorizontallyScroll && !availableSize.Width.IsClose(lastAvailableSize.Width)) ClearVisualLines(); - - RemoveInlineObjectsNow(); - lastAvailableSize = availableSize; + return DoMeasure(availableSize); + } + + /// + /// Immediately performs the text creation. + /// + /// The size of the text view. + /// + Size DoMeasure(Size availableSize) + { + bool isRealMeasure = true; + if (isRealMeasure) + RemoveInlineObjectsNow(); + if (document == null) return Size.Empty; @@ -455,7 +472,8 @@ namespace ICSharpCode.AvalonEdit.Gui if (!newVisualLines.Contains(line)) DisposeVisualLine(line); } - RemoveInlineObjectsNow(); + if (isRealMeasure) + RemoveInlineObjectsNow(); allVisualLines = newVisualLines; // visibleVisualLines = readonly copy of visual lines @@ -1032,7 +1050,6 @@ namespace ICSharpCode.AvalonEdit.Gui /// public VisualLine GetVisualLineFromVisualTop(double visualTop) { - VerifyAccess(); foreach (VisualLine vl in this.VisualLines) { if (visualTop < vl.VisualTop) continue; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs index b94adfec1f..26caf1ec1e 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs @@ -34,6 +34,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting /// /// Gets CSS code for the color. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "CSS usually uses lowercase, and all possible values are English-only")] public virtual string ToCss() { StringBuilder b = new StringBuilder(); diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/Xshd/SaveXshdVisitor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/Xshd/SaveXshdVisitor.cs index 326899097b..ee3ff97214 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/Xshd/SaveXshdVisitor.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/Xshd/SaveXshdVisitor.cs @@ -94,6 +94,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "The file format requires lowercase, and all possible values are English-only")] void WriteColorAttributes(XshdColor color) { if (color.Foreground != null) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index ed5d8d5318..283e1c5137 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -18,7 +18,7 @@ False File False - -Microsoft.Design#CA1020;-Microsoft.Design#CA1033;-Microsoft.Globalization#CA1308 + -Microsoft.Design#CA1020;-Microsoft.Design#CA1033 ..\..\..\..\bin\ ..\..\..\..\bin\ICSharpCode.AvalonEdit.xml diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs index bb1d97deea..ae63748225 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs @@ -58,22 +58,6 @@ namespace ICSharpCode.AvalonEdit.Utils (FontStretch)fe.GetValue(TextBlock.FontStretchProperty)); } - /// - /// Runs all outstanding dispatcher tasks with a priority higher or equal to . - /// - public static void DoEvents(this Dispatcher dispatcher, DispatcherPriority priority) - { - dispatcher.VerifyAccess(); - DispatcherFrame frame = new DispatcherFrame(); - dispatcher.BeginInvoke( - priority, new DispatcherOperationCallback( - delegate { - frame.Continue = false; - return null; - }), null); - Dispatcher.PushFrame(frame); - } - public static void AddRange(this ICollection collection, IEnumerable elements) { foreach (T e in elements)