@@ -814,7 +814,7 @@ bool TryMergeWithLastBounds(TextBounds currentBounds, TextBounds lastBounds)
814814 }
815815 }
816816
817- private CharacterHit GetPreviousCharacterHit ( CharacterHit characterHit , bool useGraphemeBoundaries )
817+ private CharacterHit GetPreviousCharacterHit ( CharacterHit characterHit , bool isBackspaceDelete )
818818 {
819819 if ( _textRuns . Length == 0 || _indexedTextRuns is null )
820820 {
@@ -833,8 +833,6 @@ private CharacterHit GetPreviousCharacterHit(CharacterHit characterHit, bool use
833833 return new CharacterHit ( FirstTextSourceIndex ) ;
834834 }
835835
836- var currentCharacterHit = characterHit ;
837-
838836 var currentRun = GetRunAtCharacterIndex ( characterIndex , LogicalDirection . Backward , out var currentPosition ) ;
839837
840838 var previousCharacterHit = characterHit ;
@@ -843,46 +841,38 @@ private CharacterHit GetPreviousCharacterHit(CharacterHit characterHit, bool use
843841 {
844842 case ShapedTextRun shapedRun :
845843 {
846- var offset = Math . Max ( 0 , currentPosition - shapedRun . GlyphRun . Metrics . FirstCluster ) ;
844+ //Determine the start of the first hit in local positions.
845+ var runOffset = Math . Max ( 0 , characterIndex - currentPosition ) ;
847846
848- if ( offset > 0 )
849- {
850- currentCharacterHit = new CharacterHit ( Math . Max ( 0 , characterHit . FirstCharacterIndex - offset ) , characterHit . TrailingLength ) ;
851- }
847+ var firstCluster = shapedRun . GlyphRun . Metrics . FirstCluster ;
852848
853- previousCharacterHit = shapedRun . GlyphRun . GetPreviousCaretCharacterHit ( currentCharacterHit ) ;
849+ //Current position is a text source index and first cluster is relative to the GlyphRun's buffer.
850+ var textSourceOffset = currentPosition - firstCluster ;
854851
855- if ( useGraphemeBoundaries )
852+ if ( isBackspaceDelete )
856853 {
857- var textPosition = Math . Max ( 0 , previousCharacterHit . FirstCharacterIndex - shapedRun . GlyphRun . Metrics . FirstCluster ) ;
858-
859- var text = shapedRun . GlyphRun . Characters . Slice ( textPosition ) ;
860-
861- var graphemeEnumerator = new GraphemeEnumerator ( text . Span ) ;
862-
863854 var length = 0 ;
864855
865- var clusterLength = Math . Max ( 0 , currentCharacterHit . FirstCharacterIndex + currentCharacterHit . TrailingLength -
866- previousCharacterHit . FirstCharacterIndex - previousCharacterHit . TrailingLength ) ;
867-
868- while ( graphemeEnumerator . MoveNext ( out var grapheme ) )
856+ while ( Codepoint . ReadAt ( shapedRun . GlyphRun . Characters . Span , length , out var count ) != Codepoint . ReplacementCodepoint )
869857 {
870- if ( length + grapheme . Length < clusterLength )
858+ if ( length + count >= runOffset )
871859 {
872- length += grapheme . Length ;
873-
874- continue ;
860+ break ;
875861 }
876862
877- previousCharacterHit = new CharacterHit ( previousCharacterHit . FirstCharacterIndex + length ) ;
878-
879- break ;
863+ length += count ;
880864 }
881- }
882865
883- if ( offset > 0 )
866+ previousCharacterHit = new CharacterHit ( characterIndex - runOffset + length ) ;
867+ }
868+ else
884869 {
885- previousCharacterHit = new CharacterHit ( previousCharacterHit . FirstCharacterIndex + offset , previousCharacterHit . TrailingLength ) ;
870+ previousCharacterHit = shapedRun . GlyphRun . GetPreviousCaretCharacterHit ( new CharacterHit ( firstCluster + runOffset ) ) ;
871+
872+ if ( textSourceOffset > 0 )
873+ {
874+ previousCharacterHit = new CharacterHit ( textSourceOffset + previousCharacterHit . FirstCharacterIndex , previousCharacterHit . TrailingLength ) ;
875+ }
886876 }
887877
888878 break ;
@@ -1326,8 +1316,6 @@ private TextLineMetrics CreateLineMetrics()
13261316 }
13271317 }
13281318
1329- var height = descent - ascent + lineGap ;
1330-
13311319 var inkBounds = new Rect ( ) ;
13321320
13331321 for ( var index = 0 ; index < _textRuns . Length ; index ++ )
@@ -1363,6 +1351,28 @@ private TextLineMetrics CreateLineMetrics()
13631351 }
13641352 }
13651353
1354+ var halfLineGap = lineGap * 0.5 ;
1355+ var naturalHeight = descent - ascent + lineGap ;
1356+ var baseline = - ascent + halfLineGap ;
1357+ var height = naturalHeight ;
1358+
1359+ if ( ! double . IsNaN ( lineHeight ) && ! MathUtilities . IsZero ( lineHeight ) )
1360+ {
1361+ if ( lineHeight <= naturalHeight )
1362+ {
1363+ //Clamp to the specified line height
1364+ height = lineHeight ;
1365+ baseline = - ascent ;
1366+ }
1367+ else
1368+ {
1369+ // Center the text vertically within the specified line height
1370+ height = lineHeight ;
1371+ var extra = lineHeight - ( descent - ascent ) ;
1372+ baseline = - ascent + extra / 2 ;
1373+ }
1374+ }
1375+
13661376 height += lineSpacing ;
13671377
13681378 var width = widthIncludingWhitespace ;
@@ -1395,24 +1405,14 @@ private TextLineMetrics CreateLineMetrics()
13951405 }
13961406
13971407 var extent = inkBounds . Height ;
1398- //The width of overhanging pixels at the bottom
1399- var overhangAfter = inkBounds . Bottom - height ;
1408+ //The height of overhanging pixels at the bottom
1409+ var overhangAfter = inkBounds . Bottom - height + halfLineGap ;
14001410 //The width of overhanging pixels at the natural alignment point. Positive value means we are inside.
14011411 var overhangLeading = inkBounds . Left ;
14021412 //The width of overhanging pixels at the end of the natural bounds. Positive value means we are inside.
14031413 var overhangTrailing = widthIncludingWhitespace - inkBounds . Right ;
14041414 var hasOverflowed = width > _paragraphWidth ;
14051415
1406- if ( ! double . IsNaN ( lineHeight ) && ! MathUtilities . IsZero ( lineHeight ) )
1407- {
1408- //Center the line
1409- var offset = ( height - lineHeight ) / 2 ;
1410-
1411- ascent += offset ;
1412-
1413- height = lineHeight ;
1414- }
1415-
14161416 var start = GetParagraphOffsetX ( width , widthIncludingWhitespace ) ;
14171417
14181418 _inkBounds = inkBounds . Translate ( new Vector ( start , 0 ) ) ;
@@ -1426,7 +1426,7 @@ private TextLineMetrics CreateLineMetrics()
14261426 Extent = extent ,
14271427 NewlineLength = newLineLength ,
14281428 Start = start ,
1429- TextBaseline = - ascent ,
1429+ TextBaseline = baseline ,
14301430 TrailingWhitespaceLength = trailingWhitespaceLength ,
14311431 Width = width ,
14321432 WidthIncludingTrailingWhitespace = widthIncludingWhitespace ,
0 commit comments