diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Controls/FastTextBlock.cs b/dnSpy/dnSpy.Contracts.DnSpy/Controls/FastTextBlock.cs index 3c5ff63d0a..4f95f38afc 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Controls/FastTextBlock.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Controls/FastTextBlock.cs @@ -21,18 +21,21 @@ THE SOFTWARE. */ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Windows; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.TextFormatting; +using dnSpy.Contracts.Text; namespace dnSpy.Contracts.Controls { sealed class FastTextBlock : FrameworkElement { public interface IFastTextSource { void UpdateParent(FastTextBlock ftb); TextSource Source { get; } + bool GetNextLineIndex(ref int index); } public string Text { @@ -48,7 +51,6 @@ public FastTextBlock(IFastTextSource src) { this.src = src; } - public static readonly DependencyProperty TextProperty; public static readonly DependencyProperty FontFamilyProperty; public static readonly DependencyProperty FontStyleProperty; @@ -103,7 +105,7 @@ int CacheHash() { TextFormattingMode? textFormattingMode; class TextProps : TextRunProperties { - FastTextBlock tb; + readonly FastTextBlock tb; public TextProps(FastTextBlock tb) => this.tb = tb; @@ -143,11 +145,23 @@ public void UpdateParent(FastTextBlock ftb) { } public TextSource Source => this; + + public bool GetNextLineIndex(ref int index) { + if (index >= text.Length || index < 0) + return false; + index = text.IndexOfAny(LineConstants.newLineChars, index); + if (index < 0) + return false; + if (text[index] == '\r' && index + 1 < text.Length && text[index + 1] == '\n') + index++; + index++; + return true; + } } - internal sealed class ParaProps : TextParagraphProperties { - FastTextBlock tb; - TextProps props; + sealed class ParaProps : TextParagraphProperties { + readonly FastTextBlock tb; + readonly TextProps props; public ParaProps(FastTextBlock tb) { this.tb = tb; @@ -164,9 +178,9 @@ public ParaProps(FastTextBlock tb) { public override TextWrapping TextWrapping => TextWrapping.NoWrap; } - TextFormatter? fmt = null; TextLine? line = null; + List? lines = null; Typeface GetTypeface() { var fontFamily = (FontFamily)GetValue(FontFamilyProperty); @@ -176,17 +190,29 @@ Typeface GetTypeface() { return new Typeface(fontFamily, fontStyle, fontWeight, fontStrech, null); } - IFastTextSource src; + readonly IFastTextSource src; void MakeNewText() { - if (fmt is null) - fmt = TextFormatterFactory.GetTextFormatter(this); + fmt ??= TextFormatterFactory.GetTextFormatter(this); - if (line is not null) - line.Dispose(); + line?.Dispose(); + lines?.Clear(); src.UpdateParent(this); - line = fmt.FormatLine(src.Source, 0, 0, new ParaProps(this), null); + + var paragraphProperties = new ParaProps(this); + line = fmt.FormatLine(src.Source, 0, 0, paragraphProperties, null); + + int index = 0; + while (src.GetNextLineIndex(ref index)) { + if (line is not null) { + lines ??= new List(); + lines.Add(line); + line = null; + } + // lines should not be empty at this point! + lines!.Add(fmt.FormatLine(src.Source, index, 0, paragraphProperties, null)); + } } void EnsureText() { @@ -197,17 +223,34 @@ void EnsureText() { } } - protected override Size MeasureOverride(Size availableSize) { EnsureText(); - Debug2.Assert(line is not null); - return new Size(line.Width, line.Height); + if (line is not null) + return new Size(line.Width, line.Height); + + Debug2.Assert(lines is not null); + var size = new Size(); + for (int i = 0; i < lines.Count; i++) { + var textLine = lines[i]; + size.Width = Math.Max(size.Width, textLine.Width); + size.Height += textLine.Height; + } + return size; } protected override void OnRender(DrawingContext drawingContext) { EnsureText(); - Debug2.Assert(line is not null); - line.Draw(drawingContext, new Point(0, 0), InvertAxes.None); + if (line is not null) { + line.Draw(drawingContext, new Point(0, 0), InvertAxes.None); + return; + } + Debug2.Assert(lines is not null); + double y = 0; + for (int i = 0; i < lines.Count; i++) { + var textLine = lines[i]; + textLine.Draw(drawingContext, new Point(0, y), InvertAxes.None); + y += textLine.Height; + } } } diff --git a/dnSpy/dnSpy/Controls/TextElementFactory.cs b/dnSpy/dnSpy/Controls/TextElementFactory.cs index 13412d7a17..5fa4f50dfc 100644 --- a/dnSpy/dnSpy/Controls/TextElementFactory.cs +++ b/dnSpy/dnSpy/Controls/TextElementFactory.cs @@ -77,7 +77,7 @@ static TextWrapping GetTextWrapping(TextElementFlags flags) { } public static FrameworkElement Create(IClassificationFormatMap classificationFormatMap, string text, IList tags, TextElementFlags flags) { - bool useFastTextBlock = (flags & (TextElementFlags.TrimmingMask | TextElementFlags.WrapMask | TextElementFlags.FilterOutNewLines)) == (TextElementFlags.NoTrimming | TextElementFlags.NoWrap | TextElementFlags.FilterOutNewLines); + bool useFastTextBlock = (flags & (TextElementFlags.TrimmingMask | TextElementFlags.WrapMask)) == (TextElementFlags.NoTrimming | TextElementFlags.NoWrap); bool filterOutNewLines = (flags & TextElementFlags.FilterOutNewLines) != 0; if (tags.Count != 0) { if (useFastTextBlock) { @@ -232,16 +232,15 @@ public override TextRun GetTextRun(int textSourceCharacterIndex) { info = tagsList[collIndex + 1]; } - int startIndex = info.Span.Start; int endIndex = info.Span.End; - int nlIndex = text.IndexOfAny(LineConstants.newLineChars, index, endIndex - startIndex); + int nlIndex = text.IndexOfAny(LineConstants.newLineChars, index, endIndex - index); if (nlIndex > 0) endIndex = nlIndex; var props = classificationFormatMap.GetTextProperties(info.ClassificationType); - var tokenText = text.Substring(index, endIndex - startIndex); + var tokenText = text.Substring(index, endIndex - index); var textProps = new TextProps(); textProps.fontSize = TextElement.GetFontSize(parent); @@ -261,6 +260,18 @@ public override TextRun GetTextRun(int textSourceCharacterIndex) { return new TextCharacters(tokenText.Length == 0 ? " " : tokenText, textProps); } + + public bool GetNextLineIndex(ref int index) { + if (index >= text.Length || index < 0) + return false; + index = text.IndexOfAny(LineConstants.newLineChars, index); + if (index < 0) + return false; + if (text[index] == '\r' && index + 1 < text.Length && text[index + 1] == '\n') + index++; + index++; + return true; + } } } }