From c287e288329d1a20ee1efc0e64f85ea930417695 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Sat, 17 Jul 2021 22:45:59 +0200 Subject: [PATCH] feat: add new ConsoleWindow --- .../Interface/Internal/DalamudInterface.cs | 12 +- .../Interface/Internal/InterfaceManager.cs | 12 + .../Interface/Internal/SerilogEventSink.cs | 12 +- .../Internal/Windows/ConsoleWindow.cs | 508 ++++++++++++++++++ .../Interface/Internal/Windows/LogWindow.cs | 175 ------ 5 files changed, 529 insertions(+), 190 deletions(-) create mode 100644 Dalamud/Interface/Internal/Windows/ConsoleWindow.cs delete mode 100644 Dalamud/Interface/Internal/Windows/LogWindow.cs diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index e7993914d..ba65b9bfe 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -29,7 +29,7 @@ namespace Dalamud.Interface.Internal private readonly CreditsWindow creditsWindow; private readonly DataWindow dataWindow; private readonly GamepadModeNotifierWindow gamepadModeNotifierWindow; - private readonly LogWindow logWindow; + private readonly ConsoleWindow consoleWindow; private readonly PluginStatWindow pluginStatWindow; private readonly PluginInstallerWindow pluginWindow; private readonly ScratchpadWindow scratchpadWindow; @@ -60,7 +60,7 @@ namespace Dalamud.Interface.Internal this.creditsWindow = new CreditsWindow(dalamud) { IsOpen = false }; this.dataWindow = new DataWindow(dalamud) { IsOpen = false }; this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow(); - this.logWindow = new LogWindow(dalamud) { IsOpen = this.dalamud.Configuration.LogOpenAtStartup }; + this.consoleWindow = new ConsoleWindow { IsOpen = this.dalamud.Configuration.LogOpenAtStartup }; this.pluginStatWindow = new PluginStatWindow(dalamud) { IsOpen = false }; this.pluginWindow = new PluginInstallerWindow(dalamud) { IsOpen = false }; this.scratchpadWindow = new ScratchpadWindow(dalamud) { IsOpen = false }; @@ -72,7 +72,7 @@ namespace Dalamud.Interface.Internal this.windowSystem.AddWindow(this.creditsWindow); this.windowSystem.AddWindow(this.dataWindow); this.windowSystem.AddWindow(this.gamepadModeNotifierWindow); - this.windowSystem.AddWindow(this.logWindow); + this.windowSystem.AddWindow(this.consoleWindow); this.windowSystem.AddWindow(this.pluginStatWindow); this.windowSystem.AddWindow(this.pluginWindow); this.windowSystem.AddWindow(this.scratchpadWindow); @@ -105,7 +105,7 @@ namespace Dalamud.Interface.Internal this.windowSystem.RemoveAllWindows(); this.creditsWindow.Dispose(); - this.logWindow.Dispose(); + this.consoleWindow.Dispose(); this.scratchpadWindow.Dispose(); } @@ -157,7 +157,7 @@ namespace Dalamud.Interface.Internal /// /// Opens the . /// - public void OpenLogWindow() => this.logWindow.IsOpen = true; + public void OpenLogWindow() => this.consoleWindow.IsOpen = true; /// /// Opens the . @@ -229,7 +229,7 @@ namespace Dalamud.Interface.Internal /// /// Toggles the . /// - public void ToggleLogWindow() => this.logWindow.Toggle(); + public void ToggleLogWindow() => this.consoleWindow.Toggle(); /// /// Toggles the . diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index e4745887f..b4c51d275 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -140,6 +140,11 @@ namespace Dalamud.Interface.Internal /// public static ImFontPtr IconFont { get; private set; } + /// + /// Gets an included monospaced font. + /// + public static ImFontPtr MonoFont { get; private set; } + /// /// Gets or sets an action that is exexuted when fonts are rebuilt. /// @@ -464,6 +469,13 @@ namespace Dalamud.Interface.Internal GCHandleType.Pinned); IconFont = ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathIcon, 17.0f, null, iconRangeHandle.AddrOfPinnedObject()); + var fontPathMono = Path.Combine(this.dalamud.AssetDirectory.FullName, "UIRes", "Inconsolata-Regular.ttf"); + + if (!File.Exists(fontPathMono)) + ShowFontError(fontPathMono); + + MonoFont = ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathMono, 16.0f); + Log.Verbose("[FONT] Invoke OnBuildFonts"); this.OnBuildFonts?.Invoke(); Log.Verbose("[FONT] OnBuildFonts OK!"); diff --git a/Dalamud/Interface/Internal/SerilogEventSink.cs b/Dalamud/Interface/Internal/SerilogEventSink.cs index 013463725..ac2c522ba 100644 --- a/Dalamud/Interface/Internal/SerilogEventSink.cs +++ b/Dalamud/Interface/Internal/SerilogEventSink.cs @@ -25,12 +25,7 @@ namespace Dalamud.Interface.Internal /// /// Event on a log line being emitted. /// - public event EventHandler<(string Line, LogEventLevel Level)> OnLogLine; - - /// - /// Event on a log line being emitted. - /// - public event EventHandler OnLogEvent; + public event EventHandler<(string Line, LogEventLevel Level, DateTimeOffset TimeStamp)> OnLogLine; /// /// Gets the default instance. @@ -43,15 +38,14 @@ namespace Dalamud.Interface.Internal /// Log event to be emitted. public void Emit(LogEvent logEvent) { - var message = $"[{DateTimeOffset.Now:HH:mm:ss.fff}][{logEvent.Level}] {logEvent.RenderMessage(this.formatProvider)}"; + var message = logEvent.RenderMessage(this.formatProvider); if (logEvent.Exception != null) { message += "\n" + logEvent.Exception; } - this.OnLogEvent?.Invoke(this, logEvent); - this.OnLogLine?.Invoke(this, (message, logEvent.Level)); + this.OnLogLine?.Invoke(this, (message, logEvent.Level, logEvent.Timestamp)); } } } diff --git a/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs new file mode 100644 index 000000000..2f1529650 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs @@ -0,0 +1,508 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Text; + +using Dalamud.Configuration.Internal; +using Dalamud.Game.Command; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Windowing; +using ImGuiNET; +using Serilog; +using Serilog.Events; + +namespace Dalamud.Interface.Internal.Windows +{ + /// + /// The window that displays the Dalamud log file in-game. + /// + internal class ConsoleWindow : Window, IDisposable + { + private readonly List logText = new(); + private readonly object renderLock = new(); + + private readonly string[] logLevelStrings = new[] { "None", "Verbose", "Debug", "Information", "Warning", "Error", "Fatal" }; + + private List filteredLogText = new(); + private bool autoScroll; + private bool openAtStartup; + + private bool? lastCmdSuccess; + + private string commandText = string.Empty; + + private string textFilter = string.Empty; + private LogEventLevel? levelFilter = null; + private bool isFiltered = false; + + private int historyPos; + private List history = new(); + + /// + /// Initializes a new instance of the class. + /// + /// The Dalamud instance. + public ConsoleWindow() + : base("Dalamud Console") + { + this.autoScroll = Dalamud.Instance.Configuration.LogAutoScroll; + this.openAtStartup = Dalamud.Instance.Configuration.LogOpenAtStartup; + SerilogEventSink.Instance.OnLogLine += this.OnLogLine; + + this.Size = new Vector2(500, 400); + this.SizeCondition = ImGuiCond.FirstUseEver; + } + + private List LogEntries => this.isFiltered ? this.filteredLogText : this.logText; + + /// + /// Dispose of managed and unmanaged resources. + /// + public void Dispose() + { + SerilogEventSink.Instance.OnLogLine -= this.OnLogLine; + } + + /// + /// Clear the window of all log entries. + /// + public void Clear() + { + lock (this.renderLock) + { + this.logText.Clear(); + this.filteredLogText.Clear(); + } + } + + /// + /// Add a single log line to the display. + /// + /// The line to add. + /// The level of the event. + /// The of the event. + public void HandleLogLine(string line, LogEventLevel level, DateTimeOffset offset) + { + if (line.IndexOfAny(new[] { '\n', '\r' }) != -1) + { + var subLines = line.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); + + this.AddAndFilter(subLines[0], level, offset, false); + + for (var i = 1; i < subLines.Length; i++) + { + this.AddAndFilter(subLines[i], level, offset, true); + } + } + else + { + this.AddAndFilter(line, level, offset, false); + } + } + + /// + public override void Draw() + { + // Options menu + if (ImGui.BeginPopup("Options")) + { + if (ImGui.Checkbox("Auto-scroll", ref this.autoScroll)) + { + Dalamud.Instance.Configuration.LogAutoScroll = this.autoScroll; + Dalamud.Instance.Configuration.Save(); + } + + if (ImGui.Checkbox("Open at startup", ref this.openAtStartup)) + { + Dalamud.Instance.Configuration.LogOpenAtStartup = this.openAtStartup; + Dalamud.Instance.Configuration.Save(); + } + + var prevLevel = (int)Dalamud.Instance.LogLevelSwitch.MinimumLevel; + if (ImGui.Combo("Log Level", ref prevLevel, Enum.GetValues(typeof(LogEventLevel)).Cast().Select(x => x.ToString()).ToArray(), 6)) + { + Dalamud.Instance.LogLevelSwitch.MinimumLevel = (LogEventLevel)prevLevel; + Dalamud.Instance.Configuration.LogLevel = (LogEventLevel)prevLevel; + Dalamud.Instance.Configuration.Save(); + } + + /* TEST */ + + if (ImGui.Button("Multiline test")) + { + Log.Verbose("This is a test\nTruly a multi-line test\nas it has always been.\nThank you."); + } + + if (ImGui.Button("Single line test")) + { + Log.Debug("This is a test"); + } + + if (ImGui.Button("WARNING!!")) + { + Log.Warning("I want to warn you"); + } + + if (ImGui.Button("Add 50000")) + { + for (var i = 0; i < 50000; i++) + Log.Debug("TEST " + i); + } + + if (ImGui.Button("Exception")) + { + try + { + var a = 0; + var b = 1; + var c = b / a; + } + catch (Exception e) + { + Log.Error(e, "There has been a serious problem"); + } + } + + ImGui.EndPopup(); + } + + // Filter menu + if (ImGui.BeginPopup("Filters")) + { + ImGui.Checkbox("Enabled", ref this.isFiltered); + + if (ImGui.InputTextWithHint("##filterText", "Text Filter", ref this.textFilter, 255, ImGuiInputTextFlags.EnterReturnsTrue)) + { + this.Refilter(); + } + + ImGui.TextColored(ImGuiColors.DalamudGrey, "Enter to confirm."); + + var filterVal = this.levelFilter.HasValue ? (int)this.levelFilter.Value + 1 : 0; + if (ImGui.Combo("Level", ref filterVal, this.logLevelStrings, 7)) + { + this.levelFilter = (LogEventLevel)(filterVal - 1); + this.Refilter(); + } + + ImGui.EndPopup(); + } + + ImGui.SameLine(); + ImGui.PushFont(InterfaceManager.IconFont); + + if (ImGui.Button(FontAwesomeIcon.Cog.ToIconString())) + ImGui.OpenPopup("Options"); + + ImGui.SameLine(); + if (ImGui.Button(FontAwesomeIcon.Search.ToIconString())) + ImGui.OpenPopup("Filters"); + + ImGui.SameLine(); + var clear = ImGui.Button(FontAwesomeIcon.Trash.ToIconString()); + + ImGui.SameLine(); + var copy = ImGui.Button(FontAwesomeIcon.Copy.ToIconString()); + + ImGui.PopFont(); + + ImGui.BeginChild("scrolling", new Vector2(0, ImGui.GetFrameHeightWithSpacing() - 55), false, ImGuiWindowFlags.HorizontalScrollbar); + + if (clear) + { + this.Clear(); + } + + if (copy) + { + ImGui.LogToClipboard(); + } + + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); + + ImGuiListClipperPtr clipper; + unsafe + { + clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper()); + } + + ImGui.PushFont(InterfaceManager.MonoFont); + + var childPos = ImGui.GetWindowPos(); + var childDrawList = ImGui.GetWindowDrawList(); + var childSize = ImGui.GetWindowSize(); + + lock (this.renderLock) + { + clipper.Begin(this.LogEntries.Count); + while (clipper.Step()) + { + for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + var line = this.LogEntries[i]; + + if (!line.IsMultiline) + ImGui.Separator(); + + ImGui.PushStyleColor(ImGuiCol.Header, this.GetColorForLogEventLevel(line.Level)); + ImGui.PushStyleColor(ImGuiCol.HeaderActive, this.GetColorForLogEventLevel(line.Level)); + ImGui.PushStyleColor(ImGuiCol.HeaderHovered, this.GetColorForLogEventLevel(line.Level)); + + ImGui.Selectable("###consolenull", true, ImGuiSelectableFlags.AllowItemOverlap | ImGuiSelectableFlags.SpanAllColumns); + ImGui.SameLine(); + + ImGui.PopStyleColor(3); + + if (!line.IsMultiline) + { + ImGui.TextUnformatted(line.TimeStamp.ToString("HH:mm:ss.fff")); + ImGui.SameLine(); + ImGui.SetCursorPosX(92); + ImGui.TextUnformatted("|"); + ImGui.SameLine(); + ImGui.SetCursorPosX(100); + ImGui.TextUnformatted(this.GetTextForLogEventLevel(line.Level)); + ImGui.SameLine(); + } + + ImGui.SetCursorPosX(135); + ImGui.TextUnformatted(line.Line); + } + } + + clipper.End(); + } + + ImGui.PopFont(); + + ImGui.PopStyleVar(); + + if (this.autoScroll && ImGui.GetScrollY() >= ImGui.GetScrollMaxY()) + { + ImGui.SetScrollHereY(1.0f); + } + + // Draw dividing line + childDrawList.AddLine(new Vector2(childPos.X + 127, childPos.Y), new Vector2(childPos.X + 127, childPos.Y + childSize.Y), 0x4FFFFFFF, 1.0f); + + ImGui.EndChild(); + + var hadColor = false; + if (this.lastCmdSuccess.HasValue) + { + hadColor = true; + if (this.lastCmdSuccess.Value) + { + ImGui.PushStyleColor(ImGuiCol.FrameBg, ImGuiColors.HealerGreen - new Vector4(0, 0, 0, 0.7f)); + } + else + { + ImGui.PushStyleColor(ImGuiCol.FrameBg, ImGuiColors.DalamudRed - new Vector4(0, 0, 0, 0.7f)); + } + } + + ImGui.SetNextItemWidth(ImGui.GetWindowSize().X - 80); + + var getFocus = false; + unsafe + { + if (ImGui.InputText("##commandbox", ref this.commandText, 255, ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.CallbackCompletion | ImGuiInputTextFlags.CallbackHistory, this.CommandInputCallback)) + { + this.ProcessCommand(); + getFocus = true; + } + + ImGui.SameLine(); + } + + ImGui.SetItemDefaultFocus(); + if (getFocus) + ImGui.SetKeyboardFocusHere(-1); // Auto focus previous widget + + + if (hadColor) + ImGui.PopStyleColor(); + + if (ImGui.Button("Send")) + { + this.ProcessCommand(); + } + } + + private void ProcessCommand() + { + try + { + this.historyPos = -1; + for (int i = this.history.Count - 1; i >= 0; i--) + { + if (this.history[i] == this.commandText) + { + this.history.RemoveAt(i); + break; + } + } + + this.history.Add(this.commandText); + + if (this.commandText == "clear" || this.commandText == "cls") + { + this.Clear(); + return; + } + + this.lastCmdSuccess = Dalamud.Instance.CommandManager.ProcessCommand("/" + this.commandText); + this.commandText = string.Empty; + + //TODO: Force scroll to bottom + } + catch (Exception ex) + { + Log.Error(ex, "Error during command dispatch"); + this.lastCmdSuccess = false; + } + } + + private unsafe int CommandInputCallback(ImGuiInputTextCallbackData* data) + { + var ptr = new ImGuiInputTextCallbackDataPtr(data); + + switch (data->EventFlag) + { + case ImGuiInputTextFlags.CallbackCompletion: + var textBytes = new byte[data->BufTextLen]; + Marshal.Copy((IntPtr)data->Buf, textBytes, 0, data->BufTextLen); + var text = Encoding.UTF8.GetString(textBytes); + + var words = text.Split(); + + // We can't do any completion for parameters at the moment since it just calls into CommandHandler + if (words.Length > 1) + return 0; + + // TODO: Improve this, add partial completion + // https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp#L6443-L6484 + var candidates = Dalamud.Instance.CommandManager.Commands.Where(x => x.Key.Contains("/" + words[0])).ToList(); + if (candidates.Count > 0) + { + ptr.DeleteChars(0, ptr.BufTextLen); + ptr.InsertChars(0, candidates[0].Key.Replace("/", string.Empty)); + } + + break; + case ImGuiInputTextFlags.CallbackHistory: + var prevPos = this.historyPos; + + if (ptr.EventKey == ImGuiKey.UpArrow) + { + if (this.historyPos == -1) + this.historyPos = this.history.Count - 1; + else if (this.historyPos > 0) + this.historyPos--; + } + else if (data->EventKey == ImGuiKey.DownArrow) + { + if (this.historyPos != -1) + { + if (++this.historyPos >= this.history.Count) + { + this.historyPos = -1; + } + } + } + + if (prevPos != this.historyPos) + { + var historyStr = this.historyPos >= 0 ? this.history[this.historyPos] : string.Empty; + + ptr.DeleteChars(0, ptr.BufTextLen); + ptr.InsertChars(0, historyStr); + } + + break; + } + + return 0; + } + + private void AddAndFilter(string line, LogEventLevel level, DateTimeOffset offset, bool isMultiline) + { + var entry = new LogEntry + { + IsMultiline = isMultiline, + Level = level, + Line = line, + TimeStamp = offset, + }; + + this.logText.Add(entry); + + if (!this.isFiltered) + return; + + if (this.IsFilterApplicable(entry)) + this.filteredLogText.Add(entry); + } + + private bool IsFilterApplicable(LogEntry entry) + { + if (this.levelFilter.HasValue) + { + return entry.Level == this.levelFilter.Value; + } + + if (!string.IsNullOrEmpty(this.textFilter)) + return entry.Line.Contains(this.textFilter); + + return true; + } + + private void Refilter() + { + lock (this.renderLock) + { + this.filteredLogText = this.logText.Where(this.IsFilterApplicable).ToList(); + } + } + + private string GetTextForLogEventLevel(LogEventLevel level) => level switch + { + LogEventLevel.Error => "ERR", + LogEventLevel.Verbose => "VRB", + LogEventLevel.Debug => "DBG", + LogEventLevel.Information => "INF", + LogEventLevel.Warning => "WRN", + LogEventLevel.Fatal => "FTL", + _ => throw new ArgumentOutOfRangeException(level.ToString(), "Invalid LogEventLevel"), + }; + + private uint GetColorForLogEventLevel(LogEventLevel level) => level switch + { + LogEventLevel.Error => 0x800000EE, + LogEventLevel.Verbose => 0x00000000, + LogEventLevel.Debug => 0x00000000, + LogEventLevel.Information => 0x00000000, + LogEventLevel.Warning => 0x8A0070EE, + LogEventLevel.Fatal => 0xFF00000A, + _ => throw new ArgumentOutOfRangeException(level.ToString(), "Invalid LogEventLevel"), + }; + + private void OnLogLine(object sender, (string Line, LogEventLevel Level, DateTimeOffset Offset) logEvent) + { + this.HandleLogLine(logEvent.Line, logEvent.Level, logEvent.Offset); + } + + private class LogEntry + { + public string Line { get; set; } + + public LogEventLevel Level { get; set; } + + public DateTimeOffset TimeStamp { get; set; } + + public bool IsMultiline { get; set; } + } + } +} diff --git a/Dalamud/Interface/Internal/Windows/LogWindow.cs b/Dalamud/Interface/Internal/Windows/LogWindow.cs deleted file mode 100644 index 305ff1676..000000000 --- a/Dalamud/Interface/Internal/Windows/LogWindow.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; - -using Dalamud.Configuration.Internal; -using Dalamud.Game.Command; -using Dalamud.Interface.Colors; -using Dalamud.Interface.Windowing; -using ImGuiNET; -using Serilog; -using Serilog.Events; - -namespace Dalamud.Interface.Internal.Windows -{ - /// - /// The window that displays the Dalamud log file in-game. - /// - internal class LogWindow : Window, IDisposable - { - private readonly CommandManager commandManager; - private readonly DalamudConfiguration configuration; - private readonly List<(string Line, Vector4 Color)> logText = new(); - private readonly object renderLock = new(); - private bool autoScroll; - private bool openAtStartup; - - private string commandText = string.Empty; - - /// - /// Initializes a new instance of the class. - /// - /// The Dalamud instance. - public LogWindow(Dalamud dalamud) - : base("Dalamud LOG") - { - this.commandManager = dalamud.CommandManager; - this.configuration = dalamud.Configuration; - this.autoScroll = this.configuration.LogAutoScroll; - this.openAtStartup = this.configuration.LogOpenAtStartup; - SerilogEventSink.Instance.OnLogLine += this.OnLogLine; - - this.Size = new Vector2(500, 400); - this.SizeCondition = ImGuiCond.FirstUseEver; - } - - /// - /// Dispose of managed and unmanaged resources. - /// - public void Dispose() - { - SerilogEventSink.Instance.OnLogLine -= this.OnLogLine; - } - - /// - /// Clear the window of all log entries. - /// - public void Clear() - { - lock (this.renderLock) - { - this.logText.Clear(); - } - } - - /// - /// Add a single log line to the display. - /// - /// The line to add. - /// The line coloring. - public void AddLog(string line, Vector4 color) - { - lock (this.renderLock) - { - this.logText.Add((line, color)); - } - } - - /// - public override void Draw() - { - // Options menu - if (ImGui.BeginPopup("Options")) - { - if (ImGui.Checkbox("Auto-scroll", ref this.autoScroll)) - { - this.configuration.LogAutoScroll = this.autoScroll; - this.configuration.Save(); - } - - if (ImGui.Checkbox("Open at startup", ref this.openAtStartup)) - { - this.configuration.LogOpenAtStartup = this.openAtStartup; - this.configuration.Save(); - } - - ImGui.EndPopup(); - } - - // Main window - if (ImGui.Button("Options")) - ImGui.OpenPopup("Options"); - - ImGui.SameLine(); - var clear = ImGui.Button("Clear"); - - ImGui.SameLine(); - var copy = ImGui.Button("Copy"); - - ImGui.Text("Enter command: "); - ImGui.SameLine(); - - ImGui.InputText("##commandbox", ref this.commandText, 255); - ImGui.SameLine(); - - if (ImGui.Button("Send")) - { - if (this.commandManager.ProcessCommand(this.commandText)) - { - Log.Information("Command was dispatched."); - } - else - { - Log.Information($"Command {this.commandText} is not registered."); - } - } - - ImGui.BeginChild("scrolling", new Vector2(0, 0), false, ImGuiWindowFlags.HorizontalScrollbar); - - if (clear) - { - this.Clear(); - } - - if (copy) - { - ImGui.LogToClipboard(); - } - - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); - - lock (this.renderLock) - { - foreach (var (line, color) in this.logText) - { - ImGui.TextColored(color, line); - } - } - - ImGui.PopStyleVar(); - - if (this.autoScroll && ImGui.GetScrollY() >= ImGui.GetScrollMaxY()) - { - ImGui.SetScrollHereY(1.0f); - } - - ImGui.EndChild(); - } - - private void OnLogLine(object sender, (string Line, LogEventLevel Level) logEvent) - { - var color = logEvent.Level switch - { - LogEventLevel.Error => ImGuiColors.DalamudRed, - LogEventLevel.Verbose => ImGuiColors.DalamudWhite, - LogEventLevel.Debug => ImGuiColors.DalamudWhite2, - LogEventLevel.Information => ImGuiColors.DalamudWhite, - LogEventLevel.Warning => ImGuiColors.DalamudOrange, - LogEventLevel.Fatal => ImGuiColors.DalamudRed, - _ => throw new ArgumentOutOfRangeException(logEvent.Level.ToString(), "Invalid LogEventLevel"), - }; - - this.AddLog(logEvent.Line, color); - } - } -}