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);
- }
- }
-}