diff --git a/Penumbra/Import/Models/IoNotifier.cs b/Penumbra/Import/Models/IoNotifier.cs new file mode 100644 index 00000000..e1d649f6 --- /dev/null +++ b/Penumbra/Import/Models/IoNotifier.cs @@ -0,0 +1,52 @@ +using Dalamud.Interface.Internal.Notifications; +using OtterGui.Classes; + +namespace Penumbra.Import.Models; + +public record class IoNotifier +{ + /// Notification subclass so that we have a distinct type to filter by. + private class LegallyDistinctNotification : Notification + { + public LegallyDistinctNotification(string content, NotificationType type): base(content, type) + {} + } + + private readonly DateTime _startTime = DateTime.UtcNow; + private string _context = ""; + + /// Create a new notifier with the specified context appended to any other context already present. + public IoNotifier WithContext(string context) + => this with { _context = $"{_context}{context}: "}; + + /// Send a warning with any current context to notification channels. + public void Warning(string content) + => SendNotification(content, NotificationType.Warning); + + /// Get the current warnings for this notifier. + /// This does not currently filter to notifications with the current notifier's context - it will return all IO notifications from all notifiers. + public IEnumerable GetWarnings() + => GetFilteredNotifications(NotificationType.Warning); + + /// Create an exception with any current context. + [StackTraceHidden] + public Exception Exception(string message) + => Exception(message); + + /// Create an exception of the provided type with any current context. + [StackTraceHidden] + public TException Exception(string message) + where TException : Exception, new() + => (TException)Activator.CreateInstance(typeof(TException), $"{_context}{message}")!; + + private void SendNotification(string message, NotificationType type) + => Penumbra.Messager.AddMessage( + new LegallyDistinctNotification($"{_context}{message}", type), + true, false, true, false + ); + + private IEnumerable GetFilteredNotifications(NotificationType type) + => Penumbra.Messager + .Where(p => p.Key >= _startTime && p.Value is LegallyDistinctNotification && p.Value.NotificationType == type) + .Select(p => p.Value.PrintMessage); +} diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs index 26fcd1ee..15c6cb21 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs @@ -25,6 +25,7 @@ public partial class ModEditWindow private bool _dirty; public bool PendingIo { get; private set; } public List IoExceptions { get; private set; } = []; + public List IoWarnings { get; private set; } = []; public MdlTab(ModEditWindow edit, byte[] bytes, string path) { diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index ad609285..1a200fdf 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -63,6 +63,7 @@ public partial class ModEditWindow DrawExport(tab, childSize, disabled); DrawIoExceptions(tab); + DrawIoWarnings(tab); } private void DrawImport(MdlTab tab, Vector2 size, bool _1) @@ -148,7 +149,43 @@ public partial class ModEditWindow using var exceptionNode = ImRaii.TreeNode(message); if (exceptionNode) + { + ImGui.Dummy(new Vector2(ImGui.GetStyle().IndentSpacing, 0)); + ImGui.SameLine(); ImGuiUtil.TextWrapped(exception.ToString()); + } + } + } + + private static void DrawIoWarnings(MdlTab tab) + { + if (tab.IoWarnings.Count == 0) + return; + + var size = new Vector2(ImGui.GetContentRegionAvail().X, 0); + using var frame = ImRaii.FramedGroup("Warnings", size, headerPreIcon: FontAwesomeIcon.ExclamationCircle, borderColor: 0xFF40FFFF); + + var spaceAvail = ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X - 100; + foreach (var (warning, index) in tab.IoWarnings.WithIndex()) + { + using var id = ImRaii.PushId(index); + var textSize = ImGui.CalcTextSize(warning).X; + + if (textSize <= spaceAvail) + { + ImRaii.TreeNode(warning, ImGuiTreeNodeFlags.Leaf).Dispose(); + continue; + } + + var firstLine = warning[..(int)Math.Floor(warning.Length * (spaceAvail / textSize))] + "..."; + + using var warningNode = ImRaii.TreeNode(firstLine); + if (warningNode) + { + ImGui.Dummy(new Vector2(ImGui.GetStyle().IndentSpacing, 0)); + ImGui.SameLine(); + ImGuiUtil.TextWrapped(warning.ToString()); + } } }