Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing default line height in avalon edit

Tags:

c#

wpf

avalonedit

I am writing a custom software using avalon edit and I am looking for a way to make the space (height) between lines bigger. At the moment I am forced to add an empty line every time the user has ended writing a line and wants to write another.

I have started looking into the TextView Class where defaultLineHeight seems to be calculated but the only thing I was able to affect is the height of the visual caret but not the content itself.

At the moment I am looking at making every pair line invisible but I am hoping there is an easier way to achieve the simple operation of adding more space between lines.

Here is the method from class TextView I am inspecting at the moment. Any tips or hints would be welcome.

void CalculateDefaultTextMetrics()
{
    if (defaultTextMetricsValid)
    {
        return;
    }

    defaultTextMetricsValid = true;
    if (formatter != null)
    {
        var textRunProperties = CreateGlobalTextRunProperties();
        using (
            var line = formatter.FormatLine(
                new SimpleTextSource("x", textRunProperties),
                0,
                32000,
                new VisualLineTextParagraphProperties { defaultTextRunProperties = textRunProperties },
                null))
        {
            wideSpaceWidth = Math.Max(1, line.WidthIncludingTrailingWhitespace);
            defaultBaseline = Math.Max(1, line.Baseline);
            defaultLineHeight = Math.Max(1, line.Height);
        }
    }
    else
    {
        wideSpaceWidth = FontSize / 2;
        defaultBaseline = FontSize;
        **defaultLineHeight = FontSize + 3; // bigger value only affects caret height :(**
    }

    // Update heightTree.DefaultLineHeight, if a document is loaded.
    if (heightTree != null)
    {
        heightTree.DefaultLineHeight = defaultLineHeight;
    }
}

Thanks

like image 854
legrandviking Avatar asked Sep 05 '25 03:09

legrandviking


2 Answers

The DefaultLineHeight is the height of a line in the default font, which is used as an initial assumption for the each line's height. (e.g. for calculating the scroll bar position)

Whenever a line gets actually measured (TextView.BuildVisualLine), the measured height gets stored in the height tree, overwriting the default height. This is because word wrapping (or a line transformer changing the font size) can cause each line to have a different height.

Inter-line spacing isn't really supported at the moment. If you want to add that, you can try changing the height calculation of the VisualLine, e.g. by changing VisualLine.SetTextLines().

like image 141
Daniel Avatar answered Sep 09 '25 17:09

Daniel


Including what @Peter Moore said. There's one more step required in order to force the text to render correctly.

At the bottom of VisualLine.cs lies the class VisualLineDrawingVisual which is responsible for drawing the actual text but doesn't have access to the TextView class.

Modify the constructor to include double lineSpacing as a paremeter and multiply all instances of textLine.Height by lineSpacing.

In VisualLine.Render(), pass textView.LineSpacing as the secondary parameter for the now-modified constructor for VisualLineDrawingVisual. After this everything should draw correctly.

Here's a full view of the modified code:

TextView.cs

public static readonly DependencyProperty LineSpacingProperty =
    DependencyProperty.Register("LineSpacing", typeof(double), typeof(TextView),
                                new FrameworkPropertyMetadata(1.0));

public double LineSpacing {
    get { return (double) GetValue(LineSpacingProperty); }
    set { SetValue(LineSpacingProperty, value); }
}

VisualLine.cs

Line 269

internal void SetTextLines(List<TextLine> textLines) {
    this.textLines = textLines.AsReadOnly();
    Height = 0;
    foreach (TextLine line in textLines)
        Height += line.Height * textView.LineSpacing;
}

Line 335

public double GetTextLineVisualYPosition(TextLine textLine, VisualYPosition yPositionMode) {
    if (textLine == null)
        throw new ArgumentNullException("textLine");
    double pos = VisualTop;
    foreach (TextLine tl in TextLines) {
        if (tl == textLine) {
            switch (yPositionMode) {
            case VisualYPosition.LineTop:
                return pos;
            case VisualYPosition.LineMiddle:
                return pos + tl.Height / 2 * textView.LineSpacing;
            case VisualYPosition.LineBottom:
                return pos + tl.Height * textView.LineSpacing;
            case VisualYPosition.TextTop:
                return pos + tl.Baseline - textView.DefaultBaseline;
            case VisualYPosition.TextBottom:
                return pos + tl.Baseline - textView.DefaultBaseline + textView.DefaultLineHeight;
            case VisualYPosition.TextMiddle:
                return pos + tl.Baseline - textView.DefaultBaseline + textView.DefaultLineHeight / 2;
            case VisualYPosition.Baseline:
                return pos + tl.Baseline;
            default:
                throw new ArgumentException("Invalid yPositionMode:" + yPositionMode);
            }
        }
        else {
            pos += tl.Height * textView.LineSpacing;
        }
    }
    throw new ArgumentException("textLine is not a line in this VisualLine");
}

Line 386

public TextLine GetTextLineByVisualYPosition(double visualTop) {
    const double epsilon = 0.0001;
    double pos = this.VisualTop;
    foreach (TextLine tl in TextLines) {
        pos += tl.Height * textView.LineSpacing;
        if (visualTop + epsilon < pos)
            return tl;
    }
    return TextLines[TextLines.Count - 1];
}

Line 701

internal VisualLineDrawingVisual Render() {
    Debug.Assert(phase == LifetimePhase.Live);
    if (visual == null)
        visual = new VisualLineDrawingVisual(this, textView.LineSpacing);
    return visual;
}

Line 714

public VisualLineDrawingVisual(VisualLine visualLine, double lineSpacing) {
    this.VisualLine = visualLine;
    var drawingContext = RenderOpen();
    double pos = 0;
    foreach (TextLine textLine in visualLine.TextLines) {
        textLine.Draw(drawingContext, new Point(0, pos), InvertAxes.None);
        pos += textLine.Height * lineSpacing;
    }
    this.Height = pos;
    drawingContext.Close();
}
like image 29
trigger_segfault Avatar answered Sep 09 '25 16:09

trigger_segfault