-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #65 from unoplatform/dev/dr/pointers
feat: Add support of pointers injection on supported platforms
- Loading branch information
Showing
23 changed files
with
654 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Windows.Devices.Input; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
||
#if HAS_UNO_WINUI || WINDOWS_WINUI | ||
using Microsoft.UI.Xaml; | ||
using Microsoft.UI.Xaml.Controls; | ||
#else | ||
using Windows.UI.Xaml; | ||
using Windows.UI.Xaml.Controls; | ||
#endif | ||
|
||
namespace Uno.UI.RuntimeTests.Engine; | ||
|
||
[TestClass] | ||
[RunsOnUIThread] | ||
public class PointersInjectionTests | ||
{ | ||
[TestMethod] | ||
[InjectedPointer(PointerDeviceType.Mouse)] | ||
[InjectedPointer(PointerDeviceType.Touch)] | ||
#if !HAS_UNO_SKIA && !WINDOWS | ||
[ExpectedException(typeof(NotSupportedException))] | ||
#endif | ||
public async Task When_TapCoordinates() | ||
{ | ||
var elt = new Button { Content = "Tap me" }; | ||
var clicked = false; | ||
elt.Click += (snd, e) => clicked = true; | ||
|
||
UnitTestsUIContentHelper.Content = elt; | ||
|
||
await UnitTestsUIContentHelper.WaitForLoaded(elt); | ||
|
||
InputInjectorHelper.Current.Tap(elt); | ||
|
||
Assert.IsTrue(clicked); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
src/Uno.UI.RuntimeTests.Engine.Library/Library/InputInjectorHelper.MouseHelper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
#if !UNO_RUNTIMETESTS_DISABLE_LIBRARY | ||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Windows.Foundation; | ||
using Windows.UI.Input; | ||
using Windows.UI.Input.Preview.Injection; | ||
|
||
namespace Uno.UI.RuntimeTests; | ||
|
||
public partial class InputInjectorHelper | ||
{ | ||
#pragma warning disable CA1822 // Mark members as static | ||
public class MouseHelper | ||
{ | ||
#if HAS_UNO | ||
public MouseHelper(InputInjector input) | ||
{ | ||
var getCurrent = typeof(InputInjector).GetProperty("Mouse", BindingFlags.Instance | BindingFlags.NonPublic)?.GetMethod | ||
?? throw new NotSupportedException("This version of uno is not supported for pointer injection."); | ||
var currentType = getCurrent.Invoke(input, null)!.GetType(); | ||
var getCurrentPosition = currentType.GetProperty("Position", BindingFlags.Instance | BindingFlags.Public)?.GetMethod | ||
?? throw new NotSupportedException("This version of uno is not supported for pointer injection."); | ||
var getCurrentProperties = currentType.GetProperty("Properties", BindingFlags.Instance | BindingFlags.Public)?.GetMethod | ||
?? throw new NotSupportedException("This version of uno is not supported for pointer injection."); | ||
|
||
CurrentPosition = () => (Point)getCurrentPosition.Invoke(getCurrent.Invoke(input, null)!, null)!; | ||
CurrentProperties = () => (PointerPointProperties)getCurrentProperties.Invoke(getCurrent.Invoke(input, null)!, null)!; | ||
} | ||
|
||
private Func<PointerPointProperties> CurrentProperties; | ||
|
||
private Func<Point> CurrentPosition; | ||
#else | ||
public MouseHelper(InputInjector input) | ||
{ | ||
} | ||
|
||
private Point CurrentPosition() | ||
=> Windows.UI.Core.CoreWindow.GetForCurrentThread().PointerPosition; | ||
#endif | ||
|
||
/// <summary> | ||
/// Create an injected pointer info which presses the left button | ||
/// </summary> | ||
public InjectedInputMouseInfo Press() | ||
=> new() | ||
{ | ||
TimeOffsetInMilliseconds = 1, | ||
MouseOptions = InjectedInputMouseOptions.LeftDown, | ||
}; | ||
|
||
/// <summary> | ||
/// Create an injected pointer info which release the left button | ||
/// </summary> | ||
public InjectedInputMouseInfo Release() | ||
=> new() | ||
{ | ||
TimeOffsetInMilliseconds = 1, | ||
MouseOptions = InjectedInputMouseOptions.LeftUp, | ||
}; | ||
|
||
/// <summary> | ||
/// Create an injected pointer info which releases any pressed button | ||
/// </summary> | ||
public InjectedInputMouseInfo? ReleaseAny() | ||
{ | ||
var options = default(InjectedInputMouseOptions); | ||
|
||
#if HAS_UNO | ||
var currentProps = CurrentProperties(); | ||
if (currentProps.IsLeftButtonPressed) | ||
{ | ||
options |= InjectedInputMouseOptions.LeftUp; | ||
} | ||
|
||
if (currentProps.IsMiddleButtonPressed) | ||
{ | ||
options |= InjectedInputMouseOptions.MiddleUp; | ||
} | ||
|
||
if (currentProps.IsRightButtonPressed) | ||
{ | ||
options |= InjectedInputMouseOptions.RightUp; | ||
} | ||
|
||
if (currentProps.IsXButton1Pressed) | ||
{ | ||
options |= InjectedInputMouseOptions.XUp; | ||
} | ||
#else | ||
options = InjectedInputMouseOptions.LeftUp | ||
| InjectedInputMouseOptions.MiddleUp | ||
| InjectedInputMouseOptions.RightUp | ||
| InjectedInputMouseOptions.XUp; | ||
#endif | ||
|
||
return options is default(InjectedInputMouseOptions) | ||
? null | ||
: new() | ||
{ | ||
TimeOffsetInMilliseconds = 1, | ||
MouseOptions = options | ||
}; | ||
} | ||
|
||
/// <summary> | ||
/// Create an injected pointer info which moves the mouse by the given offests | ||
/// </summary> | ||
public InjectedInputMouseInfo MoveBy(int deltaX, int deltaY) | ||
=> new() | ||
{ | ||
DeltaX = deltaX, | ||
DeltaY = deltaY, | ||
TimeOffsetInMilliseconds = 1, | ||
MouseOptions = InjectedInputMouseOptions.MoveNoCoalesce, | ||
}; | ||
|
||
/// <summary> | ||
/// Create some injected pointer infos which moves the mouse to the given coordinates | ||
/// </summary> | ||
/// <param name="x">The target x position</param> | ||
/// <param name="y">The traget y position</param> | ||
/// <param name="steps">Number injected pointer infos to generate to simutale a smooth manipulation.</param> | ||
public IEnumerable<InjectedInputMouseInfo> MoveTo(double x, double y, int? steps = null) | ||
{ | ||
var deltaX = x - CurrentPosition().X; | ||
var deltaY = y - CurrentPosition().Y; | ||
|
||
steps ??= (int)Math.Min(Math.Max(Math.Abs(deltaX), Math.Abs(deltaY)), 512); | ||
if (steps is 0) | ||
{ | ||
yield break; | ||
} | ||
|
||
var stepX = deltaX / steps.Value; | ||
var stepY = deltaY / steps.Value; | ||
|
||
stepX = stepX is > 0 ? Math.Ceiling(stepX) : Math.Floor(stepX); | ||
stepY = stepY is > 0 ? Math.Ceiling(stepY) : Math.Floor(stepY); | ||
|
||
for (var step = 0; step <= steps && (stepX is not 0 || stepY is not 0); step++) | ||
{ | ||
yield return MoveBy((int)stepX, (int)stepY); | ||
|
||
if (Math.Abs(CurrentPosition().X - x) < stepX) | ||
{ | ||
stepX = 0; | ||
} | ||
|
||
if (Math.Abs(CurrentPosition().Y - y) < stepY) | ||
{ | ||
stepY = 0; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
#endif |
Oops, something went wrong.