问题
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
回答1:
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()
.
回答2:
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();
}
回答3:
I know this is an old post, but I just came across this with the same question. I managed to add line spacing very easily with the following modifications:
- Add a new
double
dependency property toTextView
calledLineSpacing
with a default value of1.0
- In
VisualLine.SetTextLines
, multiplyline.Height
bytextView.LineSpacing
- In
VisualLine.GetTextLineVisualYPosition
andVisualLine.GetTextLineByVisualYPosition
, multiply each occurrence oftl.Height
bytextView.LineSpacing
Then you can set TextView.LineSpacing
directly in code or through a custom XAML style, etc.
That seemed to be all that was necessary.
PS - AvalonEdit is fantastic!
回答4:
In addition to what @Robert Jordan said, there is a couple more changes i had to make to get a satisfactory result.
First off, to make the text marking overlay to cover the entire height of the line, change the following lines in BackgroundGeometryBuilder.cs
ProcessTextLines method, line 196:
Line 215:
yield return new Rect(pos, y, 1, line.Height * textView.LineSpacing);
Line 259:
lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height * textView.LineSpacing);
Line 259:
Rect extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height * textView.LineSpacing);
The second issue i had was that the text was rendered at the top of the now extended line height, not the bottom. To fix that, the extra space needs to be added before the line is rendered, not after. So make the following changes:
VisualLine.cs, Line 714
public VisualLineDrawingVisual(VisualLine visualLine, double lineSpacing) {
this.VisualLine = visualLine;
var drawingContext = RenderOpen();
double pos = 0;
foreach (TextLine textLine in visualLine.TextLines) {
pos +=(textLine.Height * lineSpacing)-textLine.Height
textLine.Draw(drawingContext, new Point(0, pos), InvertAxes.None);
pos += textLine.Height;
}
this.Height = pos;
drawingContext.Close();
}
In addition, i found it trivial to convert this to support dynamic line-by-line line spacings. Just move the LineSpacing property to VisualLine, then add a VisualLineTransformer like this:
class LinePaddingTransformer : IVisualLineTransformer
{
public LinePaddingTransformer()
{
}
public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements)
{
int index = context.VisualLine.FirstDocumentLine.LineNumber - 1;
//You need to calculate your padding from the line index somehow.
//for example create a list of objects with the spacing in them and pass to this transformer.
double spacing= MyCalculateLinePaddingMethod(index);
context.VisualLine.LineSpacing= spacing;
}
}
If you require the spacing to only be added to the first line in case of text wrapping, you need to go over every single place you previously multiplied with the LineSpacing property and put a check if the TextLine is the first line in the collection.
来源:https://stackoverflow.com/questions/12205676/changing-default-line-height-in-avalon-edit