Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions samples/ControlCatalog/Pages/TextBlockPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,20 @@
</StackPanel>
</Border>
<Border>
<SelectableTextBlock Margin="10" TextWrapping="Wrap">
This <Span FontWeight="Bold">is</Span> a
<Span Background="Silver" Foreground="Maroon">TextBlock</Span>
with <Span TextDecorations="Underline">several</Span>
<Span FontStyle="Italic">Span</Span> elements,
<Span Foreground="Blue">
using a <Bold>variety</Bold> of <Italic>styles</Italic>
</Span>.
</SelectableTextBlock>
<StackPanel Spacing="8">
<SelectableTextBlock Margin="10" TextWrapping="Wrap">
This <Span FontWeight="Bold">is</Span> a
<Span Background="Silver" Foreground="Maroon">TextBlock</Span>
with <Span TextDecorations="Underline">several</Span>
<Span FontStyle="Italic">Span</Span> elements,
<Span Foreground="Blue">
using a <Bold>variety</Bold> of <Italic>styles</Italic>
</Span>.
</SelectableTextBlock>
<SelectableTextBlock Text="Left aligned text" TextAlignment="Left" />
<SelectableTextBlock Text="Center aligned text" TextAlignment="Center" />
<SelectableTextBlock Text="Right aligned text and wrap text" TextAlignment="Right" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border>
<TextBlock FontFamily="Times New Roman">
Expand Down
25 changes: 18 additions & 7 deletions src/Avalonia.Controls/SelectableTextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,13 +432,7 @@ protected override void OnPointerMoved(PointerEventArgs e)
if (e.Pointer.Captured == this && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
var text = HasComplexContent ? Inlines?.Text : Text;
var padding = Padding;

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 point = GetClampedTextLayoutPoint(e);

var hit = TextLayout.HitTestPoint(point);
var textPosition = hit.TextPosition;
Expand Down Expand Up @@ -512,6 +506,23 @@ private void UpdateCommandStates()
CanCopy = !string.IsNullOrEmpty(text);
}

private Point GetClampedTextLayoutPoint(PointerEventArgs e)
{
var padding = Padding;

var point = e.GetPosition(this) - new Point(padding.Left, padding.Top);
var maxWidth = TextLayout.WidthIncludingTrailingWhitespace;

if (!double.IsNaN(_constraint.Width) && !double.IsInfinity(_constraint.Width))
{
maxWidth = Math.Max(maxWidth, _constraint.Width);
}

return new Point(
MathUtilities.Clamp(point.X, 0, Math.Max(maxWidth, 0)),
MathUtilities.Clamp(point.Y, 0, Math.Max(TextLayout.Height, 0)));
}

private string GetSelection()
{
var text = HasComplexContent ? Inlines?.Text : Text;
Expand Down
43 changes: 42 additions & 1 deletion tests/Avalonia.Controls.UnitTests/SelectableTextBlockTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using Avalonia.Controls.Documents;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
Expand All @@ -9,6 +10,46 @@ namespace Avalonia.Controls.UnitTests
{
public class SelectableTextBlockTests : ScopedTestBase
{
[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()
{
Expand Down