diff --git a/.vsts-ci.yml b/.vsts-ci.yml
index 63db333051fd..06d37df6a01c 100644
--- a/.vsts-ci.yml
+++ b/.vsts-ci.yml
@@ -56,10 +56,10 @@ variables:
windowsScaledPool: 'Windows2022-20240421'
linuxVMImage: 'ubuntu-latest'
linuxScaledPool: 'Ubuntu2204-20230918'
- macOSVMImage: 'macOS-14'
+ macOSVMImage: 'macOS-15'
macOSVMImage_UITests: 'macOS-14'
xCodeRoot: '/Applications/Xcode_16.app'
- xCodeRoot_iOS_UITests: '/Applications/Xcode_16.app'
+ xCodeRoot_iOS_UITests: '/Applications/Xcode_15.3.app'
# Offline validation to improve build performance
NUGET_CERT_REVOCATION_MODE: offline
diff --git a/build/PackageDiffIgnore.xml b/build/PackageDiffIgnore.xml
index 875317d81ea1..6f0c700a3375 100644
--- a/build/PackageDiffIgnore.xml
+++ b/build/PackageDiffIgnore.xml
@@ -1982,6 +1982,10 @@
+
+
+
+
diff --git a/build/ci/.azure-devops-android-tests.yml b/build/ci/.azure-devops-android-tests.yml
index bd53d98f5391..df4ed81e043b 100644
--- a/build/ci/.azure-devops-android-tests.yml
+++ b/build/ci/.azure-devops-android-tests.yml
@@ -36,6 +36,11 @@ jobs:
- checkout: self
clean: true
+ # Install android 34 as we're running on macos-15
+ - bash: |
+ echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_HOME --install 'platforms;android-34' | tr '\r' '\n' | uniq
+ displayName: Install Android 34
+
- template: templates/dotnet-mobile-install-mac.yml
- template: templates/nuget-cache.yml
diff --git a/build/ci/.azure-devops-project-template-tests.yml b/build/ci/.azure-devops-project-template-tests.yml
index 6ea10c23a45d..d44825c2442c 100644
--- a/build/ci/.azure-devops-project-template-tests.yml
+++ b/build/ci/.azure-devops-project-template-tests.yml
@@ -31,8 +31,8 @@ jobs:
artifactName: 'Nuget_Packages'
- template: templates/gitversion.yml
-
- template: templates/dotnet-mobile-install-windows.yml
+ - template: templates/uno-dev-feed.yml
- script: copy $(System.ArtifactsDirectory)\Nuget_Packages\vslatest\*.nupkg $(Build.SourcesDirectory)\src\PackageCache
displayName: Copy Artifacts to PackageCache
@@ -81,6 +81,11 @@ jobs:
inputs:
artifactName: 'Nuget_Packages'
+ # Install android 34 as we're running on macos-15
+ - bash: |
+ echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_HOME --install 'platforms;android-34' | tr '\r' '\n' | uniq
+ displayName: Install Android 34
+
- template: templates/gitversion.yml
- template: templates/ios-build-select-version.yml
@@ -88,6 +93,7 @@ jobs:
xCodeRoot: ${{ parameters.xCodeRoot }}
- template: templates/dotnet-mobile-install-mac.yml
+ - template: templates/uno-dev-feed.yml
- powershell: cp $(System.ArtifactsDirectory)/Nuget_Packages/vslatest/*.nupkg $(Build.SourcesDirectory)/src/PackageCache
displayName: Copy Artifacts to PackageCache
@@ -119,6 +125,8 @@ jobs:
TestGroup: '1'
group_2:
TestGroup: '2'
+ group_3:
+ TestGroup: '3'
steps:
- task: DownloadBuildArtifacts@0
@@ -126,11 +134,10 @@ jobs:
artifactName: 'Nuget_Packages'
- template: templates/gitversion.yml
-
- template: templates/dotnet-mobile-install-linux.yml
-
- template: templates/gitversion.yml
-
+ - template: templates/uno-dev-feed.yml
+
- script: cp $(System.ArtifactsDirectory)/Nuget_Packages/vslatest/*.nupkg $(Build.SourcesDirectory)/src/PackageCache
displayName: Copy Artifacts to PackageCache
diff --git a/build/ci/templates/uno-dev-feed.yml b/build/ci/templates/uno-dev-feed.yml
new file mode 100644
index 000000000000..bc6a002029cc
--- /dev/null
+++ b/build/ci/templates/uno-dev-feed.yml
@@ -0,0 +1,7 @@
+parameters:
+ nugetPackages: '$(Pipeline.Workspace)/.nuget/packages'
+
+steps:
+
+ - pwsh: dotnet nuget add source https://pkgs.dev.azure.com/uno-platform/1dd81cbd-cb35-41de-a570-b0df3571a196/_packaging/unoplatformdev/nuget/v3/index.json -n "uno-dev"
+ displayName: Add dev feed source
diff --git a/build/test-scripts/run-net7-template-linux.ps1 b/build/test-scripts/run-net7-template-linux.ps1
index 4d92516b0629..7054ce2dc355 100644
--- a/build/test-scripts/run-net7-template-linux.ps1
+++ b/build/test-scripts/run-net7-template-linux.ps1
@@ -48,13 +48,13 @@ $projects =
@(0, "5.1/uno51blank/uno51blank.Wasm/uno51blank.Wasm.csproj", @(), @()),
# 5.1 Recommended
- @(0, "5.1/uno51recommended/uno51recommended.Skia.Gtk/uno51recommended.Skia.Gtk.csproj", @(), @()),
- @(0, "5.1/uno51recommended/uno51recommended.Skia.Linux.FrameBuffer/uno51recommended.Skia.Linux.FrameBuffer.csproj", @(), @()),
- @(0, "5.1/uno51recommended/uno51recommended.Skia.WPF/uno51recommended.Skia.WPF.csproj", @(), @()),
- @(0, "5.1/uno51recommended/uno51recommended.Wasm/uno51recommended.Wasm.csproj", @(), @()),
- @(0, "5.1/uno51recommended/uno51recommended.Server/uno51recommended.Server.csproj", @(), @()),
- @(0, "5.1/uno51recommended/uno51recommended.Tests/uno51recommended.Tests.csproj", @(), @()),
- @(0, "5.1/uno51recommended/uno51recommended.UITests/uno51recommended.UITests.csproj", @(), @()),
+ @(1, "5.1/uno51recommended/uno51recommended.Skia.Gtk/uno51recommended.Skia.Gtk.csproj", @(), @()),
+ @(1, "5.1/uno51recommended/uno51recommended.Skia.Linux.FrameBuffer/uno51recommended.Skia.Linux.FrameBuffer.csproj", @(), @()),
+ @(1, "5.1/uno51recommended/uno51recommended.Skia.WPF/uno51recommended.Skia.WPF.csproj", @(), @()),
+ @(1, "5.1/uno51recommended/uno51recommended.Wasm/uno51recommended.Wasm.csproj", @(), @()),
+ @(1, "5.1/uno51recommended/uno51recommended.Server/uno51recommended.Server.csproj", @(), @()),
+ @(1, "5.1/uno51recommended/uno51recommended.Tests/uno51recommended.Tests.csproj", @(), @()),
+ @(1, "5.1/uno51recommended/uno51recommended.UITests/uno51recommended.UITests.csproj", @(), @()),
# 5.2 Blank
@(1, "5.2/uno52blank/uno52blank/uno52blank.csproj", @(), @()),
@@ -69,16 +69,16 @@ $projects =
@(1, "5.2/uno52Lib/uno52Lib.csproj", @(), @()),
# 5.2 Uno NuGet Lib
- @(1, "5.2/uno52NuGetLib/uno52NuGetLib.csproj", @(), @()),
+ @(2, "5.2/uno52NuGetLib/uno52NuGetLib.csproj", @(), @()),
# 5.2 Uno SingleProject Lib
- @(1, "5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj", @(), @()),
+ @(2, "5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj", @(), @()),
# 5.2 Uno App with Library reference
- @(1, "5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj", @(), @()),
+ @(2, "5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj", @(), @()),
# 5.3 Blank with net9
- @(2, "5.3/uno53net9blank/uno53net9blank/uno53net9blank.csproj", @(), @())
+ @(3, "5.3/uno53net9blank/uno53net9blank/uno53net9blank.csproj", @(), @())
# 5.3 blank publish testing
# Disabled for LXD setup issues
diff --git a/build/test-scripts/run-netcore-mobile-template-tests.ps1 b/build/test-scripts/run-netcore-mobile-template-tests.ps1
index f5321ab109e4..376a9cb1da84 100644
--- a/build/test-scripts/run-netcore-mobile-template-tests.ps1
+++ b/build/test-scripts/run-netcore-mobile-template-tests.ps1
@@ -271,16 +271,16 @@ $projects =
@(1, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-f", "net8.0-desktop", $sdkFeatures), @("macOS", "NetCore")),
# Default mode for the template is WindowsAppSDKSelfContained=true, which requires specifying a target platform.
- @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.19041"), @()),
- @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:Platform=arm64" , "-p:TargetFramework=net8.0-windows10.0.19041"), @()),
+ @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.26100"), @()),
+ @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:Platform=arm64" , "-p:TargetFramework=net8.0-windows10.0.26100"), @()),
# Ensure that default without platform builds properly
- @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:TargetFramework=net8.0-windows10.0.19041"), @()),
+ @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:TargetFramework=net8.0-windows10.0.26100"), @()),
# Validate building inside VS
@(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:BuildingInsideVisualStudio=true"), @("NetCore")),
@(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:BuildingInsideVisualStudio=true", "-p:_UnoSelectedTargetFramework=net8.0-desktop"), @("NetCore")),
- @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:BuildingInsideVisualStudio=true", "-p:_UnoSelectedTargetFramework=net8.0-windows10.0.19041"), @()),
+ @(2, "5.2/uno52blank/uno52blank/uno52blank.csproj", @("-p:BuildingInsideVisualStudio=true", "-p:_UnoSelectedTargetFramework=net8.0-windows10.0.26100"), @()),
#
# 5.2 Uno Lib
@@ -301,7 +301,7 @@ $projects =
@(2, "5.2/uno52NuGetLib/uno52NuGetLib.csproj", @(), @("macOS", "NetCore")),
# Default mode for the template is WindowsAppSDKSelfContained=true, which requires specifying a target platform.
- @(2, "5.2/uno52Lib/uno52Lib.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.19041"), @("macOS")),
+ @(2, "5.2/uno52Lib/uno52Lib.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.26100"), @("macOS")),
#
# 5.2 Uno SingleProject Lib
@@ -314,7 +314,7 @@ $projects =
@(2, "5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj", @("-f", "net8.0-desktop"), @("macOS", "NetCore")),
# Default mode for the template is WindowsAppSDKSelfContained=true, which requires specifying a target platform.
- @(2, "5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.19041"), @()),
+ @(2, "5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.26100"), @()),
# 5.2 Uno App with Library reference
@(2, "5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj", @("-f", "net8.0"), @("macOS", "NetCore")),
@@ -339,7 +339,7 @@ $projects =
@(3, "5.3/uno53net9blank/uno53net9blank/uno53net9blank.csproj", @("-f", "net9.0-desktop", $sdkFeatures), @("macOS", "NetCore")),
# Default mode for the template is WindowsAppSDKSelfContained=true, which requires specifying a target platform.
- @(4, "5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.19041"), @()),
+ @(4, "5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj", @("-p:Platform=x86" , "-p:TargetFramework=net8.0-windows10.0.26100"), @()),
# Publishing validation
@(4, "5.3/uno53net9blank/uno53net9blank/uno53net9blank.csproj", @("-f", "net9.0-desktop", "-p:PackageFormat=app"), @("OnlyMacOS", "NetCore", "Publish"))
diff --git a/doc/articles/features/working-with-xaml-hot-reload.md b/doc/articles/features/working-with-xaml-hot-reload.md
index da8a2d3bcf96..c32f95322b95 100644
--- a/doc/articles/features/working-with-xaml-hot-reload.md
+++ b/doc/articles/features/working-with-xaml-hot-reload.md
@@ -293,7 +293,7 @@ Mobile targets are currently using a limited version of XAML Hot Reload and do n
## Hot Reload Indicator
-Hot Reload displays a visual indicator to help you further monitor changes while developing. It displays new information every time Hot Reload is triggered. The indicator is enabled by default within the `EnableHotReload()` method which is located in the root `App.xaml.cs` file. This displays an overlay which hosts the visual indicator. If you wish to disable it, you simply have to provide the following boolean: `EnableHotReload(disableIndicator: true)`, removing the overlay from the view.
+Hot Reload displays a visual indicator to help you further monitor changes while developing. It displays new information every time Hot Reload is triggered. The indicator is enabled by default within the `UseStudio()` method which is located in the root `App.xaml.cs` file. This displays an overlay which hosts the visual indicator. If you wish to disable it, you simply have to provide the following boolean: `EnableHotReload(disableIndicator: true)`, removing the overlay from the view.
@@ -370,7 +370,7 @@ Here's a summary of what icons and statuses you can expect:
//... in the OnLaunched method
#if DEBUG
- MainWindow.EnableHotReload();
+ MainWindow.UseStudio();
#endif
```
diff --git a/doc/articles/migrating-from-previous-releases.md b/doc/articles/migrating-from-previous-releases.md
index 6a4c5ff874f3..83be4df90077 100644
--- a/doc/articles/migrating-from-previous-releases.md
+++ b/doc/articles/migrating-from-previous-releases.md
@@ -17,6 +17,10 @@ A few considerations to take into account:
- Moving to .NET 9 or upgrading .NET 9 projects now require the use of .NET 9 RC2 and Visual Studio 17.12 Preview 3.
- To migrate a project to .NET 9, [read the directions](xref:Uno.Development.MigratingFromNet8ToNet9) from our documentation.
+### The EnableHotReload method is deprecated
+
+When upgrading to Uno 5.5, in the `App.xaml.cs` file, the `EnableHotReload()` method is deprecated and must be replaced with `UseStudio()` instead.
+
## Uno Platform 5.4
Uno Platform 5.4 contains breaking changes for Uno.Extensions.
diff --git a/doc/articles/migrating-to-uno-5.md b/doc/articles/migrating-to-uno-5.md
index 3c8b93add58b..d5466ef9f1e5 100644
--- a/doc/articles/migrating-to-uno-5.md
+++ b/doc/articles/migrating-to-uno-5.md
@@ -44,7 +44,7 @@ Hot Reload support has changed in Uno Platform 5.0 and a new API invocation is n
//... in the OnLaunched method
#if DEBUG
- MainWindow.EnableHotReload();
+ MainWindow.UseStudio();
#endif
```
diff --git a/doc/articles/uno-build-error-codes.md b/doc/articles/uno-build-error-codes.md
index 938187ee28bd..4aa58ec23bb7 100644
--- a/doc/articles/uno-build-error-codes.md
+++ b/doc/articles/uno-build-error-codes.md
@@ -178,6 +178,18 @@ Some components like `ProgressRing` and `MediaPlayerElement` requires you to ref
- For `ProgressRing`, it requires Lottie dependency. For more information about adding Lottie to your project, see [Lottie for Uno](xref:Uno.Features.Lottie).
- For `MediaPlayerElement` on WebAssembly or Gtk, it requires `Uno.WinUI.MediaPlayer.WebAssembly` or `Uno.WinUI.MediaPlayer.Skia.Gtk` NuGet package. For more information, see [MediaPlayerElement](xref:Uno.Controls.MediaPlayerElement).
+### UNO0008
+
+In Uno Platform 5.5, the `EnableHotReload` method has been deprecated and replaced by `UseStudio()`.
+
+Note that this change only applies to projects using the Uno.Sdk. If you're not using the Uno.Sdk, you can disable this warning using the following code:
+
+```xml
+#pragma warning disable UNO0008 // Replace with UseStudio() when migrating to the Uno.Sdk.
+window.EnableHotReload();
+#pragma warning restore UNO0008
+```
+
## XAML Errors
### UNOX0001
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 43c8b2315996..de865103a91d 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -223,6 +223,9 @@
$(NoWarn);CS0436
+
+
+ $(NoWarn);UNO0008
diff --git a/src/SolutionTemplate/5.1/uno51blank/Directory.Packages.props b/src/SolutionTemplate/5.1/uno51blank/Directory.Packages.props
index 296df9d6fe5a..ef290f97a282 100644
--- a/src/SolutionTemplate/5.1/uno51blank/Directory.Packages.props
+++ b/src/SolutionTemplate/5.1/uno51blank/Directory.Packages.props
@@ -10,17 +10,17 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs b/src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs
index 62e506b2f697..a1f60a65781f 100644
--- a/src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs
+++ b/src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs
@@ -9,7 +9,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
MainWindow = new Window();
#if DEBUG
+#pragma warning disable UNO0008
MainWindow.EnableHotReload();
+#pragma warning restore UNO0008
#endif
diff --git a/src/SolutionTemplate/5.1/uno51recommended/Directory.Packages.props b/src/SolutionTemplate/5.1/uno51recommended/Directory.Packages.props
index 1cb2d22cd667..bf4abe062445 100644
--- a/src/SolutionTemplate/5.1/uno51recommended/Directory.Packages.props
+++ b/src/SolutionTemplate/5.1/uno51recommended/Directory.Packages.props
@@ -17,17 +17,17 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -52,10 +52,10 @@
-
+
-
-
+
+
diff --git a/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs b/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs
index 19b13588d50f..7f9bc9aacef4 100644
--- a/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs
+++ b/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs
@@ -75,7 +75,9 @@ protected async override void OnLaunched(LaunchActivatedEventArgs args)
MainWindow = builder.Window;
#if DEBUG
+#pragma warning disable UNO0008
MainWindow.EnableHotReload();
+#pragma warning restore UNO0008
#endif
Host = await builder.NavigateAsync();
diff --git a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/App.xaml.cs b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/App.xaml.cs
index fc6929ae914b..0f72526ed656 100644
--- a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/App.xaml.cs
+++ b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/App.xaml.cs
@@ -21,7 +21,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
{
MainWindow = new Window();
#if DEBUG
+#pragma warning disable UNO0008
MainWindow.EnableHotReload();
+#pragma warning restore UNO0008
#endif
diff --git a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj
index e7d30b60c49b..66c4752e55b7 100644
--- a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj
+++ b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/uno52AppWithLib.csproj
@@ -2,7 +2,7 @@
net8.0-browserwasm;net8.0-desktop;net8.0
$(TargetFrameworks);net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0-desktop
- $(TargetFrameworks);net8.0-windows10.0.19041
+ $(TargetFrameworks);net8.0-windows10.0.26100
$(TargetFrameworks.Replace('net8.0-android',''))
@@ -52,7 +52,7 @@
BeforeTargets="AfterBuild">
+ Condition=" '$(TargetFramework)' == 'net8.0-windows10.0.26100' OR '$(TargetFramework)' == 'net8.0-desktop' ">
<_AssetsToValidate Include="$(OutputPath)Assets\SharedAssets.md" />
<_AssetsToValidate Include="$(OutputPath)Assets\Icons\icon_foreground.png" />
diff --git a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52emptylib/uno52emptylib.csproj b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52emptylib/uno52emptylib.csproj
index 4c1a25d4673f..194ad396fa14 100644
--- a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52emptylib/uno52emptylib.csproj
+++ b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52emptylib/uno52emptylib.csproj
@@ -2,7 +2,7 @@
net8.0-browserwasm;net8.0-desktop;net8.0
$(TargetFrameworks);net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0-desktop
- $(TargetFrameworks);net8.0-windows10.0.19041
+ $(TargetFrameworks);net8.0-windows10.0.26100
$(TargetFrameworks.Replace('net8.0-android',''))
@@ -39,4 +39,4 @@
-
\ No newline at end of file
+
diff --git a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52lib/uno52lib.csproj b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52lib/uno52lib.csproj
index 3b536a6df335..45540750ad7a 100644
--- a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52lib/uno52lib.csproj
+++ b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52lib/uno52lib.csproj
@@ -2,7 +2,7 @@
net8.0-browserwasm;net8.0-desktop;net8.0
$(TargetFrameworks);net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0-desktop
- $(TargetFrameworks);net8.0-windows10.0.19041
+ $(TargetFrameworks);net8.0-windows10.0.26100
$(TargetFrameworks.Replace('net8.0-android',''))
@@ -39,4 +39,4 @@
-
\ No newline at end of file
+
diff --git a/src/SolutionTemplate/5.2/uno52Lib/uno52Lib.csproj b/src/SolutionTemplate/5.2/uno52Lib/uno52Lib.csproj
index ef105687423f..fedfadc3f2d5 100644
--- a/src/SolutionTemplate/5.2/uno52Lib/uno52Lib.csproj
+++ b/src/SolutionTemplate/5.2/uno52Lib/uno52Lib.csproj
@@ -1,6 +1,6 @@
- net8.0;net8.0-ios;net8.0-maccatalyst;net8.0-windows10.0.19041;net8.0-browserwasm;net8.0-desktop
+ net8.0;net8.0-ios;net8.0-maccatalyst;net8.0-windows10.0.26100;net8.0-browserwasm;net8.0-desktop
$(TargetFrameworks);net8.0-android
diff --git a/src/SolutionTemplate/5.2/uno52NuGetLib/uno52NuGetLib.csproj b/src/SolutionTemplate/5.2/uno52NuGetLib/uno52NuGetLib.csproj
index ceb179b8eb27..3f8e15879332 100644
--- a/src/SolutionTemplate/5.2/uno52NuGetLib/uno52NuGetLib.csproj
+++ b/src/SolutionTemplate/5.2/uno52NuGetLib/uno52NuGetLib.csproj
@@ -1,6 +1,6 @@
- net8.0;net8.0-ios;net8.0-maccatalyst;net8.0-windows10.0.19041;net8.0-browserwasm;net8.0-desktop
+ net8.0;net8.0-ios;net8.0-maccatalyst;net8.0-windows10.0.26100;net8.0-browserwasm;net8.0-desktop
$(TargetFrameworks);net8.0-android
diff --git a/src/SolutionTemplate/5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj b/src/SolutionTemplate/5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj
index c2bd98afb43f..88cdd66cf72f 100644
--- a/src/SolutionTemplate/5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj
+++ b/src/SolutionTemplate/5.2/uno52SingleProjectLib/uno52SingleProjectLib.csproj
@@ -1,6 +1,6 @@
- net8.0;net8.0-ios;net8.0-maccatalyst;net8.0-windows10.0.19041;net8.0-browserwasm;net8.0-desktop
+ net8.0;net8.0-ios;net8.0-maccatalyst;net8.0-windows10.0.26100;net8.0-browserwasm;net8.0-desktop
$(TargetFrameworks);net8.0-android
diff --git a/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs b/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs
index 3d3bb856f7be..0afcd05f0c42 100644
--- a/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs
+++ b/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs
@@ -21,7 +21,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
{
MainWindow = new Window();
#if DEBUG
+#pragma warning disable UNO0008
MainWindow.EnableHotReload();
+#pragma warning restore UNO0008
#endif
diff --git a/src/SolutionTemplate/5.2/uno52blank/uno52blank/uno52blank.csproj b/src/SolutionTemplate/5.2/uno52blank/uno52blank/uno52blank.csproj
index 20a7d5370285..b2359027a9da 100644
--- a/src/SolutionTemplate/5.2/uno52blank/uno52blank/uno52blank.csproj
+++ b/src/SolutionTemplate/5.2/uno52blank/uno52blank/uno52blank.csproj
@@ -2,7 +2,7 @@
net8.0-browserwasm;net8.0-desktop;net8.0
$(TargetFrameworks);net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0-desktop
- $(TargetFrameworks);net8.0-windows10.0.19041
+ $(TargetFrameworks);net8.0-windows10.0.26100
$(TargetFrameworks.Replace('net8.0-android',''))
@@ -46,7 +46,7 @@
+ Condition="'$(TargetFramework)'=='net8.0-windows10.0.26100'">
net9.0-browserwasm;net9.0-desktop;net9.0
$(TargetFrameworks);net9.0-android;net9.0-ios;net9.0-maccatalyst;net9.0-desktop
- $(TargetFrameworks);net9.0-windows10.0.19041
+ $(TargetFrameworks);net9.0-windows10.0.26100
$(TargetFrameworks.Replace('net9.0-android',''))
@@ -65,7 +65,7 @@
BeforeTargets="AfterBuild">
+ Condition=" '$(TargetFramework)' == 'net9.0-windows10.0.26100' OR '$(TargetFramework)' == 'net9.0-desktop' ">
<_AssetsToValidate Include="$(OutputPath)Assets\SharedAssets.md" />
<_AssetsToValidate Include="$(OutputPath)Assets\Icons\icon_foreground.png" />
diff --git a/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs b/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs
index ed14cec7ab3d..d153d4bf4347 100644
--- a/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs
+++ b/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs
@@ -12,7 +12,7 @@ internal static class DiagnosticViewRegistry
{
internal static EventHandler>? Added;
- private static ImmutableArray _registrations = ImmutableArray.Empty;
+ private static ImmutableArray _registrations = [];
///
/// Gets the list of registered diagnostic providers.
@@ -35,15 +35,17 @@ public static void Register(IDiagnosticView view, DiagnosticViewRegistrationMode
}
}
-internal record DiagnosticViewRegistration(DiagnosticViewRegistrationMode Mode, IDiagnosticView View);
+internal sealed record DiagnosticViewRegistration(
+ DiagnosticViewRegistrationMode Mode,
+ IDiagnosticView View);
-internal enum DiagnosticViewRegistrationMode
+public enum DiagnosticViewRegistrationMode
{
///
/// Diagnostic is being display on at least one window.
/// I.e. only the main/first opened but move to the next one if the current window is closed.
///
- One,
+ One, // Default
///
/// Diagnostic is being rendered as overlay on each window.
@@ -55,3 +57,18 @@ internal enum DiagnosticViewRegistrationMode
///
OnDemand
}
+
+public enum DiagnosticViewRegistrationPosition
+{
+ Normal = 0, // Default
+
+ ///
+ /// Register as the first diagnostic view, ensuring it is displayed first.
+ ///
+ First = -1,
+
+ ///
+ /// Register as the last diagnostic view, ensuring it is displayed last.
+ ///
+ Last = 1,
+}
diff --git a/src/Uno.Foundation/Diagnostics/IDiagnosticView.cs b/src/Uno.Foundation/Diagnostics/IDiagnosticView.cs
index 34847ce89737..2e35bbd3021b 100644
--- a/src/Uno.Foundation/Diagnostics/IDiagnosticView.cs
+++ b/src/Uno.Foundation/Diagnostics/IDiagnosticView.cs
@@ -21,6 +21,8 @@ public interface IDiagnosticView
///
string Name { get; }
+ DiagnosticViewRegistrationPosition Position => DiagnosticViewRegistrationPosition.Normal;
+
///
/// Gets a visual element of the diagnostic, usually a value or an icon.
///
diff --git a/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json b/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json
index bf5cf925b110..5e5918a36b3a 100644
--- a/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json
+++ b/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json
@@ -234,6 +234,10 @@
"description": "Provides an explicit override for the version of Uno.Settings to use.",
"type": "nuget-version"
},
+ "UnoHotDesignVersion": {
+ "description": "Provides an explicit override for the version of Uno.HotDesign to use.",
+ "type": "nuget-version"
+ },
"MicrosoftLoggingVersion": {
"description": "Provides an explicit override for the version of Microsoft.Extensions.Logging to use.",
"type": "nuget-version"
diff --git a/src/Uno.Sdk/Services/PackageManifest.cs b/src/Uno.Sdk/Services/PackageManifest.cs
index a212120e9308..208ec470be13 100644
--- a/src/Uno.Sdk/Services/PackageManifest.cs
+++ b/src/Uno.Sdk/Services/PackageManifest.cs
@@ -138,6 +138,7 @@ public class Group
public const string Resizetizer = nameof(Resizetizer);
public const string SdkExtras = nameof(SdkExtras);
public const string Settings = nameof(Settings);
+ public const string HotDesign = nameof(HotDesign);
public const string SkiaSharp = nameof(SkiaSharp);
public const string SvgSkia = nameof(SvgSkia);
public const string WinAppSdk = nameof(WinAppSdk);
diff --git a/src/Uno.Sdk/Tasks/ImplicitPackagesResolver.cs b/src/Uno.Sdk/Tasks/ImplicitPackagesResolver.cs
index ba60a5f67d5d..5a6e6a621e51 100644
--- a/src/Uno.Sdk/Tasks/ImplicitPackagesResolver.cs
+++ b/src/Uno.Sdk/Tasks/ImplicitPackagesResolver.cs
@@ -85,6 +85,8 @@ public sealed class ImplicitPackagesResolver_v0 : Task
public string? UnoSettingsVersion { get; set; }
+ public string? UnoHotDesignVersion { get; set; }
+
public string? MicrosoftLoggingVersion { get; set; }
public string? WinAppSdkVersion { get; set; }
@@ -246,6 +248,7 @@ private void SetupRuntimePackageManifestUpdates(PackageManifest manifest)
.UpdateManifest(PackageManifest.Group.Resizetizer, UnoResizetizerVersion)
.UpdateManifest(PackageManifest.Group.SdkExtras, UnoSdkExtrasVersion)
.UpdateManifest(PackageManifest.Group.Settings, UnoSettingsVersion)
+ .UpdateManifest(PackageManifest.Group.HotDesign, UnoHotDesignVersion)
.UpdateManifest(PackageManifest.Group.SkiaSharp, SkiaSharpVersion)
.UpdateManifest(PackageManifest.Group.SvgSkia, SvgSkiaVersion)
.UpdateManifest(PackageManifest.Group.WinAppSdk, WinAppSdkVersion)
diff --git a/src/Uno.Sdk/packages.json b/src/Uno.Sdk/packages.json
index ce74950b283e..bcfc9ee81c8e 100644
--- a/src/Uno.Sdk/packages.json
+++ b/src/Uno.Sdk/packages.json
@@ -85,9 +85,16 @@
"Uno.Settings.DevServer"
]
},
+ {
+ "group": "hotdesign",
+ "version": "1.0.0-dev.53",
+ "packages": [
+ "Uno.UI.HotDesign"
+ ]
+ },
{
"group": "SkiaSharp",
- "version": "2.88.8",
+ "version": "2.88.9-preview.2.2",
"packages": [
"SkiaSharp.Skottie",
"SkiaSharp.Views.Uno.WinUI",
diff --git a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets
index e0880a8dce18..7b02dded7571 100644
--- a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets
+++ b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets
@@ -11,6 +11,10 @@
<_UnoProjectSystemPackageReference Include="Uno.Settings.DevServer" ProjectSystem="true" PrivateAssets="all" />
+
+ <_UnoProjectSystemPackageReference Include="Uno.UI.HotDesign" ProjectSystem="true" PrivateAssets="all" Condition=" '$(Optimize)' != 'true' " />
+
+
<_UnoProjectSystemPackageReference Include="Uno.WinUI.DevServer" ProjectSystem="true" Condition="$(Optimize) != 'true'" />
<_UnoProjectSystemPackageReference Include="Uno.WinUI.DevServer" ProjectSystem="true" Exclude="all" Condition="$(Optimize) == 'true'" />
diff --git a/src/Uno.Sdk/targets/Uno.Implicit.Packages.targets b/src/Uno.Sdk/targets/Uno.Implicit.Packages.targets
index ec578d78d73e..53ff56988041 100644
--- a/src/Uno.Sdk/targets/Uno.Implicit.Packages.targets
+++ b/src/Uno.Sdk/targets/Uno.Implicit.Packages.targets
@@ -63,6 +63,7 @@
UnoResizetizerVersion="$(UnoResizetizerVersion)"
UnoSdkExtrasVersion="$(UnoSdkExtrasVersion)"
UnoSettingsVersion="$(UnoSettingsVersion)"
+ UnoHotDesignVersion="$(UnoHotDesignVersion)"
MicrosoftLoggingVersion="$(MicrosoftLoggingVersion)"
WinAppSdkVersion="$(WinAppSdkVersion)"
WinAppSdkBuildToolsVersion="$(WinAppSdkBuildToolsVersion)"
diff --git a/src/Uno.UI.RemoteControl.Host/Extensibility/AddInsExtensions.cs b/src/Uno.UI.RemoteControl.Host/Extensibility/AddInsExtensions.cs
index 80491025e1b3..a9b7fec6dc9a 100644
--- a/src/Uno.UI.RemoteControl.Host/Extensibility/AddInsExtensions.cs
+++ b/src/Uno.UI.RemoteControl.Host/Extensibility/AddInsExtensions.cs
@@ -10,8 +10,8 @@ public static class AddInsExtensions
{
public static IWebHostBuilder ConfigureAddIns(this IWebHostBuilder builder, string solutionFile)
{
- AssemblyHelper.Load(AddIns.Discover(solutionFile), throwIfLoadFailed: true);
+ AssemblyHelper.Load(AddIns.Discover(solutionFile), throwIfLoadFailed: false);
- return builder.ConfigureServices(svc => svc.AddFromAttribute());
+ return builder.ConfigureServices(svc => svc.AddFromAttributes());
}
}
diff --git a/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceAttribute.cs b/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceAttribute.cs
index 136798d820de..ce2b81f08f01 100644
--- a/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceAttribute.cs
+++ b/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceAttribute.cs
@@ -10,7 +10,7 @@ namespace Uno.Utils.DependencyInjection;
/// Type of the contract (i.e. interface) implemented by the concrete type.
/// Concrete type to register in the service collection.
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
-public class ServiceAttribute(Type contract, Type implementation) : Attribute
+public sealed class ServiceAttribute(Type contract, Type implementation) : Attribute
{
///
/// Creates a new instance of the class with only a concrete type (used as contract and implementation).
diff --git a/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionExtensionAttribute.cs b/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionExtensionAttribute.cs
new file mode 100644
index 000000000000..27bb145cd539
--- /dev/null
+++ b/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionExtensionAttribute.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Uno.Utils.DependencyInjection;
+
+///
+/// Attribute to define a type able to registers some services in a service collection.
+///
+/// Type of the extension that should be instantiated with a as parameter.
+///
+/// The given type is expected to have a single constructor which takes a single parameter of type .
+/// An instance of the given type will be created during the service collection registration process (with the service collection as parameter),
+/// so the implementation will be able to add some custom services on it.
+///
+[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+public sealed class ServiceCollectionExtensionAttribute(Type type) : Attribute
+{
+ ///
+ /// Type of the extension that should be instantiated with a as parameter.
+ ///
+ public Type Type { get; } = type;
+}
diff --git a/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionServiceExtensions.cs b/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionServiceExtensions.cs
index 27cd270e5aa7..52d206340797 100644
--- a/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionServiceExtensions.cs
+++ b/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionServiceExtensions.cs
@@ -12,12 +12,22 @@ namespace Uno.Utils.DependencyInjection;
public static class ServiceCollectionServiceExtensions
{
+ ///
+ /// Register services configured with the and attributes from all loaded assemblies.
+ ///
+ /// The service collection on which services should be registered.
+ /// The service collection for fluent usage.
+ public static IServiceCollection AddFromAttributes(this IServiceCollection svc)
+ => svc
+ .AddFromServiceAttributes()
+ .AddFromServiceExtensionAttributes();
+
///
/// Register services configured with the attribute from all loaded assemblies.
///
/// The service collection on which services should be registered.
/// The service collection for fluent usage.
- public static IServiceCollection AddFromAttribute(this IServiceCollection svc)
+ public static IServiceCollection AddFromServiceAttributes(this IServiceCollection svc)
{
var attribute = typeof(ServiceAttribute);
var services = AppDomain
@@ -37,6 +47,40 @@ public static IServiceCollection AddFromAttribute(this IServiceCollection svc)
return svc;
}
+ ///
+ /// Register services configured with the attribute from all loaded assemblies.
+ ///
+ /// The service collection on which services should be registered.
+ /// The service collection for fluent usage.
+ public static IServiceCollection AddFromServiceExtensionAttributes(this IServiceCollection svc)
+ {
+ var attribute = typeof(ServiceCollectionExtensionAttribute);
+ var extensions = AppDomain
+ .CurrentDomain
+ .GetAssemblies()
+ .SelectMany(assembly => assembly.GetCustomAttributesData())
+ .Select(attrData => attrData.TryCreate(attribute) as ServiceCollectionExtensionAttribute)
+ .Where(attr => attr is not null)
+ .ToImmutableList();
+
+ foreach (var extension in extensions)
+ {
+ try
+ {
+ Activator.CreateInstance(extension!.Type, args: [svc]);
+ }
+ catch (Exception error)
+ {
+ if (svc.Log().IsEnabled(LogLevel.Error))
+ {
+ svc.Log().Log(LogLevel.Error, error, $"Failed to create an instance of extensions {extension?.Type} for dynamic service discovery..");
+ }
+ }
+ }
+
+ return svc;
+ }
+
private class AutoInitService(IServiceProvider services, IImmutableList types) : BackgroundService, IHostedService
{
///
diff --git a/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs b/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs
index be3d40bf1acd..d614fc56da3c 100644
--- a/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs
+++ b/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs
@@ -18,6 +18,8 @@ public static IImmutableList Load(IImmutableList dllFiles, boo
{
try
{
+ _log.Log(LogLevel.Debug, $"Loading add-in assembly '{dll}'.");
+
assemblies.Add(Assembly.LoadFrom(dll));
}
catch (Exception err)
diff --git a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.ClientApi.cs b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.ClientApi.cs
index fe911e17b0e5..df45c03eaea0 100644
--- a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.ClientApi.cs
+++ b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.ClientApi.cs
@@ -21,7 +21,7 @@ public partial class ClientHotReloadProcessor
///
/// Result details of a file update
///
- /// Indicates if is known to have been updated on server-side.
+ /// Indicates if file is known to have been updated on server-side.
/// Indicates if the change had an impact on the compilation of the application (might be a success-full build or an error).
/// Gets the error if any happened during the update.
public record struct UpdateResult(
diff --git a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs
index 8fad9fe3bb56..16791f6d6d1b 100644
--- a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs
+++ b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs
@@ -23,23 +23,23 @@ public partial class ClientHotReloadProcessor
///
/// Raised when the status of the hot-reload engine changes.
///
- internal EventHandler? StatusChanged;
+ public EventHandler? StatusChanged;
///
/// The current status of the hot-reload engine.
///
- internal Status CurrentStatus => _status.Current;
+ public Status CurrentStatus => _status.Current;
private readonly StatusSink _status;
- internal enum HotReloadSource
+ public enum HotReloadSource
{
Runtime,
DevServer,
Manual
}
- internal enum HotReloadClientResult
+ public enum HotReloadClientResult
{
///
/// Successful hot-reload.
@@ -63,17 +63,13 @@ internal enum HotReloadClientResult
/// The global state of the hot-reload engine (combining server and client state).
/// State and history of all hot-reload operations detected on the server.
/// State and history of all hot-reload operation received by this client.
- internal record Status(
+ public record Status(
HotReloadState State,
(HotReloadState State, IImmutableList Operations) Server,
(HotReloadState State, IImmutableList Operations) Local);
private class StatusSink(ClientHotReloadProcessor owner)
{
-#if HAS_UNO_WINUI
- private readonly DiagnosticView _view = DiagnosticView.Register("Hot reload", ctx => new HotReloadStatusView(ctx), static (view, status) => view.OnHotReloadStatusChanged(status));
-#endif
-
private HotReloadState? _serverState;
private bool _isFinalServerState;
private ImmutableDictionary _serverOperations = ImmutableDictionary.Empty;
@@ -142,9 +138,6 @@ private void NotifyStatusChanged()
var status = BuildStatus();
Current = status;
-#if HAS_UNO_WINUI
- _view.Update(status);
-#endif
owner.StatusChanged?.Invoke(this, status);
}
@@ -163,22 +156,22 @@ private Status BuildStatus()
}
}
- internal class HotReloadClientOperation
+ public class HotReloadClientOperation
{
#region Current
[ThreadStatic]
private static HotReloadClientOperation? _opForCurrentUiThread;
- public static HotReloadClientOperation? GetForCurrentThread()
+ internal static HotReloadClientOperation? GetForCurrentThread()
=> _opForCurrentUiThread;
- public void SetCurrent()
+ internal void SetCurrent()
{
Debug.Assert(_opForCurrentUiThread == null, "Only one operation should be active at once for a given UI thread.");
_opForCurrentUiThread = this;
}
- public void ResignCurrent()
+ internal void ResignCurrent()
{
Debug.Assert(_opForCurrentUiThread == this, "Another operation has been started for teh current UI thread.");
_opForCurrentUiThread = null;
@@ -208,7 +201,7 @@ internal HotReloadClientOperation(HotReloadSource source, Type[] types, Action o
public Type[] Types { get; private set; }
- internal string[] CuratedTypes => _curatedTypes ??= GetCuratedTypes();
+ public string[] CuratedTypes => _curatedTypes ??= GetCuratedTypes();
private string[] GetCuratedTypes()
{
diff --git a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.MetadataUpdate.cs b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.MetadataUpdate.cs
index 5069bd7935a2..0a38206683d8 100644
--- a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.MetadataUpdate.cs
+++ b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.MetadataUpdate.cs
@@ -499,7 +499,7 @@ private static void UpdateApplicationCore(Type[] types)
#endif
else
{
- var errorMsg = $"Unable to access Dispatcher/DispatcherQueue in order to invoke {nameof(ReloadWithUpdatedTypes)}. Make sure you have enabled hot-reload (Window.EnableHotReload()) in app startup. See https://aka.platform.uno/hot-reload";
+ var errorMsg = $"Unable to access Dispatcher/DispatcherQueue in order to invoke {nameof(ReloadWithUpdatedTypes)}. Make sure you have enabled hot-reload (Window.UseStudio()) in app startup. See https://aka.platform.uno/hot-reload";
hr?.ReportError(new InvalidOperationException(errorMsg));
if (_log.IsEnabled(LogLevel.Warning))
{
diff --git a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Entries.cs b/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Entries.cs
deleted file mode 100644
index 70ede1d818c5..000000000000
--- a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Entries.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-#nullable enable
-
-using System;
-using System.ComponentModel;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using Uno.UI.RemoteControl.HotReload.Messages;
-using static Uno.UI.RemoteControl.HotReload.ClientHotReloadProcessor;
-using static Uno.UI.RemoteControl.RemoteControlStatus;
-
-namespace Uno.UI.RemoteControl.HotReload;
-
-internal record DevServerEntry() : HotReloadLogEntry(EntrySource.DevServer, -1, DateTimeOffset.Now)
-{
- public static DevServerEntry? TryCreateNew(RemoteControlStatus? oldStatus, RemoteControlStatus newStatus)
- {
- if (oldStatus is not null && oldStatus.State == newStatus.State)
- {
- return null;
- }
-
- var (iconState, desc) = (oldStatus, newStatus) switch
- {
- (_, { State: ConnectionState.NoServer }) => (EntryIcon.Error, "No endpoint found"),
- (not null, { State: ConnectionState.Connecting }) => (EntryIcon.Loading, "Connecting..."),
- (null or { State: not ConnectionState.ConnectionTimeout }, { State: ConnectionState.ConnectionTimeout }) => (EntryIcon.Error, "Timeout"),
- (null or { State: not ConnectionState.ConnectionFailed }, { State: ConnectionState.ConnectionFailed }) => (EntryIcon.Error, "Connection error"),
-
- (null or { IsVersionValid: not false }, { IsVersionValid: false }) => (EntryIcon.Warning, "Version mismatch"),
- (null or { InvalidFrames.Count: 0 }, { InvalidFrames.Count: > 0 }) => (EntryIcon.Warning, "Unknown messages"),
- (null or { MissingRequiredProcessors.IsEmpty: true }, { MissingRequiredProcessors.IsEmpty: false }) => (EntryIcon.Warning, "Processors missing"),
-
- ({ KeepAlive.State: KeepAliveState.Idle or KeepAliveState.Ok }, { KeepAlive.State: KeepAliveState.Late }) => (EntryIcon.Error, "Connection lost (>1000ms)"),
- ({ KeepAlive.State: KeepAliveState.Idle or KeepAliveState.Ok }, { KeepAlive.State: KeepAliveState.Lost }) => (EntryIcon.Error, "Connection lost (>1s)"),
- ({ KeepAlive.State: KeepAliveState.Idle or KeepAliveState.Ok }, { KeepAlive.State: KeepAliveState.Aborted }) => (EntryIcon.Error, "Connection lost (keep-alive)"),
- ({ State: ConnectionState.Connected }, { State: ConnectionState.Disconnected }) => (EntryIcon.Error, "Connection lost"),
-
- ({ State: ConnectionState.Connected }, { State: ConnectionState.Reconnecting }) => (EntryIcon.Error, "Connection lost (reconnecting)"),
-
- _ => (default, default)
- };
-
- return desc is null
- ? null
- : new DevServerEntry { Title = desc, Icon = iconState | EntryIcon.Connection };
- }
-}
-
-internal record EngineEntry() : HotReloadLogEntry(EntrySource.Engine, -1, DateTimeOffset.Now)
-{
- public static EngineEntry? TryCreateNew(Status? oldStatus, Status status)
- => (oldStatus?.State ?? HotReloadState.Initializing, status.State) switch
- {
- ( < HotReloadState.Ready, HotReloadState.Ready) => new EngineEntry { Title = "Connected", Icon = EntryIcon.Connection | EntryIcon.Success },
- (not HotReloadState.Disabled, HotReloadState.Disabled) => new EngineEntry { Title = "Cannot initialize with debugger attached", Icon = EntryIcon.Connection | EntryIcon.Error },
- _ => null
- };
-}
-
-internal record ServerEntry : HotReloadLogEntry
-{
- public ServerEntry(HotReloadServerOperationData srvOp)
- : base(EntrySource.Server, srvOp.Id, srvOp.StartTime)
- {
- Update(srvOp);
- }
-
- ///
- /// Indicates if this notification is the final one for the operation, INCLUDING application wide.
- ///
- public bool IsFinal { get; private set; }
-
- public void Update(HotReloadServerOperationData srvOp)
- {
- IsFinal = srvOp.Result is not HotReloadServerResult.Success;
- (IsSuccess, Icon) = srvOp.Result switch
- {
- null => (default, EntryIcon.HotReload | EntryIcon.Loading),
- HotReloadServerResult.Success or HotReloadServerResult.NoChanges => (true, EntryIcon.HotReload | EntryIcon.Success),
- _ => (false, EntryIcon.HotReload | EntryIcon.Error)
- };
- Title = srvOp.Result switch
- {
- HotReloadServerResult.NoChanges => "No changes detected",
- HotReloadServerResult.RudeEdit => "Rude edit detected, restart required",
- HotReloadServerResult.Failed => "Compilation errors",
- HotReloadServerResult.Aborted => "Operation cancelled",
- HotReloadServerResult.InternalError => "An error occured",
- _ => null
- };
- Description = Join("file", srvOp.FilePaths.Select(Path.GetFileName).ToArray()!);
- Duration = srvOp.EndTime is not null ? srvOp.EndTime - srvOp.StartTime : null;
-
- RaiseChanged();
- }
-}
-
-internal record ApplicationEntry : HotReloadLogEntry
-{
- public ApplicationEntry(HotReloadClientOperation localOp)
- : base(EntrySource.Application, localOp.Id, localOp.StartTime)
- {
- Update(localOp);
- }
-
- internal void Update(HotReloadClientOperation localOp)
- {
- (IsSuccess, Icon) = localOp.Result switch
- {
- null => (default(bool?), EntryIcon.HotReload | EntryIcon.Loading),
- HotReloadClientResult.Success => (true, EntryIcon.HotReload | EntryIcon.Success),
- _ => (false, EntryIcon.HotReload | EntryIcon.Error)
- };
- Title = localOp.Result switch
- {
- null => "Processing...",
- HotReloadClientResult.Success => "Update successful",
- HotReloadClientResult.Failed => "An error occured",
- _ => null
- };
- Description = Join("type", localOp.CuratedTypes);
- Duration = localOp.EndTime is not null ? localOp.EndTime - localOp.StartTime : null;
-
- RaiseChanged();
- }
-}
-
-public enum EntrySource
-{
- DevServer,
- Engine,
- Server,
- Application
-}
-
-[Flags]
-public enum EntryIcon
-{
- // Kind
- Loading = 1 << 0,
- Success = 1 << 1,
- Warning = 1 << 2,
- Error = 1 << 3,
-
- // Source
- Connection = 1 << 8,
- HotReload = 2 << 8,
-}
-
-
-[Microsoft.UI.Xaml.Data.Bindable]
-public record HotReloadLogEntry(EntrySource Source, long Id, DateTimeOffset Timestamp) : INotifyPropertyChanged
-{
- ///
- public event PropertyChangedEventHandler? PropertyChanged;
-
- public bool? IsSuccess { get; set; }
- public TimeSpan? Duration { get; set; }
- public EntryIcon Icon { get; set; }
-
- public string? Title { get; set; }
- public string? Description { get; set; }
- public string? ToastDescription => Description ?? Title;
-
- public string TimeInfo => Duration switch
- {
- null => $"{Timestamp:T}",
- { TotalMilliseconds: < 1000 } ms => $"{ms.TotalMilliseconds:F0} ms - {Timestamp:T}",
- { } s => $"{s.TotalSeconds:N0} s - {Timestamp:T}",
- };
-
- protected void RaiseChanged()
- => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
-
- protected static string? Join(string kind, string[] items, int? total = null, int max = 5)
- {
- const int maxLength = 70;
-
- if (items is { Length: 0 } && total is null)
- {
- return null;
- }
-
- var sb = new StringBuilder(maxLength + 12 /* and xx more*/);
- int count;
- for (count = 0; count < Math.Min(items.Length, max); count++)
- {
- var item = items[count];
- if (sb.Length + 2 /*, */ + item.Length < maxLength)
- {
- if (count is not 0) sb.Append(", ");
- sb.Append(item);
- }
- else
- {
- break;
- }
- }
-
- var remaining = total - count;
- if (remaining > 0)
- {
- sb.Append((count, remaining) switch
- {
- (0, 1) => $"1 {kind}",
- (0, _) => $"{remaining} {kind}s",
- _ => $" and {remaining} more"
- });
- }
-
- return sb.ToString();
- }
-}
diff --git a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Resources.cs b/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Resources.cs
deleted file mode 100644
index 9e43ebd2302a..000000000000
--- a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Resources.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-#nullable enable
-
-using System;
-using System.Linq;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Data;
-using Uno.Extensions;
-
-namespace Uno.UI.RemoteControl.HotReload;
-
-internal sealed class EntryIconToObjectConverter : IValueConverter
-{
- public object? SuccessValue { get; set; }
- public object? FailedValue { get; set; }
- public object? ConnectionSuccessValue { get; set; }
- public object? ConnectionFailedValue { get; set; }
- public object? ConnectionWarningValue { get; set; }
-
- public object? Convert(object? value, Type targetType, object parameter, string language)
- {
- if (value is not null)
- {
- var ei = (EntryIcon)value;
-
- if (ei.HasFlag(EntryIcon.Connection))
- {
- if (ei.HasFlag(EntryIcon.Success)) return ConnectionSuccessValue;
- if (ei.HasFlag(EntryIcon.Error)) return ConnectionFailedValue;
- if (ei.HasFlag(EntryIcon.Warning)) return ConnectionWarningValue;
- }
- else if (ei.HasFlag(EntryIcon.HotReload))
- {
- if (ei.HasFlag(EntryIcon.Success)) return SuccessValue;
- if (ei.HasFlag(EntryIcon.Error)) return FailedValue;
- }
- }
-
- return ConnectionWarningValue;
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, string language)
- => throw new NotSupportedException("Only one-way conversion is supported.");
-}
-
-internal sealed class NullStringToCollapsedConverter : IValueConverter
-{
- public object? Convert(object? value, Type targetType, object parameter, string language)
- {
- if (value is string s && !string.IsNullOrEmpty(s))
- {
- return Visibility.Visible;
- }
-
- return Visibility.Collapsed;
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, string language)
- => throw new NotSupportedException("Only one-way conversion is supported.");
-}
diff --git a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.cs b/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.cs
deleted file mode 100644
index 92bfb5f3fc84..000000000000
--- a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.cs
+++ /dev/null
@@ -1,350 +0,0 @@
-#nullable enable
-
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Runtime.InteropServices;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using Uno.Diagnostics.UI;
-using static Uno.UI.RemoteControl.HotReload.ClientHotReloadProcessor;
-
-namespace Uno.UI.RemoteControl.HotReload;
-
-[TemplateVisualState(GroupName = "Status", Name = StatusUnknownVisualStateName)]
-[TemplateVisualState(GroupName = "Status", Name = StatusInitializingVisualStateName)]
-[TemplateVisualState(GroupName = "Status", Name = StatusReadyVisualStateName)]
-[TemplateVisualState(GroupName = "Status", Name = StatusWarningVisualStateName)]
-[TemplateVisualState(GroupName = "Status", Name = StatusErrorVisualStateName)]
-[TemplateVisualState(GroupName = "Result", Name = ResultNoneVisualStateName)]
-[TemplateVisualState(GroupName = "Result", Name = ResultSuccessVisualStateName)]
-[TemplateVisualState(GroupName = "Result", Name = ResultFailedVisualStateName)]
-public sealed partial class HotReloadStatusView : Control
-{
- private const string StatusUnknownVisualStateName = "Unknown";
- private const string StatusInitializingVisualStateName = "Initializing";
- private const string StatusReadyVisualStateName = "Ready";
- private const string StatusErrorVisualStateName = "Error";
- private const string StatusWarningVisualStateName = "Warning";
-
- private const string ResultNoneVisualStateName = "None";
- private const string ResultSuccessVisualStateName = "Success";
- private const string ResultFailedVisualStateName = "Failed";
-
- #region HeadLine (DP)
- public static DependencyProperty HeadLineProperty { get; } = DependencyProperty.Register(
- nameof(HeadLine),
- typeof(string),
- typeof(HotReloadStatusView),
- new PropertyMetadata(default(string), (snd, args) => ToolTipService.SetToolTip(snd, args.NewValue?.ToString())));
-
- public string? HeadLine
- {
- get => (string?)GetValue(HeadLineProperty);
- private set => SetValue(HeadLineProperty, value);
- }
- #endregion
-
- #region History (DP)
- public static DependencyProperty HistoryProperty { get; } = DependencyProperty.Register(
- nameof(History),
- typeof(ObservableCollection),
- typeof(HotReloadStatusView),
- new PropertyMetadata(default(ObservableCollection)));
-
- public ObservableCollection History
- {
- get => (ObservableCollection)GetValue(HistoryProperty);
- private init => SetValue(HistoryProperty, value);
- }
- #endregion
-
- #region ProcessingNotification (DP)
- public static readonly DependencyProperty ProcessingNotificationProperty = DependencyProperty.Register(
- nameof(ProcessingNotification),
- typeof(DiagnosticViewNotification),
- typeof(HotReloadStatusView),
- new PropertyMetadata(default(DiagnosticViewNotification?)));
-
- public DiagnosticViewNotification? ProcessingNotification
- {
- get => (DiagnosticViewNotification?)GetValue(ProcessingNotificationProperty);
- set => SetValue(ProcessingNotificationProperty, value);
- }
- #endregion
-
- #region SuccessNotification (DP)
- public static readonly DependencyProperty SuccessNotificationProperty = DependencyProperty.Register(
- nameof(SuccessNotification),
- typeof(DiagnosticViewNotification),
- typeof(HotReloadStatusView),
- new PropertyMetadata(default(DiagnosticViewNotification?)));
-
- public DiagnosticViewNotification? SuccessNotification
- {
- get => (DiagnosticViewNotification?)GetValue(SuccessNotificationProperty);
- set => SetValue(SuccessNotificationProperty, value);
- }
- #endregion
-
- #region FailureNotification (DP)
- public static readonly DependencyProperty FailureNotificationProperty = DependencyProperty.Register(
- nameof(FailureNotification),
- typeof(DiagnosticViewNotification),
- typeof(HotReloadStatusView),
- new PropertyMetadata(default(DiagnosticViewNotification?)));
-
- public DiagnosticViewNotification? FailureNotification
- {
- get => (DiagnosticViewNotification?)GetValue(FailureNotificationProperty);
- set => SetValue(FailureNotificationProperty, value);
- }
- #endregion
-
- private readonly IDiagnosticViewContext _ctx;
- private (string state, HotReloadLogEntry? entry) _result = (ResultNoneVisualStateName, null);
-
- private Status? _hotReloadStatus;
- private RemoteControlStatus? _devServerStatus;
-
- private readonly Dictionary _serverHrEntries = new();
- private readonly Dictionary _appHrEntries = new();
- private readonly ClientHotReloadProcessor? _processor; // Only when used by external tool like HD.
-
- public static HotReloadStatusView Create(IDiagnosticViewContext ctx)
- {
- var processor = RemoteControlClient.Instance?.Processors?.OfType().FirstOrDefault();
- if (processor is null)
- {
- throw new InvalidOperationException("Cannot resolve the hot-reload client.");
- }
-
- return new HotReloadStatusView(ctx, processor);
- }
-
- internal HotReloadStatusView(IDiagnosticViewContext ctx, ClientHotReloadProcessor? processor = null)
- {
- _ctx = ctx;
- _processor = processor;
-
- DefaultStyleKey = typeof(HotReloadStatusView);
- History = [];
-
- UpdateVisualStates(false);
-
- Loaded += static (snd, _) =>
- {
- // Make sure to hide the diagnostics overlay when the view is loaded (in case the template was applied while out of the visual tree).
- if (snd is HotReloadStatusView { XamlRoot: { } root } that)
- {
- DiagnosticsOverlay.Get(root).Hide(RemoteControlStatusView.Id);
- if (RemoteControlClient.Instance is { } devServer)
- {
- devServer.StatusChanged += that.OnDevServerStatusChanged;
- that.OnDevServerStatusChanged(null, devServer.Status);
- }
-
- if (that._processor is not null)
- {
- that._processor.StatusChanged += that.OnHotReloadStatusChanged;
- that.OnHotReloadStatusChanged(that._processor.CurrentStatus);
- }
- }
- };
- Unloaded += static (snd, _) =>
- {
- if (snd is HotReloadStatusView that)
- {
- if (RemoteControlClient.Instance is { } devServer)
- {
- devServer.StatusChanged -= that.OnDevServerStatusChanged;
- }
-
- if (that._processor is not null)
- {
- that._processor.StatusChanged -= that.OnHotReloadStatusChanged;
- }
- }
- };
- }
-
- private void OnDevServerStatusChanged(object? sender, RemoteControlStatus devServerStatus)
- {
- var oldStatus = _devServerStatus;
- _devServerStatus = devServerStatus;
-
- DispatcherQueue.TryEnqueue(() =>
- {
- UpdateLog(oldStatus, devServerStatus);
-
- UpdateVisualStates();
- });
- }
-
- private void OnHotReloadStatusChanged(object? sender, Status status)
- => OnHotReloadStatusChanged(status);
-
- internal void OnHotReloadStatusChanged(Status status)
- {
- var oldStatus = _hotReloadStatus;
- _hotReloadStatus = status;
-
- UpdateLog(oldStatus, status);
-
- UpdateVisualStates();
- }
-
- private void UpdateLog(RemoteControlStatus? oldStatus, RemoteControlStatus newStatus)
- {
- if (DevServerEntry.TryCreateNew(oldStatus, newStatus) is { } entry)
- {
- Insert(History, entry);
- }
- }
-
- private void UpdateLog(Status? oldStatus, Status status)
- {
- // Add or update the entries for the **operations** (server and the application).
- if (status.Server.Operations is { }) // can be null during loading, creating a NRE
- {
- foreach (var srvOp in status.Server.Operations)
- {
- ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_serverHrEntries, srvOp.Id, out var exists);
- if (exists)
- {
- entry!.Update(srvOp);
- }
- else
- {
- entry = new ServerEntry(srvOp);
- }
- }
- }
-
- if (status.Local.Operations is { }) // can be null during loading, creating a NRE
- {
- foreach (var localOp in status.Local.Operations)
- {
- ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_appHrEntries, localOp.Id, out var exists);
- if (exists)
- {
- entry!.Update(localOp);
- }
- else
- {
- entry = new ApplicationEntry(localOp);
- }
- }
- }
-
- var log = History;
- SyncLog(log, _serverHrEntries.Values);
- SyncLog(log, _appHrEntries.Values);
-
- // Add a log entry for the **status** change.
- if (EngineEntry.TryCreateNew(oldStatus, status) is { } engineEntry)
- {
- Insert(log, engineEntry);
- }
- }
-
- public void UpdateVisualStates(bool useTransitions = true)
- {
- var log = History;
-
- var connectionEntry = log.FirstOrDefault(e => e.Source is EntrySource.Engine or EntrySource.DevServer);
- var operationEntries = log.Where(entry => entry.Source is EntrySource.Server or EntrySource.Application).ToList();
-
- // Update the "status"(a.k.a. "connection state") visual state.
- if (connectionEntry is null)
- {
- HeadLine = null;
- VisualStateManager.GoToState(this, StatusUnknownVisualStateName, useTransitions);
- }
- else
- {
- HeadLine = connectionEntry.Description;
- var state = (connectionEntry.Icon & ~(EntryIcon.HotReload | EntryIcon.Connection)) switch
- {
- EntryIcon.Loading => StatusInitializingVisualStateName,
- EntryIcon.Success => StatusReadyVisualStateName,
- EntryIcon.Warning when operationEntries.Any(op => op.IsSuccess ?? false) => StatusReadyVisualStateName,
- EntryIcon.Warning => StatusWarningVisualStateName,
- EntryIcon.Error => StatusErrorVisualStateName,
- _ => StatusUnknownVisualStateName
- };
- VisualStateManager.GoToState(this, state, useTransitions);
- }
-
- // Then the "result" visual state (en send notifications).
- var result = operationEntries switch
- {
- { Count: 0 } => (ResultNoneVisualStateName, default),
- _ when operationEntries.Any(op => op.IsSuccess is null) => (ResultNoneVisualStateName, default),
- [ServerEntry { IsFinal: true, IsSuccess: true } e, ..] => (ResultSuccessVisualStateName, e),
- [ServerEntry { IsFinal: true, IsSuccess: false } e, ..] => (ResultFailedVisualStateName, e),
- [ApplicationEntry { IsSuccess: true } e, ..] => (ResultSuccessVisualStateName, e),
- [ApplicationEntry { IsSuccess: false } e, ..] => (ResultFailedVisualStateName, e),
- _ => (ResultNoneVisualStateName, default(HotReloadLogEntry))
- };
- if (result != _result)
- {
- _result = result;
- VisualStateManager.GoToState(this, _result.state, useTransitions);
-
- var notif = _result.state switch
- {
- ResultNoneVisualStateName when operationEntries is { Count: > 0 } => ProcessingNotification,
- ResultSuccessVisualStateName => SuccessNotification,
- ResultFailedVisualStateName => FailureNotification,
- _ => default
- };
- if (notif is not null)
- {
- if (notif.Content is null or HotReloadLogEntry)
- {
- notif.Content = operationEntries[0];
- }
-
- _ctx.Notify(notif);
- }
- }
- }
-
- #region Misc helpers
- private static void SyncLog(ObservableCollection history, ICollection entries)
- where TEntry : HotReloadLogEntry
- {
- foreach (var entry in entries)
- {
- if (entry.Title is null)
- {
- history.Remove(entry);
- }
- else if (!history.Contains(entry))
- {
- Insert(history, entry);
- }
- }
- }
-
- private static void Insert(ObservableCollection history, HotReloadLogEntry entry)
- {
- history.Insert(FindIndex(entry.Timestamp), entry);
-
- int FindIndex(DateTimeOffset date)
- {
- for (var i = 0; i < history.Count; i++)
- {
- if (history[i].Timestamp > date)
- {
- return i;
- }
- }
-
- return 0;
- }
- }
- #endregion
-}
diff --git a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.xaml b/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.xaml
deleted file mode 100644
index 5b8e1e6af2a3..000000000000
--- a/src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.xaml
+++ /dev/null
@@ -1,346 +0,0 @@
-
-
-
-
- #000000
- #F9F9F9
- #EBEBEB
-
-
- #FFFFFF
- #282828
- #1C1C1C
-
-
-
- #C42B1C
- #09B509
- #FD9E0F
- #8A8A8A
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadServerResult.cs b/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadServerResult.cs
index f7e0ad2e5f95..8a5f19d1905b 100644
--- a/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadServerResult.cs
+++ b/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadServerResult.cs
@@ -6,7 +6,7 @@ namespace Uno.UI.RemoteControl.HotReload.Messages;
///
/// The result of a hot-reload operation on server.
///
-internal enum HotReloadServerResult
+public enum HotReloadServerResult
{
///
/// Hot-reload completed with no changes.
diff --git a/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadState.cs b/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadState.cs
index f814f093fff8..e0f28e817910 100644
--- a/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadState.cs
+++ b/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadState.cs
@@ -3,7 +3,7 @@
namespace Uno.UI.RemoteControl.HotReload;
-internal enum HotReloadState
+public enum HotReloadState
{
///
/// Hot reload is disabled.
diff --git a/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadStatusMessage.cs b/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadStatusMessage.cs
index 59af405fd427..3aaabd1454b4 100644
--- a/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadStatusMessage.cs
+++ b/src/Uno.UI.RemoteControl/HotReload/Messages/HotReloadStatusMessage.cs
@@ -30,7 +30,7 @@ internal record HotReloadStatusMessage(
string IMessage.Name => Name;
}
- internal record HotReloadServerOperationData(
+ public record HotReloadServerOperationData(
long Id,
DateTimeOffset StartTime,
ImmutableHashSet FilePaths,
diff --git a/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs b/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs
index 7f268ae0b9ce..45defc62fbb2 100644
--- a/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs
+++ b/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs
@@ -2,6 +2,7 @@
using Uno.UI.RemoteControl.HotReload;
using Microsoft.UI.Xaml;
using Uno.Diagnostics.UI;
+using System.ComponentModel;
namespace Uno.UI;
@@ -14,6 +15,9 @@ public static class WindowExtensions
/// Enables the UI Update cycle of HotReload to be handled by Uno
///
/// The window of the application where UI updates will be applied
+ [Obsolete("Use the UseStudio() method instead if using the Uno.SDK, otherwise see https://aka.platform.uno/UNO0008 for more details.", DiagnosticId = "UNO0008", UrlFormat = "https://aka.platform.uno/UNO0008")]
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public static void EnableHotReload(this Window window)
=> ClientHotReloadProcessor.SetWindow(window, false);
@@ -22,6 +26,9 @@ public static void EnableHotReload(this Window window)
///
/// The window of the application where UI updates will be applied
/// Request to not show the on-canvas indicator by default.
+ [Obsolete("Use the UseStudio() method instead if using the Uno.SDK, otherwise see https://aka.platform.uno/UNO0008 for more details.", DiagnosticId = "UNO0008", UrlFormat = "https://aka.platform.uno/UNO0008")]
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public static void EnableHotReload(this Window window, bool disableIndicator)
=> ClientHotReloadProcessor.SetWindow(window, disableIndicator);
diff --git a/src/Uno.UI.RemoteControl/RemoteControlClient.Status.cs b/src/Uno.UI.RemoteControl/RemoteControlClient.Status.cs
index 33ffe9cb172c..f7a9dd9543bd 100644
--- a/src/Uno.UI.RemoteControl/RemoteControlClient.Status.cs
+++ b/src/Uno.UI.RemoteControl/RemoteControlClient.Status.cs
@@ -13,9 +13,9 @@ namespace Uno.UI.RemoteControl;
public partial class RemoteControlClient
{
- internal event EventHandler? StatusChanged;
+ public event EventHandler? StatusChanged;
- internal RemoteControlStatus Status => _status.BuildStatus();
+ public RemoteControlStatus Status => _status.BuildStatus();
private class StatusSink : DevServerDiagnostics.ISink
{
diff --git a/src/Uno.UI.RemoteControl/RemoteControlClient.cs b/src/Uno.UI.RemoteControl/RemoteControlClient.cs
index c05abcabf61a..5df9baac1e12 100644
--- a/src/Uno.UI.RemoteControl/RemoteControlClient.cs
+++ b/src/Uno.UI.RemoteControl/RemoteControlClient.cs
@@ -33,7 +33,63 @@ public partial class RemoteControlClient : IRemoteControlClient
public delegate void RemoteControlClientEventEventHandler(object sender, ClientEventEventArgs args);
public delegate void SendMessageFailedEventHandler(object sender, SendMessageFailedEventArgs args);
- public static RemoteControlClient? Instance { get; private set; }
+ public static RemoteControlClient? Instance
+ {
+ get => _instance;
+ private set
+ {
+ _instance = value;
+
+ if (value is { })
+ {
+ while (Interlocked.Exchange(ref _waitingList, null) is { } waitingList)
+ {
+ foreach (var action in waitingList)
+ {
+ action(value);
+ }
+ }
+ }
+ }
+ }
+
+ private static IReadOnlyCollection>? _waitingList;
+
+ ///
+ /// Add a callback to be called when the Instance is available.
+ ///
+ ///
+ /// Will be called synchronously if the instance is already available, no need to check for it before.
+ ///
+ public static void OnRemoteControlClientAvailable(Action action)
+ {
+ if (Instance is { })
+ {
+ action(Instance);
+ }
+ else
+ {
+ // Thread-safe way to add the action to a waiting list for the client to be available
+ while (true)
+ {
+ var waitingList = _waitingList;
+ IReadOnlyCollection> newList = waitingList is null
+ ? [action]
+ : [.. waitingList, action];
+
+ if (Instance is { } i) // Last chance to avoid the waiting list
+ {
+ action(i);
+ break;
+ }
+
+ if (ReferenceEquals(Interlocked.CompareExchange(ref _waitingList, newList, waitingList), waitingList))
+ {
+ break;
+ }
+ }
+ }
+ }
public static RemoteControlClient Initialize(Type appType)
=> Instance = new RemoteControlClient(appType);
@@ -59,6 +115,7 @@ internal static RemoteControlClient Initialize(Type appType, ServerEndpointAttri
private readonly StatusSink _status;
private static readonly TimeSpan _keepAliveInterval = TimeSpan.FromSeconds(30);
+ private static RemoteControlClient? _instance;
private readonly (string endpoint, int port)[]? _serverAddresses;
private readonly Dictionary _processors = new();
private readonly List _preprocessors = new();
@@ -223,7 +280,6 @@ public void RegisterPreProcessor(IRemoteControlPreProcessor preprocessor)
_status.Report(ConnectionState.Connecting);
-
const string lastEndpointKey = "__UNO__" + nameof(RemoteControlClient) + "__last_endpoint";
var preferred = ApplicationData.Current.LocalSettings.Values.TryGetValue(lastEndpointKey, out var lastValue) && lastValue is string lastEp
? _serverAddresses.FirstOrDefault(srv => srv.endpoint.Equals(lastEp, StringComparison.OrdinalIgnoreCase)).endpoint
@@ -429,7 +485,8 @@ private async Task Connect(Uri serverUri, int delay, CancellationTok
{
if (this.Log().IsEnabled(LogLevel.Trace))
{
- this.Log().Trace($"Connecting to [{serverUri}] failed: {e.Message}");
+ var innerMessage = e.InnerException is { } ie ? $" ({ie.Message})" : "";
+ this.Log().Trace($"Connecting to [{serverUri}] failed: {e.Message}{innerMessage}");
}
return new(this, serverUri, watch, null);
@@ -599,7 +656,7 @@ private void StartKeepAliveTimer()
if (Interlocked.CompareExchange(ref _keepAliveTimer, timer, null) is null)
{
- timer.Change(_keepAliveInterval, _keepAliveInterval);
+ timer.Change(TimeSpan.Zero, _keepAliveInterval);
}
}
diff --git a/src/Uno.UI.RemoteControl/RemoteControlStatus.cs b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs
index 1aa2846f45ba..0d1c3e1ac1e5 100644
--- a/src/Uno.UI.RemoteControl/RemoteControlStatus.cs
+++ b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs
@@ -5,15 +5,28 @@
namespace Uno.UI.RemoteControl;
-internal record RemoteControlStatus(
+public record RemoteControlStatus(
RemoteControlStatus.ConnectionState State,
bool? IsVersionValid,
(RemoteControlStatus.KeepAliveState State, long RoundTrip) KeepAlive,
ImmutableHashSet MissingRequiredProcessors,
(long Count, ImmutableHashSet Types) InvalidFrames)
{
- public bool IsAllGood => State == ConnectionState.Connected && IsVersionValid == true && MissingRequiredProcessors.IsEmpty && KeepAlive.State == KeepAliveState.Ok && InvalidFrames.Count == 0;
+ ///
+ /// An ***aggregated*** state of the connection to determine if everything is fine.
+ /// This is for visual representation only, the actual state of the connection is in .
+ ///
+ public bool IsAllGood =>
+ State == ConnectionState.Connected
+ && IsVersionValid == true
+ && MissingRequiredProcessors.IsEmpty
+ && KeepAlive.State == KeepAliveState.Ok
+ && InvalidFrames.Count == 0;
+
+ ///
+ /// Not (for binding purposes).
+ ///
public bool IsProblematic => !IsAllGood;
public (Classification kind, string message) GetSummary()
@@ -82,9 +95,9 @@ internal string GetDescription()
return details.ToString();
}
- internal record struct MissingProcessor(string TypeFullName, string Version, string Details, string? Error = null);
+ public readonly record struct MissingProcessor(string TypeFullName, string Version, string Details, string? Error = null);
- internal enum KeepAliveState
+ public enum KeepAliveState
{
Idle,
Ok, // Got ping/pong in expected delays
@@ -93,7 +106,7 @@ internal enum KeepAliveState
Aborted // KeepAlive was aborted
}
- internal enum ConnectionState
+ public enum ConnectionState
{
///
/// Client as not been started yet
@@ -140,7 +153,7 @@ internal enum ConnectionState
Disconnected
}
- internal enum Classification
+ public enum Classification
{
Ok,
Info,
diff --git a/src/Uno.UI.RemoteControl/RemoteControlStatusView.cs b/src/Uno.UI.RemoteControl/RemoteControlStatusView.cs
index d8b79d89a947..35e46939435f 100644
--- a/src/Uno.UI.RemoteControl/RemoteControlStatusView.cs
+++ b/src/Uno.UI.RemoteControl/RemoteControlStatusView.cs
@@ -11,7 +11,7 @@
namespace Uno.UI.RemoteControl;
-internal sealed partial class RemoteControlStatusView : Ellipse
+public sealed partial class RemoteControlStatusView : Ellipse
{
#if __ANDROID__
public new const string Id = nameof(RemoteControlStatusView);
diff --git a/src/Uno.UI.RemoteControl/Themes/Generic.xaml b/src/Uno.UI.RemoteControl/Themes/Generic.xaml
index f56651e9b43a..585844b453c4 100644
--- a/src/Uno.UI.RemoteControl/Themes/Generic.xaml
+++ b/src/Uno.UI.RemoteControl/Themes/Generic.xaml
@@ -1,10 +1,7 @@
-
+
-
-
-
-
+
+
+
diff --git a/src/Uno.UI.RemoteControl/Uno.UI.RemoteControl.Skia.csproj b/src/Uno.UI.RemoteControl/Uno.UI.RemoteControl.Skia.csproj
index 700f5c7d46bb..f2b9a0b0c4d0 100644
--- a/src/Uno.UI.RemoteControl/Uno.UI.RemoteControl.Skia.csproj
+++ b/src/Uno.UI.RemoteControl/Uno.UI.RemoteControl.Skia.csproj
@@ -68,10 +68,6 @@
-
-
-
-
$(MSBuildThisFileDirectory)**\*.xaml
diff --git a/src/Uno.UI.RuntimeTests/Tests/HotReload/Frame/HRApp/Tests/Given_TextBlock.cs b/src/Uno.UI.RuntimeTests/Tests/HotReload/Frame/HRApp/Tests/Given_TextBlock.cs
index 41784dacaac8..c87b9915f418 100644
--- a/src/Uno.UI.RuntimeTests/Tests/HotReload/Frame/HRApp/Tests/Given_TextBlock.cs
+++ b/src/Uno.UI.RuntimeTests/Tests/HotReload/Frame/HRApp/Tests/Given_TextBlock.cs
@@ -70,13 +70,15 @@ public async Task When_Changing_TextBlock_UsingHRClient()
{
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(60)).Token;
+ var content = new HR_Frame_Pages_Page2();
+
UnitTestsUIContentHelper.Content = new ContentControl
{
- Content = new HR_Frame_Pages_Page2()
+ Content = content
};
var hr = Uno.UI.RemoteControl.RemoteControlClient.Instance?.Processors.OfType().Single();
- var ctx = Uno.UI.RuntimeTests.Tests.HotReload.FrameworkElementExtensions.GetDebugParseContext(new HR_Frame_Pages_Page2());
+ var ctx = Uno.UI.RuntimeTests.Tests.HotReload.FrameworkElementExtensions.GetDebugParseContext(content);
var req = new Uno.UI.RemoteControl.HotReload.ClientHotReloadProcessor.UpdateRequest(
ctx.FileName,
SecondPageTextBlockOriginalText,
@@ -94,20 +96,49 @@ public async Task When_Changing_TextBlock_UsingHRClient()
await hr.UpdateFileAsync(req.Undo(waitForHotReload: false), CancellationToken.None);
}
}
-
+
+ ///
+ /// Ensure that UpdateFileAsync() completes when no changes are made to the file.
+ ///
+ [TestMethod]
+ public async Task When_Changing_TextBlock_UsingHRClient_NoChanges()
+ {
+ var ct = new CancellationTokenSource(TimeSpan.FromSeconds(60)).Token;
+
+ var content = new HR_Frame_Pages_Page2();
+
+ UnitTestsUIContentHelper.Content = new ContentControl
+ {
+ Content = content
+ };
+
+ var hr = Uno.UI.RemoteControl.RemoteControlClient.Instance?.Processors.OfType().Single();
+ var ctx = Uno.UI.RuntimeTests.Tests.HotReload.FrameworkElementExtensions.GetDebugParseContext(content);
+ var req = new Uno.UI.RemoteControl.HotReload.ClientHotReloadProcessor.UpdateRequest(
+ ctx.FileName,
+ SecondPageTextBlockOriginalText,
+ SecondPageTextBlockOriginalText + Environment.NewLine,
+ true)
+ .WithExtendedTimeouts(); // Required for CI
+
+ await hr.UpdateFileAsync(req, ct);
+ }
+
// Another version of the test above, but pausing the TypeMapping before calling the file update
[TestMethod]
public async Task When_Changing_TextBlock_UsingHRClient_PausingTypeMapping()
{
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(25)).Token;
+ var content = new HR_Frame_Pages_Page1();
+
UnitTestsUIContentHelper.Content = new ContentControl
{
- Content = new HR_Frame_Pages_Page1()
+ Content = content
};
var hr = Uno.UI.RemoteControl.RemoteControlClient.Instance?.Processors.OfType().Single();
- var ctx = Uno.UI.RuntimeTests.Tests.HotReload.FrameworkElementExtensions.GetDebugParseContext(new HR_Frame_Pages_Page1());
+ var ctx = Uno.UI.RuntimeTests.Tests.HotReload.FrameworkElementExtensions.GetDebugParseContext(content);
var req = new Uno.UI.RemoteControl.HotReload.ClientHotReloadProcessor.UpdateRequest(
ctx.FileName,
FirstPageTextBlockOriginalText,
diff --git a/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs b/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs
index 20ad0d418211..b26d78949ad9 100644
--- a/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs
+++ b/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs
@@ -2,6 +2,7 @@
#if WINUI || HAS_UNO_WINUI
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -316,8 +317,8 @@ public void Show(string viewId)
/// Add a UI diagnostic element to this overlay.
///
/// This will also make this overlay visible (cf. ).
- public void Add(string id, string name, UIElement preview, Func? details = null)
- => Add(new DiagnosticView(id, name, _ => preview, (_, ct) => new(details?.Invoke())));
+ public void Add(string id, string name, UIElement preview, Func? details = null, DiagnosticViewRegistrationPosition position = default)
+ => Add(new DiagnosticView(id, name, _ => preview, (_, ct) => new(details?.Invoke()), position));
///
/// Add a UI diagnostic element to this overlay.
@@ -464,8 +465,8 @@ private void EnqueueUpdate(bool forceUpdate = false)
.Where(ShouldMaterialize)
.Select(reg => reg.View)
.Concat(_localRegistrations)
- .Distinct()
- .ToList();
+ .OrderBy(r => (int)r.Position)
+ .Distinct();
foreach (var view in viewsThatShouldBeMaterialized)
{
diff --git a/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs b/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs
index 7e14c271fb0e..393379f0fcc6 100644
--- a/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs
+++ b/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs
@@ -7,7 +7,7 @@
namespace Uno.Diagnostics.UI;
-internal partial class DiagnosticView
+partial class DiagnosticView
{
///
/// Registers a dedicated diagnostic view to be displayed by the diagnostic overlay.
@@ -21,11 +21,16 @@ internal partial class DiagnosticView
///
/// Type of the control.
/// The user-friendly name of the diagnostics view.
- public static DiagnosticView Register(string friendlyName)
+ /// Defines when the registered diagnostic view should be displayed.
+ /// Defines where the item should be placed in the overlay.
+ public static DiagnosticView Register(
+ string friendlyName,
+ DiagnosticViewRegistrationMode mode = default,
+ DiagnosticViewRegistrationPosition position = default)
where TView : UIElement, new()
{
- var provider = new DiagnosticView(typeof(TView).Name, friendlyName, () => new TView());
- DiagnosticViewRegistry.Register(provider);
+ var provider = new DiagnosticView(typeof(TView).Name, friendlyName, () => new TView(), position: position);
+ DiagnosticViewRegistry.Register(provider, mode);
return provider;
}
@@ -43,10 +48,15 @@ public static DiagnosticView Register(string friendlyName)
/// The user-friendly name of the diagnostics view.
/// Factory to create an instance of the control.
/// Defines when the registered diagnostic view should be displayed.
- public static DiagnosticView Register(string friendlyName, Func factory, DiagnosticViewRegistrationMode mode = default)
+ /// Defines where the item should be placed in the overlay.
+ public static DiagnosticView Register(
+ string friendlyName,
+ Func factory,
+ DiagnosticViewRegistrationMode mode = default,
+ DiagnosticViewRegistrationPosition position = default)
where TView : UIElement
{
- var provider = new DiagnosticView(typeof(TView).Name, friendlyName, factory);
+ var provider = new DiagnosticView(typeof(TView).Name, friendlyName, factory, position: position);
DiagnosticViewRegistry.Register(provider, mode);
return provider;
}
@@ -62,17 +72,21 @@ public static DiagnosticView Register(string friendlyName, FuncThe user-friendly name of the diagnostics view.
/// Delegate to use to update the when the is being updated.
/// Optional delegate used to show more details about the diagnostic info when user taps on the view.
+ /// Defines when the registered diagnostic view should be displayed.
+ /// Defines where the item should be placed in the overlay.
/// A diagnostic view helper class which can be used to push updates of the state (cf. ).
public static DiagnosticView Register(
string friendlyName,
Action update,
- Func? details = null)
+ Func? details = null,
+ DiagnosticViewRegistrationMode mode = default,
+ DiagnosticViewRegistrationPosition position = default)
where TView : FrameworkElement, new()
{
var provider = details is null
- ? new DiagnosticView(typeof(TView).Name, friendlyName, _ => new TView(), update)
- : new DiagnosticView(typeof(TView).Name, friendlyName, _ => new TView(), update, (ctx, state, ct) => new(details(state)));
- DiagnosticViewRegistry.Register(provider);
+ ? new DiagnosticView(typeof(TView).Name, friendlyName, _ => new TView(), update, position: position)
+ : new DiagnosticView(typeof(TView).Name, friendlyName, _ => new TView(), update, (ctx, state, ct) => new(details(state)), position: position);
+ DiagnosticViewRegistry.Register(provider, mode);
return provider;
}
@@ -88,18 +102,22 @@ public static DiagnosticView Register(
/// Factory to create an instance of the generic element.
/// Delegate to use to update the when the is being updated.
/// Optional delegate used to show more details about the diagnostic info when user taps on the view.
+ /// Defines when the registered diagnostic view should be displayed.
+ /// Defines where the item should be placed in the overlay.
/// A diagnostic view helper class which can be used to push updates of the state (cf. ).
public static DiagnosticView Register(
string friendlyName,
Func factory,
Action update,
- Func? details = null)
+ Func? details = null,
+ DiagnosticViewRegistrationMode mode = default,
+ DiagnosticViewRegistrationPosition position = default)
where TView : FrameworkElement
{
var provider = details is null
- ? new DiagnosticView(typeof(TView).Name, friendlyName, factory, update)
- : new DiagnosticView(typeof(TView).Name, friendlyName, factory, update, (ctx, state, ct) => new(details(state)));
- DiagnosticViewRegistry.Register(provider);
+ ? new DiagnosticView(typeof(TView).Name, friendlyName, factory, update, position: position)
+ : new DiagnosticView(typeof(TView).Name, friendlyName, factory, update, (ctx, state, ct) => new(details(state)), position: position);
+ DiagnosticViewRegistry.Register(provider, mode);
return provider;
}
}
diff --git a/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs b/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs
index 3a87abcd3071..2c8fbbabcf79 100644
--- a/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs
+++ b/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs
@@ -10,12 +10,13 @@ namespace Uno.Diagnostics.UI;
///
/// A generic diagnostic view that can be updated with a state.
///
-internal class DiagnosticView(
+public class DiagnosticView(
string id,
string name,
Func factory,
Action update,
- Func>? details = null)
+ Func>? details = null,
+ DiagnosticViewRegistrationPosition position = default)
: IDiagnosticView
where TView : FrameworkElement
{
@@ -37,6 +38,8 @@ public void Update(TState status)
///
string IDiagnosticView.Name => name;
+ DiagnosticViewRegistrationPosition IDiagnosticView.Position => position;
+
///
object IDiagnosticView.GetElement(IDiagnosticViewContext context)
=> _elementsManager.GetView(context);
diff --git a/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs b/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs
index 5f833c7b6a4c..619d748bea12 100644
--- a/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs
+++ b/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs
@@ -10,11 +10,12 @@ namespace Uno.Diagnostics.UI;
///
/// A generic diagnostic view.
///
-internal class DiagnosticView(
+public class DiagnosticView(
string id,
string name,
Func factory,
- Func>? details = null)
+ Func>? details = null,
+ DiagnosticViewRegistrationPosition position = default)
: IDiagnosticView
where TView : UIElement
{
@@ -22,8 +23,9 @@ public DiagnosticView(
string id,
string name,
Func preview,
- Func>? details = null)
- : this(id, name, _ => preview(), async (_, ct) => details is null ? null : await details(ct))
+ Func>? details = null,
+ DiagnosticViewRegistrationPosition position = default)
+ : this(id, name, _ => preview(), async (_, ct) => details is null ? null : await details(ct), position)
{
}
@@ -33,6 +35,8 @@ public DiagnosticView(
///
string IDiagnosticView.Name => name;
+ DiagnosticViewRegistrationPosition IDiagnosticView.Position => position;
+
///
object IDiagnosticView.GetElement(IDiagnosticViewContext context) => factory(context);
diff --git a/src/Uno.UI/Diagnostics/DiagnosticView.cs b/src/Uno.UI/Diagnostics/DiagnosticView.cs
index fd1711cb6234..4a7eebf30157 100644
--- a/src/Uno.UI/Diagnostics/DiagnosticView.cs
+++ b/src/Uno.UI/Diagnostics/DiagnosticView.cs
@@ -10,19 +10,21 @@ namespace Uno.Diagnostics.UI;
///
/// A generic diagnostic view.
///
-internal partial class DiagnosticView(
+public partial class DiagnosticView(
string id,
string name,
Func factory,
- Func>? details = null)
- : DiagnosticView(id, name, factory, details)
+ Func>? details = null,
+ DiagnosticViewRegistrationPosition position = default)
+ : DiagnosticView(id, name, factory, details, position)
{
public DiagnosticView(
string id,
string name,
Func preview,
- Func>? details = null)
- : this(id, name, _ => preview(), async (_, ct) => details is null ? null : await details(ct))
+ Func>? details = null,
+ DiagnosticViewRegistrationPosition position = default)
+ : this(id, name, _ => preview(), async (_, ct) => details is null ? null : await details(ct), position)
{
}
}
diff --git a/src/Uno.UI/Diagnostics/DiagnosticViewManager.TView.TState.cs b/src/Uno.UI/Diagnostics/DiagnosticViewManager.TView.TState.cs
index 9598b2262813..ece3e7235dc3 100644
--- a/src/Uno.UI/Diagnostics/DiagnosticViewManager.TView.TState.cs
+++ b/src/Uno.UI/Diagnostics/DiagnosticViewManager.TView.TState.cs
@@ -12,7 +12,9 @@ namespace Uno.Diagnostics.UI;
/// Type of the state used to update the .
/// Factory to create an instance of the .
/// Delegate to use to update the on .
-internal class DiagnosticViewManager(Func factory, Action update)
+internal class DiagnosticViewManager(
+ Func factory,
+ Action update)
where TView : FrameworkElement
{
private event EventHandler? _changed;