mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
feat: add ImGui notifications/toasts
This commit is contained in:
parent
33634f7fb9
commit
a03db8b35d
4 changed files with 392 additions and 1 deletions
|
|
@ -148,6 +148,11 @@ namespace Dalamud.Interface.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ImFontPtr MonoFont { get; private set; }
|
public static ImFontPtr MonoFont { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the manager for notifications/toasts.
|
||||||
|
/// </summary>
|
||||||
|
public Notifications Notifications { get; init; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets an action that is exexuted when fonts are rebuilt.
|
/// Gets or sets an action that is exexuted when fonts are rebuilt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -626,6 +631,7 @@ namespace Dalamud.Interface.Internal
|
||||||
this.lastWantCapture = this.LastImGuiIoPtr.WantCaptureMouse;
|
this.lastWantCapture = this.LastImGuiIoPtr.WantCaptureMouse;
|
||||||
|
|
||||||
this.Draw?.Invoke();
|
this.Draw?.Invoke();
|
||||||
|
this.Notifications.Draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
345
Dalamud/Interface/Internal/Notifications.cs
Normal file
345
Dalamud/Interface/Internal/Notifications.cs
Normal file
|
|
@ -0,0 +1,345 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Internal;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class handling notifications/toasts in ImGui.
|
||||||
|
/// Ported from https://github.com/patrickcjk/imgui-notify.
|
||||||
|
/// </summary>
|
||||||
|
internal class Notifications
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Value indicating the bottom-left X padding.
|
||||||
|
/// </summary>
|
||||||
|
internal const float NotifyPaddingX = 20.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value indicating the bottom-left Y padding.
|
||||||
|
/// </summary>
|
||||||
|
internal const float NotifyPaddingY = 20.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value indicating the Y padding between each message.
|
||||||
|
/// </summary>
|
||||||
|
internal const float NotifyPaddingMessageY = 10.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value indicating the fade-in and out duration.
|
||||||
|
/// </summary>
|
||||||
|
internal const int NotifyFadeInOutTime = 500;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value indicating the default time until the notification is dismissed.
|
||||||
|
/// </summary>
|
||||||
|
internal const int NotifyDefaultDismiss = 3000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value indicating the maximum opacity.
|
||||||
|
/// </summary>
|
||||||
|
internal const float NotifyOpacity = 0.82f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value indicating default window flags for the notifications.
|
||||||
|
/// </summary>
|
||||||
|
internal const ImGuiWindowFlags NotifyToastFlags =
|
||||||
|
ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoInputs |
|
||||||
|
ImGuiWindowFlags.NoNav | ImGuiWindowFlags.NoBringToFrontOnFocus | ImGuiWindowFlags.NoFocusOnAppearing;
|
||||||
|
|
||||||
|
private readonly List<Notification> notifications = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a notification to the notification queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">The content of the notification.</param>
|
||||||
|
/// <param name="title">The title of the notification.</param>
|
||||||
|
/// <param name="type">The type of the notification.</param>
|
||||||
|
/// <param name="msDelay">The time the notification should be displayed for.</param>
|
||||||
|
public void AddNotification(
|
||||||
|
string content, string title = null, Notification.Type type = Notification.Type.None, int msDelay = NotifyDefaultDismiss)
|
||||||
|
{
|
||||||
|
this.notifications.Add(new Notification
|
||||||
|
{
|
||||||
|
Content = content,
|
||||||
|
Title = title,
|
||||||
|
NotificationType = type,
|
||||||
|
DurationMs = msDelay,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draw all currently queued notifications.
|
||||||
|
/// </summary>
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
var viewportSize = ImGuiHelpers.MainViewport.Size;
|
||||||
|
var height = 0f;
|
||||||
|
|
||||||
|
for (var i = 0; i < this.notifications.Count; i++)
|
||||||
|
{
|
||||||
|
var tn = this.notifications.ElementAt(i);
|
||||||
|
|
||||||
|
if (tn.GetPhase() == Notification.Phase.Expired)
|
||||||
|
{
|
||||||
|
this.notifications.RemoveAt(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var opacity = tn.GetFadePercent();
|
||||||
|
|
||||||
|
var iconColor = tn.Color;
|
||||||
|
iconColor.W = opacity;
|
||||||
|
|
||||||
|
var windowName = $"##NOTIFY{i}";
|
||||||
|
|
||||||
|
ImGuiHelpers.ForceNextWindowMainViewport();
|
||||||
|
ImGui.SetNextWindowBgAlpha(opacity);
|
||||||
|
ImGui.SetNextWindowPos(new Vector2(viewportSize.X - NotifyPaddingX, viewportSize.Y - NotifyPaddingY - height), ImGuiCond.Always, Vector2.One);
|
||||||
|
ImGui.Begin(windowName, NotifyToastFlags);
|
||||||
|
|
||||||
|
ImGui.PushTextWrapPos(viewportSize.X / 3.0f);
|
||||||
|
|
||||||
|
var wasTitleRendered = false;
|
||||||
|
|
||||||
|
if (!tn.Icon.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
wasTitleRendered = true;
|
||||||
|
ImGui.PushFont(InterfaceManager.IconFont);
|
||||||
|
ImGui.TextColored(iconColor, tn.Icon);
|
||||||
|
ImGui.PopFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
var textColor = ImGuiColors.DalamudWhite;
|
||||||
|
textColor.W = opacity;
|
||||||
|
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, textColor);
|
||||||
|
|
||||||
|
if (!tn.Title.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (!tn.Icon.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(tn.Title);
|
||||||
|
wasTitleRendered = true;
|
||||||
|
}
|
||||||
|
else if (!tn.DefaultTitle.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (!tn.Icon.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(tn.DefaultTitle);
|
||||||
|
wasTitleRendered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasTitleRendered && !tn.Content.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 5.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tn.Content.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (wasTitleRendered)
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(tn.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
|
||||||
|
ImGui.PopTextWrapPos();
|
||||||
|
|
||||||
|
height += ImGui.GetWindowHeight() + NotifyPaddingMessageY;
|
||||||
|
|
||||||
|
ImGui.End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Container class for notifications.
|
||||||
|
/// </summary>
|
||||||
|
internal class Notification
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Possible notification types.
|
||||||
|
/// </summary>
|
||||||
|
public enum Type
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No special type.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type indicating success.
|
||||||
|
/// </summary>
|
||||||
|
Success,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type indicating a warning.
|
||||||
|
/// </summary>
|
||||||
|
Warning,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type indicating an error.
|
||||||
|
/// </summary>
|
||||||
|
Error,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type indicating generic information.
|
||||||
|
/// </summary>
|
||||||
|
Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Possible notification phases.
|
||||||
|
/// </summary>
|
||||||
|
internal enum Phase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Phase indicating fade-in.
|
||||||
|
/// </summary>
|
||||||
|
FadeIn,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Phase indicating waiting until fade-out.
|
||||||
|
/// </summary>
|
||||||
|
Wait,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Phase indicating fade-out.
|
||||||
|
/// </summary>
|
||||||
|
FadeOut,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Phase indicating that the notification has expired.
|
||||||
|
/// </summary>
|
||||||
|
Expired,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the notification.
|
||||||
|
/// </summary>
|
||||||
|
internal Type NotificationType { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the title of the notification.
|
||||||
|
/// </summary>
|
||||||
|
internal string Title { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the content of the notification.
|
||||||
|
/// </summary>
|
||||||
|
internal string Content { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the duration of the notification in milliseconds.
|
||||||
|
/// </summary>
|
||||||
|
internal int DurationMs { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the creation time of the notification.
|
||||||
|
/// </summary>
|
||||||
|
internal DateTime CreationTime { get; init; } = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default color of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="NotificationType"/> is set to an out-of-range value.</exception>
|
||||||
|
internal Vector4 Color => this.NotificationType switch
|
||||||
|
{
|
||||||
|
Type.None => ImGuiColors.DalamudWhite,
|
||||||
|
Type.Success => ImGuiColors.HealerGreen,
|
||||||
|
Type.Warning => ImGuiColors.DalamudOrange,
|
||||||
|
Type.Error => ImGuiColors.DalamudRed,
|
||||||
|
Type.Info => ImGuiColors.TankBlue,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the icon of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="NotificationType"/> is set to an out-of-range value.</exception>
|
||||||
|
internal string? Icon => this.NotificationType switch
|
||||||
|
{
|
||||||
|
Type.None => null,
|
||||||
|
Type.Success => FontAwesomeIcon.CheckCircle.ToIconString(),
|
||||||
|
Type.Warning => FontAwesomeIcon.ExclamationCircle.ToIconString(),
|
||||||
|
Type.Error => FontAwesomeIcon.TimesCircle.ToIconString(),
|
||||||
|
Type.Info => FontAwesomeIcon.InfoCircle.ToIconString(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default title of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="NotificationType"/> is set to an out-of-range value.</exception>
|
||||||
|
internal string? DefaultTitle => this.NotificationType switch
|
||||||
|
{
|
||||||
|
Type.None => null,
|
||||||
|
Type.Success => Type.Success.ToString(),
|
||||||
|
Type.Warning => Type.Warning.ToString(),
|
||||||
|
Type.Error => Type.Error.ToString(),
|
||||||
|
Type.Info => Type.Info.ToString(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the elapsed time since creating the notification.
|
||||||
|
/// </summary>
|
||||||
|
internal TimeSpan ElapsedTime => DateTime.Now - this.CreationTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the phase of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The phase of the notification.</returns>
|
||||||
|
internal Phase GetPhase()
|
||||||
|
{
|
||||||
|
var elapsed = (int)this.ElapsedTime.TotalMilliseconds;
|
||||||
|
|
||||||
|
if (elapsed > NotifyFadeInOutTime + this.DurationMs + NotifyFadeInOutTime)
|
||||||
|
return Phase.Expired;
|
||||||
|
else if (elapsed > NotifyFadeInOutTime + this.DurationMs)
|
||||||
|
return Phase.FadeOut;
|
||||||
|
else if (elapsed > NotifyFadeInOutTime)
|
||||||
|
return Phase.Wait;
|
||||||
|
else
|
||||||
|
return Phase.FadeIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the opacity of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The opacity, in a range from 0 to 1.</returns>
|
||||||
|
internal float GetFadePercent()
|
||||||
|
{
|
||||||
|
var phase = this.GetPhase();
|
||||||
|
var elapsed = this.ElapsedTime.TotalMilliseconds;
|
||||||
|
|
||||||
|
if (phase == Phase.FadeIn)
|
||||||
|
{
|
||||||
|
return (float)elapsed / NotifyFadeInOutTime * NotifyOpacity;
|
||||||
|
}
|
||||||
|
else if (phase == Phase.FadeOut)
|
||||||
|
{
|
||||||
|
return (1.0f - (((float)elapsed - NotifyFadeInOutTime - this.DurationMs) /
|
||||||
|
NotifyFadeInOutTime)) * NotifyOpacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f * NotifyOpacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1018,6 +1018,38 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
ImGui.Button("THIS IS A BUTTON###hoverTestButton");
|
ImGui.Button("THIS IS A BUTTON###hoverTestButton");
|
||||||
this.dalamud.InterfaceManager.OverrideGameCursor = !ImGui.IsItemHovered();
|
this.dalamud.InterfaceManager.OverrideGameCursor = !ImGui.IsItemHovered();
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
if (ImGui.Button("Add random notification"))
|
||||||
|
{
|
||||||
|
var rand = new Random();
|
||||||
|
|
||||||
|
var title = rand.Next(0, 5) switch
|
||||||
|
{
|
||||||
|
0 => "This is a toast",
|
||||||
|
1 => "Truly, a toast",
|
||||||
|
2 => "I am testing this toast",
|
||||||
|
3 => "I hope this looks right",
|
||||||
|
4 => "Good stuff",
|
||||||
|
5 => "Nice",
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
var type = rand.Next(0, 4) switch
|
||||||
|
{
|
||||||
|
0 => Notifications.Notification.Type.Error,
|
||||||
|
1 => Notifications.Notification.Type.Warning,
|
||||||
|
2 => Notifications.Notification.Type.Info,
|
||||||
|
3 => Notifications.Notification.Type.Success,
|
||||||
|
4 => Notifications.Notification.Type.None,
|
||||||
|
_ => Notifications.Notification.Type.None,
|
||||||
|
};
|
||||||
|
|
||||||
|
var text = "Bla bla bla bla bla bla bla bla bla bla bla.\nBla bla bla bla bla bla bla bla bla bla bla bla bla bla.";
|
||||||
|
|
||||||
|
this.dalamud.InterfaceManager.Notifications.AddNotification(text, title, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawTex()
|
private void DrawTex()
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,11 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
if (this.updatePluginCount > 0)
|
if (this.updatePluginCount > 0)
|
||||||
{
|
{
|
||||||
this.dalamud.PluginManager.PrintUpdatedPlugins(this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox);
|
this.dalamud.PluginManager.PrintUpdatedPlugins(this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox);
|
||||||
|
this.dalamud.InterfaceManager.Notifications.AddNotification($"Updates for {this.updatePluginCount} of your plugins were installed.", "Updates installed!", Notifications.Notification.Type.Success);
|
||||||
|
}
|
||||||
|
else if (this.updatePluginCount == 0)
|
||||||
|
{
|
||||||
|
this.dalamud.InterfaceManager.Notifications.AddNotification("No updates were found.", "No updates", Notifications.Notification.Type.Info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -682,7 +687,10 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
{
|
{
|
||||||
// There is no need to set as Complete for an individual plugin installation
|
// There is no need to set as Complete for an individual plugin installation
|
||||||
this.installStatus = OperationStatus.Idle;
|
this.installStatus = OperationStatus.Idle;
|
||||||
this.DisplayErrorContinuation(task, Locs.ErrorModal_InstallFail(manifest.Name));
|
if (this.DisplayErrorContinuation(task, Locs.ErrorModal_InstallFail(manifest.Name)))
|
||||||
|
{
|
||||||
|
this.dalamud.InterfaceManager.Notifications.AddNotification($"The plugin {manifest.Name} was successfully installed.", "Plugin installed!", Notifications.Notification.Type.Success);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue