Add NotificationsComponent
All checks were successful
Build / Compute Version (push) Successful in 6s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 1m36s
Build / Build Nuget package (NuGet) (push) Successful in 1m49s
Build / Build Nuget package (CringePlugins) (push) Successful in 3m10s
Build / Build Nuget package (SharedCringe) (push) Successful in 2m40s
Build / Build Launcher (push) Successful in 3m26s
All checks were successful
Build / Compute Version (push) Successful in 6s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 1m36s
Build / Build Nuget package (NuGet) (push) Successful in 1m49s
Build / Build Nuget package (CringePlugins) (push) Successful in 3m10s
Build / Build Nuget package (SharedCringe) (push) Successful in 2m40s
Build / Build Launcher (push) Successful in 3m26s
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Publicize Include="VRage:VRage.Plugins.MyPlugins.m_plugins" />
|
<Publicize Include="VRage:VRage.Plugins.MyPlugins.m_plugins" />
|
||||||
<Publicize Include="VRage:VRage.Plugins.MyPlugins.m_handleInputPlugins" />
|
<Publicize Include="VRage:VRage.Plugins.MyPlugins.m_handleInputPlugins" />
|
||||||
|
<Publicize Include="VRage.Render11:VRageRender.MyCommon" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
using NLog;
|
using CringePlugins.Ui;
|
||||||
|
using ImGuiNET;
|
||||||
|
using NLog;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
|
|
||||||
namespace CringePlugins.Loader;
|
namespace CringePlugins.Loader;
|
||||||
@@ -13,6 +15,8 @@ internal sealed class PluginWrapper(PluginMetadata metadata, IPlugin plugin) : I
|
|||||||
|
|
||||||
private readonly IHandleInputPlugin? _handleInputPlugin = plugin as IHandleInputPlugin;
|
private readonly IHandleInputPlugin? _handleInputPlugin = plugin as IHandleInputPlugin;
|
||||||
|
|
||||||
|
private const float ErrorShowTime = 10f;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -23,6 +27,7 @@ internal sealed class PluginWrapper(PluginMetadata metadata, IPlugin plugin) : I
|
|||||||
{
|
{
|
||||||
Log.Error(e, "Exception while Disposing {Metadata}", metadata);
|
Log.Error(e, "Exception while Disposing {Metadata}", metadata);
|
||||||
LastException = e;
|
LastException = e;
|
||||||
|
NotificationsComponent.SpawnNotification(ErrorShowTime, RenderError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +44,7 @@ internal sealed class PluginWrapper(PluginMetadata metadata, IPlugin plugin) : I
|
|||||||
{
|
{
|
||||||
Log.Error(e, "Exception while Updating {Metadata}", metadata);
|
Log.Error(e, "Exception while Updating {Metadata}", metadata);
|
||||||
LastException = e;
|
LastException = e;
|
||||||
|
NotificationsComponent.SpawnNotification(ErrorShowTime, RenderError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +58,7 @@ internal sealed class PluginWrapper(PluginMetadata metadata, IPlugin plugin) : I
|
|||||||
{
|
{
|
||||||
Log.Error(e, "Exception while Initializing {Metadata}", metadata);
|
Log.Error(e, "Exception while Initializing {Metadata}", metadata);
|
||||||
LastException = e;
|
LastException = e;
|
||||||
|
NotificationsComponent.SpawnNotification(ErrorShowTime, RenderError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +75,16 @@ internal sealed class PluginWrapper(PluginMetadata metadata, IPlugin plugin) : I
|
|||||||
{
|
{
|
||||||
Log.Error(e, "Exception while Updating {Metadata}", metadata);
|
Log.Error(e, "Exception while Updating {Metadata}", metadata);
|
||||||
LastException = e;
|
LastException = e;
|
||||||
|
NotificationsComponent.SpawnNotification(ErrorShowTime, RenderError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => metadata.ToString();
|
public override string ToString() => metadata.ToString();
|
||||||
|
|
||||||
|
private void RenderError()
|
||||||
|
{
|
||||||
|
ImGui.TextColored(new System.Numerics.Vector4(1f, 0f, 0f, 0f), "Error: ");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextWrapped($"Fatal error in {metadata.Name}: {LastException?.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -63,6 +63,9 @@ public class PluginsLifetime(string gameFolder) : ILoadingStage
|
|||||||
|
|
||||||
progress.Report("Loading plugins");
|
progress.Report("Loading plugins");
|
||||||
|
|
||||||
|
//we can move this, but it should be before plugin init
|
||||||
|
RenderHandler.Current.RegisterComponent(new NotificationsComponent());
|
||||||
|
|
||||||
await LoadPlugins(cachedPackages, sourceMapping, packagesConfig);
|
await LoadPlugins(cachedPackages, sourceMapping, packagesConfig);
|
||||||
|
|
||||||
RenderHandler.Current.RegisterComponent(new PluginListComponent(packagesConfig, sourceMapping, configPath, gameFolder, _plugins));
|
RenderHandler.Current.RegisterComponent(new PluginListComponent(packagesConfig, sourceMapping, configPath, gameFolder, _plugins));
|
||||||
|
102
CringePlugins/Ui/NotificationsComponent.cs
Normal file
102
CringePlugins/Ui/NotificationsComponent.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using CringePlugins.Abstractions;
|
||||||
|
using HarmonyLib;
|
||||||
|
using ImGuiNET;
|
||||||
|
using System.Numerics;
|
||||||
|
using VRage;
|
||||||
|
using VRageRender;
|
||||||
|
|
||||||
|
namespace CringePlugins.Ui;
|
||||||
|
internal class NotificationsComponent : IRenderComponent
|
||||||
|
{
|
||||||
|
private static int _globalNotificationId;
|
||||||
|
private const float TransitionTime = .5f;
|
||||||
|
private const float YPadding = 5f;
|
||||||
|
|
||||||
|
private static Vector2 _notificationSize = new(300, 75);
|
||||||
|
private static readonly List<NotificationInstruction> Notifications = [];
|
||||||
|
|
||||||
|
private static Size WindowSize => ((Form)MyVRage.Platform.Windows.Window).Size;
|
||||||
|
|
||||||
|
private static float _time;
|
||||||
|
|
||||||
|
public void OnFrame()
|
||||||
|
{
|
||||||
|
var y = 0f;
|
||||||
|
|
||||||
|
var lastY = _notificationSize.Y;
|
||||||
|
var viewportPos = ImGui.GetMainViewport().Pos;
|
||||||
|
|
||||||
|
|
||||||
|
//todo: consider adding a limit to the number of messages that can be displayed at once
|
||||||
|
for (var i = Notifications.Count; i-- > 0;)
|
||||||
|
{
|
||||||
|
var lerpMult = 0f;
|
||||||
|
var notification = Notifications[i];
|
||||||
|
var startDelta = _time - notification.StartTime;
|
||||||
|
var endDelta = _time - notification.HideTime;
|
||||||
|
|
||||||
|
var inTransition = true;
|
||||||
|
if (startDelta is > 0f and < TransitionTime)
|
||||||
|
{
|
||||||
|
lerpMult = startDelta / TransitionTime;
|
||||||
|
}
|
||||||
|
else if (endDelta is > 0f and < TransitionTime)
|
||||||
|
{
|
||||||
|
lerpMult = 1f - (endDelta / TransitionTime);
|
||||||
|
}
|
||||||
|
else if (startDelta > 0f && endDelta < 0f)
|
||||||
|
{
|
||||||
|
lerpMult = 1f;
|
||||||
|
inTransition = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
y += (lastY + YPadding) * EaseInOutCubic(lerpMult);
|
||||||
|
if (!inTransition && y < _notificationSize.Y + YPadding)
|
||||||
|
y = _notificationSize.Y + YPadding;
|
||||||
|
|
||||||
|
ImGui.SetNextWindowPos(new Vector2(WindowSize.Width, y * EaseInOutCubic(lerpMult)) - _notificationSize + viewportPos);
|
||||||
|
ImGui.SetNextWindowSize(new Vector2(_notificationSize.X, 0f));
|
||||||
|
|
||||||
|
Vector2 lastWinSize = Vector2.Zero;
|
||||||
|
|
||||||
|
if (ImGui.Begin($"notification-{notification.GlobalId}", ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.Tooltip))
|
||||||
|
{
|
||||||
|
notification.RenderCallback?.Invoke();
|
||||||
|
|
||||||
|
//todo: add indicator for when message will expire. probaby just want a rectangle at the bottom of the message
|
||||||
|
//var fraction = Math.Min(1f, (_time - notification.StartTime) / (notification.HideTime - notification.StartTime));
|
||||||
|
//ImGui.ProgressBar(fraction, new Vector2(_notificationSize.X, 10));
|
||||||
|
|
||||||
|
lastWinSize = ImGui.GetWindowSize();
|
||||||
|
|
||||||
|
ImGui.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastY = lastWinSize.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Notifications.RemoveAll(x => x.IsGarbage);
|
||||||
|
|
||||||
|
_time += MyCommon.GetLastFrameDelta();
|
||||||
|
}
|
||||||
|
public static void SpawnNotification(float showTime, Action renderCallback)
|
||||||
|
=> Notifications.Add(new NotificationInstruction(_globalNotificationId++, renderCallback, _time, _time + showTime));
|
||||||
|
public static void SpawnNotification(float showTime, string text)
|
||||||
|
=> Notifications.Add(new NotificationInstruction(_globalNotificationId++, () => ImGui.TextWrapped(text), _time, _time + showTime));
|
||||||
|
|
||||||
|
private static float EaseInOutCubic(float x) => x < 0.5f
|
||||||
|
? 4f * x * x * x
|
||||||
|
: 1f - MathF.Pow((-2f * x + 2f), 3f) / 2f;
|
||||||
|
|
||||||
|
private readonly struct NotificationInstruction(int id, Action renderCallback, float startTime, float hideTime)
|
||||||
|
{
|
||||||
|
public readonly int GlobalId = id;
|
||||||
|
public readonly Action RenderCallback = renderCallback;
|
||||||
|
public readonly float StartTime = startTime;
|
||||||
|
public readonly float HideTime = hideTime;
|
||||||
|
|
||||||
|
public readonly bool IsGarbage
|
||||||
|
=> _time > HideTime + TransitionTime;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user