|
|
|
@ -23,9 +23,18 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
public sealed class VisualLine |
|
|
|
public sealed class VisualLine |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
enum LifetimePhase : byte |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Generating, |
|
|
|
|
|
|
|
Transforming, |
|
|
|
|
|
|
|
Live, |
|
|
|
|
|
|
|
Disposed |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TextView textView; |
|
|
|
TextView textView; |
|
|
|
List<VisualLineElement> elements; |
|
|
|
List<VisualLineElement> elements; |
|
|
|
internal bool hasInlineObjects; |
|
|
|
internal bool hasInlineObjects; |
|
|
|
|
|
|
|
LifetimePhase phase; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the document to which this VisualLine belongs.
|
|
|
|
/// Gets the document to which this VisualLine belongs.
|
|
|
|
@ -47,10 +56,18 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
public ReadOnlyCollection<VisualLineElement> Elements { get; private set; } |
|
|
|
public ReadOnlyCollection<VisualLineElement> Elements { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ReadOnlyCollection<TextLine> textLines; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets a read-only collection of text lines.
|
|
|
|
/// Gets a read-only collection of text lines.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
public ReadOnlyCollection<TextLine> TextLines { get; private set; } |
|
|
|
public ReadOnlyCollection<TextLine> TextLines { |
|
|
|
|
|
|
|
get { |
|
|
|
|
|
|
|
if (phase < LifetimePhase.Live) |
|
|
|
|
|
|
|
throw new InvalidOperationException(); |
|
|
|
|
|
|
|
return textLines; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the start offset of the VisualLine inside the document.
|
|
|
|
/// Gets the start offset of the VisualLine inside the document.
|
|
|
|
@ -99,6 +116,7 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
|
|
|
|
|
|
|
|
internal void ConstructVisualElements(ITextRunConstructionContext context, VisualLineElementGenerator[] generators) |
|
|
|
internal void ConstructVisualElements(ITextRunConstructionContext context, VisualLineElementGenerator[] generators) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(phase == LifetimePhase.Generating); |
|
|
|
foreach (VisualLineElementGenerator g in generators) { |
|
|
|
foreach (VisualLineElementGenerator g in generators) { |
|
|
|
g.StartGeneration(context); |
|
|
|
g.StartGeneration(context); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -108,8 +126,13 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
g.FinishGeneration(); |
|
|
|
g.FinishGeneration(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var globalTextRunProperties = context.GlobalTextRunProperties; |
|
|
|
|
|
|
|
foreach (var element in elements) { |
|
|
|
|
|
|
|
element.SetTextRunProperties(new VisualLineElementTextRunProperties(globalTextRunProperties)); |
|
|
|
|
|
|
|
} |
|
|
|
this.Elements = elements.AsReadOnly(); |
|
|
|
this.Elements = elements.AsReadOnly(); |
|
|
|
CalculateOffsets(context.GlobalTextRunProperties); |
|
|
|
CalculateOffsets(); |
|
|
|
|
|
|
|
phase = LifetimePhase.Transforming; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void PerformVisualElementConstruction(VisualLineElementGenerator[] generators) |
|
|
|
void PerformVisualElementConstruction(VisualLineElementGenerator[] generators) |
|
|
|
@ -168,14 +191,13 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CalculateOffsets(TextRunProperties globalTextRunProperties) |
|
|
|
void CalculateOffsets() |
|
|
|
{ |
|
|
|
{ |
|
|
|
int visualOffset = 0; |
|
|
|
int visualOffset = 0; |
|
|
|
int textOffset = 0; |
|
|
|
int textOffset = 0; |
|
|
|
foreach (VisualLineElement element in Elements) { |
|
|
|
foreach (VisualLineElement element in elements) { |
|
|
|
element.VisualColumn = visualOffset; |
|
|
|
element.VisualColumn = visualOffset; |
|
|
|
element.RelativeTextOffset = textOffset; |
|
|
|
element.RelativeTextOffset = textOffset; |
|
|
|
element.SetTextRunProperties(new VisualLineElementTextRunProperties(globalTextRunProperties)); |
|
|
|
|
|
|
|
visualOffset += element.VisualLength; |
|
|
|
visualOffset += element.VisualLength; |
|
|
|
textOffset += element.DocumentLength; |
|
|
|
textOffset += element.DocumentLength; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -185,6 +207,7 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
|
|
|
|
|
|
|
|
internal void RunTransformers(ITextRunConstructionContext context, IVisualLineTransformer[] transformers) |
|
|
|
internal void RunTransformers(ITextRunConstructionContext context, IVisualLineTransformer[] transformers) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(phase == LifetimePhase.Transforming); |
|
|
|
foreach (IVisualLineTransformer transformer in transformers) { |
|
|
|
foreach (IVisualLineTransformer transformer in transformers) { |
|
|
|
transformer.Transform(context, elements); |
|
|
|
transformer.Transform(context, elements); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -197,11 +220,50 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
phase = LifetimePhase.Live; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
|
|
/// Replaces the single element at <paramref name="elementIndex"/> with the specified elements.
|
|
|
|
|
|
|
|
/// The replacement operation must preserve the document length, but may change the visual length.
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
|
|
/// This method may only be called by line transformers.
|
|
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
|
|
public void ReplaceElement(int elementIndex, params VisualLineElement[] newElements) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ReplaceElement(elementIndex, 1, newElements); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
|
|
/// Replaces <paramref name="count"/> elements starting at <paramref name="elementIndex"/> with the specified elements.
|
|
|
|
|
|
|
|
/// The replacement operation must preserve the document length, but may change the visual length.
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
|
|
/// This method may only be called by line transformers.
|
|
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
|
|
public void ReplaceElement(int elementIndex, int count, params VisualLineElement[] newElements) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (phase != LifetimePhase.Transforming) |
|
|
|
|
|
|
|
throw new InvalidOperationException("This method may only be called by line transformers."); |
|
|
|
|
|
|
|
int oldDocumentLength = 0; |
|
|
|
|
|
|
|
for (int i = elementIndex; i < elementIndex + count; i++) { |
|
|
|
|
|
|
|
oldDocumentLength += elements[i].DocumentLength; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int newDocumentLength = 0; |
|
|
|
|
|
|
|
foreach (var newElement in newElements) { |
|
|
|
|
|
|
|
newDocumentLength += newElement.DocumentLength; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (oldDocumentLength != newDocumentLength) |
|
|
|
|
|
|
|
throw new InvalidOperationException("Old elements have document length " + oldDocumentLength + ", but new elements have length " + newDocumentLength); |
|
|
|
|
|
|
|
elements.RemoveRange(elementIndex, count); |
|
|
|
|
|
|
|
elements.InsertRange(elementIndex, newElements); |
|
|
|
|
|
|
|
CalculateOffsets(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
internal void SetTextLines(List<TextLine> textLines) |
|
|
|
internal void SetTextLines(List<TextLine> textLines) |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.TextLines = textLines.AsReadOnly(); |
|
|
|
this.textLines = textLines.AsReadOnly(); |
|
|
|
Height = 0; |
|
|
|
Height = 0; |
|
|
|
foreach (TextLine line in textLines) |
|
|
|
foreach (TextLine line in textLines) |
|
|
|
Height += line.Height; |
|
|
|
Height += line.Height; |
|
|
|
@ -456,7 +518,20 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets whether the visual line was disposed.
|
|
|
|
/// Gets whether the visual line was disposed.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
public bool IsDisposed { get; internal set; } |
|
|
|
public bool IsDisposed { |
|
|
|
|
|
|
|
get { return phase == LifetimePhase.Disposed; } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal void Dispose() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (phase == LifetimePhase.Disposed) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
Debug.Assert(phase == LifetimePhase.Live); |
|
|
|
|
|
|
|
phase = LifetimePhase.Disposed; |
|
|
|
|
|
|
|
foreach (TextLine textLine in TextLines) { |
|
|
|
|
|
|
|
textLine.Dispose(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the next possible caret position after visualColumn, or -1 if there is no caret position.
|
|
|
|
/// Gets the next possible caret position after visualColumn, or -1 if there is no caret position.
|
|
|
|
@ -567,6 +642,7 @@ namespace ICSharpCode.AvalonEdit.Rendering |
|
|
|
|
|
|
|
|
|
|
|
internal VisualLineDrawingVisual Render() |
|
|
|
internal VisualLineDrawingVisual Render() |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(phase == LifetimePhase.Live); |
|
|
|
if (visual == null) |
|
|
|
if (visual == null) |
|
|
|
visual = new VisualLineDrawingVisual(this); |
|
|
|
visual = new VisualLineDrawingVisual(this); |
|
|
|
return visual; |
|
|
|
return visual; |
|
|
|
|