From faf7137b2a5b6522c1abd23b057b1c819d311fc3 Mon Sep 17 00:00:00 2001 From: Yoyo Date: Fri, 7 Aug 2026 23:44:14 +0800 Subject: [PATCH] fix(SelectableTextBlock): fix SelectableTextBlock selection for centered and right-aligned text (#21369) * fix(SelectableTextBlock): fix SelectableTextBlock selection for centered and right-aligned text * Fix failing unit tests * Let TextLayout handle out-of-bounds hit testing --------- Co-authored-by: Julien Lebosquain --- .../ControlCatalog/Pages/TextBlockPage.xaml | 23 ++++++---- src/Avalonia.Controls/SelectableTextBlock.cs | 4 -- .../SelectableTextBlockTests.cs | 43 ++++++++++++++++++- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/samples/ControlCatalog/Pages/TextBlockPage.xaml b/samples/ControlCatalog/Pages/TextBlockPage.xaml index 3418cffe74..5993f65b53 100644 --- a/samples/ControlCatalog/Pages/TextBlockPage.xaml +++ b/samples/ControlCatalog/Pages/TextBlockPage.xaml @@ -119,15 +119,20 @@ - - This is a - TextBlock - with several - Span elements, - - using a variety of styles - . - + + + This is a + TextBlock + with several + Span elements, + + using a variety of styles + . + + + + + diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs index 1b8935cf1f..a218eb0356 100644 --- a/src/Avalonia.Controls/SelectableTextBlock.cs +++ b/src/Avalonia.Controls/SelectableTextBlock.cs @@ -451,10 +451,6 @@ namespace Avalonia.Controls var point = e.GetPosition(this) - new Point(padding.Left, padding.Top); - point = new Point( - MathUtilities.Clamp(point.X, 0, Math.Max(TextLayout.WidthIncludingTrailingWhitespace, 0)), - MathUtilities.Clamp(point.Y, 0, Math.Max(TextLayout.Height, 0))); - var hit = TextLayout.HitTestPoint(point); var textPosition = hit.TextPosition; diff --git a/tests/Avalonia.Controls.UnitTests/SelectableTextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/SelectableTextBlockTests.cs index e360694b69..2583f1011e 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectableTextBlockTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectableTextBlockTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Avalonia.Controls.Documents; using Avalonia.Media; using Avalonia.Media.TextFormatting; @@ -49,6 +50,46 @@ namespace Avalonia.Controls.UnitTests } } + [Theory] + [InlineData(TextAlignment.Center)] + [InlineData(TextAlignment.Right)] + public void Dragging_Selection_Should_Reach_End_Of_Text_When_Text_Is_Aligned(TextAlignment textAlignment) + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var target = new SelectableTextBlock + { + Width = 200, + Text = "Aligned text", + TextAlignment = textAlignment + }; + + var root = new TestRoot(target) + { + ClientSize = new Size(300, 100) + }; + + root.Measure(root.ClientSize); + root.Arrange(new Rect(root.ClientSize)); + root.ExecuteInitialLayoutPass(); + + var firstCharacterBounds = target.TextLayout.HitTestTextPosition(0); + var lastCharacterBounds = target.TextLayout.HitTestTextPosition(target.Text!.Length - 1); + var mouse = new MouseTestHelper(); + var startPoint = new Point( + firstCharacterBounds.X + firstCharacterBounds.Width / 2, + firstCharacterBounds.Y + firstCharacterBounds.Height / 2); + var endPoint = new Point( + Math.Min(target.Bounds.Width - 1, lastCharacterBounds.Right + 10), + lastCharacterBounds.Y + lastCharacterBounds.Height / 2); + + mouse.Down(target, position: target.TranslatePoint(startPoint, root)); + mouse.Move(target, position: target.TranslatePoint(endPoint, root).GetValueOrDefault()); + + Assert.Equal(target.Text!.Length, Math.Max(target.SelectionStart, target.SelectionEnd)); + } + } + [Fact] public void SelectionForeground_Should_Not_Reset_Run_Typeface_And_Style() {