diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/TextBoxView.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/TextBoxView.cs index 4b9ffec861d..6f24ef80301 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/TextBoxView.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/TextBoxView.cs @@ -2346,43 +2346,45 @@ private void IncrementalMeasureLinesAfterInsert(double constraintWidth, LineProp } } + // Walk back through soft-wrap predecessors (lines without a hard break: + // Length == ContentLength) so the whole wrapped paragraph is re-formatted. + int firstAffectedLineIndex = lineIndex; + if (firstAffectedLineIndex > 0) + { + firstAffectedLineIndex--; + while (firstAffectedLineIndex > 0 && + _lineMetrics[firstAffectedLineIndex - 1].Length == _lineMetrics[firstAffectedLineIndex - 1].ContentLength) + { + firstAffectedLineIndex--; + } + } + TextBoxLine line = new TextBoxLine(this); - int lineOffset; + int lineOffset = _lineMetrics[firstAffectedLineIndex].Offset; bool endOfParagraph = false; - // We need to re-format the previous line, because if someone inserted - // a hard break, the first directly affected line might now be shorter - // and mergeable with its predecessor. - if (lineIndex > 0) // we can skip this if line wrap is disabled. + // Re-format each line from firstAffectedLineIndex through lineIndex, + // absorbing any successor lines fully covered by the new line. + int idx = firstAffectedLineIndex; + while (idx <= lineIndex && !endOfParagraph) { - FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); - } - else - { - lineOffset = _lineMetrics[lineIndex].Offset; - } + FormatIncrementalLine(idx, constraintWidth, lineProperties, line, ref lineOffset, out endOfParagraph); - // Format the line directly affected by the change. - // If endOfParagraph == true, then the line was absorbed into its - // predessor (because its new content is thinner, or because the - // TextWrapping property changed). - if (!endOfParagraph) - { - using (line) + while (idx + 1 < _lineMetrics.Count && lineOffset >= _lineMetrics[idx + 1].EndOffset) { - line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); - - _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); - - lineOffset += line.Length; - endOfParagraph = line.EndOfParagraph; + _lineMetrics.RemoveAt(idx + 1); + RemoveLineVisualRange(idx + 1, 1); + if (idx + 1 <= lineIndex) + { + lineIndex--; + } } - ClearLineVisual(lineIndex); - lineIndex++; + + idx++; } // Recalc the following lines not directly affected as needed. - SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset); + SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, idx, lineOffset); desiredSize = BruteForceCalculateDesiredSize(); } @@ -2429,7 +2431,8 @@ private void IncrementalMeasureLinesAfterDelete(double constraintWidth, LineProp // and mergeable with its predecessor. if (lineIndex > 0) // we can skip this if line wrap is disabled. { - FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); + lineOffset = _lineMetrics[lineIndex - 1].Offset; + FormatIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, ref lineOffset, out endOfParagraph); } else { @@ -2470,15 +2473,10 @@ private void IncrementalMeasureLinesAfterDelete(double constraintWidth, LineProp desiredSize = BruteForceCalculateDesiredSize(); } - // Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete. - // Formats the line preceding the first directly affected line after a TextContainer change. - // In general this line might grow as content in the following line is absorbed. - private void FormatFirstIncrementalLine(int lineIndex, double constraintWidth, LineProperties lineProperties, TextBoxLine line, - out int lineOffset, out bool endOfParagraph) + // Formats the line at lineIndex, updates metrics, and clears the cached visual. + private void FormatIncrementalLine(int lineIndex, double constraintWidth, LineProperties lineProperties, TextBoxLine line, + ref int lineOffset, out bool endOfParagraph) { - int originalEndOffset = _lineMetrics[lineIndex].EndOffset; - lineOffset = _lineMetrics[lineIndex].Offset; - using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); @@ -2489,11 +2487,7 @@ private void FormatFirstIncrementalLine(int lineIndex, double constraintWidth, L endOfParagraph = line.EndOfParagraph; } - // Don't clear the cached Visual unless something changed. - if (originalEndOffset != _lineMetrics[lineIndex].EndOffset) - { - ClearLineVisual(lineIndex); - } + ClearLineVisual(lineIndex); } // Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete.