More cleanup

This commit is contained in:
Soreepeong 2024-02-27 23:54:57 +09:00
parent a7d5380796
commit 92302ffd89
13 changed files with 183 additions and 132 deletions

View file

@ -0,0 +1,9 @@
namespace Dalamud.Interface.ImGuiNotification.EventArgs;
/// <summary>Arguments for use with <see cref="IActiveNotification.Click"/>.</summary>
/// <remarks>Not to be implemented by plugins.</remarks>
public interface INotificationClickArgs
{
/// <summary>Gets the notification being clicked.</summary>
IActiveNotification Notification { get; }
}

View file

@ -0,0 +1,12 @@
namespace Dalamud.Interface.ImGuiNotification.EventArgs;
/// <summary>Arguments for use with <see cref="IActiveNotification.Dismiss"/>.</summary>
/// <remarks>Not to be implemented by plugins.</remarks>
public interface INotificationDismissArgs
{
/// <summary>Gets the notification being dismissed.</summary>
IActiveNotification Notification { get; }
/// <summary>Gets the dismiss reason.</summary>
NotificationDismissReason Reason { get; }
}

View file

@ -0,0 +1,19 @@
using System.Numerics;
namespace Dalamud.Interface.ImGuiNotification.EventArgs;
/// <summary>Arguments for use with <see cref="IActiveNotification.DrawActions"/>.</summary>
/// <remarks>Not to be implemented by plugins.</remarks>
public interface INotificationDrawArgs
{
/// <summary>Gets the notification being drawn.</summary>
IActiveNotification Notification { get; }
/// <summary>Gets the top left coordinates of the area being drawn.</summary>
Vector2 MinCoord { get; }
/// <summary>Gets the bottom right coordinates of the area being drawn.</summary>
/// <remarks>Note that <see cref="Vector2.Y"/> can be <see cref="float.MaxValue"/>, in which case there is no
/// vertical limits to the drawing region.</remarks>
Vector2 MaxCoord { get; }
}

View file

@ -1,49 +1,48 @@
using System.Threading;
using Dalamud.Interface.ImGuiNotification.EventArgs;
using Dalamud.Interface.Internal;
namespace Dalamud.Interface.ImGuiNotification;
/// <summary>Represents an active notification.</summary>
/// <remarks>Not to be implemented by plugins.</remarks>
public interface IActiveNotification : INotification
{
/// <summary>The counter for <see cref="Id"/> field.</summary>
private static long idCounter;
/// <summary>Invoked upon dismissing the notification.</summary>
/// <remarks>The event callback will not be called,
/// if a user interacts with the notification after the plugin is unloaded.</remarks>
event NotificationDismissedDelegate Dismiss;
/// <remarks>The event callback will not be called, if it gets dismissed after plugin unload.</remarks>
event Action<INotificationDismissArgs> Dismiss;
/// <summary>Invoked upon clicking on the notification.</summary>
/// <remarks>
/// Note that this function may be called even after <see cref="Dismiss"/> has been invoked.
/// Refer to <see cref="IsDismissed"/>.
/// </remarks>
event Action<IActiveNotification> Click;
/// <remarks>Note that this function may be called even after <see cref="Dismiss"/> has been invoked.</remarks>
event Action<INotificationClickArgs> Click;
/// <summary>Invoked upon drawing the action bar of the notification.</summary>
/// <remarks>
/// Note that this function may be called even after <see cref="Dismiss"/> has been invoked.
/// Refer to <see cref="IsDismissed"/>.
/// </remarks>
event Action<IActiveNotification> DrawActions;
/// <remarks>Note that this function may be called even after <see cref="Dismiss"/> has been invoked.</remarks>
event Action<INotificationDrawArgs> DrawActions;
/// <summary>Gets the ID of this notification.</summary>
/// <remarks>This value does not change.</remarks>
long Id { get; }
/// <summary>Gets the time of creating this notification.</summary>
/// <remarks>This value does not change.</remarks>
DateTime CreatedAt { get; }
/// <summary>Gets the effective expiry time.</summary>
/// <remarks>Contains <see cref="DateTime.MaxValue"/> if the notification does not expire.</remarks>
/// <remarks>This value will change depending on property changes and user interactions.</remarks>
DateTime EffectiveExpiry { get; }
/// <summary>Gets a value indicating whether the notification has been dismissed.</summary>
/// <summary>Gets the reason how this notification got dismissed. <c>null</c> if not dismissed.</summary>
/// <remarks>This includes when the hide animation is being played.</remarks>
bool IsDismissed { get; }
NotificationDismissReason? DismissReason { get; }
/// <summary>Dismisses this notification.</summary>
/// <remarks>If the notification has already been dismissed, this function does nothing.</remarks>
void DismissNow();
/// <summary>Extends this notifiation.</summary>
@ -57,8 +56,8 @@ public interface IActiveNotification : INotification
/// <remarks>
/// <para>The texture passed will be disposed when the notification is dismissed or a new different texture is set
/// via another call to this function. You do not have to dispose it yourself.</para>
/// <para>If <see cref="IsDismissed"/> is <c>true</c>, then calling this function will simply dispose the passed
/// <paramref name="textureWrap"/> without actually updating the icon.</para>
/// <para>If <see cref="DismissReason"/> is not <c>null</c>, then calling this function will simply dispose the
/// passed <paramref name="textureWrap"/> without actually updating the icon.</para>
/// </remarks>
void SetIconTexture(IDalamudTextureWrap? textureWrap);

View file

@ -4,6 +4,7 @@ using Dalamud.Plugin.Services;
namespace Dalamud.Interface.ImGuiNotification;
/// <summary>Represents a notification.</summary>
/// <remarks>Not to be implemented by plugins.</remarks>
public interface INotification
{
/// <summary>Gets or sets the content body of the notification.</summary>

View file

@ -0,0 +1,87 @@
using System.Numerics;
using Dalamud.Interface.ImGuiNotification.EventArgs;
namespace Dalamud.Interface.ImGuiNotification.Internal;
/// <summary>Represents an active notification.</summary>
internal sealed partial class ActiveNotification : INotificationDismissArgs
{
/// <inheritdoc/>
public event Action<INotificationDismissArgs>? Dismiss;
/// <inheritdoc/>
IActiveNotification INotificationDismissArgs.Notification => this;
/// <inheritdoc/>
NotificationDismissReason INotificationDismissArgs.Reason =>
this.DismissReason
?? throw new InvalidOperationException("DismissReason must be set before using INotificationDismissArgs");
private void InvokeDismiss()
{
try
{
this.Dismiss?.Invoke(this);
}
catch (Exception e)
{
this.LogEventInvokeError(e, $"{nameof(this.Dismiss)} error");
}
}
}
/// <summary>Represents an active notification.</summary>
internal sealed partial class ActiveNotification : INotificationClickArgs
{
/// <inheritdoc/>
public event Action<INotificationClickArgs>? Click;
/// <inheritdoc/>
IActiveNotification INotificationClickArgs.Notification => this;
private void InvokeClick()
{
try
{
this.Click?.Invoke(this);
}
catch (Exception e)
{
this.LogEventInvokeError(e, $"{nameof(this.Click)} error");
}
}
}
/// <summary>Represents an active notification.</summary>
internal sealed partial class ActiveNotification : INotificationDrawArgs
{
private Vector2 drawActionArgMinCoord;
private Vector2 drawActionArgMaxCoord;
/// <inheritdoc/>
public event Action<INotificationDrawArgs>? DrawActions;
/// <inheritdoc/>
IActiveNotification INotificationDrawArgs.Notification => this;
/// <inheritdoc/>
Vector2 INotificationDrawArgs.MinCoord => this.drawActionArgMinCoord;
/// <inheritdoc/>
Vector2 INotificationDrawArgs.MaxCoord => this.drawActionArgMaxCoord;
private void InvokeDrawActions(Vector2 minCoord, Vector2 maxCoord)
{
this.drawActionArgMinCoord = minCoord;
this.drawActionArgMaxCoord = maxCoord;
try
{
this.DrawActions?.Invoke(this);
}
catch (Exception e)
{
this.LogEventInvokeError(e, $"{nameof(this.DrawActions)} error; event registration cancelled");
}
}
}

View file

@ -2,7 +2,6 @@ using System.Numerics;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Utility;
using Dalamud.Utility;
using ImGuiNET;
@ -83,7 +82,7 @@ internal sealed partial class ActiveNotification
this.EffectiveExpiry = this.CalculateEffectiveExpiry(ref warrantsExtension);
if (!this.IsDismissed && DateTime.Now > this.EffectiveExpiry)
if (DateTime.Now > this.EffectiveExpiry)
this.DismissNow(NotificationDismissReason.Timeout);
if (this.ExtensionDurationSinceLastInterest > TimeSpan.Zero && warrantsExtension)
@ -121,7 +120,7 @@ internal sealed partial class ActiveNotification
if (ImGui.IsMouseClicked(ImGuiMouseButton.Left)
|| ImGui.IsMouseClicked(ImGuiMouseButton.Right)
|| ImGui.IsMouseClicked(ImGuiMouseButton.Middle))
this.Click.InvokeSafely(this);
this.InvokeClick();
}
}
@ -419,22 +418,16 @@ internal sealed partial class ActiveNotification
ImGui.PopTextWrapPos();
if (this.DrawActions is not null)
{
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + NotificationConstants.ScaledComponentGap);
try
{
this.DrawActions.Invoke(this);
}
catch
{
// ignore
}
this.InvokeDrawActions(
minCoord with { Y = ImGui.GetCursorPosY() + NotificationConstants.ScaledComponentGap },
new(minCoord.X + width, float.MaxValue));
}
}
private void DrawExpiryBar(DateTime effectiveExpiry, bool warrantsExtension)
{
float barL, barR;
if (this.IsDismissed)
if (this.DismissReason is not null)
{
var v = this.hideEasing.IsDone ? 0f : 1f - (float)this.hideEasing.Value;
var midpoint = (this.prevProgressL + this.prevProgressR) / 2f;

View file

@ -1,16 +1,15 @@
using System.Numerics;
using System.Runtime.Loader;
using System.Threading;
using Dalamud.Interface.Animation;
using Dalamud.Interface.Animation.EasingFunctions;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Utility;
using Serilog;
using Serilog.Events;
namespace Dalamud.Interface.ImGuiNotification.Internal;
@ -71,15 +70,6 @@ internal sealed partial class ActiveNotification : IActiveNotification
this.progressEasing.Start();
}
/// <inheritdoc/>
public event NotificationDismissedDelegate? Dismiss;
/// <inheritdoc/>
public event Action<IActiveNotification>? Click;
/// <inheritdoc/>
public event Action<IActiveNotification>? DrawActions;
/// <inheritdoc/>
public long Id { get; } = IActiveNotification.CreateNewId();
@ -90,60 +80,35 @@ internal sealed partial class ActiveNotification : IActiveNotification
public string Content
{
get => this.underlyingNotification.Content;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.Content = value;
}
set => this.underlyingNotification.Content = value;
}
/// <inheritdoc/>
public string? Title
{
get => this.underlyingNotification.Title;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.Title = value;
}
set => this.underlyingNotification.Title = value;
}
/// <inheritdoc/>
public string? MinimizedText
{
get => this.underlyingNotification.MinimizedText;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.MinimizedText = value;
}
set => this.underlyingNotification.MinimizedText = value;
}
/// <inheritdoc/>
public NotificationType Type
{
get => this.underlyingNotification.Type;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.Type = value;
}
set => this.underlyingNotification.Type = value;
}
/// <inheritdoc/>
public INotificationIcon? Icon
{
get => this.underlyingNotification.Icon;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.Icon = value;
}
set => this.underlyingNotification.Icon = value;
}
/// <inheritdoc/>
@ -152,7 +117,7 @@ internal sealed partial class ActiveNotification : IActiveNotification
get => this.underlyingNotification.HardExpiry;
set
{
if (this.underlyingNotification.HardExpiry == value || this.IsDismissed)
if (this.underlyingNotification.HardExpiry == value)
return;
this.underlyingNotification.HardExpiry = value;
this.lastInterestTime = DateTime.Now;
@ -165,8 +130,6 @@ internal sealed partial class ActiveNotification : IActiveNotification
get => this.underlyingNotification.InitialDuration;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.InitialDuration = value;
this.lastInterestTime = DateTime.Now;
}
@ -178,8 +141,6 @@ internal sealed partial class ActiveNotification : IActiveNotification
get => this.underlyingNotification.ExtensionDurationSinceLastInterest;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.ExtensionDurationSinceLastInterest = value;
this.lastInterestTime = DateTime.Now;
}
@ -188,56 +149,36 @@ internal sealed partial class ActiveNotification : IActiveNotification
/// <inheritdoc/>
public DateTime EffectiveExpiry { get; private set; }
/// <inheritdoc/>
public NotificationDismissReason? DismissReason { get; private set; }
/// <inheritdoc/>
public bool ShowIndeterminateIfNoExpiry
{
get => this.underlyingNotification.ShowIndeterminateIfNoExpiry;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.ShowIndeterminateIfNoExpiry = value;
}
set => this.underlyingNotification.ShowIndeterminateIfNoExpiry = value;
}
/// <inheritdoc/>
public bool Minimized
{
get => this.newMinimized ?? this.underlyingNotification.Minimized;
set
{
if (this.IsDismissed)
return;
this.newMinimized = value;
}
set => this.newMinimized = value;
}
/// <inheritdoc/>
public bool UserDismissable
{
get => this.underlyingNotification.UserDismissable;
set
{
if (this.IsDismissed)
return;
this.underlyingNotification.UserDismissable = value;
}
set => this.underlyingNotification.UserDismissable = value;
}
/// <inheritdoc/>
public float Progress
{
get => this.newProgress ?? this.underlyingNotification.Progress;
set
{
if (this.IsDismissed)
return;
this.newProgress = value;
set => this.newProgress = value;
}
}
/// <inheritdoc/>
public bool IsDismissed => this.hideEasing.IsRunning;
/// <summary>Gets the eased progress.</summary>
private float ProgressEased
@ -271,20 +212,12 @@ internal sealed partial class ActiveNotification : IActiveNotification
/// <param name="reason">The reason of dismissal.</param>
public void DismissNow(NotificationDismissReason reason)
{
if (this.hideEasing.IsRunning)
if (this.DismissReason is not null)
return;
this.DismissReason = reason;
this.hideEasing.Start();
try
{
this.Dismiss?.Invoke(this, reason);
}
catch (Exception e)
{
Log.Error(
e,
$"{nameof(this.Dismiss)} error; notification is owned by {this.initiatorPlugin?.Name ?? NotificationConstants.DefaultInitiator}");
}
this.InvokeDismiss();
}
/// <inheritdoc/>
@ -298,7 +231,7 @@ internal sealed partial class ActiveNotification : IActiveNotification
/// <inheritdoc/>
public void SetIconTexture(IDalamudTextureWrap? textureWrap)
{
if (this.IsDismissed)
if (this.DismissReason is not null)
{
textureWrap?.Dispose();
return;
@ -408,4 +341,9 @@ internal sealed partial class ActiveNotification : IActiveNotification
this.DrawActions = null;
this.initiatorPlugin = null;
}
private void LogEventInvokeError(Exception exception, string message) =>
Log.Error(
exception,
$"[{nameof(ActiveNotification)}:{this.initiatorPlugin?.Name ?? NotificationConstants.DefaultInitiator}] {message}");
}

View file

@ -137,7 +137,7 @@ internal class NotificationManagerPluginScoped : INotificationManager, IServiceT
{
var an = this.notificationManagerService.AddNotification(notification, this.localPlugin);
_ = this.notifications.TryAdd(an, 0);
an.Dismiss += (a, unused) => this.notifications.TryRemove(an, out _);
an.Dismiss += a => this.notifications.TryRemove(a.Notification, out _);
return an;
}

View file

@ -1,8 +0,0 @@
namespace Dalamud.Interface.ImGuiNotification;
/// <summary>Delegate representing the dismissal of an active notification.</summary>
/// <param name="notification">The notification being dismissed.</param>
/// <param name="dismissReason">The reason of dismissal.</param>
public delegate void NotificationDismissedDelegate(
IActiveNotification notification,
NotificationDismissReason dismissReason);

View file

@ -219,7 +219,7 @@ internal class ImGuiWidget : IDataWindowWidget
Task.Run(
async () =>
{
for (var i = 0; i <= 10 && !n.IsDismissed; i++)
for (var i = 0; i <= 10 && !n.DismissReason.HasValue; i++)
{
await Task.Delay(500);
n.Progress = i / 10f;
@ -230,7 +230,7 @@ internal class ImGuiWidget : IDataWindowWidget
Task.Run(
async () =>
{
for (var i = 0; i <= 10 && !n.IsDismissed; i++)
for (var i = 0; i <= 10 && !n.DismissReason.HasValue; i++)
{
await Task.Delay(500);
n.Progress = i / 10f;
@ -257,16 +257,17 @@ internal class ImGuiWidget : IDataWindowWidget
if (ImGui.Button("Update"))
{
NewRandom(out title, out type, out progress);
an.Title = title;
an.Type = type;
an.Progress = progress;
an.Notification.Title = title;
an.Notification.Type = type;
an.Notification.Progress = progress;
}
ImGui.SameLine();
if (ImGui.Button("Dismiss"))
an.DismissNow();
an.Notification.DismissNow();
ImGui.SameLine();
ImGui.SetNextItemWidth(an.MaxCoord.X - ImGui.GetCursorPosX());
ImGui.InputText("##input", ref testString, 255);
};
}

View file

@ -583,7 +583,7 @@ public sealed class UiBuilder : IDisposable
},
this.localPlugin);
_ = this.notifications.TryAdd(an, 0);
an.Dismiss += (a, unused) => this.notifications.TryRemove(an, out _);
an.Dismiss += a => this.notifications.TryRemove(a.Notification, out _);
}
/// <summary>