Skip to content

Commit

Permalink
feat: Added the ability to specify a positionning to DiagnosticsView …
Browse files Browse the repository at this point in the history
…registrations
  • Loading branch information
carldebilly committed Oct 31, 2024
1 parent b693141 commit 01d0111
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 16 deletions.
45 changes: 40 additions & 5 deletions src/Uno.Foundation/Diagnostics/DiagnosticViewRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal static class DiagnosticViewRegistry
{
internal static EventHandler<IImmutableList<DiagnosticViewRegistration>>? Added;

private static ImmutableArray<DiagnosticViewRegistration> _registrations = ImmutableArray<DiagnosticViewRegistration>.Empty;
private static ImmutableArray<DiagnosticViewRegistration> _registrations = [];

/// <summary>
/// Gets the list of registered diagnostic providers.
Expand All @@ -24,26 +24,46 @@ internal static class DiagnosticViewRegistry
/// </summary>
/// <param name="view">A diagnostic view to display.</param>
/// <param name="mode">Defines when the registered diagnostic view should be displayed.</param>
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<DiagnosticViewRegistration>
{
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
{
/// <summary>
/// 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.
/// </summary>
One,
One, // Default

/// <summary>
/// Diagnostic is being rendered as overlay on each window.
Expand All @@ -55,3 +75,18 @@ public enum DiagnosticViewRegistrationMode
/// </summary>
OnDemand
}

public enum DiagnosticViewRegistrationPosition
{
Normal = 0, // Default

/// <summary>
/// Register as the first diagnostic view, ensuring it is displayed first.
/// </summary>
First = -1,

/// <summary>
/// Register as the last diagnostic view, ensuring it is displayed last.
/// </summary>
Last = 1,
}
7 changes: 4 additions & 3 deletions src/Uno.UI.Toolkit/Diagnostics/DiagnosticsOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down
34 changes: 26 additions & 8 deletions src/Uno.UI/Diagnostics/DiagnosticView.Factories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ partial class DiagnosticView
/// </remarks>
/// <typeparam name="TView">Type of the control.</typeparam>
/// <param name="friendlyName">The user-friendly name of the diagnostics view.</param>
public static DiagnosticView<TView> Register<TView>(string friendlyName)
/// <param name="mode">Defines when the registered diagnostic view should be displayed.</param>
/// <param name="position">Defines where the item should be placed in the overlay.</param>
public static DiagnosticView<TView> Register<TView>(
string friendlyName,
DiagnosticViewRegistrationMode mode = default,
DiagnosticViewRegistrationPosition position = default)
where TView : UIElement, new()
{
var provider = new DiagnosticView<TView>(typeof(TView).Name, friendlyName, () => new TView());
DiagnosticViewRegistry.Register(provider);
DiagnosticViewRegistry.Register(provider, mode, position);
return provider;
}

Expand All @@ -43,11 +48,16 @@ public static DiagnosticView<TView> Register<TView>(string friendlyName)
/// <param name="friendlyName">The user-friendly name of the diagnostics view.</param>
/// <param name="factory">Factory to create an instance of the control.</param>
/// <param name="mode">Defines when the registered diagnostic view should be displayed.</param>
public static DiagnosticView<TView> Register<TView>(string friendlyName, Func<TView> factory, DiagnosticViewRegistrationMode mode = default)
/// <param name="position">Defines where the item should be placed in the overlay.</param>
public static DiagnosticView<TView> Register<TView>(
string friendlyName,
Func<TView> factory,
DiagnosticViewRegistrationMode mode = default,
DiagnosticViewRegistrationPosition position = default)
where TView : UIElement
{
var provider = new DiagnosticView<TView>(typeof(TView).Name, friendlyName, factory);
DiagnosticViewRegistry.Register(provider, mode);
DiagnosticViewRegistry.Register(provider, mode, position);
return provider;
}

Expand All @@ -62,17 +72,21 @@ public static DiagnosticView<TView> Register<TView>(string friendlyName, Func<TV
/// <param name="friendlyName">The user-friendly name of the diagnostics view.</param>
/// <param name="update">Delegate to use to update the <typeparamref name="TView"/> when the <typeparamref name="TState"/> is being updated.</param>
/// <param name="details">Optional delegate used to show more details about the diagnostic info when user taps on the view.</param>
/// <param name="mode">Defines when the registered diagnostic view should be displayed.</param>
/// <param name="position">Defines where the item should be placed in the overlay.</param>
/// <returns>A diagnostic view helper class which can be used to push updates of the state (cf. <see cref="DiagnosticView{TView,TState}.Update"/>).</returns>
public static DiagnosticView<TView, TState> Register<TView, TState>(
string friendlyName,
Action<TView, TState> update,
Func<TState, object?>? details = null)
Func<TState, object?>? details = null,
DiagnosticViewRegistrationMode mode = default,
DiagnosticViewRegistrationPosition position = default)
where TView : FrameworkElement, new()
{
var provider = details is null
? new DiagnosticView<TView, TState>(typeof(TView).Name, friendlyName, _ => new TView(), update)
: new DiagnosticView<TView, TState>(typeof(TView).Name, friendlyName, _ => new TView(), update, (ctx, state, ct) => new(details(state)));
DiagnosticViewRegistry.Register(provider);
DiagnosticViewRegistry.Register(provider, mode, position);
return provider;
}

Expand All @@ -88,18 +102,22 @@ public static DiagnosticView<TView, TState> Register<TView, TState>(
/// <param name="factory">Factory to create an instance of the generic element.</param>
/// <param name="update">Delegate to use to update the <typeparamref name="TView"/> when the <typeparamref name="TState"/> is being updated.</param>
/// <param name="details">Optional delegate used to show more details about the diagnostic info when user taps on the view.</param>
/// <param name="mode">Defines when the registered diagnostic view should be displayed.</param>
/// <param name="position">Defines where the item should be placed in the overlay.</param>
/// <returns>A diagnostic view helper class which can be used to push updates of the state (cf. <see cref="DiagnosticView{TView,TState}.Update"/>).</returns>
public static DiagnosticView<TView, TState> Register<TView, TState>(
string friendlyName,
Func<IDiagnosticViewContext, TView> factory,
Action<TView, TState> update,
Func<TState, object?>? details = null)
Func<TState, object?>? details = null,
DiagnosticViewRegistrationMode mode = default,
DiagnosticViewRegistrationPosition position = default)
where TView : FrameworkElement
{
var provider = details is null
? new DiagnosticView<TView, TState>(typeof(TView).Name, friendlyName, factory, update)
: new DiagnosticView<TView, TState>(typeof(TView).Name, friendlyName, factory, update, (ctx, state, ct) => new(details(state)));
DiagnosticViewRegistry.Register(provider);
DiagnosticViewRegistry.Register(provider, mode, position);
return provider;
}
}

0 comments on commit 01d0111

Please sign in to comment.