In my AvalonEdit-based document editor, I am trying to add marker lines to the text view to indicate folds in the document, in a similar way to how Visual Studio joins the start and end of code block braces with dotted lines.
I have something that produces the correct result if the document is scrolled to the very top, but it doesn't update correctly if the document is scrolled down. Specifically, the lines are drawn as if the text view wasn't scrolled at all, and was still at the top of the document. I suspect the problem has something to do with the TextViewPosition and GetVisualPosition lines, but I don't understand how to correctly get the adjusted visual position with scrolling.
(To be clear, I have checked, and the Draw method is being called at the appropriate times to update the background, it's just that scrolling isn't accounted for when it does)
What I have so far, is the following, on a class which implements IBackgroundRenderer:
public void Draw(TextView textView, DrawingContext drawingContext) {
if (textView == null) { throw new ArgumentNullException("textView"); }
if (drawingContext == null) { throw new ArgumentNullException("drawingContext"); }
if (!textView.VisualLinesValid) { return; }
ReadOnlyCollection<VisualLine> visualLines = textView.VisualLines;
if (visualLines.Count == 0) { return; }
foreach (FoldingSection fold in foldingManager.AllFoldings.Where(f => !f.IsFolded)) {
DocumentLine startLine = textView.Document.GetLineByOffset(fold.StartOffset);
ISegment whitespace = TextUtilities.GetLeadingWhitespace(textView.Document, startLine);
if(whitespace.Length == 0) { continue; }
DocumentLine endLine = textView.Document.GetLineByOffset(fold.EndOffset);
TextLocation foldStart = textView.Document.GetLocation(whitespace.EndOffset);
TextLocation foldEnd = textView.Document.GetLocation(textView.Document.GetOffset(endLine.LineNumber, foldStart.Column));
// I am unsure exactly what TextViewPosition is meant to represent, in contrast to TextLocation
TextViewPosition startViewPos = new TextViewPosition(foldStart);
TextViewPosition endViewPos = new TextViewPosition(foldEnd);
// These lines are definitely not returning what I expect
Point foldStartPos = textView.GetVisualPosition(startViewPos, VisualYPosition.LineBottom);
Point foldEndPos = textView.GetVisualPosition(endViewPos, VisualYPosition.LineBottom);
Brush brush = new SolidColorBrush(LineColor);
brush.Freeze();
Pen dashPen = new Pen(brush, 0.5) { DashStyle = new DashStyle(new double[] { 2, 2 }, 0) };
dashPen.Freeze();
// New point created to avoid issues with nested folds causing slanted lines
drawingContext.DrawLine(dashPen, foldStartPos, new Point(foldStartPos.X, foldEndPos.Y));
}
}
The folding for the document is based on whitespace (very similar to Python-style indentation), hence the use of the leading whitespace for finding the column.
In short, how does one get the properly adjusted visual position from the document line number and column?
GetVisualPositionis documented as:To use it for painting, you'll want to subtract the scroll position from it:
As for
TextLocationvs.TextViewPosition: there are some cases where there are multiple possible locations that map to the sameTextLocation. There might be customVisualLineElements with adocumentLengthof 0. Or maybe word-wrap is enabled and a line is wrapped at a position where there is no space character: then both the end of the firstTextLineand the beginning of the secondTextLinerefer to the same position in the document.A
TextViewPositioncarries some extra information that allows distinguishing these cases. This is mostly important for the caret, so that clicking somewhere places the caret in the clicked position; not another equivalent position.