diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/DefaultTextRunTypographyProperties.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/DefaultTextRunTypographyProperties.cs index f2e9fca09d..a725417b3e 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/DefaultTextRunTypographyProperties.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/DefaultTextRunTypographyProperties.cs @@ -12,115 +12,158 @@ namespace ICSharpCode.AvalonEdit.Rendering /// public class DefaultTextRunTypographyProperties : TextRunTypographyProperties { + /// public override FontVariants Variants { get { return FontVariants.Normal; } } + /// public override bool StylisticSet1 { get { return false; } } + /// public override bool StylisticSet2 { get { return false; } } + /// public override bool StylisticSet3 { get { return false; } } + /// public override bool StylisticSet4 { get { return false; } } + /// public override bool StylisticSet5 { get { return false; } } + /// public override bool StylisticSet6 { get { return false; } } + /// public override bool StylisticSet7 { get { return false; } } + /// public override bool StylisticSet8 { get { return false; } } + /// public override bool StylisticSet9 { get { return false; } } + /// public override bool StylisticSet10 { get { return false; } } + /// public override bool StylisticSet11 { get { return false; } } + /// public override bool StylisticSet12 { get { return false; } } + /// public override bool StylisticSet13 { get { return false; } } + /// public override bool StylisticSet14 { get { return false; } } + /// public override bool StylisticSet15 { get { return false; } } + /// public override bool StylisticSet16 { get { return false; } } + /// public override bool StylisticSet17 { get { return false; } } + /// public override bool StylisticSet18 { get { return false; } } + /// public override bool StylisticSet19 { get { return false; } } + /// public override bool StylisticSet20 { get { return false; } } + /// public override int StylisticAlternates { get { return 0; } } + /// public override int StandardSwashes { get { return 0; } } + /// public override bool StandardLigatures { get { return true; } } + /// public override bool SlashedZero { get { return false; } } + /// public override FontNumeralStyle NumeralStyle { get { return FontNumeralStyle.Normal; } } + /// public override FontNumeralAlignment NumeralAlignment { get { return FontNumeralAlignment.Normal; } } + /// public override bool MathematicalGreek { get { return false; } } + /// public override bool Kerning { get { return true; } } + /// public override bool HistoricalLigatures { get { return false; } } + /// public override bool HistoricalForms { get { return false; } } + /// public override FontFraction Fraction { get { return FontFraction.Normal; } } + /// public override FontEastAsianWidths EastAsianWidths { get { return FontEastAsianWidths.Normal; } } + /// public override FontEastAsianLanguage EastAsianLanguage { get { return FontEastAsianLanguage.Normal; } } + /// public override bool EastAsianExpertForms { get { return false; } } + /// public override bool DiscretionaryLigatures { get { return false; } } + /// public override int ContextualSwashes { get { return 0; } } + /// public override bool ContextualLigatures { get { return true; } } + /// public override bool ContextualAlternates { get { return true; } } + /// public override bool CaseSensitiveForms { get { return false; } } + /// public override bool CapitalSpacing { get { return false; } } + /// public override FontCapitals Capitals { get { return FontCapitals.Normal; } } + /// public override int AnnotationAlternates { get { return 0; } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs index 2482f16f1a..6687c3a1f2 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs @@ -696,10 +696,7 @@ namespace ICSharpCode.AvalonEdit.Rendering throw new ArgumentException("Cannot dispose visual line because it is in construction!"); } visibleVisualLines = null; - visualLine.IsDisposed = true; - foreach (TextLine textLine in visualLine.TextLines) { - textLine.Dispose(); - } + visualLine.Dispose(); RemoveInlineObjects(visualLine); } #endregion diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs index fb0f0133d7..2dce9c29a0 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs @@ -23,9 +23,18 @@ namespace ICSharpCode.AvalonEdit.Rendering /// public sealed class VisualLine { + enum LifetimePhase : byte + { + Generating, + Transforming, + Live, + Disposed + } + TextView textView; List elements; internal bool hasInlineObjects; + LifetimePhase phase; /// /// Gets the document to which this VisualLine belongs. @@ -47,10 +56,18 @@ namespace ICSharpCode.AvalonEdit.Rendering /// public ReadOnlyCollection Elements { get; private set; } + ReadOnlyCollection textLines; + /// /// Gets a read-only collection of text lines. /// - public ReadOnlyCollection TextLines { get; private set; } + public ReadOnlyCollection TextLines { + get { + if (phase < LifetimePhase.Live) + throw new InvalidOperationException(); + return textLines; + } + } /// /// 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) { + Debug.Assert(phase == LifetimePhase.Generating); foreach (VisualLineElementGenerator g in generators) { g.StartGeneration(context); } @@ -108,8 +126,13 @@ namespace ICSharpCode.AvalonEdit.Rendering g.FinishGeneration(); } + var globalTextRunProperties = context.GlobalTextRunProperties; + foreach (var element in elements) { + element.SetTextRunProperties(new VisualLineElementTextRunProperties(globalTextRunProperties)); + } this.Elements = elements.AsReadOnly(); - CalculateOffsets(context.GlobalTextRunProperties); + CalculateOffsets(); + phase = LifetimePhase.Transforming; } void PerformVisualElementConstruction(VisualLineElementGenerator[] generators) @@ -168,14 +191,13 @@ namespace ICSharpCode.AvalonEdit.Rendering } } - void CalculateOffsets(TextRunProperties globalTextRunProperties) + void CalculateOffsets() { int visualOffset = 0; int textOffset = 0; - foreach (VisualLineElement element in Elements) { + foreach (VisualLineElement element in elements) { element.VisualColumn = visualOffset; element.RelativeTextOffset = textOffset; - element.SetTextRunProperties(new VisualLineElementTextRunProperties(globalTextRunProperties)); visualOffset += element.VisualLength; textOffset += element.DocumentLength; } @@ -185,6 +207,7 @@ namespace ICSharpCode.AvalonEdit.Rendering internal void RunTransformers(ITextRunConstructionContext context, IVisualLineTransformer[] transformers) { + Debug.Assert(phase == LifetimePhase.Transforming); foreach (IVisualLineTransformer transformer in transformers) { transformer.Transform(context, elements); } @@ -197,11 +220,50 @@ namespace ICSharpCode.AvalonEdit.Rendering } } } + phase = LifetimePhase.Live; + } + + /// + /// Replaces the single element at with the specified elements. + /// The replacement operation must preserve the document length, but may change the visual length. + /// + /// + /// This method may only be called by line transformers. + /// + public void ReplaceElement(int elementIndex, params VisualLineElement[] newElements) + { + ReplaceElement(elementIndex, 1, newElements); + } + + /// + /// Replaces elements starting at with the specified elements. + /// The replacement operation must preserve the document length, but may change the visual length. + /// + /// + /// This method may only be called by line transformers. + /// + 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 textLines) { - this.TextLines = textLines.AsReadOnly(); + this.textLines = textLines.AsReadOnly(); Height = 0; foreach (TextLine line in textLines) Height += line.Height; @@ -456,7 +518,20 @@ namespace ICSharpCode.AvalonEdit.Rendering /// /// Gets whether the visual line was disposed. /// - 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(); + } + } /// /// 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() { + Debug.Assert(phase == LifetimePhase.Live); if (visual == null) visual = new VisualLineDrawingVisual(this); return visual;