From dcea7bc6fe68451a575a1c9a3a26680bae123828 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 01:49:54 +0200 Subject: [PATCH 01/14] Use ReadOnlyCollection.Empty over new ReadOnlyCollection(new List(0)) --- .../System/Windows/Controls/TextBlock.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index 72c1e1cd5d7..74860b3a933 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -1747,7 +1747,7 @@ protected virtual ReadOnlyCollection GetRectanglesCore(ContentElement chil if (!IsLayoutDataValid) { // return empty collection - return new ReadOnlyCollection(new List(0)); + return ReadOnlyCollection.Empty; } // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing. @@ -1757,20 +1757,20 @@ protected virtual ReadOnlyCollection GetRectanglesCore(ContentElement chil if (_complexContent == null || !(_complexContent.TextContainer is TextContainer)) { // return empty collection - return new ReadOnlyCollection(new List(0)); + return ReadOnlyCollection.Empty; } // First find the element start and end position - TextPointer start = FindElementPosition((IInputElement)child); + TextPointer start = FindElementPosition(child); if (start == null) { - return new ReadOnlyCollection(new List(0)); + return ReadOnlyCollection.Empty; } TextPointer end = null; - if (child is TextElement) + if (child is TextElement element) { - end = new TextPointer(((TextElement)child).ElementEnd); + end = new TextPointer(element.ElementEnd); } else if (child is FrameworkContentElement) { @@ -1780,7 +1780,7 @@ protected virtual ReadOnlyCollection GetRectanglesCore(ContentElement chil if (end == null) { - return new ReadOnlyCollection(new List(0)); + return ReadOnlyCollection.Empty; } int startOffset = _complexContent.TextContainer.Start.GetOffsetToPosition(start); From d96501d8e9a7eeaf40755e415fd241c5406c6f76 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 01:54:14 +0200 Subject: [PATCH 02/14] Pre-allocate the List in GetRangeBounds --- .../PresentationFramework/MS/Internal/Text/Line.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs index 15fc3bd89c1..b4fb9594d97 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs @@ -161,9 +161,7 @@ internal Rect GetBoundsFromTextPosition(int characterIndex, out FlowDirection fl /// of the text bounds. /// internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffset) - { - List rectangles = new List(); - + { // Adjust x offset for trailing spaces double delta = CalculateXOffsetShift(); double adjustedXOffset = xOffset + delta; @@ -173,7 +171,7 @@ internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffs { // We should not shift offset in this case Invariant.Assert(DoubleUtil.AreClose(delta, 0)); - System.Windows.Media.TextFormatting.TextLine line = _line.Collapse(GetCollapsingProps(_wrappingWidth, _owner.ParagraphProperties)); + TextLine line = _line.Collapse(GetCollapsingProps(_wrappingWidth, _owner.ParagraphProperties)); Invariant.Assert(line.HasCollapsed, "Line has not been collapsed"); textBounds = line.GetTextBounds(cp, cch); } @@ -181,8 +179,10 @@ internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffs { textBounds = _line.GetTextBounds(cp, cch); } - Invariant.Assert(textBounds.Count > 0); + // Pre-allocate List as we need it + Invariant.Assert(textBounds.Count > 0); + List rectangles = new(textBounds.Count); for (int boundIndex = 0; boundIndex < textBounds.Count; boundIndex++) { @@ -191,6 +191,7 @@ internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffs rect.Y += yOffset; rectangles.Add(rect); } + return rectangles; } From 776f37d802d721e3eb64427f496dcaf0afa35a76 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 02:01:48 +0200 Subject: [PATCH 03/14] Use ReadOnlyCollection.Empty and stop creating a List(1) --- .../System/Windows/Controls/TextBlock.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index 74860b3a933..f30f2e5acea 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -1859,26 +1859,22 @@ protected virtual IEnumerator HostedElementsCore { get { - if(CheckFlags(Flags.ContentChangeInProgress)) + if (CheckFlags(Flags.ContentChangeInProgress)) { - #pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.TextContainerChangingReentrancyInvalid); } if (_complexContent == null || !(_complexContent.TextContainer is TextContainer)) { // Return empty collection - return new HostedElements(new ReadOnlyCollection(new List(0))); + return new HostedElements(ReadOnlyCollection.Empty); } // Create a TextSegment from TextContainer, use it to return enumerator - System.Collections.Generic.List textSegmentsList = new System.Collections.Generic.List(1); - TextSegment textSegment = new TextSegment(_complexContent.TextContainer.Start, _complexContent.TextContainer.End); - textSegmentsList.Insert(0, textSegment); - ReadOnlyCollection textSegments = new ReadOnlyCollection(textSegmentsList); + TextSegment[] textSegment = new TextSegment[1] { new TextSegment(_complexContent.TextContainer.Start, _complexContent.TextContainer.End) }; // Return enumerator created from textSegments - return new HostedElements(textSegments); + return new HostedElements(new ReadOnlyCollection(textSegment)); } } From 973d7cd199863989c72416bb14ed394d99249f07 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 02:07:21 +0200 Subject: [PATCH 04/14] Easy pattern-matching --- .../System/Windows/Controls/TextBlock.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index f30f2e5acea..1342cb33586 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -3468,10 +3468,7 @@ private void AlignContent() // ------------------------------------------------------------------ private static void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs args) { - TextBlock textBlock = sender as TextBlock; - ContentElement child = args.TargetObject as ContentElement; - - if (textBlock != null && child != null) + if (sender is TextBlock textBlock && args.TargetObject is ContentElement child) { if (TextBlock.ContainsContentElement(textBlock, child)) { From a42a69bd146591c8c2857d96668c338cfabcdbc3 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 18:51:55 +0200 Subject: [PATCH 05/14] Remove uncalled CalcContentOffset in AlignContent --- .../System/Windows/Controls/TextBlock.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index 1342cb33586..8362ef86de6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -3416,19 +3416,18 @@ private void AlignContent() LineProperties lineProperties = GetLineProperties(); double wrappingWidth = CalcWrappingWidth(RenderSize.Width); - Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth); // Create / format all lines. // Since we are disposing line object, it can be reused to format following lines. Line line = CreateLine(lineProperties); - TextRunCache textRunCache = new TextRunCache(); + TextRunCache textRunCache = new(); int dcp = 0; double lineOffset = 0; int lineCount = LineCount; for (int i = 0; i < lineCount; i++) { -Debug.Assert(lineCount == LineCount); + Debug.Assert(lineCount == LineCount); LineMetrics lineMetrics = GetLine(i); using (line) @@ -3438,7 +3437,7 @@ private void AlignContent() double lineHeight = CalcLineAdvance(line.Height, lineProperties); // Check consistency of line formatting - MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync"); + Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync"); Debug.Assert(DoubleUtil.AreClose(lineHeight, lineMetrics.Height), "Line height is out of sync."); // Calculated line width might be different from measure width in following cases: From a55a15da8c853cd0c9813929d4719ae6f49213da Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 18:53:42 +0200 Subject: [PATCH 06/14] Remove uncalled dead code --- .../System/Windows/Controls/TextBlock.cs | 54 ++----------------- 1 file changed, 3 insertions(+), 51 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index 8362ef86de6..f9279f172f7 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -2919,19 +2919,6 @@ private void EnsureComplexContent(ITextContainer textContainer) } } - // ------------------------------------------------------------------ - // Make sure that complex content is cleared. - // ------------------------------------------------------------------ - private void ClearComplexContent() - { - if (_complexContent != null) - { - _complexContent.Detach(this); - _complexContent = null; - Invariant.Assert(_contentCache == null, "Content cache should be null when complex content exists."); - } - } - // ------------------------------------------------------------------ // Invalidates a portion of text affected by a highlight change. // ------------------------------------------------------------------ @@ -3566,57 +3553,22 @@ private bool CheckFlags(Flags flags) // ------------------------------------------------------------------ private void VerifyReentrancy() { - if(CheckFlags(Flags.MeasureInProgress)) + if (CheckFlags(Flags.MeasureInProgress)) { throw new InvalidOperationException(SR.MeasureReentrancyInvalid); } - if(CheckFlags(Flags.ArrangeInProgress)) + if (CheckFlags(Flags.ArrangeInProgress)) { throw new InvalidOperationException(SR.ArrangeReentrancyInvalid); } - if(CheckFlags(Flags.ContentChangeInProgress)) + if (CheckFlags(Flags.ContentChangeInProgress)) { throw new InvalidOperationException(SR.TextContainerChangingReentrancyInvalid); } } - /// - /// Returns index of the line that starts at the given dcp. Returns -1 if - /// no line or the line metrics collection starts at the given dcp - /// - /// - /// Start dcp of required line - /// - private int GetLineIndexFromDcp(int dcpLine) - { - Invariant.Assert(dcpLine >= 0); - int lineIndex = 0; - int lineStartOffset = 0; - - int lineCount = LineCount; - while (lineIndex < lineCount) - { -Debug.Assert(lineCount == LineCount); - if (lineStartOffset == dcpLine) - { - // Found line that starts at given dcp - return lineIndex; - } - else - { - lineStartOffset += GetLine(lineIndex).Length; - ++lineIndex; - } - } - - // No line found starting at this position. Return -1. - // We should never hit this code - Invariant.Assert(false, "Dcp passed is not at start of any line in TextBlock"); - return -1; - } - // ------------------------------------------------------------------ // IContentHost Helpers // ------------------------------------------------------------------ From adf59639b33b14f8f7f221536a5b67640b626194 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 18:55:41 +0200 Subject: [PATCH 07/14] Remove empty Initialize function --- .../System/Windows/Controls/TextBlock.cs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index f9279f172f7..67a0840454e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -337,25 +337,16 @@ static TextBlock() /// /// Initializes a new instance of TextBlock class. /// - public TextBlock() : base() - { - Initialize(); - } + public TextBlock() : base() { } /// /// Initializes a new inslace of TextBlock class and adds a first Inline to its Inline collection. /// - public TextBlock(Inline inline) - : base() + public TextBlock(Inline inline) : base() { - Initialize(); ArgumentNullException.ThrowIfNull(inline); - this.Inlines.Add(inline); - } - - private void Initialize() - { + Inlines.Add(inline); } #endregion Constructors From e94f222e315d40771373b9d5bc10ad64a174bc85 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 19:27:23 +0200 Subject: [PATCH 08/14] Use ReadOnlySpan/Rect[] combo over (I)List, simplify and speed up loop --- .../MS/Internal/Text/Line.cs | 8 ++-- .../System/Windows/Controls/TextBlock.cs | 40 +++++++++---------- .../System/Windows/Documents/CaretElement.cs | 7 ++++ 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs index b4fb9594d97..fdadd53dad5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs @@ -160,7 +160,7 @@ internal Rect GetBoundsFromTextPosition(int characterIndex, out FlowDirection fl /// it uses those as the bounding rectangles. If not, it returns the rectangle for the first (and only) element /// of the text bounds. /// - internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffset) + internal ReadOnlySpan GetRangeBounds(int cp, int cch, double xOffset, double yOffset) { // Adjust x offset for trailing spaces double delta = CalculateXOffsetShift(); @@ -182,14 +182,14 @@ internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffs // Pre-allocate List as we need it Invariant.Assert(textBounds.Count > 0); - List rectangles = new(textBounds.Count); + Rect[] rectangles = new Rect[textBounds.Count]; - for (int boundIndex = 0; boundIndex < textBounds.Count; boundIndex++) + for (int boundIndex = 0; boundIndex < rectangles.Length; boundIndex++) { Rect rect = textBounds[boundIndex].Rectangle; rect.X += adjustedXOffset; rect.Y += yOffset; - rectangles.Add(rect); + rectangles[boundIndex] = rect; } return rectangles; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index 67a0840454e..ae7578437da 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -1826,8 +1826,8 @@ protected virtual ReadOnlyCollection GetRectanglesCore(ContentElement chil double xOffset = contentOffset.X; double yOffset = contentOffset.Y + lineHeightOffset; - List lineBounds = line.GetRangeBounds(boundStart, boundEnd - boundStart, xOffset, yOffset); - Debug.Assert(lineBounds.Count > 0); + ReadOnlySpan lineBounds = line.GetRangeBounds(boundStart, boundEnd - boundStart, xOffset, yOffset); + Debug.Assert(lineBounds.Length > 0); rectangles.AddRange(lineBounds); } } @@ -2312,7 +2312,7 @@ internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPo if (Invariant.Strict) { // Check consistency of line formatting - MS.Internal.Invariant.Assert(GetLine(i).Length == line.Length, "Line length is out of sync"); + Invariant.Assert(GetLine(i).Length == line.Length, "Line length is out of sync"); } int dcpStart = Math.Max(dcpLineStart, dcpPositionStart); @@ -2320,28 +2320,24 @@ internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPo if (dcpStart != dcpEnd) { - IList aryTextBounds = line.GetRangeBounds(dcpStart, dcpEnd - dcpStart, contentOffset.X, contentOffset.Y + lineOffset); - - if (aryTextBounds.Count > 0) + ReadOnlySpan aryTextBounds = line.GetRangeBounds(dcpStart, dcpEnd - dcpStart, contentOffset.X, contentOffset.Y + lineOffset); + if (!aryTextBounds.IsEmpty) { - int j = 0; - int c = aryTextBounds.Count; + // Process the bounds minus the last one + for (int j = 0; j < aryTextBounds.Length - 1; j++) + { + CaretElement.AddGeometry(ref geometry, new RectangleGeometry(aryTextBounds[j])); + } - do + // Process the last bounds entry + Rect lastRect = aryTextBounds[aryTextBounds.Length - 1]; + if (dcpPositionEnd >= dcpLineEnd && TextPointerBase.IsNextToAnyBreak(endOfLineTextPointer, LogicalDirection.Backward)) { - Rect rect = aryTextBounds[j]; - - if (j == (c - 1) - && dcpPositionEnd >= dcpLineEnd - && TextPointerBase.IsNextToAnyBreak(endOfLineTextPointer, LogicalDirection.Backward)) - { - double endOfParaGlyphWidth = FontSize * CaretElement.c_endOfParaMagicMultiplier; - rect.Width = rect.Width + endOfParaGlyphWidth; - } - - RectangleGeometry rectGeometry = new RectangleGeometry(rect); - CaretElement.AddGeometry(ref geometry, rectGeometry); - } while (++j < c); + double endOfParaGlyphWidth = FontSize * CaretElement.c_endOfParaMagicMultiplier; + lastRect.Width += endOfParaGlyphWidth; + } + + CaretElement.AddGeometry(ref geometry, new RectangleGeometry(lastRect)); } } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/CaretElement.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/CaretElement.cs index 64a21fd7fc3..4426710c23f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/CaretElement.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/CaretElement.cs @@ -478,6 +478,13 @@ internal void UpdateSelection() } } + /// + /// Combines the using + /// with the parameter. In case is , + /// it is only assigned from . In case both are , it's a no-op. + /// + /// The to combine/assign into. + /// The to be combined/assigned. internal static void AddGeometry(ref Geometry geometry, Geometry addedGeometry) { if (addedGeometry != null) From 96de88a4aa16799fb55d1d9b7b8ec1e9df49712a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 19:43:03 +0200 Subject: [PATCH 09/14] Mark CalcLineAdvance as static and swap params --- .../System/Windows/Controls/TextBlock.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index ae7578437da..3c3a5dbc323 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -1277,7 +1277,7 @@ protected sealed override Size MeasureOverride(Size constraint) // paragraph ellipsis at this time. Since TextBlock is auto-sized we do not know the RenderSize until we finish Measure line.Format(dcp, contentSize.Width, GetLineProperties(dcp == 0, lineProperties), textLineBreakIn, _textBlockCache._textRunCache, /*Show paragraph ellipsis*/ false); - double lineHeight = CalcLineAdvance(line.Height, lineProperties); + double lineHeight = CalcLineAdvance(lineProperties, line.Height); #if DEBUG LineMetrics metrics = new LineMetrics(contentSize.Width, line.Length, line.Width, lineHeight, line.BaselineOffset, line.HasInlineObjects(), textLineBreakIn); @@ -3067,7 +3067,7 @@ private TextParagraphProperties GetLineProperties(bool firstLine, bool showParag // // Returns: Line advance distance.. //------------------------------------------------------------------- - private double CalcLineAdvance(double lineHeight, LineProperties lineProperties) + private static double CalcLineAdvance(LineProperties lineProperties, double lineHeight) { return lineProperties.CalcLineAdvance(lineHeight); } @@ -3408,7 +3408,7 @@ private void AlignContent() { bool ellipsis = ParagraphEllipsisShownOnLine(i, lineOffset); Format(line, lineMetrics.Length, dcp, wrappingWidth, GetLineProperties(dcp == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, ellipsis); - double lineHeight = CalcLineAdvance(line.Height, lineProperties); + double lineHeight = CalcLineAdvance(lineProperties, line.Height); // Check consistency of line formatting Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync"); From 3bffc2696d8316ecab0061727480704ef3292684 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 19:45:26 +0200 Subject: [PATCH 10/14] Remove double null assigment to ie in InputHitTestCore --- .../System/Windows/Controls/TextBlock.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index 3c3a5dbc323..6ef691318ba 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -1653,7 +1653,8 @@ protected sealed override HitTestResult HitTestCore(PointHitTestParameters hitTe protected virtual IInputElement InputHitTestCore(Point point) { // If layout data is not updated return 'this'. - if (!IsLayoutDataValid) { return this; } + if (!IsLayoutDataValid) + return this; // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing. LineProperties lineProperties = GetLineProperties(); @@ -1663,23 +1664,23 @@ protected virtual IInputElement InputHitTestCore(Point point) // a) use cached line information to find which line has been hit, // b) re-create the line that has been hit, // c) hit-test the line. - IInputElement ie = null; double wrappingWidth = CalcWrappingWidth(RenderSize.Width); Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth); point -= contentOffset; // // Take into account content offset. - if (point.X < 0 || point.Y < 0) return this; + if (point.X < 0 || point.Y < 0) + return this; - ie = null; + IInputElement ie = null; int dcp = 0; double lineOffset = 0; - TextRunCache textRunCache = new TextRunCache(); + TextRunCache textRunCache = new(); int lineCount = LineCount; for (int i = 0; i < lineCount; i++) { -Debug.Assert(lineCount == LineCount); + Debug.Assert(lineCount == LineCount); LineMetrics lineMetrics = GetLine(i); if (lineOffset + lineMetrics.Height > point.Y) @@ -1718,7 +1719,7 @@ protected virtual IInputElement InputHitTestCore(Point point) } // If nothing has been hit, assume that element itself has been hit. - return (ie != null) ? ie : this; + return ie ?? this; } /// From 9859041b0cb3aa67bbc65831abd0191026c911e0 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 22:08:58 +0200 Subject: [PATCH 11/14] Swap ArrayList for List --- .../System/Windows/Controls/TextBlock.cs | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs index 6ef691318ba..f219f1df09f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs @@ -1152,7 +1152,7 @@ protected override Visual GetVisualChild(int index) { if (_complexContent == null) { - throw new ArgumentOutOfRangeException("index"); + throw new ArgumentOutOfRangeException(nameof(index)); } return _complexContent.VisualChildren[index]; } @@ -1189,7 +1189,7 @@ protected sealed override Size MeasureOverride(Size constraint) // a) content is dirty (properties or content) // b) there are inline objects (they may be dynamically sized) int lineCount = LineCount; - if ((lineCount > 0) && IsMeasureValid && InlineObjects == null) + if ((lineCount > 0) && IsMeasureValid && InlineObjects is null) { // Assuming that all of above conditions are true, Measure can be // skipped in following situations: @@ -1388,9 +1388,9 @@ protected sealed override Size ArrangeOverride(Size arrangeSize) _complexContent.VisualChildren.Clear(); } - ArrayList inlineObjects = InlineObjects; + List inlineObjects = InlineObjects; int lineCount = LineCount; - if (inlineObjects != null && lineCount > 0) + if (inlineObjects is not null && lineCount > 0) { bool exceptionThrown = true; @@ -1414,7 +1414,7 @@ protected sealed override Size ArrangeOverride(Size arrangeSize) for (int i = 0; i < lineCount; i++) { -Debug.Assert(lineCount == LineCount); + Debug.Assert(lineCount == LineCount); LineMetrics lineMetrics = GetLine(i); if (lineMetrics.HasInlineObjects) @@ -1969,20 +1969,20 @@ internal Size MeasureChild(InlineObject inlineObject) desiredSize = inlineObject.Element.DesiredSize; // Store inline object in the cache. - ArrayList inlineObjects = InlineObjects; + List inlineObjects = InlineObjects; bool alreadyCached = false; - if (inlineObjects == null) + if (inlineObjects is null) { - InlineObjects = inlineObjects = new ArrayList(1); + InlineObjects = inlineObjects = new List(1); } else { // Find out if inline object is already cached. for (int index = 0; index < inlineObjects.Count; index++) { - if (((InlineObject)inlineObjects[index]).Dcp == inlineObject.Dcp) + if (inlineObjects[index].Dcp == inlineObject.Dcp) { - Debug.Assert(((InlineObject)inlineObjects[index]).Element == inlineObject.Element, "InlineObject cache is out of sync."); + Debug.Assert(inlineObjects[index].Element == inlineObject.Element, "InlineObject cache is out of sync."); alreadyCached = true; break; } @@ -2765,10 +2765,14 @@ internal bool IsTypographyDefaultValue //------------------------------------------------------------------- // InlineObjects //------------------------------------------------------------------- - private ArrayList InlineObjects + private List InlineObjects { - get { return (_complexContent == null) ? null : _complexContent.InlineObjects; } - set { if (_complexContent != null) _complexContent.InlineObjects = value; } + get => _complexContent?.InlineObjects; + set + { + if (_complexContent is not null) + _complexContent.InlineObjects = value; + } } //------------------------------------------------------------------- @@ -3841,7 +3845,7 @@ private enum Flags //------------------------------------------------------------------- // Represents complex content. //------------------------------------------------------------------- - private class ComplexContent + private sealed class ComplexContent { //--------------------------------------------------------------- // Ctor @@ -3909,7 +3913,7 @@ internal void Detach(TextBlock owner) //--------------------------------------------------------------- // Collection of inline objects hosted by the TextBlock control. //--------------------------------------------------------------- - internal ArrayList InlineObjects; + internal List InlineObjects; } //------------------------------------------------------------------- From 231af1d5fedac2d4a3d59b3cc7d8d3da3053ec17 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 22:12:40 +0200 Subject: [PATCH 12/14] Remove unused LineOffset calculation in ComplexLine.Arrange; use pattern-match --- .../MS/Internal/Text/ComplexLine.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/ComplexLine.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/ComplexLine.cs index 569380a63ff..16386871602 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/ComplexLine.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/ComplexLine.cs @@ -160,7 +160,6 @@ internal override void Arrange(VisualCollection vc, Vector lineOffset) Debug.Assert(runs != null, "Cannot retrieve runs collection."); // Calculate offset shift due to trailing spaces - double adjustedXOffset = lineOffset.X + CalculateXOffsetShift(); foreach (TextSpan textSpan in runs) { TextRun run = textSpan.Value; @@ -169,8 +168,7 @@ internal override void Arrange(VisualCollection vc, Vector lineOffset) InlineObject inlineObject = run as InlineObject; // Disconnect visual from its old parent, if necessary. - Visual currentParent = VisualTreeHelper.GetParent(inlineObject.Element) as Visual; - if (currentParent != null) + if (VisualTreeHelper.GetParent(inlineObject.Element) is Visual currentParent) { ContainerVisual parent = currentParent as ContainerVisual; Invariant.Assert(parent != null, "parent should always derives from ContainerVisual"); @@ -178,17 +176,16 @@ internal override void Arrange(VisualCollection vc, Vector lineOffset) } // Get position of inline object withing the text line. - FlowDirection flowDirection; - Rect rect = GetBoundsFromPosition(runDcp, inlineObject.Length, out flowDirection); + Rect rect = GetBoundsFromPosition(runDcp, inlineObject.Length, out FlowDirection flowDirection); Debug.Assert(DoubleUtil.GreaterThanOrClose(rect.Width, 0), "Negative inline object's width."); ContainerVisual proxyVisual = new ContainerVisual(); - if (inlineObject.Element is FrameworkElement) + if (inlineObject.Element is FrameworkElement frameworkElement) { FlowDirection parentFlowDirection = _owner.FlowDirection; // Check parent's FlowDirection to determine if mirroring is needed - DependencyObject parent = ((FrameworkElement)inlineObject.Element).Parent; + DependencyObject parent = frameworkElement.Parent; if(parent != null) { parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty); From 7a569763dc8c9580140b2c7e39bf19e55f8dcb46 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 22:36:44 +0200 Subject: [PATCH 13/14] Remove warning suppressions in HostedElements and seal the class --- .../MS/Internal/documents/HostedElements.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/HostedElements.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/HostedElements.cs index 21677df16fe..b654e96f74c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/HostedElements.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/HostedElements.cs @@ -6,14 +6,10 @@ // Enumerator class for returning descendants of TextBlock and FlowDocumentPage // +using System.Diagnostics; using System.Collections; -using MS.Internal.Documents; using System.Collections.Generic; using System.Collections.ObjectModel; -using System; -using System.Diagnostics; - -#pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace System.Windows.Documents { @@ -21,7 +17,7 @@ namespace System.Windows.Documents /// Enumerator class for implementation of IContentHost on TextBlock and FlowDocumentPage. /// Used to iterate through descendants of the content host /// - internal class HostedElements : IEnumerator + internal sealed class HostedElements : IEnumerator { //------------------------------------------------------------------- // @@ -164,19 +160,13 @@ public IInputElement Current { get { - // Disable PRESharp warning 6503: "Property get methods should not throw exceptions". - // HostedElements must throw exception if Current property is incorrectly accessed if (_textSegments == null) { - // Collection was modified -#pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.EnumeratorCollectionDisposed); } if (_currentPosition == null) { - // Enumerator not started. Call MoveNext to see if we can move ahead -#pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.EnumeratorNotStarted); } From 2d334926fcc349a937a199c1639c7f11b99dbbe3 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 24 Oct 2024 22:45:01 +0200 Subject: [PATCH 14/14] Remove a comment --- .../src/PresentationFramework/MS/Internal/Text/Line.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs index fdadd53dad5..87b96e815b5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Text/Line.cs @@ -180,8 +180,8 @@ internal ReadOnlySpan GetRangeBounds(int cp, int cch, double xOffset, doub textBounds = _line.GetTextBounds(cp, cch); } - // Pre-allocate List as we need it Invariant.Assert(textBounds.Count > 0); + Rect[] rectangles = new Rect[textBounds.Count]; for (int boundIndex = 0; boundIndex < rectangles.Length; boundIndex++)