diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.skia.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.skia.cs index e0af091cdbc0..3c33b3ad689d 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.skia.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.skia.cs @@ -2895,6 +2895,55 @@ public async Task When_Undo_Redo_ContextMenu_Basic() Assert.AreEqual("hello", SUT.Text); } + [TestMethod] + public async Task When_Right_Tap_Selection_Persists() + { + using var _ = new TextBoxFeatureConfigDisposable(); + using var __ = new DisposableAction(() => (VisualTreeHelper.GetOpenPopupsForXamlRoot(WindowHelper.XamlRoot)).ForEach((_, p) => p.IsOpen = false)); + + var SUT = new TextBox + { + Width = 40 + }; + + WindowHelper.WindowContent = SUT; + + await WindowHelper.WaitForIdle(); + await WindowHelper.WaitForLoaded(SUT); + + SUT.Focus(FocusState.Programmatic); + await WindowHelper.WaitForIdle(); + + SUT.SafeRaiseEvent(UIElement.KeyDownEvent, new KeyRoutedEventArgs(SUT, VirtualKey.H, VirtualKeyModifiers.None, unicodeKey: 'h')); + SUT.SafeRaiseEvent(UIElement.KeyDownEvent, new KeyRoutedEventArgs(SUT, VirtualKey.E, VirtualKeyModifiers.None, unicodeKey: 'e')); + SUT.SafeRaiseEvent(UIElement.KeyDownEvent, new KeyRoutedEventArgs(SUT, VirtualKey.L, VirtualKeyModifiers.None, unicodeKey: 'l')); + SUT.SafeRaiseEvent(UIElement.KeyDownEvent, new KeyRoutedEventArgs(SUT, VirtualKey.L, VirtualKeyModifiers.None, unicodeKey: 'l')); + SUT.SafeRaiseEvent(UIElement.KeyDownEvent, new KeyRoutedEventArgs(SUT, VirtualKey.O, VirtualKeyModifiers.None, unicodeKey: 'o')); + await WindowHelper.WaitForIdle(); + + Assert.AreEqual("hello", SUT.Text); + + var injector = InputInjector.TryCreate() ?? throw new InvalidOperationException("Failed to init the InputInjector"); + using var mouse = injector.GetMouse(); + + mouse.MoveTo(SUT.GetAbsoluteBounds().Location + new Point(10, 10)); + await WindowHelper.WaitForIdle(); + + mouse.Press(); + mouse.MoveBy(8, 0); + mouse.Release(); + await WindowHelper.WaitForIdle(); + + var selection = (SUT.SelectionStart, SUT.SelectionLength); + + mouse.MoveBy(-4, 0); + mouse.PressRight(); + mouse.ReleaseRight(); + await WindowHelper.WaitForIdle(); + + Assert.AreEqual(selection, (SUT.SelectionStart, SUT.SelectionLength)); + } + [TestMethod] public async Task When_Text_Changed_History_Cleared() { diff --git a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs index a05407a9f6fe..b20edb2935b6 100644 --- a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs @@ -91,8 +91,13 @@ protected override void OnRightTapped(RightTappedRoutedEventArgs e) // this textbox but GetPosition assumes that it is relative to the displayBlock, so we compensate. // var position = e.GetPosition(displayBlock); var position = displayBlock.TransformToVisual(this).Inverse.TransformPoint(e.GetPosition(displayBlock)); + var index = Math.Max(0, displayBlock.Inlines.GetIndexAt(position, true, true)); - Select(index, 0); + if (index < SelectionStart || index >= SelectionStart + SelectionLength) + { + // Right tapping should move the caret to the current pointer location if outside the selection + Select(index, 0); + } OpenContextMenu(position); }