AvalonEdit: получение визуальной позиции для IBackgroundRenderer

В моем редакторе документов на основе AvalonEdit я пытаюсь добавить маркерные линии в текстовое представление, чтобы указать сгибы в документе, аналогично тому, как Visual Studio соединяет начало и конец фигурных скобок блока кода пунктирными линиями.

У меня есть что-то, что дает правильный результат, если документ прокручивается до самого верха, но не обновляется правильно, если документ прокручивается вниз. В частности, линии рисуются так, как если бы текстовое представление вообще не прокручивалось и все еще находилось в верхней части документа. Я подозреваю, что проблема как-то связана со строками TextViewPosition и GetVisualPosition, но я не понимаю, как правильно получить скорректированное визуальное положение при прокрутке.

(Для ясности, я проверил, и метод Draw вызывается в соответствующее время для обновления фона, просто прокрутка не учитывается, когда она это делает)

Что у меня есть до сих пор, так это следующее в классе, который реализует 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));
    }
}

Свертывание документа основано на пробелах (очень похоже на отступы в стиле Python), поэтому для поиска столбца используется начальный пробел.

Короче говоря, как получить правильно настроенное визуальное положение по номеру строки и столбцу документа?


person Gebodal    schedule 21.07.2020    source источник


Ответы (1)


GetVisualPosition документируется как:

Возвращает: положение в аппаратно-независимых пикселях WPF относительно левого верхнего угла документа.

Чтобы использовать его для рисования, вам нужно вычесть из него позицию прокрутки:

Point foldStartPos = textView.GetVisualPosition(startViewPos, VisualYPosition.LineBottom);
Point foldEndPos = textView.GetVisualPosition(endViewPos, VisualYPosition.LineBottom);
foldStartPos -= textView.ScrollOffset;
foldEndPos -= textView.ScrollOffset;

Что касается TextLocation и TextViewPosition: в некоторых случаях существует несколько возможных местоположений, которые сопоставляются с одним и тем же TextLocation. Могут быть пользовательские VisualLineElement с documentLength равным 0. Или, может быть, включен перенос слов, и строка переносится в позиции, где нет пробела: тогда и конец первого TextLine, и начало второго TextLine относятся к той же позиции в документе.

TextViewPosition несет некоторую дополнительную информацию, которая позволяет различать эти случаи. Это в основном важно для каретки, так что щелчок где-нибудь помещает каретку в положение, в котором она была нажата; не другая эквивалентная позиция.

person Daniel    schedule 22.07.2020