diff --git a/src/Uno.Extensions.Core/IDispatcher.cs b/src/Uno.Extensions.Core/IDispatcher.cs index c174969c03..02f18592f2 100644 --- a/src/Uno.Extensions.Core/IDispatcher.cs +++ b/src/Uno.Extensions.Core/IDispatcher.cs @@ -10,4 +10,9 @@ public interface IDispatcher /// An cancellation token to cancel the async operation. /// A ValueTask to asynchronously get the result of the operation. ValueTask ExecuteAsync(AsyncFunc func, CancellationToken cancellation); + + /// + /// Gets a value that specifies whether the current execution context is on the UI thread. + /// + bool HasThreadAccess { get; } } diff --git a/src/Uno.Extensions.Core/PlatformHelper.cs b/src/Uno.Extensions.Core/PlatformHelper.cs index b5728c2f62..602d810909 100644 --- a/src/Uno.Extensions.Core/PlatformHelper.cs +++ b/src/Uno.Extensions.Core/PlatformHelper.cs @@ -1,6 +1,6 @@ namespace Uno.Extensions; -public class PlatformHelper +public static class PlatformHelper { private static bool _isNetCore; private static bool _initialized; @@ -30,6 +30,15 @@ public static bool IsNetCore } } + /// + /// Determines if the current runtime supports threading + /// + public static bool IsThreadingEnabled + { get; } = !IsWebAssembly || IsWebAssemblyThreadingSupported; + + private static bool IsWebAssemblyThreadingSupported { get; } = Environment.GetEnvironmentVariable("UNO_BOOTSTRAP_MONO_RUNTIME_CONFIGURATION").StartsWith("threads", StringComparison.OrdinalIgnoreCase); + + /// /// Initialization is performed explicitly to avoid a mono/mono issue regarding .cctor and FullAOT /// see https://github.com/unoplatform/uno/issues/5395 diff --git a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs index ac29d551d1..b8f592eedb 100644 --- a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs +++ b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs @@ -33,5 +33,15 @@ public Dispatcher(FrameworkElement element) /// public async ValueTask ExecuteAsync(AsyncFunc func, CancellationToken cancellation) - => await _dispatcher.ExecuteAsync(func, cancellation); + { + if (PlatformHelper.IsThreadingEnabled && + HasThreadAccess) + { + return await func(cancellation); + } + return await _dispatcher.ExecuteAsync(func, cancellation); + } + + /// + public bool HasThreadAccess => _dispatcher.HasThreadAccess; } diff --git a/src/Uno.Extensions.Navigation.UI/Navigator.cs b/src/Uno.Extensions.Navigation.UI/Navigator.cs index a6f51a32bb..ba5dcceec1 100644 --- a/src/Uno.Extensions.Navigation.UI/Navigator.cs +++ b/src/Uno.Extensions.Navigation.UI/Navigator.cs @@ -45,6 +45,15 @@ protected Navigator( var regionUpdateId = RouteUpdater?.StartNavigation(Region) ?? Guid.Empty; try { + + if (Dispatcher.HasThreadAccess && + !request.InProgress) + { + request = request with { InProgress = true }; // Prevent multiple re-entrance where HasThreadAccess always returns true eg WASM + if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformationMessage($"Navigation started on UI thread, so moving to background thread"); + return await Task.Run(() => NavigateAsync(request)); + } + if (request.Source is null) { if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformationMessage($"Starting Navigation - Navigator: {this.GetType().Name} Request: {request.Route}"); diff --git a/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs b/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs index d74c288d7b..4d13c908c5 100644 --- a/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs +++ b/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs @@ -99,27 +99,28 @@ protected override async Task RegionCanNavigate(Route route, RouteInfo? ro return path; } - protected override Task PostNavigateAsync() + protected override async Task PostNavigateAsync() { if (Control is not null) { - foreach (var child in Control.Children.OfType()) + await Dispatcher.ExecuteAsync(async cancellation => { - if(child == CurrentlyVisibleControl) + foreach (var child in Control.Children.OfType()) { - child.Opacity = 1; - child.Visibility = Visibility.Visible; - } - else - { - child.Opacity = 0; - child.Visibility = Visibility.Collapsed; + if (child == CurrentlyVisibleControl) + { + child.Opacity = 1; + child.Visibility = Visibility.Visible; + } + else + { + child.Opacity = 0; + child.Visibility = Visibility.Collapsed; + } } - } + }); } - - return Task.CompletedTask; } private FrameworkElement? FindByPath(string? path) diff --git a/src/Uno.Extensions.Navigation/NavigationRequest.cs b/src/Uno.Extensions.Navigation/NavigationRequest.cs index 068de8814f..24f27a10c3 100644 --- a/src/Uno.Extensions.Navigation/NavigationRequest.cs +++ b/src/Uno.Extensions.Navigation/NavigationRequest.cs @@ -4,6 +4,8 @@ public record NavigationRequest(object Sender, Route Route, CancellationToken? Cancellation = default, Type? Result = null, INavigator? Source = null) #pragma warning restore SA1313 // Parameter names should begin with lower-case letter { + internal bool InProgress { get; init; } + public override string ToString() => $"Request [Sender: {Sender.GetType().Name}, Route:{Route}, Result: {Result?.Name ?? "N/A"}]"; internal virtual IResponseNavigator? GetResponseNavigator(IResponseNavigatorFactory responseFactory, INavigator navigator) => default; diff --git a/testing/TestHarness/Directory.Build.targets b/testing/TestHarness/Directory.Build.targets index 0712cae045..b6ff508d83 100644 --- a/testing/TestHarness/Directory.Build.targets +++ b/testing/TestHarness/Directory.Build.targets @@ -34,4 +34,9 @@ + + + diff --git a/testing/TestHarness/TestHarness-ui.slnf b/testing/TestHarness/TestHarness-ui.slnf index bd035a1ba6..1a38851a44 100644 --- a/testing/TestHarness/TestHarness-ui.slnf +++ b/testing/TestHarness/TestHarness-ui.slnf @@ -8,6 +8,7 @@ "..\\..\\src\\Uno.Extensions.Authentication.UI\\Uno.Extensions.Authentication.UI.csproj", "..\\..\\src\\Uno.Extensions.Authentication\\Uno.Extensions.Authentication.csproj", "..\\..\\src\\Uno.Extensions.Configuration\\Uno.Extensions.Configuration.csproj", + "..\\..\\src\\Uno.Extensions.Core.Generators\\Uno.Extensions.Core.Generators.csproj", "..\\..\\src\\Uno.Extensions.Core\\Uno.Extensions.Core.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.UWP.Skia.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.UWP.Wasm.csproj", @@ -15,8 +16,8 @@ "..\\..\\src\\Uno.Extensions.Hosting\\Uno.Extensions.Hosting.csproj", "..\\..\\src\\Uno.Extensions.Http.Refit\\Uno.Extensions.Http.Refit.csproj", "..\\..\\src\\Uno.Extensions.Http\\Uno.Extensions.Http.csproj", - "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Localization.UI\\Uno.Extensions.Localization.UI.csproj", + "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Logging.Serilog\\Uno.Extensions.Logging.Serilog.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.UWP.Skia.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.UWP.Wasm.csproj", diff --git a/testing/TestHarness/TestHarness-winui.slnf b/testing/TestHarness/TestHarness-winui.slnf index 1bc234818d..2b9df3f0a7 100644 --- a/testing/TestHarness/TestHarness-winui.slnf +++ b/testing/TestHarness/TestHarness-winui.slnf @@ -8,6 +8,7 @@ "..\\..\\src\\Uno.Extensions.Authentication.UI\\Uno.Extensions.Authentication.WinUI.csproj", "..\\..\\src\\Uno.Extensions.Authentication\\Uno.Extensions.Authentication.csproj", "..\\..\\src\\Uno.Extensions.Configuration\\Uno.Extensions.Configuration.csproj", + "..\\..\\src\\Uno.Extensions.Core.Generators\\Uno.Extensions.Core.Generators.csproj", "..\\..\\src\\Uno.Extensions.Core\\Uno.Extensions.Core.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.WinUI.Skia.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.WinUI.Wasm.csproj", @@ -15,8 +16,8 @@ "..\\..\\src\\Uno.Extensions.Hosting\\Uno.Extensions.Hosting.csproj", "..\\..\\src\\Uno.Extensions.Http.Refit\\Uno.Extensions.Http.Refit.csproj", "..\\..\\src\\Uno.Extensions.Http\\Uno.Extensions.Http.csproj", - "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Localization.UI\\Uno.Extensions.Localization.WinUI.csproj", + "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Logging.Serilog\\Uno.Extensions.Logging.Serilog.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.WinUI.Skia.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.WinUI.Wasm.csproj", diff --git a/testing/TestHarness/TestHarness.UITest/Constants.cs b/testing/TestHarness/TestHarness.UITest/Constants.cs index db7bc9d198..2443c49665 100644 --- a/testing/TestHarness/TestHarness.UITest/Constants.cs +++ b/testing/TestHarness/TestHarness.UITest/Constants.cs @@ -4,7 +4,7 @@ namespace TestHarness.UITest; public class Constants { - public readonly static string WebAssemblyDefaultUri = "https://localhost:51571"; + public readonly static string WebAssemblyDefaultUri = "https://localhost:64052"; public readonly static string iOSAppName = "uno.platform.extensions.demo"; public readonly static string AndroidAppName = "uno.platform.extensions.demo"; public readonly static string iOSDeviceNameOrId = "iPad Pro (12.9-inch) (4th generation)";