Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix WASM changing scroll position on focus changing + fix keyboard focus not bringing the new focus target into view #15354

Merged
merged 10 commits into from
Feb 16, 2024
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
#if HAS_UNO // Testing internal UnoFocusInputHandler, not available on Windows
using System.Threading.Tasks;
using Private.Infrastructure;
using Uno.UI.Xaml.Core;
using Uno.UI.Xaml.Input;
using Windows.System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Uno.UI.RuntimeTests.Helpers;
using static Private.Infrastructure.TestServices;

namespace Uno.UI.RuntimeTests.Tests.Uno_UI_Xaml_Input;

[TestClass]
[RunsOnUIThread]
[RequiresFullWindow]
public class Given_UnoFocusInputHandler
{
private const int CenterButtonIndex = 4;
Expand All @@ -26,6 +26,7 @@ public class Given_UnoFocusInputHandler
[DataRow(XYFocusKeyboardNavigationMode.Enabled, VirtualKey.Up, true, TopButtonIndex)]
[DataRow(XYFocusKeyboardNavigationMode.Enabled, VirtualKey.Down, true, BottomButtonIndex)]
[DataRow(XYFocusKeyboardNavigationMode.Disabled, VirtualKey.Down, false, -1)]
[RequiresFullWindow]
[TestMethod]
public async Task When_VerifyEnabledXYKeyboardNavigation(XYFocusKeyboardNavigationMode mode, VirtualKey key, bool shouldSucceed, int targetIndex)
{
Expand All @@ -36,12 +37,43 @@ await VerifyXYNavigationAsync(
targetIndex);
}

[TestMethod]
public async Task When_Tab_BringIntoView()
{
var ts1 = new ToggleSwitch();
var ts2 = new ToggleSwitch();
var SUT = new ScrollViewer
{
new StackPanel
{
Spacing = 1200,
Children =
{
ts1,
ts2
}
}
};

await UITestHelper.Load(SUT);

Assert.AreEqual(0, SUT.VerticalOffset);

ts1.Focus(FocusState.Programmatic);
await WindowHelper.WaitForIdle();
Assert.AreEqual(0, SUT.VerticalOffset);

KeyboardHelper.Tab();
await WindowHelper.WaitForIdle();
Assert.AreEqual(SUT.ScrollableHeight, SUT.VerticalOffset);
}

private async Task VerifyXYNavigationAsync(XYFocusKeyboardNavigationMode mode, VirtualKey key, bool shouldSucceed, int targetIndex)
{
var grid = CreateButtonGrid(mode);

TestServices.WindowHelper.WindowContent = grid;
await TestServices.WindowHelper.WaitForIdle();
WindowHelper.WindowContent = grid;
await WindowHelper.WaitForIdle();

var centerButton = (Button)grid.Children[CenterButtonIndex];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -804,5 +804,54 @@ public async Task When_Paste()

Assert.AreEqual(pasteCount, 1);
}

[TestMethod]
[RunsOnUIThread]
#if __ANDROID__
[Ignore("https://github.com/unoplatform/uno/issues/15457")]
#endif
public async Task When_GotFocus_BringIntoView()
{
var tb = new TextBox();
var ts = new ToggleSwitch();
var SUT = new ScrollViewer
{
Content = new StackPanel
{
Spacing = 1200,
Children =
{
tb,
ts
}
}
};

await UITestHelper.Load(SUT);

Assert.AreEqual(0, SUT.VerticalOffset);

ts.Focus(FocusState.Programmatic);
await WindowHelper.WaitForIdle();
#if __WASM__ // wasm needs an additional delay for some reason, probably because of smooth scrolling?
await Task.Delay(2000);
#endif

Assert.AreEqual(0, SUT.VerticalOffset);
SUT.ScrollToVerticalOffset(99999);

await WindowHelper.WaitForIdle();
#if __WASM__ // wasm needs an additional delay for some reason, probably because of smooth scrolling?
await Task.Delay(2000);
#endif

tb.Focus(FocusState.Programmatic);
await WindowHelper.WaitForIdle();
#if __WASM__ // wasm needs an additional delay for some reason, probably because of smooth scrolling?
await Task.Delay(2000);
#endif

Assert.AreEqual(0, SUT.VerticalOffset);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Uno.UI.RuntimeTests.Helpers;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Input
{
Expand Down Expand Up @@ -463,6 +464,49 @@ public async Task When_ScrollViewer_TryMoveFocus()
Assert.AreEqual(button, focused);
}

[TestMethod]
[RunsOnUIThread]
#if __ANDROID__
[Ignore("https://github.com/unoplatform/uno/issues/15457")]
#endif
public async Task When_FocusChanged_PreventScroll()
{
var ts1 = new ToggleSwitch();
var ts2 = new ToggleSwitch();
var SUT = new ScrollViewer
{
Content = new StackPanel
{
Spacing = 1200,
Children =
{
ts1, ts2
}
}
};

await UITestHelper.Load(SUT);

Assert.AreEqual(0, SUT.VerticalOffset);

ts2.Focus(FocusState.Programmatic);
await TestServices.WindowHelper.WaitForIdle();
#if __WASM__ // wasm needs an additional delay for some reason, probably because of smooth scrolling?
await Task.Delay(2000);
#endif

Assert.AreEqual(0, SUT.VerticalOffset);
SUT.ScrollToVerticalOffset(99999);

ts1.Focus(FocusState.Programmatic);
await TestServices.WindowHelper.WaitForIdle();
#if __WASM__ // wasm needs an additional delay for some reason, probably because of smooth scrolling?
await Task.Delay(2000);
#endif

Assert.AreEqual(SUT.ScrollableHeight, SUT.VerticalOffset);
}

private async Task WaitForLoadedEvent(FocusNavigationPage page)
{
await TestServices.WindowHelper.WaitFor(() => page.LoadedEventFinished);
Expand Down
5 changes: 5 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ private void InitializeProperties()
InitializePropertiesPartial();
}

protected override void OnGotFocus(RoutedEventArgs e) => StartBringIntoView(new BringIntoViewOptions
{
AnimationDesired = false
});

protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
Expand Down
1 change: 1 addition & 0 deletions src/Uno.UI/UI/Xaml/Input/Internal/UnoFocusInputHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ internal bool TryHandleTabFocus(bool isShiftDown)
var focusMovement = new FocusMovement(XYFocusOptions.Default, direction, null);
focusMovement.IsShiftPressed = _isShiftDown;
focusMovement.IsProcessingTab = true;
focusMovement.ForceBringIntoView = true;
var result = focusManager?.FindAndSetNextFocus(focusMovement);
return result?.WasMoved == true;
}
Expand Down
11 changes: 9 additions & 2 deletions src/Uno.UI/UI/Xaml/Internal/InputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,16 @@ internal bool ShouldRequestFocusSound()
return false;
}

internal void NotifyFocusChanged(DependencyObject? focusedElement, bool bringIntoView, bool animateIfBringIntoView)
internal void NotifyFocusChanged(DependencyObject focusedElement, bool bringIntoView, bool animateIfBringIntoView)
{
//TODO Uno: Implement
//TODO Uno: match WinUI
if (bringIntoView)
{
((UIElement)focusedElement).StartBringIntoView(new BringIntoViewOptions
{
AnimationDesired = animateIfBringIntoView
});
}
}

internal bool LastInputWasNonFocusNavigationKeyFromSIP()
Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI/ts/WindowManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,7 @@ namespace Uno.UI {
throw `Element id ${elementId} is not focusable.`;
}

element.focus();
element.focus({ preventScroll: true });
}

/**
Expand Down
Loading