diff --git a/Directory.Build.props b/Directory.Build.props index ba547fbef232..63821417f071 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,12 +27,16 @@ $(NetPrevious)-windows $(NetCurrent)-windows + $(NetPrevious)-windows10.0.19041.0 + $(NetCurrent)-windows10.0.19041.0 + $(NetPreviousNetCoreMobile);$(NetCurrentNetCoreMobile) $(NetPrevious)-android;$(NetCurrent)-android $(NetPreviousWpf);$(NetCurrentWpf) $(NetPrevious);$(NetCurrent) $(NetPrevious);$(NetCurrent) $(NetPrevious);$(NetCurrent) + $(NetPreviousWinAppSDK);$(NetCurrentWinAppSDK) $(NetPrevious) uap10.0.19041 diff --git a/src/SamplesApp/SamplesApp.Windows/SamplesApp.Windows.csproj b/src/SamplesApp/SamplesApp.Windows/SamplesApp.Windows.csproj index d4933a2d0290..a942036561d1 100644 --- a/src/SamplesApp/SamplesApp.Windows/SamplesApp.Windows.csproj +++ b/src/SamplesApp/SamplesApp.Windows/SamplesApp.Windows.csproj @@ -1,7 +1,7 @@  WinExe - $(NetPrevious)-windows10.0.19041.0 + $(NetPreviousWinAppSDK) 10.0.17763.0 SamplesApp.Windows app.manifest @@ -19,6 +19,8 @@ 10.0.19041.53 + + diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems index 55205d5a899e..cb7f2da5b0e7 100644 --- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems +++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems @@ -4266,6 +4266,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -8238,6 +8242,9 @@ PointerEventArgsTests.xaml + + PointerEvent_Timestamp.xaml + DragCoordinates_Automated.xaml diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs index 358f27386660..5b0895c7e68d 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs @@ -43,7 +43,7 @@ private static void SleepOnTouchDown(object sender, PointerRoutedEventArgs e) { // Ugly hack: The test engine does not allows us to perform a custom gesture (hold for 300 ms then drag) // So we just freeze the UI thread enough to simulate the delay ... - const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayTicks */ + 50 /* safety */; + const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayMicroseconds */ + 50 /* safety */; Thread.Sleep(holdDelay); } } diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs index 3bb33644da5d..9ba6f313cf46 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs @@ -41,7 +41,7 @@ protected override void OnPointerPressed(PointerRoutedEventArgs e) { // Ugly hack: The test engine does not allows us to perform a custom gesture (hold for 300 ms then drag) // So we just freeze the UI thread enough to simulate the delay ... - const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayTicks */ + 50 /* safety */; + const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayMicroseconds */ + 50 /* safety */; Thread.Sleep(holdDelay); } diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml new file mode 100644 index 000000000000..c74efebaf40d --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml.cs new file mode 100644 index 000000000000..1fa9862df39c --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Uno.UI.Samples.Controls; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System.Collections.ObjectModel; + +namespace UITests.Shared.Windows_UI_Xaml_Input.Pointers +{ + [SampleControlInfo( + "Pointers", + Description = + "Click the red rectangle repeatedly. You should see tickmarks in the logs (✔️) indicating that time delta matches timestamp delta.", + IsManualTest = true)] + public sealed partial class PointerEvent_Timestamp : UserControl + { + private ulong? _lastTimestamp; + private uint? _lastFrameId; + private double? _lastElapsedTime; + private readonly Stopwatch _stopwatch = new(); + + public PointerEvent_Timestamp() + { + this.InitializeComponent(); + TestBorder.PointerPressed += PointerEventArgsTests_PointerPressed; + _stopwatch.Start(); + Unloaded += (s, e) => _stopwatch.Stop(); + } + + public ObservableCollection Logs { get; } = new ObservableCollection(); + + private void PointerEventArgsTests_PointerPressed(object sender, PointerRoutedEventArgs e) + { + var point = e.GetCurrentPoint(TestBorder); + var timestamp = point.Timestamp; + var frameId = point.FrameId; + var time = _stopwatch.Elapsed.TotalMicroseconds; + + var log = $"Timestamp: {timestamp}, FrameId: {frameId}" + Environment.NewLine; + if (_lastTimestamp.HasValue) + { + var timeDelta = (ulong)(time - _lastElapsedTime.Value); + var timestampDelta = (timestamp - _lastTimestamp.Value); + log += $"Time Δ: {timeDelta}"; + + // As long as the delta differs by less than 100ms, it probably is correct. + var seemsCorrect = Math.Abs((double)timeDelta - timestampDelta) < 50_000; + log += $", Timestamp Δ: {timestampDelta} {(seemsCorrect ? "✔️" : "❌")}"; + + var frameIdDelta = frameId - _lastFrameId.Value; + log += $", FrameId Δ: {frameIdDelta}"; + } + _lastElapsedTime = time; + _lastTimestamp = timestamp; + _lastFrameId = frameId; + Logs.Add(log); + } + } +} diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Given_ResourceDictionary.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Given_ResourceDictionary.cs index d1adc337b18b..d887907eaf31 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Given_ResourceDictionary.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Given_ResourceDictionary.cs @@ -173,4 +173,16 @@ public MainPage() await test.RunAsync(); } + + [TestMethod] + public async Task When_Nested_With_Sibling_Ref_And_Event() + { + var test = new TestSetup( + xamlFileName: "ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml", + subFolder: Path.Combine("SourceGenerators", "Uno.UI.SourceGenerators.Tests", "XamlCodeGeneratorTests", "TestCases")) + { + }; + + await Verify.AssertXamlGenerator(test); + } } diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_GlobalStaticResources.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_GlobalStaticResources.cs new file mode 100644 index 000000000000..b80cdbab3f0e --- /dev/null +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_GlobalStaticResources.cs @@ -0,0 +1,55 @@ +// +namespace MyProject +{ + /// + /// Contains all the static resources defined for the application + /// + public sealed partial class GlobalStaticResources + { + static bool _initialized; + private static bool _stylesRegistered; + private static bool _dictionariesRegistered; + internal static global::Uno.UI.Xaml.XamlParseContext __ParseContext_ { get; } = new global::Uno.UI.Xaml.XamlParseContext() + { + AssemblyName = "TestProject", + } + ; + + static GlobalStaticResources() + { + Initialize(); + } + public static void Initialize() + { + if (!_initialized) + { + _initialized = true; + global::Uno.UI.GlobalStaticResources.Initialize(); + global::Uno.UI.GlobalStaticResources.RegisterDefaultStyles(); + global::Uno.UI.GlobalStaticResources.RegisterResourceDictionariesBySource(); + } + } + public static void RegisterDefaultStyles() + { + if(!_stylesRegistered) + { + _stylesRegistered = true; + RegisterDefaultStyles_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20(); + } + } + // Register ResourceDictionaries using ms-appx:/// syntax, this is called for external resources + public static void RegisterResourceDictionariesBySource() + { + if(!_dictionariesRegistered) + { + _dictionariesRegistered = true; + } + } + // Register ResourceDictionaries using ms-resource:/// syntax, this is called for local resources + internal static void RegisterResourceDictionariesBySourceLocal() + { + } + static partial void RegisterDefaultStyles_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20(); + + } +} diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_LocalizationResources.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_LocalizationResources.cs new file mode 100644 index 000000000000..115ce87c0105 --- /dev/null +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_LocalizationResources.cs @@ -0,0 +1,2 @@ +// +[assembly: global::System.Reflection.AssemblyMetadata("UnoHasLocalizationResources", "False")] \ No newline at end of file diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20.cs new file mode 100644 index 000000000000..b925ff3b30c4 --- /dev/null +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WNWSRAE/XamlCodeGenerator_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20.cs @@ -0,0 +1,238 @@ +// +#pragma warning disable CS0114 +#pragma warning disable CS0108 +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Uno.UI; +using Uno.UI.Xaml; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Documents; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Animation; +using Microsoft.UI.Xaml.Shapes; +using Windows.UI.Text; +using Uno.Extensions; +using Uno; +using Uno.UI.Helpers; +using Uno.UI.Helpers.Xaml; +using MyProject; + +#if __ANDROID__ +using _View = Android.Views.View; +#elif __IOS__ +using _View = UIKit.UIView; +#elif __MACOS__ +using _View = AppKit.NSView; +#else +using _View = Microsoft.UI.Xaml.UIElement; +#endif + +namespace Uno.UI.Tests.Given_ResourceDictionary +{ + partial class When_Nested_With_Sibling_Ref_And_Event : global::Microsoft.UI.Xaml.Controls.Page + { + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + private const string __baseUri_prefix_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20 = "ms-appx:///TestProject/"; + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + private const string __baseUri_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20 = "ms-appx:///TestProject/"; + private global::Microsoft.UI.Xaml.NameScope __nameScope = new global::Microsoft.UI.Xaml.NameScope(); + private void InitializeComponent() + { + NameScope.SetNameScope(this, __nameScope); + var __that = this; + base.IsParsing = true; + Resources[ + "RootResource" + ] = + new global::Uno.UI.Xaml.WeakResourceInitializer(this, __ResourceOwner_1 => + { + return + new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1, (__owner) => new _ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_UnoUITestsGiven_ResourceDictionaryWhen_Nested_With_Sibling_Ref_And_EventSC0().Build(__owner) + ) ; + } + ) + ; + // Source 0\ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml (Line 1:2) + ; + + this + .ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply((ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20XamlApplyExtensions.XamlApplyHandler0)(__p1 => + { + // Source 0\ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml (Line 1:2) + + // WARNING Property __p1.base does not exist on {http://schemas.microsoft.com/winfx/2006/xaml/presentation}Page, the namespace is http://www.w3.org/XML/1998/namespace. This error was considered irrelevant by the XamlFileGenerator + } + )) + .ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply((ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20XamlApplyExtensions.XamlApplyHandler0)(__p1 => + { + // Class Uno.UI.Tests.Given_ResourceDictionary.When_Nested_With_Sibling_Ref_And_Event + global::Uno.UI.FrameworkElementHelper.SetBaseUri(__p1, __baseUri_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20); + __p1.CreationComplete(); + } + )) + ; + OnInitializeCompleted(); + + } + partial void OnInitializeCompleted(); + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026")] + [global::System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2111")] + private class _ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_UnoUITestsGiven_ResourceDictionaryWhen_Nested_With_Sibling_Ref_And_EventSC0 + { + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + private const string __baseUri_prefix_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20 = "ms-appx:///TestProject/"; + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + private const string __baseUri_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20 = "ms-appx:///TestProject/"; + global::Microsoft.UI.Xaml.NameScope __nameScope = new global::Microsoft.UI.Xaml.NameScope(); + global::System.Object __ResourceOwner_1; + _View __rootInstance = null; + public _View Build(object __ResourceOwner_1) + { + var __that = this; + this.__ResourceOwner_1 = __ResourceOwner_1; + this.__rootInstance = + new global::Microsoft.UI.Xaml.Controls.Border + { + IsParsing = true, + Resources = { + [ + "SiblingResource" + ] = + new global::Microsoft.UI.Xaml.Controls.FontIconSource + { + // Source 0\ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml (Line 14:7) + } + , + [ + "FailingResource" + ] = + new global::Uno.UI.Xaml.WeakResourceInitializer(__ResourceOwner_1, __ResourceOwner_2 => + { + return + new global::Microsoft.UI.Xaml.Controls.SwipeItems + { + // Source 0\ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml (Line 15:7) + } + .ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply((ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20XamlApplyExtensions.XamlApplyHandler1)(__p1 => + { + __p1.Add( + new global::Microsoft.UI.Xaml.Controls.SwipeItem + { + // Source 0\ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml (Line 16:8) + } + .ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply((ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20XamlApplyExtensions.XamlApplyHandler2)(__p1 => + { + /* _isTopLevelDictionary:False */ + __that._component_0 = __p1; + global::Microsoft.UI.Xaml.NameScope.SetNameScope(__that._component_0, __nameScope); + global::Uno.UI.ResourceResolverSingleton.Instance.ApplyResource(__p1, global::Microsoft.UI.Xaml.Controls.SwipeItem.IconSourceProperty, "SiblingResource", isThemeResourceExtension: false, isHotReloadSupported: false, context: global::MyProject.GlobalStaticResources.__ParseContext_); + var Invoked_AnEventHandler_That = (__ResourceOwner_2 as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference; + /* second level */ __p1.Invoked += (AnEventHandler_sender,AnEventHandler_args) => (Invoked_AnEventHandler_That.Target as global::Uno.UI.Tests.Given_ResourceDictionary.When_Nested_With_Sibling_Ref_And_Event)?.AnEventHandler(AnEventHandler_sender,AnEventHandler_args); + } + )) + ); + } + )) + ; + } + ) + , + }, + // Source 0\ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml (Line 12:5) + } + .ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply((ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20XamlApplyExtensions.XamlApplyHandler3)(__p1 => + { + /* _isTopLevelDictionary:False */ + __that._component_1 = __p1; + global::Uno.UI.FrameworkElementHelper.SetBaseUri(__p1, __baseUri_ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20); + __p1.CreationComplete(); + } + )) + ; + if (__rootInstance is FrameworkElement __fe) + { + __fe.Loading += __UpdateBindingsAndResources; + } + if (__rootInstance is DependencyObject d) + { + if (global::Microsoft.UI.Xaml.NameScope.GetNameScope(d) == null) + { + global::Microsoft.UI.Xaml.NameScope.SetNameScope(d, __nameScope); + __nameScope.Owner = d; + } + global::Uno.UI.FrameworkElementHelper.AddObjectReference(d, this); + } + return __rootInstance; + } + private global::Microsoft.UI.Xaml.Markup.ComponentHolder _component_0_Holder = new global::Microsoft.UI.Xaml.Markup.ComponentHolder(isWeak: true); + private global::Microsoft.UI.Xaml.Controls.SwipeItem _component_0 + { + get + { + return (global::Microsoft.UI.Xaml.Controls.SwipeItem)_component_0_Holder.Instance; + } + set + { + _component_0_Holder.Instance = value; + } + } + private global::Microsoft.UI.Xaml.Markup.ComponentHolder _component_1_Holder = new global::Microsoft.UI.Xaml.Markup.ComponentHolder(isWeak: true); + private global::Microsoft.UI.Xaml.Controls.Border _component_1 + { + get + { + return (global::Microsoft.UI.Xaml.Controls.Border)_component_1_Holder.Instance; + } + set + { + _component_1_Holder.Instance = value; + } + } + private void __UpdateBindingsAndResources(global::Microsoft.UI.Xaml.FrameworkElement s, object e) + { + var owner = this; + _component_0.UpdateResourceBindings(); + } + } + } +} +namespace MyProject +{ + static class ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20XamlApplyExtensions + { + public delegate void XamlApplyHandler0(global::Microsoft.UI.Xaml.Controls.Page instance); + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static global::Microsoft.UI.Xaml.Controls.Page ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply(this global::Microsoft.UI.Xaml.Controls.Page instance, XamlApplyHandler0 handler) + { + handler(instance); + return instance; + } + public delegate void XamlApplyHandler1(global::Microsoft.UI.Xaml.Controls.SwipeItems instance); + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static global::Microsoft.UI.Xaml.Controls.SwipeItems ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply(this global::Microsoft.UI.Xaml.Controls.SwipeItems instance, XamlApplyHandler1 handler) + { + handler(instance); + return instance; + } + public delegate void XamlApplyHandler2(global::Microsoft.UI.Xaml.Controls.SwipeItem instance); + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static global::Microsoft.UI.Xaml.Controls.SwipeItem ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply(this global::Microsoft.UI.Xaml.Controls.SwipeItem instance, XamlApplyHandler2 handler) + { + handler(instance); + return instance; + } + public delegate void XamlApplyHandler3(global::Microsoft.UI.Xaml.Controls.Border instance); + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static global::Microsoft.UI.Xaml.Controls.Border ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event_6d62c5ee15120ed189e095faf6d37e20_XamlApply(this global::Microsoft.UI.Xaml.Controls.Border instance, XamlApplyHandler3 handler) + { + handler(instance); + return instance; + } + } +} diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/TestCases/ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/TestCases/ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml new file mode 100644 index 000000000000..fad6f4cdb8b0 --- /dev/null +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/TestCases/ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/TestCases/ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/TestCases/ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml.cs new file mode 100644 index 000000000000..2ead1ddd999b --- /dev/null +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/TestCases/ResourceDictionary_When_Nested_With_Sibling_Ref_And_Event.xaml.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; + +namespace Uno.UI.Tests.Given_ResourceDictionary +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class When_Nested_With_Sibling_Ref_And_Event : Page + { + public When_Nested_With_Sibling_Ref_And_Event() + { + this.InitializeComponent(); + } + + private void AnEventHandler(SwipeItem sender, SwipeItemInvokedEventArgs args) + { + } + } +} diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs index b422b1f943d8..5b90d7fa8fdb 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs @@ -3756,8 +3756,7 @@ IMethodSymbol FindTargetMethodSymbol(INamedTypeSymbol? sourceType) // use the WeakReferenceProvider to get a self reference to avoid adding the cost of the // creation of a WeakReference. // - var thatEventSource = eventSource != "__that" ? "__that." + eventSource : eventSource; - writer.AppendLineIndented($"var {member.Member.Name}_{sanitizedMemberValue}_That = ({thatEventSource} as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference;"); + writer.AppendLineIndented($"var {member.Member.Name}_{sanitizedMemberValue}_That = ({eventSource} as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference;"); writer.AppendLineIndented($"/* second level */ {closureName}.{member.Member.Name} += ({parms}) => ({member.Member.Name}_{sanitizedMemberValue}_That.Target as {_xClassName})?.{member.Value}({parms});"); } diff --git a/src/Uno.CrossTargetting.targets b/src/Uno.CrossTargetting.targets index 08c09d9d52a7..5ddbb2fa0ceb 100644 --- a/src/Uno.CrossTargetting.targets +++ b/src/Uno.CrossTargetting.targets @@ -102,7 +102,7 @@ 21.0 - + $(DefineConstants);HAS_UNO $(DefineConstants);HAS_UNO_WINUI @@ -111,7 +111,7 @@ WinUI - + $(DefineConstants);HAS_INPUT_INJECTOR;WINDOWS_WINUI;HAS_RENDER_TARGET_BITMAP;HAS_COMPOSITION_API diff --git a/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs b/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs index c163cb6e5185..a3cbcb4170fd 100644 --- a/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs +++ b/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs @@ -501,10 +501,10 @@ private void UseDevice(PointerPoint pointer, Gdk.Device device) } properties.IsInRange = true; - + var timeInMicroseconds = time * 1000; var pointerPoint = new Windows.UI.Input.PointerPoint( frameId: time, - timestamp: time * (ulong)TimeSpan.TicksPerMillisecond, // time is in ms, timestamp is in ticks + timestamp: timeInMicroseconds, device: pointerDevice, pointerId: pointerId, rawPosition: rawPosition, diff --git a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs index 84522357b42e..3d8fa5ac1359 100644 --- a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs +++ b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs @@ -102,9 +102,10 @@ double GetAxisValue(libinput_pointer_axis axis) properties.IsMiddleButtonPressed = _pointerPressed.Contains(libinput_event_code.BTN_MIDDLE); properties.IsRightButtonPressed = _pointerPressed.Contains(libinput_event_code.BTN_RIGHT); + var timestampInMicroseconds = timestamp; var pointerPoint = new Windows.UI.Input.PointerPoint( frameId: (uint)timestamp, // UNO TODO: How should set the frame, timestamp may overflow. - timestamp: timestamp * TimeSpan.TicksPerMicrosecond, + timestamp: timestampInMicroseconds, device: PointerDevice.For(PointerDeviceType.Mouse), pointerId: 0, rawPosition: _mousePosition, diff --git a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs index c261c91a1ff4..9106000a6a56 100644 --- a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs +++ b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs @@ -83,9 +83,10 @@ public void ProcessTouchEvent(IntPtr rawEvent, libinput_event_type rawEventType) properties.IsLeftButtonPressed = rawEventType != LIBINPUT_EVENT_TOUCH_UP && rawEventType != LIBINPUT_EVENT_TOUCH_CANCEL; + var timestampInMicroseconds = timestamp; var pointerPoint = new Windows.UI.Input.PointerPoint( frameId: (uint)timestamp, // UNO TODO: How should set the frame, timestamp may overflow. - timestamp: timestamp * TimeSpan.TicksPerMicrosecond, + timestamp: timestampInMicroseconds, device: PointerDevice.For(PointerDeviceType.Touch), pointerId: pointerId, rawPosition: currentPosition, diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h index 0fb4900ab266..d2f5766b7e81 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h +++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h @@ -12,7 +12,6 @@ typedef void (*system_theme_change_fn_ptr)(void); system_theme_change_fn_ptr uno_get_system_theme_change_callback(void); void uno_set_system_theme_change_callback(system_theme_change_fn_ptr p); uint32 uno_get_system_theme(void); -NSTimeInterval uno_get_system_uptime(void); bool uno_app_initialize(bool *supportsMetal); NSWindow* uno_app_get_main_window(void); diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m index 5eac3ebf1a47..029056058d55 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m +++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m @@ -7,7 +7,6 @@ static UNOApplicationDelegate *ad; static system_theme_change_fn_ptr system_theme_change; static id device; -static NSTimeInterval uptime = 0; inline system_theme_change_fn_ptr uno_get_system_theme_change_callback(void) { @@ -28,14 +27,6 @@ void uno_set_system_theme_change_callback(system_theme_change_fn_ptr p) return [appearanceName isEqualToString:NSAppearanceNameAqua] ? 0 : 1; } -NSTimeInterval uno_get_system_uptime(void) -{ - if (uptime == 0) { - uptime = NSProcessInfo.processInfo.systemUptime; - } - return uptime; -} - bool uno_app_initialize(bool *metal) { NSApplication *app = [NSApplication sharedApplication]; diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m index 9d9a6ddc5142..528cd0e6d481 100644 --- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m +++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m @@ -912,10 +912,7 @@ - (void)sendEvent:(NSEvent *)event { NSTimeInterval ts = event.timestamp; data.frameId = (uint)(ts * 10.0); - - NSDate *now = [[NSDate alloc] init]; - NSDate *boot = [[NSDate alloc] initWithTimeInterval:uno_get_system_uptime() sinceDate:now]; - data.timestamp = (uint64)(boot.timeIntervalSinceNow * 1000000); + data.timestamp = (uint64)(ts * 1000000); handled = uno_get_window_mouse_event_callback()(self, &data); #if DEBUG_MOUSE // very noisy diff --git a/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs b/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs index 8b9524aa07c1..bf31f2fed63e 100644 --- a/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs +++ b/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs @@ -296,7 +296,7 @@ private IntPtr OnWmMessage(IntPtr hwnd, int msg, IntPtr wparamOriginal, IntPtr l var point = new Windows.UI.Input.PointerPoint( frameId: FrameIdProvider.GetNextFrameId(), - timestamp: (ulong)Environment.TickCount, + timestamp: (ulong)(Environment.TickCount64 * 1000), device: PointerDevice.For(PointerDeviceType.Mouse), pointerId: 1, rawPosition: position, @@ -406,11 +406,12 @@ private PointerEventArgs BuildPointerArgs(InputEventArgs args, bool? isReleaseOr throw new ArgumentException(); } + var timestampInMicroseconds = (ulong)(args.Timestamp * 1000); properties = properties.SetUpdateKindFromPrevious(_previous?.CurrentPoint.Properties); var modifiers = GetKeyModifiers(); var point = new PointerPoint( frameId: FrameIdProvider.GetNextFrameId(), - timestamp: (ulong)(args.Timestamp * TimeSpan.TicksPerMillisecond), + timestamp: timestampInMicroseconds, device: GetPointerDevice(args), pointerId: pointerId, rawPosition: new Windows.Foundation.Point(position.X, position.Y), diff --git a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs index 9c241394073b..937fdcf3696a 100644 --- a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs +++ b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs @@ -123,9 +123,10 @@ private PointerPoint CreatePointFromCurrentState(IntPtr time) ? root.RasterizationScale : 1; + var timeInMicroseconds = (ulong)(time * 1000); // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535 var point = new PointerPoint( frameId: (uint)time, // UNO TODO: How should set the frame, timestamp may overflow. - timestamp: (uint)(time * TimeSpan.TicksPerMillisecond), // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535 + timestamp: timeInMicroseconds, PointerDevice.For(PointerDeviceType.Mouse), 0, // TODO: XInput new Point(_mousePosition.X / scale, _mousePosition.Y / scale), diff --git a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs index a8f20948e6cb..52bfdf30ba4b 100644 --- a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs +++ b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs @@ -305,11 +305,14 @@ public unsafe PointerEventArgs CreatePointerEventArgsFromDeviceEvent(XIDeviceEve ? XamlRoot.GetDisplayInformation(root).RawPixelsPerViewPixel : 1; + var timeInMicroseconds = (ulong)(data.time * 1000); // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535 + var deviceType = data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? PointerDeviceType.Touch : PointerDeviceType.Mouse; + var pointerId = (uint)(data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? data.detail : data.sourceid); // for touch, data.detail is the touch ID var point = new PointerPoint( frameId: (uint)data.time, // UNO TODO: How should we set the frame, timestamp may overflow. - timestamp: (uint)(data.time * TimeSpan.TicksPerMillisecond), // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535 - PointerDevice.For(data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? PointerDeviceType.Touch : PointerDeviceType.Mouse), - (uint)(data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? data.detail : data.sourceid), // for touch, data.detail is the touch ID + timestamp: timeInMicroseconds, + PointerDevice.For(deviceType), + pointerId, new Point(data.event_x / scale, data.event_y / scale), new Point(data.event_x / scale, data.event_y / scale), properties.HasPressedButton, @@ -345,9 +348,10 @@ public unsafe PointerEventArgs CreatePointerEventArgsFromEnterLeaveEvent(XIEnter IsHorizontalMouseWheel = false, }; + var timestampInMicroseconds = (ulong)(data.time * 1000); // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535 var point = new PointerPoint( frameId: (uint)data.time, // UNO TODO: How should we set the frame, timestamp may overflow. - timestamp: (ulong)data.time, + timestamp: timestampInMicroseconds, PointerDevice.For(PointerDeviceType.Mouse), (uint)data.sourceid, new Point(data.event_x, data.event_y), diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs index b4e54ec9d649..e9566393b64b 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs @@ -1144,6 +1144,7 @@ public async Task When_IsTextSelectionEnabled_SurrogatePair_Copy() #endif public async Task When_IsTextSelectionEnabled_CRLF() { + var delayToAvoidDoubleTap = 600; var SUT = new TextBlock { Text = "FirstLine\r\n Second", @@ -1165,7 +1166,7 @@ public async Task When_IsTextSelectionEnabled_CRLF() mouse.Release(); mouse.Press(); mouse.Release(); - await WindowHelper.WaitForIdle(); + await Task.Delay(delayToAvoidDoubleTap); SUT.CopySelectionToClipboard(); await WindowHelper.WaitForIdle(); @@ -1180,7 +1181,7 @@ public async Task When_IsTextSelectionEnabled_CRLF() mouse.Release(); mouse.Press(); mouse.Release(); - await WindowHelper.WaitForIdle(); + await Task.Delay(delayToAvoidDoubleTap); SUT.CopySelectionToClipboard(); await WindowHelper.WaitForIdle(); diff --git a/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs b/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs index ece88216c482..c109abe69e72 100644 --- a/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs +++ b/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs @@ -20,6 +20,8 @@ namespace Uno.UI.Tests.Windows_UI_Input [TestClass] public class Given_GestureRecognizer { + private const int MicrosecondsPerMillisecond = 1000; + private const GestureSettings ManipulationsWithoutInertia = GestureSettings.ManipulationTranslateX | GestureSettings.ManipulationTranslateY | GestureSettings.ManipulationTranslateRailsX @@ -162,7 +164,7 @@ public void DoubleTapped_Duration() taps.Should().BeEquivalentTo(Tap(25, 25)); // Double tapped - var tooSlow = GetPoint(25, 25, ts: 1 + GestureRecognizer.MultiTapMaxDelayTicks + 1); + var tooSlow = GetPoint(25, 25, ts: 1 + GestureRecognizer.MultiTapMaxDelayMicroseconds + 1); sut.CanBeDoubleTap(tooSlow).Should().BeFalse(); sut.ProcessDownEvent(tooSlow); @@ -1002,8 +1004,8 @@ public void Manipulation_Inertia_Translate() // flick at 2 px/ms sut.ProcessDownEvent(10, 10, ts: 0); - sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond); - sut.ProcessUpEvent(102, 102, ts: 101 * TimeSpan.TicksPerMillisecond); + sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond); + sut.ProcessUpEvent(102, 102, ts: 101 * MicrosecondsPerMillisecond); sut.RunInertiaSync(); @@ -1049,8 +1051,8 @@ public void Manipulation_Inertia_TranslateXOnly() // flick at 2 px/ms sut.ProcessDownEvent(10, 10, ts: 0); - sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond); - sut.ProcessUpEvent(102, 102, ts: 101 * TimeSpan.TicksPerMillisecond); + sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond); + sut.ProcessUpEvent(102, 102, ts: 101 * MicrosecondsPerMillisecond); sut.RunInertiaSync(); @@ -1096,8 +1098,8 @@ public void Manipulation_Inertia_TranslateYOnly() // flick at 2 px/ms sut.ProcessDownEvent(10, 10, ts: 0); - sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond); - sut.ProcessUpEvent(102, 102, ts: 101 * TimeSpan.TicksPerMillisecond); + sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond); + sut.ProcessUpEvent(102, 102, ts: 101 * MicrosecondsPerMillisecond); sut.RunInertiaSync(); @@ -1143,8 +1145,8 @@ public void Manipulation_Inertia_Translate_Negative() // flick at 2 px/ms sut.ProcessDownEvent(10, 10, ts: 0); - sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond); - sut.ProcessUpEvent(98, 98, ts: 101 * TimeSpan.TicksPerMillisecond); + sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond); + sut.ProcessUpEvent(98, 98, ts: 101 * MicrosecondsPerMillisecond); sut.RunInertiaSync(); @@ -1192,8 +1194,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InFirstQuadrant() // Rotate of pi/2 in a quarter of second sut.ProcessDownEvent(50, -25, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(25, -50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(25, -50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1222,8 +1224,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InSecondQuadrant() // Rotate of Pi/4 in a quarter of second sut.ProcessDownEvent(-25, -50, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(-50, -25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(-50, -25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1252,8 +1254,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InThirdQuadrant() // Rotate of pi/2 in a quarter of second sut.ProcessDownEvent(-50, 25, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(-25, 50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(-25, 50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1282,8 +1284,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InForthQuadrant() // Rotate of pi/2 in a quarter of second sut.ProcessDownEvent(25, 50, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(50, 25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(50, 25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1312,8 +1314,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InFirstQuadrant() sut.ProcessDownEvent(25, -25, id: 1, ts: 0); sut.ProcessDownEvent(25, -50, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(50, -25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(50, -25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1341,8 +1343,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InSecondQuadrant() sut.ProcessDownEvent(-25, -25, id: 1, ts: 0); sut.ProcessDownEvent(-50, -25, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(-25, -50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(-25, -50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1371,8 +1373,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InThirdQuadrant() sut.ProcessDownEvent(-25, 25, id: 1, ts: 0); sut.ProcessDownEvent(-25, 50, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(-50, 25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(-50, 25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1401,8 +1403,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InForthQuadrant() sut.ProcessDownEvent(25, 25, id: 1, ts: 0); sut.ProcessDownEvent(50, 25, id: 2, ts: 1); // Angle = 0 - sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4 - sut.ProcessUpEvent(25, 50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2 + sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4 + sut.ProcessUpEvent(25, 50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2 sut.RunInertiaSync(); @@ -1449,8 +1451,8 @@ public void Drag_Started_Mouse_Hold() // Start mouse dragging sut.ProcessDownEvent(25, 25, ts: 0); - sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1); - var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2); + sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1); + var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2); drags.Should().BeEquivalentTo(Drag(start, DraggingState.Started)); } @@ -1480,8 +1482,8 @@ public void Drag_Started_Pen_Hold() using var _ = Pen(); sut.ProcessDownEvent(25, 25, ts: 0); - sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1); - var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2); + sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1); + var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2); drags.Should().BeEquivalentTo(Drag(start, DraggingState.Started)); } @@ -1511,8 +1513,8 @@ public void Drag_Started_Touch_Hold() using var _ = Touch(); sut.ProcessDownEvent(25, 25, ts: 0); - sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1); - var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2); + sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1); + var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2); drags.Should().BeEquivalentTo(Drag(start, DraggingState.Started)); } @@ -1529,8 +1531,8 @@ public void Drag_Started_Touch_MovedTooFar() sut.ProcessDownEvent(25, 25, ts: 0); sut.ProcessMoveEvent(50, 50, ts: 1); sut.ProcessMoveEvent(25, 25, ts: 2); - sut.ProcessMoveEvent(25, 25, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1); - sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2); + sut.ProcessMoveEvent(25, 25, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1); + sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2); drags.Should().BeEmpty(); } @@ -1545,10 +1547,10 @@ public void Drag_CompleteGesture() using var _ = Touch(); sut.ProcessDownEvent(25, 25, ts: 0); - sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1); - var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2); - var move = sut.ProcessMoveEvent(51, 51, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1); - var end = sut.ProcessUpEvent(52, 52, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2); + sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1); + var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2); + var move = sut.ProcessMoveEvent(51, 51, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1); + var end = sut.ProcessUpEvent(52, 52, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2); drags.Should().BeEquivalentTo( Drag(start, DraggingState.Started), @@ -1559,7 +1561,7 @@ public void Drag_CompleteGesture() [TestMethod] public void Drag_And_Holding_Touch() { - var delay = (ulong)Math.Max(GestureRecognizer.DragWithTouchMinDelayTicks, GestureRecognizer.HoldMinDelayTicks); + var delay = (ulong)Math.Max(GestureRecognizer.DragWithTouchMinDelayMicroseconds, GestureRecognizer.HoldMinDelayMicroseconds); var sut = new GestureRecognizer { GestureSettings = GestureSettings.Drag | GestureSettings.Hold }; var drags = new List(); var holds = new List(); diff --git a/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs b/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs index 51c2741190c9..f03d40afd4f5 100644 --- a/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs +++ b/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs @@ -28,6 +28,7 @@ internal partial class BrowserPointerInputSource : IUnoCorePointerInputSource private static readonly Logger _log = typeof(BrowserPointerInputSource).Log(); private static readonly Logger? _logTrace = _log.IsTraceEnabled(LogLevel.Trace) ? _log : null; + // TODO: Verify the boot time unit (ms or ticks) private ulong _bootTime; private bool _isOver; private PointerPoint? _lastPoint; @@ -321,7 +322,7 @@ internal static uint ToFrameId(double timestamp) => (uint)(timestamp % uint.MaxValue); private ulong ToTimeStamp(double timestamp) - => _bootTime + (ulong)(timestamp * TimeSpan.TicksPerMillisecond); + => _bootTime + (ulong)(timestamp * 1000); private static PointerUpdateKind ToUpdateKind(HtmlPointerButtonUpdate update, PointerPointProperties props) => update switch diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs b/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs index 03ee67cffdce..ba0d1f858947 100644 --- a/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs +++ b/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs @@ -249,7 +249,7 @@ private bool SupportsHolding() private void StartHoldingTimer() { _holdingTimer = DispatcherQueue.GetForCurrentThread().CreateTimer(); - _holdingTimer.Interval = TimeSpan.FromTicks(HoldMinDelayTicks); + _holdingTimer.Interval = TimeSpan.FromMicroseconds(HoldMinDelayMicroseconds); _holdingTimer.State = this; _holdingTimer.Tick += OnHoldingTimerTick; _holdingTimer.Start(); @@ -323,7 +323,7 @@ public static bool IsMultiTapGesture((ulong id, ulong ts, Point position) previo var currentPosition = down.Position; return previousTap.id == currentId - && currentTs - previousTap.ts <= MultiTapMaxDelayTicks + && currentTs - previousTap.ts <= MultiTapMaxDelayMicroseconds && !IsOutOfTapRange(previousTap.position, currentPosition); } @@ -394,7 +394,7 @@ private static bool IsRightTapGesture(Gesture points, out bool isLongPress) } private static bool IsLongPress(PointerPoint down, PointerPoint current) - => current.Timestamp - down.Timestamp > HoldMinDelayTicks; + => current.Timestamp - down.Timestamp > HoldMinDelayMicroseconds; #endregion } } diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs index 53d8b981f8eb..94f3a4f9b7df 100644 --- a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs +++ b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs @@ -88,7 +88,7 @@ public InertiaProcessor(Manipulation owner, Point position, ManipulationDelta cu /// Depending of the platform, the timestamp provided by pointer events might not be absolute, /// so it's preferable to not compare timestamp between pointers and inertia processor. /// - public long Elapsed => _timer.LastTickElapsed.Ticks; + public double Elapsed => _timer.LastTickElapsed.TotalMicroseconds; public void Start() { diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs index 29813e861dcb..1c6153501259 100644 --- a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs +++ b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs @@ -499,8 +499,8 @@ private ManipulationVelocities GetVelocities(ManipulationDelta delta) { // The _currents.Timestamp is not updated once inertia as started, we must get the elapsed duration from the inertia processor // (and not compare it to PointerPoint.Timestamp in any way, cf. remarks on InertiaProcessor.Elapsed) - var elapsedTicks = _inertia?.Elapsed ?? (double)_currents.Timestamp - _lastPublishedState.timestamp; - var elapsedMs = elapsedTicks / TimeSpan.TicksPerMillisecond; + var elapsedMicroseconds = _inertia?.Elapsed ?? (_currents.Timestamp - _lastPublishedState.timestamp); + var elapsedMs = elapsedMicroseconds / 1000; // With uno a single native event might produce multiple managed pointer events. // In that case we would get an empty velocities ... which is often not relevant! @@ -541,7 +541,7 @@ private void StartDragTimer() if (_isDraggingEnable && _deviceType == PointerDeviceType.Touch) { _dragHoldTimer = DispatcherQueue.GetForCurrentThread().CreateTimer(); - _dragHoldTimer.Interval = new TimeSpan(DragWithTouchMinDelayTicks); + _dragHoldTimer.Interval = TimeSpan.FromMicroseconds(DragWithTouchMinDelayMicroseconds); _dragHoldTimer.IsRepeating = false; _dragHoldTimer.Tick += TouchDragMightStart; _dragHoldTimer.Start(); @@ -565,7 +565,7 @@ private void StopDragTimer() } // For pen and mouse this only means down -> * moves out of tap range; - // For touch it means down -> * moves close to origin for DragUsingFingerMinDelayTicks -> * moves far from the origin + // For touch it means down -> * moves close to origin for DragWithTouchMinDelayMicroseconds -> * moves far from the origin private bool IsBeginningOfDragManipulation() { if (!_isDraggingEnable) @@ -592,7 +592,7 @@ private bool IsBeginningOfDragManipulation() // This means that this method is expected to be invoked on each move (until manipulation starts) // in order to update the _isDraggingEnable state. - var isInHoldPhase = current.Timestamp - down.Timestamp < DragWithTouchMinDelayTicks; + var isInHoldPhase = current.Timestamp - down.Timestamp < DragWithTouchMinDelayMicroseconds; if (isInHoldPhase && isOutOfRange) { // The pointer moved out of range while in the hold phase, so we completely disable the drag manipulation diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.cs b/src/Uno.UI/UI/Input/GestureRecognizer.cs index 21e15e9200f4..b714182873c2 100644 --- a/src/Uno.UI/UI/Input/GestureRecognizer.cs +++ b/src/Uno.UI/UI/Input/GestureRecognizer.cs @@ -26,12 +26,12 @@ public partial class GestureRecognizer internal const int TapMaxXDelta = 10; internal const int TapMaxYDelta = 10; - internal const ulong MultiTapMaxDelayTicks = TimeSpan.TicksPerMillisecond * 500; + internal const ulong MultiTapMaxDelayMicroseconds = 500000; - internal const long HoldMinDelayTicks = TimeSpan.TicksPerMillisecond * 800; + internal const long HoldMinDelayMicroseconds = 800000; internal const float HoldMinPressure = .75f; - internal const long DragWithTouchMinDelayTicks = TimeSpan.TicksPerMillisecond * 300; // https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#open-a-context-menu-on-an-item-you-can-drag-with-touch + internal const long DragWithTouchMinDelayMicroseconds = 300000; // https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#open-a-context-menu-on-an-item-you-can-drag-with-touch private readonly Logger _log; private IDictionary _gestures = new Dictionary(_defaultGesturesSize); diff --git a/src/Uno.UI/UI/Input/PointerPoint.cs b/src/Uno.UI/UI/Input/PointerPoint.cs index d70b5ed43a4e..b6e4d8014a5b 100644 --- a/src/Uno.UI/UI/Input/PointerPoint.cs +++ b/src/Uno.UI/UI/Input/PointerPoint.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Text; using System.Threading; using Windows.Devices.Input; 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 c1197f21be79..a05407a9f6fe 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 @@ -104,7 +104,7 @@ private static bool IsMultiTapGesture((ulong id, ulong ts, Point position) previ var currentPosition = down.Position; return previousTap.id == currentId - && currentTs - previousTap.ts <= GestureRecognizer.MultiTapMaxDelayTicks + && currentTs - previousTap.ts <= GestureRecognizer.MultiTapMaxDelayMicroseconds && !GestureRecognizer.IsOutOfTapRange(previousTap.position, currentPosition); } @@ -176,7 +176,7 @@ partial void OnPointerReleasedPartial(PointerRoutedEventArgs args) _isPressed = false; - if ((args.GetCurrentPoint(null).Timestamp - _lastPointerDown.point.Timestamp) >= GestureRecognizer.HoldMinDelayTicks) + if ((args.GetCurrentPoint(null).Timestamp - _lastPointerDown.point.Timestamp) >= GestureRecognizer.HoldMinDelayMicroseconds) { // Touch holding OpenContextMenu(args.GetCurrentPoint(this).Position); diff --git a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs index 6044821033f0..9a45fbb35473 100644 --- a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs +++ b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs @@ -250,9 +250,10 @@ private PointerPointProperties GetProperties(MotionEvent nativeEvent, MotionEven private static ulong ToTimeStamp(long uptimeMillis) { + // Timestamp is in microseconds if (FeatureConfiguration.PointerRoutedEventArgs.AllowRelativeTimeStamp) { - return (ulong)(TimeSpan.TicksPerMillisecond * uptimeMillis); + return (ulong)(uptimeMillis * 1000); } else { @@ -261,9 +262,7 @@ private static ulong ToTimeStamp(long uptimeMillis) var sleepTime = Android.OS.SystemClock.ElapsedRealtime() - Android.OS.SystemClock.UptimeMillis(); var realUptime = (ulong)(uptimeMillis + sleepTime); - var timestamp = TimeSpan.TicksPerMillisecond * (_unixEpochMs + realUptime); - - return timestamp; + return realUptime * 1000; } } diff --git a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs index 2d4427a8879c..80062282b032 100644 --- a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs +++ b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs @@ -8,8 +8,6 @@ using Uno.UI.Xaml.Core; using Uno.UI.Xaml.Input; - - #if HAS_UNO_WINUI using Microsoft.UI.Input; #else @@ -77,7 +75,7 @@ internal PointerRoutedEventArgs(uint pointerId, UITouch nativeTouch, UIEvent nat public PointerPoint GetCurrentPoint(UIElement relativeTo) { - var timestamp = ToTimeStamp(_nativeTouch.Timestamp); + var timestamp = ToTimestamp(_nativeTouch.Timestamp); var device = global::Windows.Devices.Input.PointerDevice.For((global::Windows.Devices.Input.PointerDeviceType)Pointer.PointerDeviceType); var rawPosition = (Point)_nativeTouch.GetPreciseLocation(null); var position = relativeTo == null @@ -112,13 +110,11 @@ private PointerPointProperties GetProperties() }; #region Misc static helpers - private static long? _bootTime; - private static ulong ToTimeStamp(double timestamp) + private static ulong ToTimestamp(double nativeTimestamp) { - _bootTime ??= DateTime.UtcNow.Ticks - (long)(TimeSpan.TicksPerSecond * new NSProcessInfo().SystemUptime); - - return (ulong)_bootTime.Value + (ulong)(TimeSpan.TicksPerSecond * timestamp); + // iOS Timestamp is in seconds from boot time, convert to microseconds. + return (ulong)(nativeTimestamp * 1000 * 1000); } private static double? _firstTimestamp; diff --git a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs index 91c38268a3f6..e92d773bb4df 100644 --- a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs +++ b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs @@ -100,9 +100,10 @@ internal PointerEventArgs ToEventArgs(InjectedInputState state, VirtualKeyModifi properties.PointerUpdateKind = update; + var timestampInMicroseconds = state.Timestamp + TimeOffsetInMilliseconds * 1000; var point = new PointerPoint( state.FrameId + TimeOffsetInMilliseconds, - state.Timestamp + TimeOffsetInMilliseconds, + timestampInMicroseconds, PointerDevice.For(PointerDeviceType.Mouse), uint.MaxValue - 42, // Try to avoid conflict with the real mouse pointer position, diff --git a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs index 7d521bfeac0f..609de983a4a5 100644 --- a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs +++ b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs @@ -78,10 +78,11 @@ internal PointerPoint ToPointerPoint(InjectedInputState state) properties.IsBarrelButtonPressed = properties.IsRightButtonPressed; } + var timestampInMicroseconds = state.Timestamp + TimeOffsetInMilliseconds * 1000; var location = new Point(PixelLocation.PositionX, PixelLocation.PositionY); var point = new PointerPoint( state.FrameId + (uint)PerformanceCount, - state.Timestamp + (ulong)(TimeOffsetInMilliseconds * TimeSpan.TicksPerMillisecond), + timestampInMicroseconds, PointerDevice.For(state.Type), isNew ? PointerId : state.PointerId, location,