From 4311ef025c5b664a8c56d5cddb6c9e4f1fe89ee3 Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Thu, 31 Oct 2024 11:02:54 -0400 Subject: [PATCH 01/21] chore: Changed visibility of some structure to public --- .../ClientHotReloadProcessor.Common.Status.cs | 14 +++++++------- .../HotReload/Messages/HotReloadServerResult.cs | 2 +- .../HotReload/Messages/HotReloadState.cs | 2 +- .../HotReload/Messages/HotReloadStatusMessage.cs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs index 8fad9fe3bb56..c36874286bee 100644 --- a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs +++ b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs @@ -23,7 +23,7 @@ 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. @@ -32,7 +32,7 @@ public partial class ClientHotReloadProcessor private readonly StatusSink _status; - internal enum HotReloadSource + public enum HotReloadSource { Runtime, DevServer, @@ -63,7 +63,7 @@ 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); @@ -163,22 +163,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; 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, From b5f7086ef5985e4be11334a7e36af1c03be6caf8 Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Thu, 31 Oct 2024 15:34:52 -0400 Subject: [PATCH 02/21] chore: Moved the Hot Reload Indicator (`HotreloadStatusView`) to another project. --- .../Diagnostics/DiagnosticViewRegistry.cs | 2 +- .../ClientHotReloadProcessor.Common.Status.cs | 18 +- .../HotReload/HotReloadStatusView.Entries.cs | 215 ----------- .../HotReloadStatusView.Resources.cs | 59 --- .../HotReload/HotReloadStatusView.cs | 350 ------------------ .../HotReload/HotReloadStatusView.xaml | 346 ----------------- .../RemoteControlClient.Status.cs | 4 +- .../RemoteControlStatus.cs | 10 +- .../RemoteControlStatusView.cs | 2 +- src/Uno.UI.RemoteControl/Themes/Generic.xaml | 11 +- .../Uno.UI.RemoteControl.Skia.csproj | 4 - .../Frame/HRApp/Tests/Given_TextBlock.cs | 41 +- .../Diagnostics/DiagnosticView.Factories.cs | 2 +- .../DiagnosticView.TView.TState.cs | 2 +- .../Diagnostics/DiagnosticView.TView.cs | 2 +- src/Uno.UI/Diagnostics/DiagnosticView.cs | 2 +- 16 files changed, 62 insertions(+), 1008 deletions(-) delete mode 100644 src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Entries.cs delete mode 100644 src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.Resources.cs delete mode 100644 src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.cs delete mode 100644 src/Uno.UI.RemoteControl/HotReload/HotReloadStatusView.xaml diff --git a/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs b/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs index ed14cec7ab3d..41a3a91a7b64 100644 --- a/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs +++ b/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs @@ -37,7 +37,7 @@ public static void Register(IDiagnosticView view, DiagnosticViewRegistrationMode internal record DiagnosticViewRegistration(DiagnosticViewRegistrationMode Mode, IDiagnosticView View); -internal enum DiagnosticViewRegistrationMode +public enum DiagnosticViewRegistrationMode { /// /// Diagnostic is being display on at least one window. diff --git a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs index c36874286bee..c30dd6017b6b 100644 --- a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs +++ b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs @@ -28,7 +28,7 @@ public partial class ClientHotReloadProcessor /// /// The current status of the hot-reload engine. /// - internal Status CurrentStatus => _status.Current; + public Status CurrentStatus => _status.Current; private readonly StatusSink _status; @@ -39,7 +39,7 @@ public enum HotReloadSource Manual } - internal enum HotReloadClientResult + public enum HotReloadClientResult { /// /// Successful hot-reload. @@ -70,9 +70,9 @@ public record Status( 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 +// #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; @@ -142,9 +142,9 @@ private void NotifyStatusChanged() var status = BuildStatus(); Current = status; -#if HAS_UNO_WINUI - _view.Update(status); -#endif +// #if HAS_UNO_WINUI +// _view.Update(status); +// #endif owner.StatusChanged?.Invoke(this, status); } @@ -208,7 +208,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/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/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/RemoteControlStatus.cs b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs index 1aa2846f45ba..87de6eb8d095 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlStatus.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs @@ -5,7 +5,7 @@ namespace Uno.UI.RemoteControl; -internal record RemoteControlStatus( +public record RemoteControlStatus( RemoteControlStatus.ConnectionState State, bool? IsVersionValid, (RemoteControlStatus.KeepAliveState State, long RoundTrip) KeepAlive, @@ -82,9 +82,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 +93,7 @@ internal enum KeepAliveState Aborted // KeepAlive was aborted } - internal enum ConnectionState + public enum ConnectionState { /// /// Client as not been started yet @@ -140,7 +140,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/Diagnostics/DiagnosticView.Factories.cs b/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs index 7e14c271fb0e..d67177d8ce0f 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. diff --git a/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs b/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs index 3a87abcd3071..a2bcbf6b0851 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs @@ -10,7 +10,7 @@ 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, diff --git a/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs b/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs index 5f833c7b6a4c..3f0cf8997cb1 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs @@ -10,7 +10,7 @@ namespace Uno.Diagnostics.UI; /// /// A generic diagnostic view. /// -internal class DiagnosticView( +public class DiagnosticView( string id, string name, Func factory, diff --git a/src/Uno.UI/Diagnostics/DiagnosticView.cs b/src/Uno.UI/Diagnostics/DiagnosticView.cs index fd1711cb6234..9849ba1e2294 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.cs @@ -10,7 +10,7 @@ namespace Uno.Diagnostics.UI; /// /// A generic diagnostic view. /// -internal partial class DiagnosticView( +public partial class DiagnosticView( string id, string name, Func factory, From b14f69e8abbb14c007d5ea05f8ff1b9194536f7b Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Thu, 31 Oct 2024 16:48:11 -0400 Subject: [PATCH 03/21] chore: clean-up dead code --- .../HotReload/ClientHotReloadProcessor.Common.Status.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs index c30dd6017b6b..16791f6d6d1b 100644 --- a/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs +++ b/src/Uno.UI.RemoteControl/HotReload/ClientHotReloadProcessor.Common.Status.cs @@ -70,10 +70,6 @@ public record Status( 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); } From fd8a299f9a1dc474223c3e51680d32ca3c75c2b0 Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Thu, 31 Oct 2024 16:32:47 -0400 Subject: [PATCH 04/21] feat: Added the ability to specify a positionning to DiagnosticsView registrations --- .../Diagnostics/DiagnosticViewRegistry.cs | 45 ++++++++++++++++--- .../Diagnostics/DiagnosticsOverlay.cs | 7 +-- .../Diagnostics/DiagnosticView.Factories.cs | 34 ++++++++++---- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs b/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs index 41a3a91a7b64..f0e22250f3a0 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. @@ -24,18 +24,38 @@ internal static class DiagnosticViewRegistry /// /// A diagnostic view to display. /// Defines when the registered diagnostic view should be displayed. - public static void Register(IDiagnosticView view, DiagnosticViewRegistrationMode mode = default) + public static void Register(IDiagnosticView view, DiagnosticViewRegistrationMode mode = default, DiagnosticViewRegistrationPosition position = default) { ImmutableInterlocked.Update( ref _registrations, static (providers, provider) => providers.Add(provider), - new DiagnosticViewRegistration(mode, view)); + new DiagnosticViewRegistration(mode, position, view)); Added?.Invoke(null, _registrations); } } -internal record DiagnosticViewRegistration(DiagnosticViewRegistrationMode Mode, IDiagnosticView View); +internal sealed record DiagnosticViewRegistration( + DiagnosticViewRegistrationMode Mode, + DiagnosticViewRegistrationPosition Position, + IDiagnosticView View) : IComparable +{ + public int CompareTo(DiagnosticViewRegistration? other) + { + if (other is null) + { + return 1; + } + + if (Position == other.Position) + { + // If the position is the same, we compare the view id to ensure a stable order. + return string.Compare(View.Id, other.View.Id, StringComparison.Ordinal); + } + + return (int)Position - (int)other.Position; + } +} public enum DiagnosticViewRegistrationMode { @@ -43,7 +63,7 @@ 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 +75,18 @@ public 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.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs b/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs index 20ad0d418211..f5aafa64ffaf 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; @@ -462,10 +463,10 @@ private void EnqueueUpdate(bool forceUpdate = false) var viewsThatShouldBeMaterialized = DiagnosticViewRegistry .Registrations .Where(ShouldMaterialize) + .Order() // See DiagnosticViewRegistration.CompareTo .Select(reg => reg.View) - .Concat(_localRegistrations) - .Distinct() - .ToList(); + .Concat(_localRegistrations) // They are at the end of the list. + .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 d67177d8ce0f..2ec8b905a3bb 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs @@ -21,11 +21,16 @@ 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); + DiagnosticViewRegistry.Register(provider, mode, position); return provider; } @@ -43,11 +48,16 @@ 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); - DiagnosticViewRegistry.Register(provider, mode); + DiagnosticViewRegistry.Register(provider, mode, position); 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); + DiagnosticViewRegistry.Register(provider, mode, position); 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); + DiagnosticViewRegistry.Register(provider, mode, position); return provider; } } From 75273ed6fc7065b703e83f305dc64addd62e7357 Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Thu, 31 Oct 2024 17:40:58 -0400 Subject: [PATCH 05/21] feat: Added the ability to wait for the RemoteControlClient to be available --- .../RemoteControlClient.cs | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Uno.UI.RemoteControl/RemoteControlClient.cs b/src/Uno.UI.RemoteControl/RemoteControlClient.cs index c05abcabf61a..de8d05b302b3 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 not null) + { + 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(); From 9ea0de3c18b1c1f598501720fb933b0c901d0501 Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Thu, 31 Oct 2024 22:04:29 -0400 Subject: [PATCH 06/21] ci: Fix build --- src/Uno.UI.RemoteControl/RemoteControlClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Uno.UI.RemoteControl/RemoteControlClient.cs b/src/Uno.UI.RemoteControl/RemoteControlClient.cs index de8d05b302b3..306aea9a7390 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlClient.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlClient.cs @@ -63,7 +63,7 @@ private set /// public static void OnRemoteControlClientAvailable(Action action) { - if(Instance is not null) + if (Instance is { }) { action(Instance); } @@ -77,7 +77,7 @@ public static void OnRemoteControlClientAvailable(Action ac ? [action] : [.. waitingList, action]; - if(Instance is { } i) // Last chance to avoid the waiting list + if (Instance is { } i) // Last chance to avoid the waiting list { action(i); break; From 6020ef93e5893776c13579b1a0a7eabcdabce33a Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Fri, 1 Nov 2024 13:12:11 -0400 Subject: [PATCH 07/21] feat: Improved the error logging to give more context on the connection failed reason --- .../RemoteControlClient.cs | 3 ++- .../RemoteControlStatus.cs | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Uno.UI.RemoteControl/RemoteControlClient.cs b/src/Uno.UI.RemoteControl/RemoteControlClient.cs index 306aea9a7390..9d35fad28c75 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlClient.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlClient.cs @@ -486,7 +486,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); diff --git a/src/Uno.UI.RemoteControl/RemoteControlStatus.cs b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs index 87de6eb8d095..f1915e455f34 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlStatus.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs @@ -12,8 +12,27 @@ public record RemoteControlStatus( ImmutableHashSet MissingRequiredProcessors, (long Count, ImmutableHashSet Types) InvalidFrames) { - public bool IsAllGood => State == ConnectionState.Connected && IsVersionValid == true && MissingRequiredProcessors.IsEmpty && KeepAlive.State == KeepAliveState.Ok && InvalidFrames.Count == 0; + /// + /// A boolean indicating if everything is fine with the connection and the handshaking succeeded. + /// + public bool IsAllGood => + State == ConnectionState.Connected +#if !DEBUG + // For debug builds, it's annoying to have the version mismatch preventing the connection + // Only Uno devs should get this issue, let's not block them. + && IsVersionValid == true +#endif + && MissingRequiredProcessors.IsEmpty + && KeepAlive.State == KeepAliveState.Ok + && InvalidFrames.Count == 0; + + /// + /// If the connection is problematic, meaning that the connection is not in a good state. + /// + /// + /// It's just a negation of . + /// public bool IsProblematic => !IsAllGood; public (Classification kind, string message) GetSummary() From 9ffad577bc3a5aca8d532e7dd6400d2e4889b54a Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Fri, 1 Nov 2024 19:35:56 -0400 Subject: [PATCH 08/21] refactor: Make the _local registrations_ of the diagnostics overlay sortable like other registrations --- .../Diagnostics/DiagnosticViewRegistry.cs | 24 +++---------------- .../Diagnostics/IDiagnosticView.cs | 2 ++ .../RemoteControlClient.cs | 1 - .../Diagnostics/DiagnosticsOverlay.cs | 8 +++---- .../Diagnostics/DiagnosticView.Factories.cs | 20 ++++++++-------- .../DiagnosticView.TView.TState.cs | 5 +++- .../Diagnostics/DiagnosticView.TView.cs | 10 +++++--- src/Uno.UI/Diagnostics/DiagnosticView.cs | 10 ++++---- .../DiagnosticViewManager.TView.TState.cs | 4 +++- 9 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs b/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs index f0e22250f3a0..d153d4bf4347 100644 --- a/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs +++ b/src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs @@ -24,12 +24,12 @@ internal static class DiagnosticViewRegistry /// /// A diagnostic view to display. /// Defines when the registered diagnostic view should be displayed. - public static void Register(IDiagnosticView view, DiagnosticViewRegistrationMode mode = default, DiagnosticViewRegistrationPosition position = default) + public static void Register(IDiagnosticView view, DiagnosticViewRegistrationMode mode = default) { ImmutableInterlocked.Update( ref _registrations, static (providers, provider) => providers.Add(provider), - new DiagnosticViewRegistration(mode, position, view)); + new DiagnosticViewRegistration(mode, view)); Added?.Invoke(null, _registrations); } @@ -37,25 +37,7 @@ public static void Register(IDiagnosticView view, DiagnosticViewRegistrationMode internal sealed record DiagnosticViewRegistration( DiagnosticViewRegistrationMode Mode, - DiagnosticViewRegistrationPosition Position, - IDiagnosticView View) : IComparable -{ - public int CompareTo(DiagnosticViewRegistration? other) - { - if (other is null) - { - return 1; - } - - if (Position == other.Position) - { - // If the position is the same, we compare the view id to ensure a stable order. - return string.Compare(View.Id, other.View.Id, StringComparison.Ordinal); - } - - return (int)Position - (int)other.Position; - } -} + IDiagnosticView View); public enum DiagnosticViewRegistrationMode { 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.UI.RemoteControl/RemoteControlClient.cs b/src/Uno.UI.RemoteControl/RemoteControlClient.cs index 9d35fad28c75..12f6223f5874 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlClient.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlClient.cs @@ -280,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 diff --git a/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs b/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs index f5aafa64ffaf..b26d78949ad9 100644 --- a/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs +++ b/src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs @@ -317,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. @@ -463,9 +463,9 @@ private void EnqueueUpdate(bool forceUpdate = false) var viewsThatShouldBeMaterialized = DiagnosticViewRegistry .Registrations .Where(ShouldMaterialize) - .Order() // See DiagnosticViewRegistration.CompareTo .Select(reg => reg.View) - .Concat(_localRegistrations) // They are at the end of the list. + .Concat(_localRegistrations) + .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 2ec8b905a3bb..393379f0fcc6 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs @@ -29,8 +29,8 @@ public static DiagnosticView Register( DiagnosticViewRegistrationPosition position = default) where TView : UIElement, new() { - var provider = new DiagnosticView(typeof(TView).Name, friendlyName, () => new TView()); - DiagnosticViewRegistry.Register(provider, mode, position); + var provider = new DiagnosticView(typeof(TView).Name, friendlyName, () => new TView(), position: position); + DiagnosticViewRegistry.Register(provider, mode); return provider; } @@ -56,8 +56,8 @@ public static DiagnosticView Register( DiagnosticViewRegistrationPosition position = default) where TView : UIElement { - var provider = new DiagnosticView(typeof(TView).Name, friendlyName, factory); - DiagnosticViewRegistry.Register(provider, mode, position); + var provider = new DiagnosticView(typeof(TView).Name, friendlyName, factory, position: position); + DiagnosticViewRegistry.Register(provider, mode); return provider; } @@ -84,9 +84,9 @@ public static DiagnosticView Register( 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, mode, position); + ? 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; } @@ -115,9 +115,9 @@ public static DiagnosticView Register( 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, mode, position); + ? 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 a2bcbf6b0851..2c8fbbabcf79 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.TView.TState.cs @@ -15,7 +15,8 @@ public class DiagnosticView( 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 3f0cf8997cb1..619d748bea12 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.TView.cs @@ -14,7 +14,8 @@ 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 9849ba1e2294..4a7eebf30157 100644 --- a/src/Uno.UI/Diagnostics/DiagnosticView.cs +++ b/src/Uno.UI/Diagnostics/DiagnosticView.cs @@ -14,15 +14,17 @@ 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; From 208c358d3bd4cd0ea0d56493039011080458d21f Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Fri, 1 Nov 2024 20:51:30 -0400 Subject: [PATCH 09/21] ci: Fix build by adjusting PackageDiff --- build/PackageDiffIgnore.xml | 4 ++++ 1 file changed, 4 insertions(+) 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 @@ + + + + From 54b1d480039c4803c58a18084c28cac2196c44a6 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 4 Nov 2024 15:20:48 -0500 Subject: [PATCH 10/21] docs: Add details --- .../HotReload/ClientHotReloadProcessor.ClientApi.cs | 2 +- src/Uno.UI.RemoteControl/RemoteControlStatus.cs | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) 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/RemoteControlStatus.cs b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs index f1915e455f34..94c8e397437c 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlStatus.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs @@ -14,7 +14,8 @@ public record RemoteControlStatus( { /// - /// A boolean indicating if everything is fine with the connection and the handshaking succeeded. + /// 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 @@ -28,11 +29,8 @@ public record RemoteControlStatus( && InvalidFrames.Count == 0; /// - /// If the connection is problematic, meaning that the connection is not in a good state. + /// Not (for binding purposes). /// - /// - /// It's just a negation of . - /// public bool IsProblematic => !IsAllGood; public (Classification kind, string message) GetSummary() From 1d2053468e2cfc7a14b28a507370375fa6489c43 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 4 Nov 2024 15:21:40 -0500 Subject: [PATCH 11/21] feat: Add ability for add-ins to have full access to teh ServiceCollection --- .../ServiceCollectionExtensionAttribute.cs | 23 ++++++++++ .../ServiceCollectionServiceExtensions.cs | 46 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionExtensionAttribute.cs 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..d324a22c4e1a --- /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 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 { /// From a169ec569bd500f8c29cceb4ca639fc11e9d5243 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 4 Nov 2024 15:22:59 -0500 Subject: [PATCH 12/21] fix: Do not throw if an add-in load fail --- .../Extensibility/AddInsExtensions.cs | 4 ++-- src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) 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/Helpers/AssemblyHelper.cs b/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs index be3d40bf1acd..44f97a159ab2 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.Information, $"Loading add-in assembly '{dll}'."); + assemblies.Add(Assembly.LoadFrom(dll)); } catch (Exception err) From f872b05fe396a5fefaf545643a1a7482ef52572d Mon Sep 17 00:00:00 2001 From: David Date: Mon, 4 Nov 2024 15:28:22 -0500 Subject: [PATCH 13/21] chore: Fix (non enforced) code analysis rule --- .../Uno.Utils.DependencyInjection/ServiceAttribute.cs | 2 +- .../ServiceCollectionExtensionAttribute.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 index d324a22c4e1a..27bb145cd539 100644 --- a/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionExtensionAttribute.cs +++ b/src/Uno.UI.RemoteControl.Host/Extensibility/Uno.Utils.DependencyInjection/ServiceCollectionExtensionAttribute.cs @@ -14,7 +14,7 @@ namespace Uno.Utils.DependencyInjection; /// so the implementation will be able to add some custom services on it. /// [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] -public class ServiceCollectionExtensionAttribute(Type type) : Attribute +public sealed class ServiceCollectionExtensionAttribute(Type type) : Attribute { /// /// Type of the extension that should be instantiated with a as parameter. From 40b7d46c3cde62a6ab546a1cca159a09b0f2c92e Mon Sep 17 00:00:00 2001 From: David Date: Tue, 5 Nov 2024 10:23:28 -0500 Subject: [PATCH 14/21] chore: Update logging level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Laban --- src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs b/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs index 44f97a159ab2..d614fc56da3c 100644 --- a/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs +++ b/src/Uno.UI.RemoteControl.Host/Helpers/AssemblyHelper.cs @@ -18,7 +18,7 @@ public static IImmutableList Load(IImmutableList dllFiles, boo { try { - _log.Log(LogLevel.Information, $"Loading add-in assembly '{dll}'."); + _log.Log(LogLevel.Debug, $"Loading add-in assembly '{dll}'."); assemblies.Add(Assembly.LoadFrom(dll)); } From ba3a2ce0a52c79e7813ee1ae7c9be25022a68f11 Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Mon, 4 Nov 2024 14:20:47 -0500 Subject: [PATCH 15/21] feat: Improved the way pings are done to the remote control server, reducing the delay before the first ping. --- src/Uno.UI.RemoteControl/RemoteControlClient.cs | 2 +- src/Uno.UI.RemoteControl/RemoteControlStatus.cs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Uno.UI.RemoteControl/RemoteControlClient.cs b/src/Uno.UI.RemoteControl/RemoteControlClient.cs index 12f6223f5874..5df9baac1e12 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlClient.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlClient.cs @@ -656,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 94c8e397437c..0d1c3e1ac1e5 100644 --- a/src/Uno.UI.RemoteControl/RemoteControlStatus.cs +++ b/src/Uno.UI.RemoteControl/RemoteControlStatus.cs @@ -19,11 +19,7 @@ public record RemoteControlStatus( /// public bool IsAllGood => State == ConnectionState.Connected -#if !DEBUG - // For debug builds, it's annoying to have the version mismatch preventing the connection - // Only Uno devs should get this issue, let's not block them. && IsVersionValid == true -#endif && MissingRequiredProcessors.IsEmpty && KeepAlive.State == KeepAliveState.Ok && InvalidFrames.Count == 0; From dbbfe2c1a11c2b05f2210bd843805d36bb3d08d1 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Mon, 4 Nov 2024 16:54:39 -0500 Subject: [PATCH 16/21] chore: Update implicit references --- build/ci/.azure-devops-project-template-tests.yml | 8 ++++---- build/ci/templates/uno-dev-feed.yml | 7 +++++++ doc/articles/features/working-with-xaml-hot-reload.md | 4 ++-- doc/articles/migrating-from-previous-releases.md | 4 ++++ doc/articles/migrating-to-uno-5.md | 2 +- src/Directory.Build.props | 3 +++ src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs | 2 +- .../5.1/uno51recommended/uno51recommended/App.cs | 2 +- .../5.2/uno52AppWithLib/uno52AppWithLib/App.xaml.cs | 2 +- .../5.2/uno52blank/uno52blank/App.xaml.cs | 2 +- .../5.3/uno53net9blank/uno53net9blank/App.xaml.cs | 2 +- src/Uno.Sdk/Sdk/Sdk.props.buildschema.json | 4 ++++ src/Uno.Sdk/Services/PackageManifest.cs | 1 + src/Uno.Sdk/Tasks/ImplicitPackagesResolver.cs | 3 +++ src/Uno.Sdk/packages.json | 7 +++++++ .../targets/Uno.Implicit.Packages.ProjectSystem.targets | 1 + src/Uno.Sdk/targets/Uno.Implicit.Packages.targets | 1 + .../HotReload/ClientHotReloadProcessor.MetadataUpdate.cs | 2 +- src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs | 7 +++++++ 19 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 build/ci/templates/uno-dev-feed.yml diff --git a/build/ci/.azure-devops-project-template-tests.yml b/build/ci/.azure-devops-project-template-tests.yml index 6ea10c23a45d..d480011e2920 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 @@ -88,6 +88,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 @@ -126,11 +127,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/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.

A hot reload visual indicator @@ -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/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/uno51blank/App.cs b/src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs index 62e506b2f697..b52a5c4e507a 100644 --- a/src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs +++ b/src/SolutionTemplate/5.1/uno51blank/uno51blank/App.cs @@ -9,7 +9,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) MainWindow = new Window(); #if DEBUG - MainWindow.EnableHotReload(); + MainWindow.UseStudio(); #endif diff --git a/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs b/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs index 19b13588d50f..02b6c1e4e73d 100644 --- a/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs +++ b/src/SolutionTemplate/5.1/uno51recommended/uno51recommended/App.cs @@ -75,7 +75,7 @@ protected async override void OnLaunched(LaunchActivatedEventArgs args) MainWindow = builder.Window; #if DEBUG - MainWindow.EnableHotReload(); + MainWindow.UseStudio(); #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..bf27f78b2815 100644 --- a/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/App.xaml.cs +++ b/src/SolutionTemplate/5.2/uno52AppWithLib/uno52AppWithLib/App.xaml.cs @@ -21,7 +21,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) { MainWindow = new Window(); #if DEBUG - MainWindow.EnableHotReload(); + MainWindow.UseStudio(); #endif diff --git a/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs b/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs index 3d3bb856f7be..1979f64729ea 100644 --- a/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs +++ b/src/SolutionTemplate/5.2/uno52blank/uno52blank/App.xaml.cs @@ -21,7 +21,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) { MainWindow = new Window(); #if DEBUG - MainWindow.EnableHotReload(); + MainWindow.UseStudio(); #endif diff --git a/src/SolutionTemplate/5.3/uno53net9blank/uno53net9blank/App.xaml.cs b/src/SolutionTemplate/5.3/uno53net9blank/uno53net9blank/App.xaml.cs index 622781bbb6d5..302b0aab62db 100644 --- a/src/SolutionTemplate/5.3/uno53net9blank/uno53net9blank/App.xaml.cs +++ b/src/SolutionTemplate/5.3/uno53net9blank/uno53net9blank/App.xaml.cs @@ -21,7 +21,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) { MainWindow = new Window(); #if DEBUG - MainWindow.EnableHotReload(); + MainWindow.UseStudio(); #endif 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..56348beaf653 100644 --- a/src/Uno.Sdk/packages.json +++ b/src/Uno.Sdk/packages.json @@ -85,6 +85,13 @@ "Uno.Settings.DevServer" ] }, + { + "group": "hotdesign", + "version": "1.0.0-dev.3", + "packages": [ + "Uno.UI.HotDesign" + ] + }, { "group": "SkiaSharp", "version": "2.88.8", diff --git a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets index e0880a8dce18..ce5194b7d6b3 100644 --- a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets +++ b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets @@ -9,6 +9,7 @@ <_UnoProjectSystemPackageReference Include="Uno.Resizetizer" ProjectSystem="true" PrivateAssets="all" /> <_UnoProjectSystemPackageReference Include="Uno.Sdk.Extras" ProjectSystem="true" PrivateAssets="all" /> <_UnoProjectSystemPackageReference Include="Uno.Settings.DevServer" ProjectSystem="true" PrivateAssets="all" /> + <_UnoProjectSystemPackageReference Include="Uno.UI.HotDesign" ProjectSystem="true" PrivateAssets="all" /> 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/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/WindowExtensions.cs b/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs index 7f268ae0b9ce..4c3db93087f1 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", 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", 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); From 1aa1bc4101d10590b6ab4d4434a404cf66101775 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Tue, 5 Nov 2024 21:42:00 -0500 Subject: [PATCH 17/21] chore: Adjust skiasharp defaults --- .../run-netcore-mobile-template-tests.ps1 | 14 +++++----- doc/articles/uno-build-error-codes.md | 12 ++++++++ .../5.1/uno51blank/Directory.Packages.props | 22 +++++++-------- .../5.1/uno51blank/uno51blank/App.cs | 4 ++- .../uno51recommended/Directory.Packages.props | 28 +++++++++---------- .../uno51recommended/uno51recommended/App.cs | 4 ++- .../uno52AppWithLib/App.xaml.cs | 4 ++- .../uno52AppWithLib/uno52AppWithLib.csproj | 4 +-- .../uno52emptylib/uno52emptylib.csproj | 4 +-- .../uno52AppWithLib/uno52lib/uno52lib.csproj | 4 +-- .../5.2/uno52Lib/uno52Lib.csproj | 2 +- .../5.2/uno52NuGetLib/uno52NuGetLib.csproj | 2 +- .../uno52SingleProjectLib.csproj | 2 +- .../5.2/uno52blank/uno52blank/App.xaml.cs | 4 ++- .../uno52blank/uno52blank/uno52blank.csproj | 4 +-- .../uno53net9blank/uno53net9blank.csproj | 4 +-- src/Uno.Sdk/packages.json | 4 +-- ...no.Implicit.Packages.ProjectSystem.targets | 5 +++- .../HotReload/WindowExtensions.cs | 4 +-- 19 files changed, 77 insertions(+), 54 deletions(-) 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/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/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 b52a5c4e507a..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 - MainWindow.UseStudio(); +#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 02b6c1e4e73d..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 - MainWindow.UseStudio(); +#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 bf27f78b2815..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 - MainWindow.UseStudio(); +#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 1979f64729ea..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 - MainWindow.UseStudio(); +#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.Sdk/packages.json b/src/Uno.Sdk/packages.json index 56348beaf653..bcfc9ee81c8e 100644 --- a/src/Uno.Sdk/packages.json +++ b/src/Uno.Sdk/packages.json @@ -87,14 +87,14 @@ }, { "group": "hotdesign", - "version": "1.0.0-dev.3", + "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 ce5194b7d6b3..7b02dded7571 100644 --- a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets +++ b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.targets @@ -9,7 +9,10 @@ <_UnoProjectSystemPackageReference Include="Uno.Resizetizer" ProjectSystem="true" PrivateAssets="all" /> <_UnoProjectSystemPackageReference Include="Uno.Sdk.Extras" ProjectSystem="true" PrivateAssets="all" /> <_UnoProjectSystemPackageReference Include="Uno.Settings.DevServer" ProjectSystem="true" PrivateAssets="all" /> - <_UnoProjectSystemPackageReference Include="Uno.UI.HotDesign" ProjectSystem="true" PrivateAssets="all" /> + + + + <_UnoProjectSystemPackageReference Include="Uno.UI.HotDesign" ProjectSystem="true" PrivateAssets="all" Condition=" '$(Optimize)' != 'true' " /> diff --git a/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs b/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs index 4c3db93087f1..45defc62fbb2 100644 --- a/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs +++ b/src/Uno.UI.RemoteControl/HotReload/WindowExtensions.cs @@ -15,7 +15,7 @@ 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", DiagnosticId = "UNO0008", UrlFormat = "https://aka.platform.uno/UNO0008")] + [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) @@ -26,7 +26,7 @@ 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", DiagnosticId = "UNO0008", UrlFormat = "https://aka.platform.uno/UNO0008")] + [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) From b62cc0f55c8293bac4253dcc8eeedf6473fec534 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Thu, 7 Nov 2024 11:18:04 -0500 Subject: [PATCH 18/21] chore: Split linux tests into more groups --- .../.azure-devops-project-template-tests.yml | 2 ++ .../test-scripts/run-net7-template-linux.ps1 | 22 +++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/build/ci/.azure-devops-project-template-tests.yml b/build/ci/.azure-devops-project-template-tests.yml index d480011e2920..7eb7ebb14542 100644 --- a/build/ci/.azure-devops-project-template-tests.yml +++ b/build/ci/.azure-devops-project-template-tests.yml @@ -120,6 +120,8 @@ jobs: TestGroup: '1' group_2: TestGroup: '2' + group_3: + TestGroup: '3' steps: - task: DownloadBuildArtifacts@0 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 From 564438dcac810c58001e1630227beddfba41769b Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Thu, 7 Nov 2024 15:37:40 -0500 Subject: [PATCH 19/21] chore: Adjust test build machine --- .vsts-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 12492515c49684cb7ddd603d9cda9511a24edb9e Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Thu, 7 Nov 2024 17:01:50 -0500 Subject: [PATCH 20/21] chore: adjust for missing android 34 sdk on macOS tests --- build/ci/.azure-devops-project-template-tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/ci/.azure-devops-project-template-tests.yml b/build/ci/.azure-devops-project-template-tests.yml index 7eb7ebb14542..d44825c2442c 100644 --- a/build/ci/.azure-devops-project-template-tests.yml +++ b/build/ci/.azure-devops-project-template-tests.yml @@ -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 From a3f12c99014e1717413b25c1ff549a13a74d0621 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Thu, 7 Nov 2024 17:20:28 -0500 Subject: [PATCH 21/21] chore: Adjust android build --- build/ci/.azure-devops-android-tests.yml | 5 +++++ 1 file changed, 5 insertions(+) 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