Browse Source

AvalonEdit: Use indentation from first line on all following lines when word-wrapping.

pull/14/head
Daniel Grunwald 15 years ago
parent
commit
8266dce10a
  1. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/NewLineElementGenerator.cs
  2. 10
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs
  3. 50
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs
  4. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineElement.cs
  5. 7
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs
  6. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextParagraphProperties.cs
  7. 37
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/NewLineElementGenerator.cs

@ -77,6 +77,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
} }
} }
public override bool IsWhitespace(int visualColumn)
{
return true;
}
public override bool HandlesLineBorders { public override bool HandlesLineBorders {
get { return true; } get { return true; }
} }

10
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs

@ -118,6 +118,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
else else
return -1; return -1;
} }
public override bool IsWhitespace(int visualColumn)
{
return true;
}
} }
sealed class TabTextElement : VisualLineElement sealed class TabTextElement : VisualLineElement
@ -148,6 +153,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
else else
return -1; return -1;
} }
public override bool IsWhitespace(int visualColumn)
{
return true;
}
} }
sealed class TabGlyphRun : TextEmbeddedObject sealed class TabGlyphRun : TextEmbeddedObject

50
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

@ -544,7 +544,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
VisualLine l = GetVisualLine(documentLine.LineNumber); VisualLine l = GetVisualLine(documentLine.LineNumber);
if (l == null) { if (l == null) {
TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties(); TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties();
TextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties); VisualLineTextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties);
while (heightTree.GetIsCollapsed(documentLine)) { while (heightTree.GetIsCollapsed(documentLine)) {
documentLine = documentLine.PreviousLine; documentLine = documentLine.PreviousLine;
@ -710,7 +710,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
double CreateAndMeasureVisualLines(Size availableSize) double CreateAndMeasureVisualLines(Size availableSize)
{ {
TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties(); TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties();
TextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties); VisualLineTextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties);
Debug.WriteLine("Measure availableSize=" + availableSize + ", scrollOffset=" + scrollOffset); Debug.WriteLine("Measure availableSize=" + availableSize + ", scrollOffset=" + scrollOffset);
var firstLineInView = heightTree.GetLineByVisualPosition(scrollOffset.Y); var firstLineInView = heightTree.GetLineByVisualPosition(scrollOffset.Y);
@ -786,7 +786,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
}; };
} }
TextParagraphProperties CreateParagraphProperties(TextRunProperties defaultTextRunProperties) VisualLineTextParagraphProperties CreateParagraphProperties(TextRunProperties defaultTextRunProperties)
{ {
return new VisualLineTextParagraphProperties { return new VisualLineTextParagraphProperties {
defaultTextRunProperties = defaultTextRunProperties, defaultTextRunProperties = defaultTextRunProperties,
@ -797,7 +797,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
VisualLine BuildVisualLine(DocumentLine documentLine, VisualLine BuildVisualLine(DocumentLine documentLine,
TextRunProperties globalTextRunProperties, TextRunProperties globalTextRunProperties,
TextParagraphProperties paragraphProperties, VisualLineTextParagraphProperties paragraphProperties,
VisualLineElementGenerator[] elementGeneratorsArray, VisualLineElementGenerator[] elementGeneratorsArray,
IVisualLineTransformer[] lineTransformersArray, IVisualLineTransformer[] lineTransformersArray,
Size availableSize) Size availableSize)
@ -829,6 +829,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
int textOffset = 0; int textOffset = 0;
TextLineBreak lastLineBreak = null; TextLineBreak lastLineBreak = null;
var textLines = new List<TextLine>(); var textLines = new List<TextLine>();
paragraphProperties.indent = 0;
paragraphProperties.firstLineInParagraph = true;
while (textOffset <= visualLine.VisualLength) { while (textOffset <= visualLine.VisualLength) {
TextLine textLine = formatter.FormatLine( TextLine textLine = formatter.FormatLine(
textSource, textSource,
@ -840,12 +842,52 @@ namespace ICSharpCode.AvalonEdit.Rendering
textLines.Add(textLine); textLines.Add(textLine);
textOffset += textLine.Length; textOffset += textLine.Length;
// exit loop so that we don't do the indentation calculation if there's only a single line
if (textOffset >= visualLine.VisualLength)
break;
if (paragraphProperties.firstLineInParagraph) {
paragraphProperties.firstLineInParagraph = false;
TextEditorOptions options = this.Options;
double indentation = 0;
if (options.InheritWordWrapIndentation) {
// determine indentation for next line:
int indentVisualColumn = GetIndentationVisualColumn(visualLine);
if (indentVisualColumn > 0 && indentVisualColumn < textOffset) {
indentation = textLine.GetDistanceFromCharacterHit(new CharacterHit(indentVisualColumn, 0));
}
}
indentation += options.WordWrapIndentation;
// apply the calculated indentation unless it's more than half of the text editor size:
if (indentation > 0 && indentation * 2 < availableSize.Width)
paragraphProperties.indent = indentation;
}
lastLineBreak = textLine.GetTextLineBreak(); lastLineBreak = textLine.GetTextLineBreak();
} }
visualLine.SetTextLines(textLines); visualLine.SetTextLines(textLines);
heightTree.SetHeight(visualLine.FirstDocumentLine, visualLine.Height); heightTree.SetHeight(visualLine.FirstDocumentLine, visualLine.Height);
return visualLine; return visualLine;
} }
static int GetIndentationVisualColumn(VisualLine visualLine)
{
if (visualLine.Elements.Count == 0)
return 0;
int column = 0;
int elementIndex = 0;
VisualLineElement element = visualLine.Elements[elementIndex];
while (element.IsWhitespace(column)) {
column++;
if (column == element.VisualColumn + element.VisualLength) {
elementIndex++;
if (elementIndex == visualLine.Elements.Count)
break;
element = visualLine.Elements[elementIndex];
}
}
return column;
}
#endregion #endregion
#region Arrange #region Arrange

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineElement.cs

@ -200,6 +200,14 @@ namespace ICSharpCode.AvalonEdit.Rendering
return -1; return -1;
} }
/// <summary>
/// Gets whether the specified offset in this element is considered whitespace.
/// </summary>
public virtual bool IsWhitespace(int visualColumn)
{
return false;
}
/// <summary> /// <summary>
/// Gets whether the <see cref="GetNextCaretPosition"/> implementation handles line borders. /// Gets whether the <see cref="GetNextCaretPosition"/> implementation handles line borders.
/// If this property returns false, the caller of GetNextCaretPosition should handle the line /// If this property returns false, the caller of GetNextCaretPosition should handle the line

7
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs

@ -57,6 +57,13 @@ namespace ICSharpCode.AvalonEdit.Rendering
return new TextCharacters(text, 0, text.Length, this.TextRunProperties); return new TextCharacters(text, 0, text.Length, this.TextRunProperties);
} }
/// <inheritdoc/>
public override bool IsWhitespace(int visualColumn)
{
int offset = visualColumn - this.VisualColumn + parentVisualLine.FirstDocumentLine.Offset + this.RelativeTextOffset;
return char.IsWhiteSpace(parentVisualLine.Document.GetCharAt(offset));
}
/// <inheritdoc/> /// <inheritdoc/>
public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context) public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context)
{ {

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextParagraphProperties.cs

@ -7,11 +7,13 @@ using System.Windows.Media.TextFormatting;
namespace ICSharpCode.AvalonEdit.Rendering namespace ICSharpCode.AvalonEdit.Rendering
{ {
class VisualLineTextParagraphProperties : TextParagraphProperties sealed class VisualLineTextParagraphProperties : TextParagraphProperties
{ {
internal TextRunProperties defaultTextRunProperties; internal TextRunProperties defaultTextRunProperties;
internal TextWrapping textWrapping; internal TextWrapping textWrapping;
internal double tabSize; internal double tabSize;
internal double indent;
internal bool firstLineInParagraph;
public override double DefaultIncrementalTab { public override double DefaultIncrementalTab {
get { return tabSize; } get { return tabSize; }
@ -20,10 +22,10 @@ namespace ICSharpCode.AvalonEdit.Rendering
public override FlowDirection FlowDirection { get { return FlowDirection.LeftToRight; } } public override FlowDirection FlowDirection { get { return FlowDirection.LeftToRight; } }
public override TextAlignment TextAlignment { get { return TextAlignment.Left; } } public override TextAlignment TextAlignment { get { return TextAlignment.Left; } }
public override double LineHeight { get { return double.NaN; } } public override double LineHeight { get { return double.NaN; } }
public override bool FirstLineInParagraph { get { return false; } } public override bool FirstLineInParagraph { get { return firstLineInParagraph; } }
public override TextRunProperties DefaultTextRunProperties { get { return defaultTextRunProperties; } } public override TextRunProperties DefaultTextRunProperties { get { return defaultTextRunProperties; } }
public override TextWrapping TextWrapping { get { return textWrapping; } } public override TextWrapping TextWrapping { get { return textWrapping; } }
public override TextMarkerProperties TextMarkerProperties { get { return null; } } public override TextMarkerProperties TextMarkerProperties { get { return null; } }
public override double Indent { get { return 0; } } public override double Indent { get { return indent; } }
} }
} }

37
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs

@ -267,5 +267,42 @@ namespace ICSharpCode.AvalonEdit
} }
} }
} }
double wordWrapIndentation = 100;
/// <summary>
/// Gets/Sets the indentation used for all lines except the first when word-wrapping.
/// The default value is 0.
/// </summary>
[DefaultValue(100)]
public virtual double WordWrapIndentation {
get { return wordWrapIndentation; }
set {
if (double.IsNaN(value) || double.IsInfinity(value))
throw new ArgumentOutOfRangeException("value", value, "value must not be NaN/infinity");
if (value != wordWrapIndentation) {
wordWrapIndentation = value;
OnPropertyChanged("WordWrapIndentation");
}
}
}
bool inheritWordWrapIndentation = true;
/// <summary>
/// Gets/Sets whether the indentation is inherited from the first line when word-wrapping.
/// The default value is true.
/// </summary>
/// <remarks>When combined with <see cref="WordWrapIndentation"/>, the inherited indentation is added to the word wrap indentation.</remarks>
[DefaultValue(true)]
public virtual bool InheritWordWrapIndentation {
get { return inheritWordWrapIndentation; }
set {
if (value != inheritWordWrapIndentation) {
inheritWordWrapIndentation = value;
OnPropertyChanged("InheritWordWrapIndentation");
}
}
}
} }
} }

Loading…
Cancel
Save