From 4701bb3f6d2de07341cb9d8c40ae6bbd35834ad4 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Tue, 9 Apr 2024 00:49:15 +1000 Subject: [PATCH 01/12] feat: clickable URLs in chat log Adds a parsing step when constructing `Message` objects that scans the message content for anything that looks URL-like, and inserts new `TextChunk`s into the message content with a URIPayload set. Hovering over a URL shows an on-hover effect. Clicking a URL opens it in the default browser. Right clicking shows the hostname, with an option to open and an option to copy the URL to the clipboard. --- .editorconfig | 6 ++ ChatTwo/Chunk.cs | 13 ++++ ChatTwo/Message.cs | 89 +++++++++++++++++++++++++- ChatTwo/PayloadHandler.cs | 54 ++++++++++++++++ ChatTwo/Resources/Language.Designer.cs | 54 ++++++++++++++++ ChatTwo/Resources/Language.resx | 18 ++++++ ChatTwo/Store.cs | 6 ++ ChatTwo/Util/ChunkUtil.cs | 5 ++ ChatTwo/Util/ImGuiUtil.cs | 6 +- ChatTwo/Util/Payloads.cs | 48 ++++++++++++++ 10 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ca3df2c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*] +indent_style = space +tab_width = 4 +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/ChatTwo/Chunk.cs b/ChatTwo/Chunk.cs index d88c263..fd755ba 100755 --- a/ChatTwo/Chunk.cs +++ b/ChatTwo/Chunk.cs @@ -59,6 +59,19 @@ internal class TextChunk : Chunk { public TextChunk() : base(ChunkSource.None, null) { } #pragma warning restore CS8618 + + /// + /// Creates a new TextChunk with identical styling to this one. + /// + public TextChunk NewWithStyle(ChunkSource source, Payload? link, string content) + { + return new TextChunk(source, link, content) { + FallbackColour = this.FallbackColour, + Foreground = this.Foreground, + Glow = this.Glow, + Italic = this.Italic, + }; + } } internal class IconChunk : Chunk { diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index bcba4ce..f0cc9fc 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -1,7 +1,9 @@ using ChatTwo.Code; +using ChatTwo.Util; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using LiteDB; +using System.Text.RegularExpressions; namespace ChatTwo; @@ -70,7 +72,7 @@ internal class Message { this.Date = DateTime.UtcNow; this.Code = code; this.Sender = sender; - this.Content = content; + this.Content = ReplaceContentURLs(content); this.SenderSource = senderSource; this.ContentSource = contentSource; this.SortCode = new SortCode(this.Code.Type, this.Code.Source); @@ -89,6 +91,8 @@ internal class Message { this.Date = date; this.Code = BsonMapper.Global.ToObject(code); this.Sender = BsonMapper.Global.Deserialize>(sender); + // Don't call ReplaceContentURLs here since we're loading the message + // from the database and it should already have parsed URL data. this.Content = BsonMapper.Global.Deserialize>(content); this.SenderSource = BsonMapper.Global.Deserialize(senderSource); this.ContentSource = BsonMapper.Global.Deserialize(contentSource); @@ -108,6 +112,8 @@ internal class Message { this.Date = date; this.Code = BsonMapper.Global.ToObject(code); this.Sender = BsonMapper.Global.Deserialize>(sender); + // Don't call ReplaceContentURLs here since we're loading the message + // from the database and it should already have parsed URL data. this.Content = BsonMapper.Global.Deserialize>(content); this.SenderSource = BsonMapper.Global.Deserialize(senderSource); this.ContentSource = BsonMapper.Global.Deserialize(contentSource); @@ -138,4 +144,85 @@ internal class Message { return Guid.Empty; } + + /// + /// URLRegex returns a regex object that matches URLs like: + /// - https://example.com + /// - http://example.com + /// - www.example.com + /// - https://sub.example.com + /// - example.com + /// - sub.example.com + /// + /// It matches URLs with www. or https:// prefix, and also matches URLs + /// without a prefix on specific TLDs. + /// + private static Regex URLRegex = new Regex( + @"((https?:\/\/|www\.)[a-z0-9-]+(\.[a-z0-9-]+)*|([a-z0-9-]+(\.[a-z0-9-]+)*\.(com|net|org|co|io|app)))(:[\d]{1,5})?(\/[^\s]+)?", + RegexOptions.Compiled | RegexOptions.IgnoreCase + ); + + /// + /// Finds all URL strings in all TextChunks, splits the parent TextChunk + /// apart and inserts a new TextChunk with a URIPayload. + /// + private List ReplaceContentURLs(List content) + { + var newChunks = new List(); + void AddChunkWithMessage(Chunk chunk) { + chunk.Message = this; + newChunks.Add(chunk); + } + + foreach (var chunk in content) + { + // Use as is if it's not a text chunk or it already has a payload. + if (chunk is not TextChunk text || chunk.Link != null) + { + // No need to call AddChunkWithMessage here since the chunk + // already has the Message field set. + newChunks.Add(chunk); + continue; + } + + // Find all URLs with the regex and insert a new TextChunk with a + // URIPayload. + var matches = URLRegex.Matches(text.Content); + var remainderIndex = 0; + foreach (Match match in matches.Cast()) + { + // Add the text before the URL. + if (match.Index > remainderIndex) + { + AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, text.Content[remainderIndex..match.Index])); + } + + // Update the remainder index. + remainderIndex = match.Index + match.Length; + + // Create a new TextChunk with a URIPayload for the URL text. + try + { + var link = URIPayload.ResolveURI(match.Value); + AddChunkWithMessage(text.NewWithStyle(chunk.Source, link, match.Value)); + } + catch (UriFormatException) + { + Plugin.Log.Debug($"Invalid URL accepted by Regex but failed URI parsing: '{match.Value}'"); + // If the URL is invalid, set the remainder index to the + // beginning of the match so it'll get included in the next + // regular text chunk. + remainderIndex = match.Index; + } + } + + // Add the text after the last URL. + if (remainderIndex < text.Content.Length) + { + AddChunkWithMessage(text.NewWithStyle(chunk.Source, null, text.Content[remainderIndex..])); + } + } + + return newChunks; + } } diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index a971615..e448125 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -21,6 +21,7 @@ using Lumina.Excel.GeneratedSheets; using Action = System.Action; using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload; using ChatTwoPartyFinderPayload = ChatTwo.Util.PartyFinderPayload; +using System.Diagnostics; namespace ChatTwo; @@ -86,6 +87,11 @@ public sealed class PayloadHandler { drawn = true; break; } + case URIPayload uri: { + DrawUriPopup(uri); + drawn = true; + break; + } } ContextFooter(drawn, chunk); @@ -215,6 +221,11 @@ public sealed class PayloadHandler { DoHover(() => HoverItem(item), hoverSize); break; } + case URIPayload uri: + { + DoHover(() => HoverURI(uri), hoverSize); + break; + } } } @@ -334,6 +345,11 @@ public sealed class PayloadHandler { } } + private void HoverURI(URIPayload uri) { + ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority)); + ImGuiUtil.WarningText(Language.Context_URLWarning); + } + private void LeftClickPayload(Chunk chunk, Payload? payload) { switch (payload) { case MapLinkPayload map: { @@ -372,6 +388,10 @@ public sealed class PayloadHandler { break; } + case URIPayload uri: { + TryOpenURI(uri.Uri); + break; + } } } @@ -625,4 +645,38 @@ public sealed class PayloadHandler { return null; } + + private void DrawUriPopup(URIPayload uri) + { + ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority)); + ImGuiUtil.WarningText(Language.Context_URLWarning, false); + ImGui.Separator(); + + if (ImGui.Selectable(Language.Context_OpenInBrowser)) + { + TryOpenURI(uri.Uri); + } + + if (ImGui.Selectable(Language.Context_CopyLink)) + { + ImGui.SetClipboardText(uri.Uri.ToString()); + WrapperUtil.AddNotification(Language.Context_CopyLinkNotification, NotificationType.Info); + } + } + + private void TryOpenURI(Uri uri) + { + new Thread(() => { + try + { + Plugin.Log.Info($"Opening URI {uri} in default browser"); + Process.Start(new ProcessStartInfo(uri.ToString()) { UseShellExecute = true }); + } + catch (Exception ex) + { + Plugin.Log.Error($"Error opening URI: {ex}"); + WrapperUtil.AddNotification(Language.Context_OpenInBrowserError, NotificationType.Error); + } + }).Start(); + } } diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index 2412b26..deda984 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1040,6 +1040,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Copy link to clipboard. + /// + internal static string Context_CopyLink { + get { + return ResourceManager.GetString("Context_CopyLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copied link to clipboard. + /// + internal static string Context_CopyLinkNotification { + get { + return ResourceManager.GetString("Context_CopyLinkNotification", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide chat. /// @@ -1121,6 +1139,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Open link in browser. + /// + internal static string Context_OpenInBrowser { + get { + return ResourceManager.GetString("Context_OpenInBrowser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to open the link in the browser, please report this issue. + /// + internal static string Context_OpenInBrowserError { + get { + return ResourceManager.GetString("Context_OpenInBrowserError", resourceCulture); + } + } + /// /// Looks up a localized string similar to Promote. /// @@ -1202,6 +1238,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to URL at {0}. + /// + internal static string Context_URLDomain { + get { + return ResourceManager.GetString("Context_URLDomain", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only open URLs from websites you trust. + /// + internal static string Context_URLWarning { + get { + return ResourceManager.GetString("Context_URLWarning", resourceCulture); + } + } + /// /// Looks up a localized string similar to Chinese (full). /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index 4af8f8a..69cda28 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -871,4 +871,22 @@ Use this option if you experience cut-off tooltips. + + Copy link to clipboard + + + Copied link to clipboard + + + Open link in browser + + + Failed to open the link in the browser, please report this issue + + + URL at {0} + + + Only open URLs from websites you trust + diff --git a/ChatTwo/Store.cs b/ChatTwo/Store.cs index dda6102..5aea584 100755 --- a/ChatTwo/Store.cs +++ b/ChatTwo/Store.cs @@ -86,6 +86,11 @@ internal class Store : IDisposable { ["Type"] = new("PartyFinder"), ["Id"] = new(partyFinder.Id), }); + case URIPayload uri: + return new BsonDocument(new Dictionary { + ["Type"] = new("URI"), + ["Uri"] = new(uri.Uri.ToString()), + }); } return payload?.Encode(); @@ -99,6 +104,7 @@ internal class Store : IDisposable { return bson["Type"].AsString switch { "Achievement" => new AchievementPayload((uint) bson["Id"].AsInt64), "PartyFinder" => new PartyFinderPayload((uint) bson["Id"].AsInt64), + "URI" => new URIPayload(new Uri(bson["Uri"].AsString)), _ => null, }; } diff --git a/ChatTwo/Util/ChunkUtil.cs b/ChatTwo/Util/ChunkUtil.cs index 45368da..b4c01fc 100755 --- a/ChatTwo/Util/ChunkUtil.cs +++ b/ChatTwo/Util/ChunkUtil.cs @@ -1,6 +1,7 @@ using ChatTwo.Code; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; +using System.Text; namespace ChatTwo.Util; @@ -100,6 +101,10 @@ internal static class ChunkUtil { var reader = new BinaryReader(new MemoryStream(rawPayload.Data[4..])); var id = GetInteger(reader); link = new AchievementPayload(id); + } else if (rawPayload.Data.Length > 5 && rawPayload.Data[1] == 0x27 && rawPayload.Data[3] == 0x07) { + // uri payload + var uri = new Uri(Encoding.UTF8.GetString(rawPayload.Data[4..])); + link = new URIPayload(uri); } else if (Equals(rawPayload, RawPayload.LinkTerminator)) { link = null; } diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs index 0075556..31c4efc 100755 --- a/ChatTwo/Util/ImGuiUtil.cs +++ b/ChatTwo/Util/ImGuiUtil.cs @@ -197,16 +197,16 @@ internal static class ImGuiUtil { } } - internal static void WarningText(string text) { + internal static void WarningText(string text, bool wrap = true) { var style = StyleModel.GetConfiguredStyle() ?? StyleModel.GetFromCurrent(); var dalamudOrange = style.BuiltInColors?.DalamudOrange; if (dalamudOrange != null) { ImGui.PushStyleColor(ImGuiCol.Text, dalamudOrange.Value); } - ImGui.PushTextWrapPos(); + if (wrap) ImGui.PushTextWrapPos(); ImGui.TextUnformatted(text); - ImGui.PopTextWrapPos(); + if (wrap) ImGui.PopTextWrapPos(); if (dalamudOrange != null) { ImGui.PopStyleColor(); diff --git a/ChatTwo/Util/Payloads.cs b/ChatTwo/Util/Payloads.cs index a1d0a58..06d7028 100755 --- a/ChatTwo/Util/Payloads.cs +++ b/ChatTwo/Util/Payloads.cs @@ -37,3 +37,51 @@ internal class AchievementPayload : Payload { throw new NotImplementedException(); } } + + +internal class URIPayload(Uri uri) : Payload +{ + public override PayloadType Type => (PayloadType) 0x52; + + public Uri Uri { get; init; } = uri; + + private static readonly string[] ExpectedSchemes = ["http", "https"]; + private static readonly string DefaultScheme = "https"; + + /// + /// Create a URIPayload from a raw URI string. If the URI does not have a + /// scheme, it will default to https://. + /// + /// + /// If the URI is invalid, or if the scheme is not supported. + /// + public static URIPayload ResolveURI(string rawURI) + { + ArgumentNullException.ThrowIfNull(rawURI); + + // Check for expected scheme ://, if not add https:// + foreach (var scheme in ExpectedSchemes) + { + if (rawURI.StartsWith($"{scheme}://")) + { + return new URIPayload(new Uri(rawURI)); + } + } + if (rawURI.Contains("://")) + { + throw new UriFormatException($"Unsupported scheme in URL: {rawURI}"); + } + + return new URIPayload(new Uri($"{DefaultScheme}://{rawURI}")); + } + + protected override void DecodeImpl(BinaryReader reader, long endOfStream) + { + throw new NotImplementedException(); + } + + protected override byte[] EncodeImpl() + { + throw new NotImplementedException(); + } +} From ae6c966e279600ca610e1a3511c4753565e20d75 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Tue, 9 Apr 2024 01:39:26 +1000 Subject: [PATCH 02/12] feat: copy directly to clipboard, add copy content option Moves the options in the "Chat 2" submenu when right clicking a message to the parent menu if there are no specific context menu items for the right clicked payload. This moves the "Copy" button to the main context menu in the majority of cases and reduces the amount of clicks required to copy. Changes the "Copy" button to copy directly to the system clipboard and adds a notification. Adds a "Copy content" button beneath the "Copy" button which only copies the message content (not the sender). If the message doesn't have a sender, this option does not appear as they function identically in such cases. --- ChatTwo/PayloadHandler.cs | 56 ++++++++++++++++---------- ChatTwo/Resources/Language.Designer.cs | 27 +++++++++++++ ChatTwo/Resources/Language.resx | 17 ++++++-- 3 files changed, 75 insertions(+), 25 deletions(-) diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index a971615..f1ca361 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -100,6 +100,7 @@ public sealed class PayloadHandler { if (registered.Count == 0) { return; } + ImGui.Separator(); var contentId = chunk.Message?.ContentId ?? 0; var sender = chunk.Message?.Sender @@ -127,13 +128,18 @@ public sealed class PayloadHandler { } } - private void ContextFooter(bool separator, Chunk chunk) { - if (separator) { + private void ContextFooter(bool didCustomContext, Chunk chunk) { + if (didCustomContext) { ImGui.Separator(); - } - if (!ImGui.BeginMenu(Plugin.PluginName)) { - return; + // Only place these menu items in a submenu if we've already drawn + // custom context menu items based on the payload. + // + // It makes it much more convenient in the majority of cases to + // copy the message content without having to open a submenu. + if (!ImGui.BeginMenu(Plugin.PluginName)) { + return; + } } ImGui.Checkbox(Language.Context_ScreenshotMode, ref LogWindow.ScreenshotMode); @@ -143,21 +149,16 @@ public sealed class PayloadHandler { } if (chunk.Message is { } message) { - if (ImGui.BeginMenu(Language.Context_Copy)) { - var text = message.Sender - .Concat(message.Content) - .Where(chunk => chunk is TextChunk) - .Cast() - .Select(text => text.Content) - .Aggregate(string.Concat); - ImGui.InputTextMultiline( - "##chat2-copy", - ref text, - (uint) text.Length, - new Vector2(350, 100) * ImGuiHelpers.GlobalScale, - ImGuiInputTextFlags.ReadOnly - ); - ImGui.EndMenu(); + if (ImGui.Selectable(Language.Context_Copy)) { + ImGui.SetClipboardText(StringifyMessage(message, true)); + WrapperUtil.AddNotification(Language.Context_CopySuccess, NotificationType.Info); + } + + // Only show a separate "Copy content" option if the message has + // Sender chunks so it doesn't show for system messages. + if (message.Sender.Count > 0 && ImGui.Selectable(Language.Context_CopyContent)) { + ImGui.SetClipboardText(StringifyMessage(message, false)); + WrapperUtil.AddNotification(Language.Context_CopyContentSuccess, NotificationType.Info); } var col = ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled]; @@ -172,7 +173,20 @@ public sealed class PayloadHandler { } } - ImGui.EndMenu(); + if (didCustomContext) ImGui.EndMenu(); + } + + internal static string StringifyMessage(Message message, bool withSender = false) + { + if (message == null) + return string.Empty; + + var chunks = withSender ? message.Sender.Concat(message.Content) : message.Content; + return chunks + .Where(chunk => chunk is TextChunk) + .Cast() + .Select(text => text.Content) + .Aggregate(string.Concat); } internal void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index 2412b26..98170f2 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1031,6 +1031,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Copy content. + /// + internal static string Context_CopyContent { + get { + return ResourceManager.GetString("Context_CopyContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copied message content to clipboard. + /// + internal static string Context_CopyContentSuccess { + get { + return ResourceManager.GetString("Context_CopyContentSuccess", resourceCulture); + } + } + /// /// Looks up a localized string similar to Copy Item Name. /// @@ -1040,6 +1058,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Copied message to clipboard. + /// + internal static string Context_CopySuccess { + get { + return ResourceManager.GetString("Context_CopySuccess", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide chat. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index 4af8f8a..d0dce97 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -859,16 +859,25 @@ Do not close FFXIV, unload Dalamud, or turn off your computer during this time. - + View Adventurer Plate - + Unable to open adventurer plate at this moment - + Tooltip offset - + Use this option if you experience cut-off tooltips. + + Copy content + + + Copied message content to clipboard + + + Copied message to clipboard + From 5da271e0c202f806f6e477e77a36a3f4af0c8ab2 Mon Sep 17 00:00:00 2001 From: Infi Date: Tue, 9 Apr 2024 15:58:53 +0200 Subject: [PATCH 03/12] - Fully support role colors and icons - Fix #5 - Add SeString debugger window --- ChatTwo/PayloadHandler.cs | 4 +- ChatTwo/Plugin.cs | 4 + ChatTwo/Store.cs | 2 + ChatTwo/Ui/ChatLogWindow.cs | 22 +-- ChatTwo/Ui/CommandHelpWindow.cs | 4 + ChatTwo/Ui/SeStringDebugger.cs | 319 ++++++++++++++++++++++++++++++++ ChatTwo/Util/ChunkUtil.cs | 18 +- ChatTwo/Util/ColourUtil.cs | 11 +- ChatTwo/Util/ExtraPayload.cs | 109 +++++++++++ ChatTwo/Util/IconUtil.cs | 235 ++++++++++++++--------- 10 files changed, 611 insertions(+), 117 deletions(-) create mode 100644 ChatTwo/Ui/SeStringDebugger.cs create mode 100644 ChatTwo/Util/ExtraPayload.cs diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index a971615..3980c44 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -102,9 +102,7 @@ public sealed class PayloadHandler { } var contentId = chunk.Message?.ContentId ?? 0; - var sender = chunk.Message?.Sender - .Select(chunk => chunk.Link) - .FirstOrDefault(chunk => chunk is PlayerPayload) as PlayerPayload; + var sender = chunk.Message?.Sender.Select(c => c.Link).FirstOrDefault(p => p is PlayerPayload) as PlayerPayload; if (ImGui.BeginMenu(Language.Context_Integrations)) { var cursor = ImGui.GetCursorPos(); diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 45c1092..3a06b03 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -42,6 +42,7 @@ public sealed class Plugin : IDalamudPlugin { public SettingsWindow SettingsWindow { get; } public ChatLogWindow ChatLogWindow { get; } public CommandHelpWindow CommandHelpWindow { get; } + public SeStringDebugger SeStringDebugger { get; } internal Configuration Config { get; } internal Commands Commands { get; } @@ -81,10 +82,12 @@ public sealed class Plugin : IDalamudPlugin { ChatLogWindow = new ChatLogWindow(this); SettingsWindow = new SettingsWindow(this); CommandHelpWindow = new CommandHelpWindow(ChatLogWindow); + SeStringDebugger = new SeStringDebugger(this); WindowSystem.AddWindow(ChatLogWindow); WindowSystem.AddWindow(SettingsWindow); WindowSystem.AddWindow(CommandHelpWindow); + WindowSystem.AddWindow(SeStringDebugger); FontManager.BuildFonts(); Interface.UiBuilder.DisableCutsceneUiHide = true; @@ -114,6 +117,7 @@ public sealed class Plugin : IDalamudPlugin { WindowSystem.RemoveAllWindows(); ChatLogWindow.Dispose(); SettingsWindow.Dispose(); + SeStringDebugger.Dispose(); ExtraChat.Dispose(); Ipc.Dispose(); diff --git a/ChatTwo/Store.cs b/ChatTwo/Store.cs index dda6102..a493d2f 100755 --- a/ChatTwo/Store.cs +++ b/ChatTwo/Store.cs @@ -257,6 +257,7 @@ internal class Store : IDisposable { } } + public (SeString? Sender, SeString? Message) LastMessage = (null, null); private void ChatMessage(XivChatType type, uint senderId, SeString sender, SeString message) { var chatCode = new ChatCode((ushort) type); @@ -265,6 +266,7 @@ internal class Store : IDisposable { formatting = FormatFor(chatCode.Type); } + LastMessage = (sender, message); var senderChunks = new List(); if (formatting is { IsPresent: true }) { senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.Before) { diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index c5f73f6..193985b 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -75,12 +75,8 @@ public sealed class ChatLogWindow : Window, IUiComponent { Plugin = plugin; Salt = new Random().Next().ToString(); + Size = new Vector2(500, 250); SizeCondition = ImGuiCond.FirstUseEver; - SizeConstraints = new WindowSizeConstraints - { - MinimumSize = new Vector2(500, 250), - MaximumSize = new Vector2(float.MaxValue, float.MaxValue) - }; PayloadHandler = new PayloadHandler(this); HandlerLender = new Lender(() => new PayloadHandler(this)); @@ -1257,10 +1253,8 @@ public sealed class ChatLogWindow : Window, IUiComponent { || cmd.Alias.RawString == command || cmd.ShortCommand.RawString == command || cmd.ShortAlias.RawString == command); - if (cmd != null) { + if (cmd != null) Plugin.CommandHelpWindow.UpdateContent(cmd); - Plugin.CommandHelpWindow.IsOpen = true; - } } if (data->EventFlag != ImGuiInputTextFlags.CallbackHistory) { @@ -1332,15 +1326,15 @@ public sealed class ChatLogWindow : Window, IUiComponent { private void DrawChunk(Chunk chunk, bool wrap = true, PayloadHandler? handler = null, float lineWidth = 0f) { if (chunk is IconChunk icon && _fontIcon != null) { - var bounds = IconUtil.GetBounds((byte) icon.Icon); - if (bounds != null) { + var bounds = IconUtil.GfdFileView.TryGetEntry((uint) icon.Icon, out var entry); + if (bounds) { var texSize = new Vector2(_fontIcon.Width, _fontIcon.Height); - var sizeRatio = Plugin.Config.FontSize / bounds.Value.W; - var size = new Vector2(bounds.Value.Z, bounds.Value.W) * sizeRatio * ImGuiHelpers.GlobalScale; + var sizeRatio = Plugin.Config.FontSize / entry.Height; + var size = new Vector2(entry.Width, entry.Height) * sizeRatio * ImGuiHelpers.GlobalScale; - var uv0 = new Vector2(bounds.Value.X, bounds.Value.Y - 2) / texSize; - var uv1 = new Vector2(bounds.Value.X + bounds.Value.Z, bounds.Value.Y - 2 + bounds.Value.W) / texSize; + var uv0 = new Vector2(entry.Left, entry.Top) / texSize; + var uv1 = new Vector2(entry.Left + entry.Width, entry.Top + entry.Height) / texSize; ImGui.Image(_fontIcon.ImGuiHandle, size, uv0, uv1); ImGuiUtil.PostPayload(chunk, handler); } diff --git a/ChatTwo/Ui/CommandHelpWindow.cs b/ChatTwo/Ui/CommandHelpWindow.cs index ab9ac55..23590a3 100644 --- a/ChatTwo/Ui/CommandHelpWindow.cs +++ b/ChatTwo/Ui/CommandHelpWindow.cs @@ -20,6 +20,7 @@ public class CommandHelpWindow : Window { ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.AlwaysAutoResize; } + // Sets IsOpen to true if it should be drawn public void UpdateContent(TextCommand command) { Command = command; @@ -36,6 +37,7 @@ public class CommandHelpWindow : Window { break; case CommandHelpSide.None: default: + IsOpen = false; return; } @@ -45,6 +47,8 @@ public class CommandHelpWindow : Window { MinimumSize = new Vector2(width, 0), MaximumSize = LogWindow.LastWindowSize with { X = width } }; + + IsOpen = true; } public override void Draw() diff --git a/ChatTwo/Ui/SeStringDebugger.cs b/ChatTwo/Ui/SeStringDebugger.cs new file mode 100644 index 0000000..4bddcc3 --- /dev/null +++ b/ChatTwo/Ui/SeStringDebugger.cs @@ -0,0 +1,319 @@ +using System.Numerics; +using System.Text; +using ChatTwo.Util; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Interface.Windowing; +using ImGuiNET; +using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload; + +namespace ChatTwo.Ui; + +public class SeStringDebugger : Window +{ + private readonly Plugin Plugin; + + public SeStringDebugger(Plugin plugin) : base($"SeString Debugger###chat2-sestringdebugger") + { + Plugin = plugin; + + SizeConstraints = new WindowSizeConstraints + { + MinimumSize = new Vector2(475, 600), + MaximumSize = new Vector2(float.MaxValue, float.MaxValue) + }; + + Plugin.Commands.Register("/chat2Debugger").Execute += Toggle; + } + + public void Dispose() + { + Plugin.Commands.Register("/chat2Debugger").Execute -= Toggle; + } + + private void Toggle(string _, string __) => Toggle(); + + public override void Draw() + { + ImGui.TextUnformatted("SeString Content"); + ImGui.Spacing(); + + if (Plugin.Store.LastMessage.Sender == null) + { + ImGui.TextUnformatted("Nothing to show"); + return; + } + + // TODO: Make SeString freely selectable through chat + foreach (var payload in Plugin.Store.LastMessage.Sender.Payloads) + { + switch (payload) + { + case UIForegroundPayload color: + { + RenderMetadataDictionary("Link ForegroundColor", new Dictionary + { + { "Enabled?", color.IsEnabled.ToString() }, + { "ColorKey", color.IsEnabled ? color.ColorKey.ToString() : "Color Ended" }, + }); + break; + } + case MapLinkPayload map: + { + RenderMetadataDictionary("Link MapLinkPayload", new Dictionary + { + { "Map.RowId", map.Map?.RowId.ToString() }, + { "Map.PlaceName", map.Map?.PlaceName.Value?.Name.ToString() }, + { "Map.PlaceNameRegion", map.Map?.PlaceNameRegion.Value?.Name.ToString() }, + { "Map.PlaceNameSub", map.Map?.PlaceNameSub.Value?.Name.ToString() }, + { "TerritoryType.RowId", map.TerritoryType?.RowId.ToString() }, + { "RawX", map.RawX.ToString() }, + { "RawY", map.RawY.ToString() }, + { "XCoord", map.XCoord.ToString() }, + { "YCoord", map.YCoord.ToString() }, + { "CoordinateString", map.CoordinateString }, + { "DataString", map.DataString }, + }); + break; + } + case QuestPayload quest: + { + RenderMetadataDictionary("Link QuestPayload", new Dictionary + { + { "Quest.RowId", quest.Quest?.RowId.ToString() }, + { "Quest.Name", quest.Quest?.Name.ToString() }, + }); + break; + } + case DalamudLinkPayload link: + { + RenderMetadataDictionary("Link DalamudLinkPayload", new Dictionary + { + { "CommandId", link.CommandId.ToString() }, + { "Plugin", link.Plugin }, + }); + break; + } + case DalamudPartyFinderPayload pf: + { + RenderMetadataDictionary("Link PartyFinderPayload", new Dictionary + { + { "ListingId", pf.ListingId.ToString() }, + { "LinkType", EnumName(pf.LinkType) }, + }); + break; + } + case PlayerPayload player: + { + RenderMetadataDictionary("Link PlayerPayload", new Dictionary + { + { "Real", player.DisplayedName }, + { "PlayerName", player.PlayerName }, + { "World.Name", player.World.Name }, + }); + break; + } + case ItemPayload item: + { + RenderMetadataDictionary("Link ItemPayload", new Dictionary + { + { "ItemId", item.ItemId.ToString() }, + { "RawItemId", item.RawItemId.ToString() }, + { "Kind", EnumName(item.Kind) }, + { "IsHQ", item.IsHQ.ToString() }, + { "Item.Name", item.Item?.Name.ToString() }, + }); + break; + } + case AutoTranslatePayload at: + { + RenderMetadataDictionary("Link AutoTranslatePayload", new Dictionary + { + { "Text", at.Text }, + }); + break; + } + case IconPayload icon: + { + var found = IconUtil.GfdFileView.TryGetEntry((uint) icon.Icon, out var entry); + RenderMetadataDictionary("Link IconPayload", new Dictionary + { + { "Found", found.ToString() }, + { "Icon ID", ((uint) icon.Icon).ToString() }, + }); + break; + } + case RawPayload raw: + { + var colorPayload = ColorPayload.From(raw.Data); + if (colorPayload != null) + { + var push = colorPayload.Enabled && colorPayload.Color != 0; + // if (push) ImGui.PushStyleColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(colorPayload.U)); + RenderMetadataDictionary("Link ColorPayload", new Dictionary + { + { "Unshifted", colorPayload.UnshiftedColor.ToString("X8") }, + { "Color", colorPayload.Color.ToString("X8") }, + { "Enabled?", colorPayload.Enabled.ToString() }, + }); + // if (push) ImGui.PopStyleColor(); + } + else + { + RenderMetadataDictionary("Link RawPayload", new Dictionary + { + { "Data", string.Join(" ", raw.Data.Select(b => b.ToString("X2"))) }, + { "Type", EnumName(raw.Type) }, + }); + } + break; + } + case StatusPayload status: + { + RenderMetadataDictionary("Link StatusPayload", new Dictionary + { + { "Status.RowId", status.Status.RowId.ToString() }, + { "Status.Name", status.Status.Name }, + { "Status.Icon", status.Status.Icon.ToString() } + }); + break; + } + + case Util.PartyFinderPayload pf: + { + RenderMetadataDictionary("Link PartyFinderPayload", new Dictionary + { + { "Id", pf.Id.ToString() } + }); + break; + } + case AchievementPayload achievement: + { + RenderMetadataDictionary("Link AchievementPayload", new Dictionary + { + { "Id", achievement.Id.ToString() } + }); + break; + } + default: + var payloadData = payload.Encode(); + + var initialByte = payloadData.First(); + if (initialByte != 0x02) + { + RenderMetadataDictionary("Text Payload", new Dictionary + { + { "Content", Encoding.UTF8.GetString(payloadData) }, + }); + } + else + { + var unknown = new RawPayload(payloadData); + RenderMetadataDictionary("Link Unknown", new Dictionary + { + { "Unknown", string.Join(" ", unknown.Data.Select(b => b.ToString("X2"))) }, + }); + } + break; + } + } + } + + private static string? EnumName(T? value) where T : Enum + { + if (value == null) + { + return null; + } + var rawValue = Convert.ChangeType(value, value.GetTypeCode()); + return (Enum.GetName(value.GetType(), value) ?? "Unknown") + $" ({rawValue})"; + } + + private static void RenderMetadataDictionary(string name, Dictionary metadata) + { + var style = ImGui.GetStyle(); + + ImGui.Text($"{name}:"); + ImGui.Indent(style.IndentSpacing); + if (!ImGui.BeginTable($"##chat3-{name}", 2, 0)) + { + ImGui.EndTable(); + ImGui.Unindent(style.IndentSpacing); + return; + } + ImGui.TableSetupColumn($"##chat3-{name}-key", 0, 0.4f); + ImGui.TableSetupColumn($"##chat3-{name}-value"); + for (var i = 0; i < metadata.Count; i++) + { + var (key, value) = metadata.ElementAt(i); + ImGui.PushID(i); + ImGui.TableNextColumn(); + ImGui.Text(key); + ImGui.TableNextColumn(); + ImGuiTextVisibleWhitespace(value); + ImGui.PopID(); + } + ImGui.EndTable(); + ImGui.Unindent(style.IndentSpacing); + ImGui.NewLine(); + } + + // ImGuiTextVisibleWhitespace replaces leading and trailing whitespace with + // visible characters. The extra characters are rendered with a muted font. + private static void ImGuiTextVisibleWhitespace(string? original, bool wrap = true) + { + if (string.IsNullOrEmpty(original)) + { + var str = original == null ? "(null)" : "(empty)"; + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 1, 1, 0.5f)); + ImGui.TextUnformatted(str); + ImGui.PopStyleColor(); + return; + } + + var text = original; + var start = 0; + var end = text.Length; + + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); + + void WriteText(string text) + { + if (wrap) + { + ImGui.TextWrapped(text); + } + else + { + ImGui.TextUnformatted(text); + } + } + + while (start < end && char.IsWhiteSpace(text[start])) + { + start++; + } + if (start > 0) + { + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 1, 1, 0.5f)); + WriteText(new string('_', start)); + ImGui.PopStyleColor(); + ImGui.SameLine(); + } + + while (end > start && char.IsWhiteSpace(text[end - 1])) + { + end--; + } + + WriteText(text[start..end]); + if (end < text.Length) + { + ImGui.SameLine(); + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 1, 1, 0.5f)); + WriteText(new string('_', text.Length - end)); + ImGui.PopStyleColor(); + } + + ImGui.PopStyleVar(); + } +} \ No newline at end of file diff --git a/ChatTwo/Util/ChunkUtil.cs b/ChatTwo/Util/ChunkUtil.cs index 45368da..ab045ea 100755 --- a/ChatTwo/Util/ChunkUtil.cs +++ b/ChatTwo/Util/ChunkUtil.cs @@ -68,15 +68,19 @@ internal static class ChunkUtil { break; case PayloadType.Unknown: var rawPayload = (RawPayload) payload; - if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x13) + var colorPayload = ColorPayload.From(rawPayload.Data); + if (colorPayload != null) { - if (foreground.Count > 0) { - foreground.Pop(); - } - else if (rawPayload.Data.Length > 6 && rawPayload.Data[2] == 0x05 && rawPayload.Data[3] == 0xF6) + if (colorPayload.Enabled) { - var (r, g, b) = (rawPayload.Data[4], rawPayload.Data[5], rawPayload.Data[6]); - foreground.Push(ColourUtil.ComponentsToRgba(r, g, b)); + if (colorPayload.Color > 0) + foreground.Push(colorPayload.Color); + else if (foreground.Count > 0) // Push the previous color as we don't want invisible text + foreground.Push(foreground.Peek()); + } + else if (foreground.Count > 0) + { + foreground.Pop(); } } else if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x14) diff --git a/ChatTwo/Util/ColourUtil.cs b/ChatTwo/Util/ColourUtil.cs index 0887e4c..145afe6 100755 --- a/ChatTwo/Util/ColourUtil.cs +++ b/ChatTwo/Util/ColourUtil.cs @@ -12,8 +12,8 @@ internal static class ColourUtil { } internal static uint RgbaToAbgr(uint rgba) { - var (r, g, b, a) = RgbaToComponents(rgba); - return (uint) ((a << 24) | (b << 16) | (g << 8) | r); + var tmp = ((rgba << 8) & 0xFF00FF00) | ((rgba >> 8) & 0xFF00FF); + return (tmp << 16) | (tmp >> 16); } internal static Vector3 RgbaToVector3(uint rgba) { @@ -38,6 +38,13 @@ internal static class ColourUtil { )); } + public static unsafe uint ArgbToRgba(uint x) + { + var buf = (byte*)&x; + (buf[1], buf[2], buf[3], buf[0]) = (buf[0], buf[1], buf[2], buf[3]); + return x; + } + internal static uint ComponentsToRgba(byte red, byte green, byte blue, byte alpha = 0xFF) => alpha | (uint) (red << 24) | (uint) (green << 16) diff --git a/ChatTwo/Util/ExtraPayload.cs b/ChatTwo/Util/ExtraPayload.cs new file mode 100644 index 0000000..9e736c9 --- /dev/null +++ b/ChatTwo/Util/ExtraPayload.cs @@ -0,0 +1,109 @@ +using Dalamud.Utility; +using FFXIVClientStructs.FFXIV.Client.UI.Misc; +using FFXIVClientStructs.FFXIV.Component.Text; + +namespace ChatTwo.Util; + +public class ColorPayload +{ + private const byte START_BYTE = 2; + + public bool Enabled; + public uint Color; + public uint UnshiftedColor; + + public static ColorPayload? From(byte[] data) + { + using var stream = new MemoryStream(data); + if (stream.ReadByte() != START_BYTE || stream.ReadByte() != 0x13) + return null; + + stream.ReadByte(); // skip the length byte; + + var typeByte = stream.ReadByte(); + var payload = new ColorPayload(); + if (typeByte == 0xEC) + { + payload.Enabled = false; + return payload; + } + + if (typeByte == 0xE9) + { + var param = stream.ReadByte(); + var ok = TryGetGNumDefault((uint) (param - 2), out var value); + if (!ok) + { + Plugin.Log.Error($"Unable to GetGNum for param {param - 2}"); + return null; + } + + payload.Enabled = true; + payload.UnshiftedColor = value; + payload.Color = ColourUtil.ArgbToRgba(value); + + return payload; + } + + if (typeByte is >= 0xF0 and <= 0xFE) + { + // From: https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Text/Expressions/IntegerExpression.cs#L119-L128 + uint ShiftAndThrowIfZero(int v, int shift) + { + return v switch + { + -1 => throw new ArgumentException("Encountered premature end of input (unexpected EOF).", nameof(v)), + 0 => throw new ArgumentException("Encountered premature end of input (unexpected null character).", nameof(v)), + _ => (uint)v << shift + }; + } + + typeByte += 1; + var value = 0u; + if ((typeByte & 8) != 0) + value |= ShiftAndThrowIfZero(stream.ReadByte(), 24); + else + value |= 0xff000000u; + + if( (typeByte & 4) != 0 ) value |= ShiftAndThrowIfZero( stream.ReadByte(), 16 ); + if( (typeByte & 2) != 0 ) value |= ShiftAndThrowIfZero( stream.ReadByte(), 8 ); + if( (typeByte & 1) != 0 ) value |= ShiftAndThrowIfZero( stream.ReadByte(), 0 ); + + payload.Enabled = true; + payload.Color = ColourUtil.ArgbToRgba(value); + + return payload; + } + + return null; + } + + private static unsafe bool TryGetGNumDefault(uint parameterIndex, out uint value) + { + value = 0u; + + var rtm = RaptureTextModule.Instance(); + if (rtm is null) + return false; + + if (!ThreadSafety.IsMainThread) + { + Plugin.Log.Error("Global parameters may only be used from the main thread."); + return false; + } + + ref var gp = ref rtm->TextModule.MacroDecoder.GlobalParameters; + if (parameterIndex >= gp.MySize) + return false; + + var p = rtm->TextModule.MacroDecoder.GlobalParameters.Get(parameterIndex); + switch (p.Type) + { + case TextParameterType.Integer: + value = (uint)p.IntValue; + return true; + default: + return false; + } + } +} \ No newline at end of file diff --git a/ChatTwo/Util/IconUtil.cs b/ChatTwo/Util/IconUtil.cs index d4e68de..11cd426 100755 --- a/ChatTwo/Util/IconUtil.cs +++ b/ChatTwo/Util/IconUtil.cs @@ -1,95 +1,148 @@ -using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace ChatTwo.Util; -internal static class IconUtil { - internal static Vector4? GetBounds(byte id) => id switch { - 1 => new Vector4(0, 342, 40, 40), - 2 => new Vector4(40, 342, 40, 40), - 3 => new Vector4(80, 342, 40, 40), - 4 => new Vector4(120, 342, 40, 40), - 5 => new Vector4(160, 342, 40, 40), - 6 => new Vector4(0, 382, 40, 40), - 7 => new Vector4(40, 382, 40, 40), - 8 => new Vector4(80, 382, 40, 40), - 9 => new Vector4(120, 382, 40, 40), - 10 => new Vector4(160, 382, 40, 40), - 11 => new Vector4(0, 422, 40, 40), - 12 => new Vector4(40, 422, 40, 40), - 13 => new Vector4(80, 422, 40, 40), - 14 => new Vector4(120, 422, 40, 40), - 15 => new Vector4(160, 422, 40, 40), - 16 => new Vector4(120, 542, 40, 40), - 17 => new Vector4(160, 542, 40, 40), - 18 => new Vector4(0, 462, 108, 40), - 19 => new Vector4(108, 462, 108, 40), - 20 => new Vector4(120, 502, 40, 40), - 21 => new Vector4(0, 502, 56, 40), - 22 => new Vector4(56, 502, 64, 40), - 23 => new Vector4(160, 502, 40, 40), - 24 => new Vector4(0, 542, 56, 40), - 25 => new Vector4(56, 542, 64, 40), - 51 => new Vector4(248, 342, 40, 40), - 52 => new Vector4(288, 342, 40, 40), - 53 => new Vector4(328, 342, 40, 40), - 54 => new Vector4(200, 342, 24, 40), - 55 => new Vector4(224, 342, 24, 40), - 56 => new Vector4(200, 382, 40, 40), - 57 => new Vector4(240, 382, 40, 40), - 58 => new Vector4(280, 382, 40, 40), - 59 => new Vector4(200, 422, 40, 40), - 60 => new Vector4(240, 422, 40, 40), - 61 => new Vector4(280, 422, 40, 40), - 62 => new Vector4(320, 382, 40, 40), - 63 => new Vector4(320, 422, 40, 40), - 64 => new Vector4(368, 342, 40, 40), - 65 => new Vector4(408, 342, 40, 40), - 66 => new Vector4(448, 342, 40, 40), - 67 => new Vector4(360, 382, 40, 40), - 68 => new Vector4(400, 382, 40, 40), - 70 => new Vector4(360, 422, 40, 40), - 71 => new Vector4(400, 422, 40, 40), - 72 => new Vector4(440, 422, 40, 40), - 73 => new Vector4(440, 382, 40, 40), - 74 => new Vector4(216, 462, 40, 40), - 75 => new Vector4(256, 462, 40, 40), - 76 => new Vector4(296, 462, 40, 40), - 77 => new Vector4(336, 462, 40, 40), - 78 => new Vector4(376, 462, 40, 40), - 79 => new Vector4(416, 462, 40, 40), - 80 => new Vector4(456, 462, 40, 40), - 81 => new Vector4(200, 502, 40, 40), - 82 => new Vector4(240, 502, 40, 40), - 83 => new Vector4(280, 502, 40, 40), - 84 => new Vector4(320, 502, 40, 40), - 85 => new Vector4(360, 502, 40, 40), - 86 => new Vector4(400, 502, 40, 40), - 87 => new Vector4(440, 502, 40, 40), - 88 => new Vector4(200, 542, 40, 40), - 89 => new Vector4(240, 542, 40, 40), - 90 => new Vector4(280, 542, 40, 40), - 91 => new Vector4(320, 542, 40, 40), - 92 => new Vector4(360, 542, 40, 40), - 93 => new Vector4(400, 542, 40, 40), - 94 => new Vector4(440, 542, 40, 40), - 95 => new Vector4(0, 582, 40, 40), - 96 => new Vector4(40, 582, 40, 40), - 97 => new Vector4(80, 582, 40, 40), - 98 => new Vector4(120, 582, 40, 40), - 99 => new Vector4(160, 582, 40, 40), - 100 => new Vector4(200, 582, 40, 40), - 101 => new Vector4(240, 582, 40, 40), - 102 => new Vector4(280, 582, 40, 40), - 103 => new Vector4(320, 582, 40, 40), - 104 => new Vector4(360, 582, 40, 40), - 105 => new Vector4(400, 582, 40, 40), - 106 => new Vector4(440, 582, 40, 40), - 107 => new Vector4(0, 622, 40, 40), - 108 => new Vector4(40, 622, 40, 40), - 109 => new Vector4(80, 622, 40, 40), - 110 => new Vector4(120, 622, 40, 40), - 111 => new Vector4(160, 622, 40, 40), - 112 => new Vector4(200, 622, 40, 40), - _ => null, - }; +// From Kizer: https://github.com/Soreepeong/Dalamud/blob/feature/log-wordwrap/Dalamud/Interface/Spannables/Internal/GfdFileView.cs +public readonly unsafe ref struct GfdFileView +{ + private readonly ReadOnlySpan Span; + private readonly bool DirectLookup; + + /// Initializes a new instance of the struct. + /// The data. + public GfdFileView(ReadOnlySpan span) + { + Span = span; + if (span.Length < sizeof(GfdHeader)) + throw new InvalidDataException($"Not enough space for a {nameof(GfdHeader)}"); + if (span.Length < sizeof(GfdHeader) + (Header.Count * sizeof(GfdEntry))) + throw new InvalidDataException($"Not enough space for all the {nameof(GfdEntry)}"); + + var entries = Entries; + DirectLookup = true; + for (var i = 0; i < entries.Length && DirectLookup; i++) + DirectLookup &= i + 1 == entries[i].Id; + } + + /// Gets the header. + private ref readonly GfdHeader Header => ref MemoryMarshal.AsRef(Span); + + /// Gets the entries. + private ReadOnlySpan Entries => MemoryMarshal.Cast(Span[sizeof(GfdHeader)..]); + + /// Attempts to get an entry. + /// The icon ID. + /// The entry. + /// Whether to follow redirects. + /// true if found. + public bool TryGetEntry(uint iconId, out GfdEntry entry, bool followRedirect = true) + { + if (iconId == 0) + { + entry = default; + return false; + } + + var entries = Entries; + if (DirectLookup) + { + if (iconId <= entries.Length) + { + entry = entries[(int)(iconId - 1)]; + return !entry.IsEmpty; + } + + entry = default; + return false; + } + + var lo = 0; + var hi = entries.Length; + while (lo <= hi) + { + var i = lo + ((hi - lo) >> 1); + if (entries[i].Id == iconId) + { + if (followRedirect && entries[i].Redirect != 0) + { + iconId = entries[i].Redirect; + lo = 0; + hi = entries.Length; + continue; + } + + entry = entries[i]; + return !entry.IsEmpty; + } + + if (entries[i].Id < iconId) + lo = i + 1; + else + hi = i - 1; + } + + entry = default; + return false; + } + + /// Header of a .gfd file. + [StructLayout(LayoutKind.Sequential)] + public struct GfdHeader + { + /// Signature: "gftd0100". + public fixed byte Signature[8]; + + /// Number of entries. + public int Count; + + /// Unused/unknown. + public fixed byte Padding[4]; + } + + /// An entry of a .gfd file. + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + public struct GfdEntry + { + /// ID of the entry. + public ushort Id; + + /// The left offset of the entry. + public ushort Left; + + /// The top offset of the entry. + public ushort Top; + + /// The width of the entry. + public ushort Width; + + /// The height of the entry. + public ushort Height; + + /// Unknown/unused. + public ushort Unk0A; + + /// The redirected entry, maybe. + public ushort Redirect; + + /// Unknown/unused. + public ushort Unk0E; + + /// Gets a value indicating whether this entry is effectively empty. + public bool IsEmpty => Width == 0 || Height == 0; + } +} + + + +internal static class IconUtil { + private static byte[]? GfdFile; + public static unsafe GfdFileView GfdFileView + { + get + { + GfdFile ??= Plugin.DataManager.GetFile("common/font/gfdata.gfd")!.Data; + return new GfdFileView(new ReadOnlySpan(Unsafe.AsPointer(ref GfdFile[0]), GfdFile.Length)); + } + } } From 8446cb3c1f63895fc7a25dc3cfada78a9239d2b3 Mon Sep 17 00:00:00 2001 From: Infi Date: Tue, 9 Apr 2024 16:37:58 +0200 Subject: [PATCH 04/12] + Switch to dalamud method for opening URLs + Fix popout min size --- ChatTwo/ChatTwo.csproj | 2 +- ChatTwo/Message.cs | 4 +--- ChatTwo/PayloadHandler.cs | 5 ++--- ChatTwo/Ui/Popout.cs | 8 ++------ 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index 275f83e..28c7abf 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -1,7 +1,7 @@ - 1.20.0 + 1.20.1 net8.0-windows enable enable diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index f0cc9fc..5a40276 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -157,7 +157,7 @@ internal class Message { /// It matches URLs with www. or https:// prefix, and also matches URLs /// without a prefix on specific TLDs. /// - private static Regex URLRegex = new Regex( + private static Regex URLRegex = new( @"((https?:\/\/|www\.)[a-z0-9-]+(\.[a-z0-9-]+)*|([a-z0-9-]+(\.[a-z0-9-]+)*\.(com|net|org|co|io|app)))(:[\d]{1,5})?(\/[^\s]+)?", RegexOptions.Compiled | RegexOptions.IgnoreCase ); @@ -218,9 +218,7 @@ internal class Message { // Add the text after the last URL. if (remainderIndex < text.Content.Length) - { AddChunkWithMessage(text.NewWithStyle(chunk.Source, null, text.Content[remainderIndex..])); - } } return newChunks; diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index 4452d09..b286f76 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -21,7 +21,6 @@ using Lumina.Excel.GeneratedSheets; using Action = System.Action; using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload; using ChatTwoPartyFinderPayload = ChatTwo.Util.PartyFinderPayload; -using System.Diagnostics; namespace ChatTwo; @@ -681,8 +680,8 @@ public sealed class PayloadHandler { new Thread(() => { try { - Plugin.Log.Info($"Opening URI {uri} in default browser"); - Process.Start(new ProcessStartInfo(uri.ToString()) { UseShellExecute = true }); + Plugin.Log.Debug($"Opening URI {uri} in default browser"); + Dalamud.Utility.Util.OpenLink(uri.ToString()); } catch (Exception ex) { diff --git a/ChatTwo/Ui/Popout.cs b/ChatTwo/Ui/Popout.cs index 5aa0636..a3ff375 100644 --- a/ChatTwo/Ui/Popout.cs +++ b/ChatTwo/Ui/Popout.cs @@ -16,12 +16,8 @@ internal class Popout : Window Tab = tab; Idx = idx; + Size = new Vector2(350, 350); SizeCondition = ImGuiCond.FirstUseEver; - SizeConstraints = new WindowSizeConstraints - { - MinimumSize = new Vector2(350, 350), - MaximumSize = new Vector2(float.MaxValue, float.MaxValue) - }; } public override void PreDraw() @@ -64,4 +60,4 @@ internal class Popout : Window Tab.PopOut = false; ChatLogWindow.Plugin.SaveConfig(); } -} \ No newline at end of file +} From 9feb70d26234443051fe8d2f5eea775aacfd85de Mon Sep 17 00:00:00 2001 From: Infi Date: Tue, 9 Apr 2024 21:58:32 +0200 Subject: [PATCH 05/12] Add more info to the about page --- ChatTwo/ChatTwo.csproj | 2 +- ChatTwo/Plugin.cs | 5 +- ChatTwo/Resources/Language.Designer.cs | 64 ++++++++++++++++++++++---- ChatTwo/Resources/Language.resx | 19 +++++++- ChatTwo/Ui/SeStringDebugger.cs | 6 +-- ChatTwo/Ui/SettingsTabs/About.cs | 42 ++++++++++++----- 6 files changed, 110 insertions(+), 28 deletions(-) diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index 28c7abf..c8a8829 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -1,7 +1,7 @@ - 1.20.1 + 1.20.2 net8.0-windows enable enable diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 3a06b03..8aadb58 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using System.Reflection; using ChatTwo.Ipc; using ChatTwo.Resources; using ChatTwo.Ui; @@ -37,8 +38,10 @@ public sealed class Plugin : IDalamudPlugin { [PluginService] internal static INotificationManager Notification { get; private set; } = null!; [PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!; - public readonly WindowSystem WindowSystem = new(PluginName); + public const string Authors = "Infi, Anna"; + public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "Unknown"; + public readonly WindowSystem WindowSystem = new(PluginName); public SettingsWindow SettingsWindow { get; } public ChatLogWindow ChatLogWindow { get; } public CommandHelpWindow CommandHelpWindow { get; } diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index 8696c23..c4faac5 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1059,14 +1059,6 @@ namespace ChatTwo.Resources { } /// - /// Looks up a localized string similar to Copied message to clipboard. - /// - internal static string Context_CopySuccess { - get { - return ResourceManager.GetString("Context_CopySuccess", resourceCulture); - } - } - /// Looks up a localized string similar to Copy link to clipboard. /// internal static string Context_CopyLink { @@ -1084,6 +1076,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Copied message to clipboard. + /// + internal static string Context_CopySuccess { + get { + return ResourceManager.GetString("Context_CopySuccess", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide chat. /// @@ -1426,6 +1427,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Authors: . + /// + internal static string Options_About_Authors { + get { + return ResourceManager.GetString("Options_About_Authors", resourceCulture); + } + } + /// /// Looks up a localized string similar to Click the button to the left to see what's being worked on and what's next.. /// @@ -1436,7 +1446,7 @@ namespace ChatTwo.Resources { } /// - /// Looks up a localized string similar to Click the button to the left to help translate {0}.. + /// Looks up a localized string similar to Help to translate: . /// internal static string Options_About_CrowdIn { get { @@ -1444,6 +1454,33 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Discord: . + /// + internal static string Options_About_Discord { + get { + return ResourceManager.GetString("Options_About_Discord", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Get help in the discord thread: . + /// + internal static string Options_About_Discord_Thread { + get { + return ResourceManager.GetString("Options_About_Discord_Thread", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Get help through github issues: . + /// + internal static string Options_About_Github_Issues { + get { + return ResourceManager.GetString("Options_About_Github_Issues", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} is a project to completely recreate the in-game chat and make it even better.. /// @@ -1471,6 +1508,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Version: . + /// + internal static string Options_About_Version { + get { + return ResourceManager.GetString("Options_About_Version", resourceCulture); + } + } + /// /// Looks up a localized string similar to Allow moving chat. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index 374e688..33959a2 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -377,7 +377,7 @@ Click the button to the left to see what's being worked on and what's next. - Click the button to the left to help translate {0}. + Help to translate: Translators @@ -871,7 +871,7 @@ Use this option if you experience cut-off tooltips. - + Copy content @@ -899,4 +899,19 @@ Only open URLs from websites you trust + + Authors: + + + Discord: + + + Version: + + + Get help through github issues: + + + Get help in the discord thread: + diff --git a/ChatTwo/Ui/SeStringDebugger.cs b/ChatTwo/Ui/SeStringDebugger.cs index 4bddcc3..06cc665 100644 --- a/ChatTwo/Ui/SeStringDebugger.cs +++ b/ChatTwo/Ui/SeStringDebugger.cs @@ -37,14 +37,14 @@ public class SeStringDebugger : Window ImGui.TextUnformatted("SeString Content"); ImGui.Spacing(); - if (Plugin.Store.LastMessage.Sender == null) + if (Plugin.Store.LastMessage.Message == null) { ImGui.TextUnformatted("Nothing to show"); return; } // TODO: Make SeString freely selectable through chat - foreach (var payload in Plugin.Store.LastMessage.Sender.Payloads) + foreach (var payload in Plugin.Store.LastMessage.Message.Payloads) { switch (payload) { @@ -316,4 +316,4 @@ public class SeStringDebugger : Window ImGui.PopStyleVar(); } -} \ No newline at end of file +} diff --git a/ChatTwo/Ui/SettingsTabs/About.cs b/ChatTwo/Ui/SettingsTabs/About.cs index 4c212e2..d7b0927 100755 --- a/ChatTwo/Ui/SettingsTabs/About.cs +++ b/ChatTwo/Ui/SettingsTabs/About.cs @@ -2,6 +2,8 @@ using System.Numerics; using ChatTwo.Resources; using ChatTwo.Util; using Dalamud.Interface; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using ImGuiNET; namespace ChatTwo.Ui.SettingsTabs; @@ -31,23 +33,40 @@ internal sealed class About : ISettingsTab { ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName)); + ImGuiHelpers.ScaledDummy(10.0f); + + ImGui.TextUnformatted(Language.Options_About_Authors); + ImGui.SameLine(); + ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Authors); + + ImGui.TextUnformatted(Language.Options_About_Discord); + ImGui.SameLine(); + ImGui.TextColored(ImGuiColors.ParsedGold, "@infi"); + + ImGui.TextUnformatted(Language.Options_About_Version); + ImGui.SameLine(); + ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Version); + + ImGuiHelpers.ScaledDummy(10.0f); + + ImGui.TextUnformatted(Language.Options_About_Discord_Thread); + ImGui.SameLine(); + if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "discordThread")) + Dalamud.Utility.Util.OpenLink("https://canary.discord.com/channels/581875019861328007/1224865018789761126"); + ImGui.Spacing(); - if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "clickup")) - { - Dalamud.Utility.Util.OpenLink("https://sharing.clickup.com/b/h/6-122378074-2/1047d21a39a4140"); - } - + ImGui.TextUnformatted(Language.Options_About_Github_Issues); ImGui.SameLine(); - ImGui.TextUnformatted(Language.Options_About_ClickUp); + if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "githubIssues")) + Dalamud.Utility.Util.OpenLink("https://github.com/Infiziert90/ChatTwo/issues"); + ImGuiHelpers.ScaledDummy(10.0f); + + ImGui.TextUnformatted(Language.Options_About_CrowdIn); + ImGui.SameLine(); if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "crowdin")) - { Dalamud.Utility.Util.OpenLink("https://crowdin.com/project/chattwo"); - } - - ImGui.SameLine(); - ImGui.TextUnformatted(string.Format(Language.Options_About_CrowdIn, Plugin.PluginName)); ImGui.Spacing(); @@ -68,7 +87,6 @@ internal sealed class About : ISettingsTab { ImGui.EndChild(); } - ImGuiUtil.HelpText($"{Plugin.PluginName} v{GetType().Assembly.GetName().Version?.ToString(3)}"); ImGui.PopTextWrapPos(); } } From 9412711050d08ade2aa45ecb5a6ee255d0bb0d31 Mon Sep 17 00:00:00 2001 From: Spider <29214314+spide-r@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:29:35 -0500 Subject: [PATCH 06/12] Added The ability to override the plugin style --- ChatTwo/Configuration.cs | 5 +++++ ChatTwo/Plugin.cs | 13 +++++++++++++ ChatTwo/Ui/SettingsTabs/Display.cs | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index b36ae12..6f7a2b0 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -50,6 +50,9 @@ internal class Configuration : IPluginConfiguration { public Dictionary ChatColours = new(); public List Tabs = new(); + public bool OverrideStyle = false; + public string ChosenStyle = ""; + public uint DatabaseMigration = LatestDbVersion; internal void UpdateFrom(Configuration other) { @@ -88,6 +91,8 @@ internal class Configuration : IPluginConfiguration { ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value); Tabs = other.Tabs.Select(t => t.Clone()).ToList(); DatabaseMigration = other.DatabaseMigration; + OverrideStyle = other.OverrideStyle; + ChosenStyle = other.ChosenStyle; } public void Migrate() { diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 8aadb58..9da9925 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -6,6 +6,7 @@ using ChatTwo.Resources; using ChatTwo.Ui; using ChatTwo.Util; using Dalamud.Game.ClientState.Objects; +using Dalamud.Interface.Style; using Dalamud.Interface.Windowing; using Dalamud.IoC; using Dalamud.Plugin; @@ -133,6 +134,12 @@ public sealed class Plugin : IDalamudPlugin { private void Draw() { + if (Config.OverrideStyle) + { + var styles = StyleModel.GetConfiguredStyles(); + styles?.First(style => style.Name.Equals(Config.ChosenStyle)).Push(); + } + Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden; ChatLogWindow.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text]; @@ -140,6 +147,12 @@ public sealed class Plugin : IDalamudPlugin { { WindowSystem.Draw(); } + + if (Config.OverrideStyle) + { + var styles = StyleModel.GetConfiguredStyles(); + styles?.First(style => style.Name.Equals(Config.ChosenStyle)).Pop(); + } } internal void SaveConfig() { diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs index 4f54571..f200e58 100755 --- a/ChatTwo/Ui/SettingsTabs/Display.cs +++ b/ChatTwo/Ui/SettingsTabs/Display.cs @@ -1,5 +1,6 @@ using ChatTwo.Resources; using ChatTwo.Util; +using Dalamud.Interface.Style; using ImGuiNET; namespace ChatTwo.Ui.SettingsTabs; @@ -89,6 +90,24 @@ internal sealed class Display : ISettingsTab { ImGuiUtil.OptionCheckbox(ref Mutable.ShowPopOutTitleBar, Language.Options_ShowPopOutTitleBar_Name); ImGui.Spacing(); + ImGui.Checkbox("Override Style", ref Mutable.OverrideStyle); + ImGui.Spacing(); + + if (Mutable.OverrideStyle) + { + var currentStyle = Mutable.ChosenStyle.Equals("") ? StyleModel.GetConfiguredStyle().Name : Mutable.ChosenStyle; + if (ImGui.BeginCombo("REMOVEME Styles", currentStyle)) { + foreach (var style in StyleModel.GetConfiguredStyles()) { + if (ImGui.Selectable(style.Name, this.Mutable.ChosenStyle == style.Name)) { + Mutable.ChosenStyle = style.Name; + } + } + + ImGui.EndCombo(); + } + } + ImGui.Spacing(); + ImGui.PopTextWrapPos(); } } From eb7b8b96ea0625a0fc6386f50df2dea9f36377f8 Mon Sep 17 00:00:00 2001 From: Spider <29214314+spide-r@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:05:34 -0500 Subject: [PATCH 07/12] Fix to prevent overriden style from appling to the config window --- ChatTwo/Plugin.cs | 11 ----------- ChatTwo/Ui/ChatLogWindow.cs | 19 +++++++++++++++++++ ChatTwo/Ui/Popout.cs | 12 ++++++++++++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 9da9925..158f53e 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -134,11 +134,6 @@ public sealed class Plugin : IDalamudPlugin { private void Draw() { - if (Config.OverrideStyle) - { - var styles = StyleModel.GetConfiguredStyles(); - styles?.First(style => style.Name.Equals(Config.ChosenStyle)).Push(); - } Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden; ChatLogWindow.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text]; @@ -147,12 +142,6 @@ public sealed class Plugin : IDalamudPlugin { { WindowSystem.Draw(); } - - if (Config.OverrideStyle) - { - var styles = StyleModel.GetConfiguredStyles(); - styles?.First(style => style.Name.Equals(Config.ChosenStyle)).Pop(); - } } internal void SaveConfig() { diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 193985b..3c12d81 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -13,6 +13,7 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Interface.Internal; +using Dalamud.Interface.Style; using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Memory; @@ -96,6 +97,24 @@ public sealed class ChatLogWindow : Window, IUiComponent { Plugin.AddonLifecycle.RegisterListener(AddonEvent.PostRequestedUpdate, "ItemDetail", PayloadHandler.MoveTooltip); } + public override void PreDraw() + { + if (Plugin.Config.OverrideStyle) + { + var styles = StyleModel.GetConfiguredStyles(); + styles?.First(style => style.Name.Equals(Plugin.Config.ChosenStyle)).Push(); + } + } + + public override void PostDraw() + { + if (Plugin.Config.OverrideStyle) + { + var styles = StyleModel.GetConfiguredStyles(); + styles?.First(style => style.Name.Equals(Plugin.Config.ChosenStyle)).Pop(); + } + } + public void Dispose() { Plugin.AddonLifecycle.UnregisterListener(AddonEvent.PostRequestedUpdate, "ItemDetail", PayloadHandler.MoveTooltip); Plugin.ClientState.Logout -= Logout; diff --git a/ChatTwo/Ui/Popout.cs b/ChatTwo/Ui/Popout.cs index a3ff375..96c0dfa 100644 --- a/ChatTwo/Ui/Popout.cs +++ b/ChatTwo/Ui/Popout.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Dalamud.Interface.Style; using Dalamud.Interface.Windowing; using ImGuiNET; @@ -22,6 +23,11 @@ internal class Popout : Window public override void PreDraw() { + if (ChatLogWindow.Plugin.Config.OverrideStyle) + { + var styles = StyleModel.GetConfiguredStyles(); + styles?.First(style => style.Name.Equals(ChatLogWindow.Plugin.Config.ChosenStyle)).Push(); + } Flags = ImGuiWindowFlags.None; if (!ChatLogWindow.Plugin.Config.ShowPopOutTitleBar) Flags |= ImGuiWindowFlags.NoTitleBar; @@ -49,7 +55,13 @@ internal class Popout : Window public override void PostDraw() { + ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked(); + if (ChatLogWindow.Plugin.Config.OverrideStyle) + { + var styles = StyleModel.GetConfiguredStyles(); + styles?.First(style => style.Name.Equals(ChatLogWindow.Plugin.Config.ChosenStyle)).Pop(); + } } public override void OnClose() From 7885824248a6f580ea9fb74cee5a20fe56cd4549 Mon Sep 17 00:00:00 2001 From: Spider <29214314+spide-r@users.noreply.github.com> Date: Tue, 9 Apr 2024 19:21:55 -0500 Subject: [PATCH 08/12] Added Translation support for prior changes --- ChatTwo/Resources/Language.Designer.cs | 18 ++++++++++++++++++ ChatTwo/Resources/Language.resx | 6 ++++++ ChatTwo/Ui/SettingsTabs/Display.cs | 4 ++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index c4faac5..d6749bd 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1958,6 +1958,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Override Style. + /// + internal static string Options_OverrideStyle_Name { + get { + return ResourceManager.GetString("Options_OverrideStyle_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Styles. + /// + internal static string Options_OverrideStyleDropdown_Name { + get { + return ResourceManager.GetString("Options_OverrideStyleDropdown_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Display messages in a more modern style.. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index 33959a2..5fe2004 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -502,6 +502,12 @@ If this is enabled, the Auto Translate list will be sorted alphabetically. + + Override Style + + + Styles + Ctrl + {0} diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs index f200e58..662d3a5 100755 --- a/ChatTwo/Ui/SettingsTabs/Display.cs +++ b/ChatTwo/Ui/SettingsTabs/Display.cs @@ -90,13 +90,13 @@ internal sealed class Display : ISettingsTab { ImGuiUtil.OptionCheckbox(ref Mutable.ShowPopOutTitleBar, Language.Options_ShowPopOutTitleBar_Name); ImGui.Spacing(); - ImGui.Checkbox("Override Style", ref Mutable.OverrideStyle); + ImGui.Checkbox(Language.Options_OverrideStyle_Name, ref Mutable.OverrideStyle); ImGui.Spacing(); if (Mutable.OverrideStyle) { var currentStyle = Mutable.ChosenStyle.Equals("") ? StyleModel.GetConfiguredStyle().Name : Mutable.ChosenStyle; - if (ImGui.BeginCombo("REMOVEME Styles", currentStyle)) { + if (ImGui.BeginCombo(Language.Options_OverrideStyleDropdown_Name, currentStyle)) { foreach (var style in StyleModel.GetConfiguredStyles()) { if (ImGui.Selectable(style.Name, this.Mutable.ChosenStyle == style.Name)) { Mutable.ChosenStyle = style.Name; From 486ece07d2a61afb4cacef7078426b0237cb4d73 Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 10 Apr 2024 17:11:46 +0200 Subject: [PATCH 09/12] Make the debugger command depend on debug build --- ChatTwo/Ui/SeStringDebugger.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChatTwo/Ui/SeStringDebugger.cs b/ChatTwo/Ui/SeStringDebugger.cs index 06cc665..74664e7 100644 --- a/ChatTwo/Ui/SeStringDebugger.cs +++ b/ChatTwo/Ui/SeStringDebugger.cs @@ -22,12 +22,16 @@ public class SeStringDebugger : Window MaximumSize = new Vector2(float.MaxValue, float.MaxValue) }; + #if DEBUG Plugin.Commands.Register("/chat2Debugger").Execute += Toggle; + #endif } public void Dispose() { + #if DEBUG Plugin.Commands.Register("/chat2Debugger").Execute -= Toggle; + #endif } private void Toggle(string _, string __) => Toggle(); From 948383b0c5561b74639cbc7e2131524369771035 Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 10 Apr 2024 17:45:25 +0200 Subject: [PATCH 10/12] Remove this. everywhere, add small note to override style --- ChatTwo/ChatTwo.csproj | 2 +- ChatTwo/Chunk.cs | 22 ++--- ChatTwo/Code/ChatCode.cs | 24 ++--- ChatTwo/Commands.cs | 24 ++--- ChatTwo/GameFunctions/Context.cs | 28 +++--- ChatTwo/GameFunctions/GameFunctions.cs | 62 ++++++------- ChatTwo/GameFunctions/Party.cs | 28 +++--- .../GameFunctions/Types/ChannelSwitchInfo.cs | 8 +- .../GameFunctions/Types/ChatActivatedArgs.cs | 2 +- .../GameFunctions/Types/TellHistoryInfo.cs | 6 +- ChatTwo/GameFunctions/Types/TellTarget.cs | 8 +- ChatTwo/Ipc/ExtraChat.cs | 32 +++---- ChatTwo/IpcManager.cs | 28 +++--- ChatTwo/Message.cs | 92 +++++++++---------- ChatTwo/Resources/Language.Designer.cs | 9 ++ ChatTwo/Resources/Language.resx | 3 + ChatTwo/TextureCache.cs | 34 +++---- ChatTwo/Ui/AutoCompleteInfo.cs | 6 +- ChatTwo/Ui/Fonts.cs | 12 +-- ChatTwo/Ui/SettingsTabs/ChatColours.cs | 14 +-- ChatTwo/Ui/SettingsTabs/Database.cs | 28 +++--- ChatTwo/Ui/SettingsTabs/Display.cs | 4 +- ChatTwo/Ui/SettingsTabs/Fonts.cs | 56 +++++------ ChatTwo/Ui/SettingsTabs/Miscellaneous.cs | 20 ++-- ChatTwo/Ui/SettingsTabs/Tabs.cs | 38 ++++---- ChatTwo/Util/AutoTranslate.cs | 16 ++-- ChatTwo/Util/Lender.cs | 10 +- ChatTwo/Util/Payloads.cs | 4 +- 28 files changed, 316 insertions(+), 304 deletions(-) diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index c8a8829..a1b0450 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -1,7 +1,7 @@ - 1.20.2 + 1.20.3 net8.0-windows enable enable diff --git a/ChatTwo/Chunk.cs b/ChatTwo/Chunk.cs index fd755ba..245b26c 100755 --- a/ChatTwo/Chunk.cs +++ b/ChatTwo/Chunk.cs @@ -12,14 +12,14 @@ internal abstract class Chunk { internal Payload? Link { get; set; } protected Chunk(ChunkSource source, Payload? link) { - this.Source = source; - this.Link = link; + Source = source; + Link = link; } - internal SeString? GetSeString() => this.Source switch { + internal SeString? GetSeString() => Source switch { ChunkSource.None => null, - ChunkSource.Sender => this.Message?.SenderSource, - ChunkSource.Content => this.Message?.ContentSource, + ChunkSource.Sender => Message?.SenderSource, + ChunkSource.Content => Message?.ContentSource, _ => null, }; @@ -52,7 +52,7 @@ internal class TextChunk : Chunk { internal string Content { get; set; } internal TextChunk(ChunkSource source, Payload? link, string content) : base(source, link) { - this.Content = content; + Content = content; } #pragma warning disable CS8618 @@ -66,10 +66,10 @@ internal class TextChunk : Chunk { public TextChunk NewWithStyle(ChunkSource source, Payload? link, string content) { return new TextChunk(source, link, content) { - FallbackColour = this.FallbackColour, - Foreground = this.Foreground, - Glow = this.Glow, - Italic = this.Italic, + FallbackColour = FallbackColour, + Foreground = Foreground, + Glow = Glow, + Italic = Italic, }; } } @@ -78,7 +78,7 @@ internal class IconChunk : Chunk { internal BitmapFontIcon Icon { get; set; } public IconChunk(ChunkSource source, Payload? link, BitmapFontIcon icon) : base(source, link) { - this.Icon = icon; + Icon = icon; } public IconChunk() : base(ChunkSource.None, null) { diff --git a/ChatTwo/Code/ChatCode.cs b/ChatTwo/Code/ChatCode.cs index 2a7a411..c1ecffd 100755 --- a/ChatTwo/Code/ChatCode.cs +++ b/ChatTwo/Code/ChatCode.cs @@ -10,24 +10,24 @@ internal class ChatCode { internal ChatType Type { get; } internal ChatSource Source { get; } internal ChatSource Target { get; } - private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((this.Raw >> shift) & 0xF)); + private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((Raw >> shift) & 0xF)); internal ChatCode(ushort raw) { - this.Raw = raw; - this.Type = (ChatType) (this.Raw & Clear7); - this.Source = this.SourceFrom(11); - this.Target = this.SourceFrom(7); + Raw = raw; + Type = (ChatType) (Raw & Clear7); + Source = SourceFrom(11); + Target = SourceFrom(7); } [BsonCtor] public ChatCode(ushort raw, ChatType type, ChatSource source, ChatSource target) { - this.Raw = raw; - this.Type = type; - this.Source = source; - this.Target = target; + Raw = raw; + Type = type; + Source = source; + Target = target; } - internal ChatType Parent() => this.Type switch { + internal ChatType Parent() => Type switch { ChatType.Say => ChatType.Say, ChatType.GmSay => ChatType.Say, ChatType.Shout => ChatType.Shout, @@ -81,11 +81,11 @@ internal class ChatCode { ChatType.FreeCompanyLoginLogout => ChatType.FreeCompanyAnnouncement, ChatType.PvpTeamAnnouncement => ChatType.PvpTeamAnnouncement, ChatType.PvpTeamLoginLogout => ChatType.PvpTeamAnnouncement, - _ => this.Type, + _ => Type, }; internal bool IsBattle() { - switch (this.Type) { + switch (Type) { case ChatType.Damage: case ChatType.Miss: case ChatType.Action: diff --git a/ChatTwo/Commands.cs b/ChatTwo/Commands.cs index 7b84a3b..bda10e5 100755 --- a/ChatTwo/Commands.cs +++ b/ChatTwo/Commands.cs @@ -7,18 +7,18 @@ internal sealed class Commands : IDisposable { private Dictionary Registered { get; } = new(); internal Commands(Plugin plugin) { - this.Plugin = plugin; + Plugin = plugin; } public void Dispose() { - foreach (var name in this.Registered.Keys) { + foreach (var name in Registered.Keys) { Plugin.CommandManager.RemoveHandler(name); } } internal void Initialise() { - foreach (var wrapper in this.Registered.Values) { - Plugin.CommandManager.AddHandler(wrapper.Name, new CommandInfo(this.Invoke) { + foreach (var wrapper in Registered.Values) { + Plugin.CommandManager.AddHandler(wrapper.Name, new CommandInfo(Invoke) { HelpMessage = wrapper.Description ?? string.Empty, ShowInHelp = wrapper.ShowInHelp, }); @@ -26,7 +26,7 @@ internal sealed class Commands : IDisposable { } internal CommandWrapper Register(string name, string? description = null, bool? showInHelp = null) { - if (this.Registered.TryGetValue(name, out var wrapper)) { + if (Registered.TryGetValue(name, out var wrapper)) { if (description != null) { wrapper.Description = description; } @@ -38,12 +38,12 @@ internal sealed class Commands : IDisposable { return wrapper; } - this.Registered[name] = new CommandWrapper(name, description, showInHelp ?? true); - return this.Registered[name]; + Registered[name] = new CommandWrapper(name, description, showInHelp ?? true); + return Registered[name]; } private void Invoke(string command, string arguments) { - if (!this.Registered.TryGetValue(command, out var wrapper)) { + if (!Registered.TryGetValue(command, out var wrapper)) { Plugin.Log.Warning($"Missing registration for command {command}"); return; } @@ -64,12 +64,12 @@ internal sealed class CommandWrapper { internal event Action? Execute; internal CommandWrapper(string name, string? description, bool showInHelp) { - this.Name = name; - this.Description = description; - this.ShowInHelp = showInHelp; + Name = name; + Description = description; + ShowInHelp = showInHelp; } internal void Invoke(string command, string arguments) { - this.Execute?.Invoke(command, arguments); + Execute?.Invoke(command, arguments); } } diff --git a/ChatTwo/GameFunctions/Context.cs b/ChatTwo/GameFunctions/Context.cs index b51ca87..a2c4237 100755 --- a/ChatTwo/GameFunctions/Context.cs +++ b/ChatTwo/GameFunctions/Context.cs @@ -39,21 +39,21 @@ internal sealed unsafe class Context { private Plugin Plugin { get; } internal Context(Plugin plugin) { - this.Plugin = plugin; + Plugin = plugin; Plugin.GameInteropProvider.InitializeFromAttributes(this); } internal void InviteToNoviceNetwork(string name, ushort world) { - if (this._inviteToNoviceNetwork == null) { + if (_inviteToNoviceNetwork == null) { return; } // 6.3: 221EFD - var a1 = this.Plugin.Functions.GetInfoProxyByIndex(0x14); + var a1 = Plugin.Functions.GetInfoProxyByIndex(0x14); fixed (byte* namePtr = name.ToTerminatedBytes()) { // can specify content id if we have it, but there's no need - this._inviteToNoviceNetwork(a1, 0, world, namePtr); + _inviteToNoviceNetwork(a1, 0, world, namePtr); } } @@ -66,48 +66,48 @@ internal sealed unsafe class Context { // 0x10006: search recipes using this material internal void TryOn(uint itemId, byte stainId) { - if (this._tryOn == null) { + if (_tryOn == null) { return; } - this._tryOn(0xFF, itemId, stainId, 0, 0); + _tryOn(0xFF, itemId, stainId, 0, 0); } internal void LinkItem(uint itemId) { - if (this._linkItem == null) { + if (_linkItem == null) { return; } var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog); - this._linkItem(agent, itemId); + _linkItem(agent, itemId); } internal void OpenItemComparison(uint itemId) { - if (this._itemComparison == null) { + if (_itemComparison == null) { return; } var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemCompare); - this._itemComparison(agent, 0x4D, itemId, 0); + _itemComparison(agent, 0x4D, itemId, 0); } internal void SearchForRecipesUsingItem(uint itemId) { - if (this._searchForRecipesUsingItem == null || this._searchForRecipesUsingItemVfunc is not { } offset) { + if (_searchForRecipesUsingItem == null || _searchForRecipesUsingItemVfunc is not { } offset) { return; } var uiModule = Framework.Instance()->GetUiModule(); var vf = (delegate* unmanaged) uiModule->vfunc[offset / 8]; var a1 = vf(uiModule); - this._searchForRecipesUsingItem(a1, itemId); + _searchForRecipesUsingItem(a1, itemId); } internal void SearchForItem(uint itemId) { - if (this._searchForItem == null) { + if (_searchForItem == null) { return; } var itemFinder = Framework.Instance()->GetUiModule()->GetItemFinderModule(); - this._searchForItem(itemFinder, itemId, 1); + _searchForItem(itemFinder, itemId, 1); } } diff --git a/ChatTwo/GameFunctions/GameFunctions.cs b/ChatTwo/GameFunctions/GameFunctions.cs index 8a604fe..bb71eaa 100755 --- a/ChatTwo/GameFunctions/GameFunctions.cs +++ b/ChatTwo/GameFunctions/GameFunctions.cs @@ -66,26 +66,26 @@ internal unsafe class GameFunctions : IDisposable { internal Context Context { get; } internal GameFunctions(Plugin plugin) { - this.Plugin = plugin; - this.Party = new Party(this.Plugin); - this.Chat = new Chat(this.Plugin); - this.Context = new Context(this.Plugin); + Plugin = plugin; + Party = new Party(Plugin); + Chat = new Chat(Plugin); + Context = new Context(Plugin); Plugin.GameInteropProvider.InitializeFromAttributes(this); - this.ResolveTextCommandPlaceholderHook?.Enable(); + ResolveTextCommandPlaceholderHook?.Enable(); } public void Dispose() { - this.Chat.Dispose(); + Chat.Dispose(); - this.ResolveTextCommandPlaceholderHook?.Dispose(); + ResolveTextCommandPlaceholderHook?.Dispose(); - Marshal.FreeHGlobal(this._placeholderNamePtr); + Marshal.FreeHGlobal(_placeholderNamePtr); } private IntPtr GetInfoModule() { - if (this._infoModuleVfunc is not { } vfunc) { + if (_infoModuleVfunc is not { } vfunc) { return IntPtr.Zero; } @@ -95,25 +95,25 @@ internal unsafe class GameFunctions : IDisposable { } internal IntPtr GetInfoProxyByIndex(uint idx) { - var infoModule = this.GetInfoModule(); - return infoModule == IntPtr.Zero ? IntPtr.Zero : this._getInfoProxyByIndex(infoModule, idx); + var infoModule = GetInfoModule(); + return infoModule == IntPtr.Zero ? IntPtr.Zero : _getInfoProxyByIndex(infoModule, idx); } internal uint? GetCurrentChatLogEntryIndex() { - if (this._currentChatEntryOffset == null) { + if (_currentChatEntryOffset == null) { return null; } var log = (IntPtr) Framework.Instance()->GetUiModule()->GetRaptureLogModule(); - return *(uint*) (log + this._currentChatEntryOffset.Value); + return *(uint*) (log + _currentChatEntryOffset.Value); } internal void SendFriendRequest(string name, ushort world) { - this.ListCommand(name, world, "friendlist"); + ListCommand(name, world, "friendlist"); } internal void AddToBlacklist(string name, ushort world) { - this.ListCommand(name, world, "blist"); + ListCommand(name, world, "blist"); } private void ListCommand(string name, ushort world, string commandName) { @@ -123,8 +123,8 @@ internal unsafe class GameFunctions : IDisposable { } var worldName = row.Name.RawString; - this._replacementName = $"{name}@{worldName}"; - this.Plugin.Common.Functions.Chat.SendMessage($"/{commandName} add {this._placeholder}"); + _replacementName = $"{name}@{worldName}"; + Plugin.Common.Functions.Chat.SendMessage($"/{commandName} add {_placeholder}"); } internal static void SetAddonInteractable(string name, bool interactable) { @@ -236,41 +236,41 @@ internal unsafe class GameFunctions : IDisposable { } internal bool IsMentor() { - if (this._isMentor == null || this._isMentorA1 == null || this._isMentorA1.Value == IntPtr.Zero) { + if (_isMentor == null || _isMentorA1 == null || _isMentorA1.Value == IntPtr.Zero) { return false; } - return this._isMentor(this._isMentorA1.Value) > 0; + return _isMentor(_isMentorA1.Value) > 0; } internal void OpenPartyFinder(uint id) { - if (this._openPartyFinder == null) { + if (_openPartyFinder == null) { return; } var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.LookingForGroup); if (agent != null) { - this._openPartyFinder(agent, id); + _openPartyFinder(agent, id); } } internal void OpenAchievement(uint id) { - if (this._openAchievement == null) { + if (_openAchievement == null) { return; } var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.Achievement); if (agent != null) { - this._openAchievement(agent, id); + _openAchievement(agent, id); } } internal bool IsInInstance() { - if (this._inInstance == null) { + if (_inInstance == null) { return false; } - return this._inInstance() != 0; + return _inInstance() != 0; } internal bool TryOpenAdventurerPlate(ulong playerId) @@ -304,21 +304,21 @@ internal unsafe class GameFunctions : IDisposable { private string? _replacementName; private IntPtr ResolveTextCommandPlaceholderDetour(IntPtr a1, byte* placeholderText, byte a3, byte a4) { - if (this._replacementName == null) { + if (_replacementName == null) { goto Original; } var placeholder = MemoryHelper.ReadStringNullTerminated((IntPtr) placeholderText); - if (placeholder != this._placeholder) { + if (placeholder != _placeholder) { goto Original; } - MemoryHelper.WriteString(this._placeholderNamePtr, this._replacementName); - this._replacementName = null; + MemoryHelper.WriteString(_placeholderNamePtr, _replacementName); + _replacementName = null; - return this._placeholderNamePtr; + return _placeholderNamePtr; Original: - return this.ResolveTextCommandPlaceholderHook!.Original(a1, placeholderText, a3, a4); + return ResolveTextCommandPlaceholderHook!.Original(a1, placeholderText, a3, a4); } } diff --git a/ChatTwo/GameFunctions/Party.cs b/ChatTwo/GameFunctions/Party.cs index 3e582f8..55e9afd 100755 --- a/ChatTwo/GameFunctions/Party.cs +++ b/ChatTwo/GameFunctions/Party.cs @@ -25,57 +25,57 @@ internal sealed unsafe class Party { private Plugin Plugin { get; } internal Party(Plugin plugin) { - this.Plugin = plugin; + Plugin = plugin; Plugin.GameInteropProvider.InitializeFromAttributes(this); } internal void InviteSameWorld(string name, ushort world, ulong contentId) { - if (this._inviteToParty == null) { + if (_inviteToParty == null) { return; } // 6.11: 214A55 - var a1 = this.Plugin.Functions.GetInfoProxyByIndex(2); + var a1 = Plugin.Functions.GetInfoProxyByIndex(2); fixed (byte* namePtr = name.ToTerminatedBytes()) { // this only works if target is on the same world - this._inviteToParty(a1, contentId, namePtr, world); + _inviteToParty(a1, contentId, namePtr, world); } } internal void InviteOtherWorld(ulong contentId) { - if (this._inviteToPartyContentId == null) { + if (_inviteToPartyContentId == null) { return; } // 6.11: 214A55 - var a1 = this.Plugin.Functions.GetInfoProxyByIndex(2); + var a1 = Plugin.Functions.GetInfoProxyByIndex(2); if (contentId != 0) { // third param is world, but it requires a specific world // if they're not on that world, it will fail // pass 0 and it will work on any world EXCEPT for the world the // current player is on - this._inviteToPartyContentId(a1, contentId, 0); + _inviteToPartyContentId(a1, contentId, 0); } } internal void InviteInInstance(ulong contentId) { - if (this._inviteToPartyInInstance == null) { + if (_inviteToPartyInInstance == null) { return; } // 6.11: 214A55 - var a1 = this.Plugin.Functions.GetInfoProxyByIndex(2); + var a1 = Plugin.Functions.GetInfoProxyByIndex(2); if (contentId != 0) { // third param is world, but it requires a specific world // if they're not on that world, it will fail // pass 0 and it will work on any world EXCEPT for the world the // current player is on - this._inviteToPartyInInstance(a1, contentId); + _inviteToPartyInInstance(a1, contentId); } } internal void Kick(string name, ulong contentId) { - if (this._kick == null) { + if (_kick == null) { return; } @@ -85,12 +85,12 @@ internal sealed unsafe class Party { } fixed (byte* namePtr = name.ToTerminatedBytes()) { - this._kick(agent, namePtr, 0, contentId); + _kick(agent, namePtr, 0, contentId); } } internal void Promote(string name, ulong contentId) { - if (this._promote == null) { + if (_promote == null) { return; } @@ -100,7 +100,7 @@ internal sealed unsafe class Party { } fixed (byte* namePtr = name.ToTerminatedBytes()) { - this._promote(agent, namePtr, 0, contentId); + _promote(agent, namePtr, 0, contentId); } } } diff --git a/ChatTwo/GameFunctions/Types/ChannelSwitchInfo.cs b/ChatTwo/GameFunctions/Types/ChannelSwitchInfo.cs index f4d1c87..3d82528 100755 --- a/ChatTwo/GameFunctions/Types/ChannelSwitchInfo.cs +++ b/ChatTwo/GameFunctions/Types/ChannelSwitchInfo.cs @@ -9,9 +9,9 @@ internal class ChannelSwitchInfo { internal string? Text { get; } internal ChannelSwitchInfo(InputChannel? channel, bool permanent = false, RotateMode rotate = RotateMode.None, string? text = null) { - this.Channel = channel; - this.Permanent = permanent; - this.Rotate = rotate; - this.Text = text; + Channel = channel; + Permanent = permanent; + Rotate = rotate; + Text = text; } } diff --git a/ChatTwo/GameFunctions/Types/ChatActivatedArgs.cs b/ChatTwo/GameFunctions/Types/ChatActivatedArgs.cs index da753da..ada612a 100755 --- a/ChatTwo/GameFunctions/Types/ChatActivatedArgs.cs +++ b/ChatTwo/GameFunctions/Types/ChatActivatedArgs.cs @@ -8,6 +8,6 @@ internal sealed class ChatActivatedArgs { internal TellTarget? TellTarget { get; init; } internal ChatActivatedArgs(ChannelSwitchInfo channelSwitchInfo) { - this.ChannelSwitchInfo = channelSwitchInfo; + ChannelSwitchInfo = channelSwitchInfo; } } diff --git a/ChatTwo/GameFunctions/Types/TellHistoryInfo.cs b/ChatTwo/GameFunctions/Types/TellHistoryInfo.cs index 3804724..dc9d2f8 100755 --- a/ChatTwo/GameFunctions/Types/TellHistoryInfo.cs +++ b/ChatTwo/GameFunctions/Types/TellHistoryInfo.cs @@ -6,8 +6,8 @@ internal sealed class TellHistoryInfo { internal ulong ContentId { get; } internal TellHistoryInfo(string name, uint world, ulong contentId) { - this.Name = name; - this.World = world; - this.ContentId = contentId; + Name = name; + World = world; + ContentId = contentId; } } diff --git a/ChatTwo/GameFunctions/Types/TellTarget.cs b/ChatTwo/GameFunctions/Types/TellTarget.cs index 47b6324..a8705d1 100755 --- a/ChatTwo/GameFunctions/Types/TellTarget.cs +++ b/ChatTwo/GameFunctions/Types/TellTarget.cs @@ -7,9 +7,9 @@ internal sealed class TellTarget { internal TellReason Reason { get; } internal TellTarget(string name, ushort world, ulong contentId, TellReason reason) { - this.Name = name; - this.World = world; - this.ContentId = contentId; - this.Reason = reason; + Name = name; + World = world; + ContentId = contentId; + Reason = reason; } } diff --git a/ChatTwo/Ipc/ExtraChat.cs b/ChatTwo/Ipc/ExtraChat.cs index bfc607d..113a025 100644 --- a/ChatTwo/Ipc/ExtraChat.cs +++ b/ChatTwo/Ipc/ExtraChat.cs @@ -19,47 +19,47 @@ internal sealed class ExtraChat : IDisposable { internal (string, uint)? ChannelOverride { get; set; } private Dictionary ChannelCommandColoursInternal { get; set; } = new(); - internal IReadOnlyDictionary ChannelCommandColours => this.ChannelCommandColoursInternal; + internal IReadOnlyDictionary ChannelCommandColours => ChannelCommandColoursInternal; private Dictionary ChannelNamesInternal { get; set; } = new(); - internal IReadOnlyDictionary ChannelNames => this.ChannelNamesInternal; + internal IReadOnlyDictionary ChannelNames => ChannelNamesInternal; internal ExtraChat(Plugin plugin) { - this.Plugin = plugin; + Plugin = plugin; - this.OverrideChannelGate = Plugin.Interface.GetIpcSubscriber("ExtraChat.OverrideChannelColour"); - this.ChannelCommandColoursGate = Plugin.Interface.GetIpcSubscriber, Dictionary>("ExtraChat.ChannelCommandColours"); - this.ChannelNamesGate = Plugin.Interface.GetIpcSubscriber, Dictionary>("ExtraChat.ChannelNames"); + OverrideChannelGate = Plugin.Interface.GetIpcSubscriber("ExtraChat.OverrideChannelColour"); + ChannelCommandColoursGate = Plugin.Interface.GetIpcSubscriber, Dictionary>("ExtraChat.ChannelCommandColours"); + ChannelNamesGate = Plugin.Interface.GetIpcSubscriber, Dictionary>("ExtraChat.ChannelNames"); - this.OverrideChannelGate.Subscribe(this.OnOverrideChannel); - this.ChannelCommandColoursGate.Subscribe(this.OnChannelCommandColours); - this.ChannelNamesGate.Subscribe(this.OnChannelNames); + OverrideChannelGate.Subscribe(OnOverrideChannel); + ChannelCommandColoursGate.Subscribe(OnChannelCommandColours); + ChannelNamesGate.Subscribe(OnChannelNames); try { - this.ChannelCommandColoursInternal = this.ChannelCommandColoursGate.InvokeFunc(null!); - this.ChannelNamesInternal = this.ChannelNamesGate.InvokeFunc(null!); + ChannelCommandColoursInternal = ChannelCommandColoursGate.InvokeFunc(null!); + ChannelNamesInternal = ChannelNamesGate.InvokeFunc(null!); } catch (Exception) { // no-op } } public void Dispose() { - this.OverrideChannelGate.Unsubscribe(this.OnOverrideChannel); + OverrideChannelGate.Unsubscribe(OnOverrideChannel); } private void OnOverrideChannel(OverrideInfo info) { if (info.Channel == null) { - this.ChannelOverride = null; + ChannelOverride = null; return; } - this.ChannelOverride = (info.Channel, info.Rgba); + ChannelOverride = (info.Channel, info.Rgba); } private void OnChannelCommandColours(Dictionary obj) { - this.ChannelCommandColoursInternal = obj; + ChannelCommandColoursInternal = obj; } private void OnChannelNames(Dictionary obj) { - this.ChannelNamesInternal = obj; + ChannelNamesInternal = obj; } } diff --git a/ChatTwo/IpcManager.cs b/ChatTwo/IpcManager.cs index 0148d53..801b996 100755 --- a/ChatTwo/IpcManager.cs +++ b/ChatTwo/IpcManager.cs @@ -15,38 +15,38 @@ internal sealed class IpcManager : IDisposable { internal List Registered { get; } = new(); public IpcManager(DalamudPluginInterface pluginInterface) { - this.Interface = pluginInterface; + Interface = pluginInterface; - this.RegisterGate = this.Interface.GetIpcProvider("ChatTwo.Register"); - this.RegisterGate.RegisterFunc(this.Register); + RegisterGate = Interface.GetIpcProvider("ChatTwo.Register"); + RegisterGate.RegisterFunc(Register); - this.AvailableGate = this.Interface.GetIpcProvider("ChatTwo.Available"); + AvailableGate = Interface.GetIpcProvider("ChatTwo.Available"); - this.UnregisterGate = this.Interface.GetIpcProvider("ChatTwo.Unregister"); - this.UnregisterGate.RegisterAction(this.Unregister); + UnregisterGate = Interface.GetIpcProvider("ChatTwo.Unregister"); + UnregisterGate.RegisterAction(Unregister); - this.InvokeGate = this.Interface.GetIpcProvider("ChatTwo.Invoke"); + InvokeGate = Interface.GetIpcProvider("ChatTwo.Invoke"); - this.AvailableGate.SendMessage(); + AvailableGate.SendMessage(); } internal void Invoke(string id, PlayerPayload? sender, ulong contentId, Payload? payload, SeString? senderString, SeString? content) { - this.InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content); + InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content); } private string Register() { var id = Guid.NewGuid().ToString(); - this.Registered.Add(id); + Registered.Add(id); return id; } private void Unregister(string id) { - this.Registered.Remove(id); + Registered.Remove(id); } public void Dispose() { - this.UnregisterGate.UnregisterFunc(); - this.RegisterGate.UnregisterFunc(); - this.Registered.Clear(); + UnregisterGate.UnregisterFunc(); + RegisterGate.UnregisterFunc(); + Registered.Clear(); } } diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index 5a40276..4f5803e 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -12,15 +12,15 @@ internal class SortCode { internal ChatSource Source { get; set; } internal SortCode(ChatType type, ChatSource source) { - this.Type = type; - this.Source = source; + Type = type; + Source = source; } public SortCode() { } private bool Equals(SortCode other) { - return this.Type == other.Type && this.Source == other.Source; + return Type == other.Type && Source == other.Source; } public override bool Equals(object? obj) { @@ -32,12 +32,12 @@ internal class SortCode { return true; } - return obj.GetType() == this.GetType() && this.Equals((SortCode) obj); + return obj.GetType() == GetType() && Equals((SortCode) obj); } public override int GetHashCode() { unchecked { - return ((int) this.Type * 397) ^ (int) this.Source; + return ((int) Type * 397) ^ (int) Source; } } } @@ -68,16 +68,16 @@ internal class Message { internal int Hash { get; } internal Message(ulong receiver, ChatCode code, List sender, List content, SeString senderSource, SeString contentSource) { - this.Receiver = receiver; - this.Date = DateTime.UtcNow; - this.Code = code; - this.Sender = sender; - this.Content = ReplaceContentURLs(content); - this.SenderSource = senderSource; - this.ContentSource = contentSource; - this.SortCode = new SortCode(this.Code.Type, this.Code.Source); - this.ExtraChatChannel = this.ExtractExtraChatChannel(); - this.Hash = this.GenerateHash(); + Receiver = receiver; + Date = DateTime.UtcNow; + Code = code; + Sender = sender; + Content = ReplaceContentURLs(content); + SenderSource = senderSource; + ContentSource = contentSource; + SortCode = new SortCode(Code.Type, Code.Source); + ExtraChatChannel = ExtractExtraChatChannel(); + Hash = GenerateHash(); foreach (var chunk in sender.Concat(content)) { chunk.Message = this; @@ -85,56 +85,56 @@ internal class Message { } internal Message(ObjectId id, ulong receiver, ulong contentId, DateTime date, BsonDocument code, BsonArray sender, BsonArray content, BsonValue senderSource, BsonValue contentSource, BsonDocument sortCode) { - this.Id = id; - this.Receiver = receiver; - this.ContentId = contentId; - this.Date = date; - this.Code = BsonMapper.Global.ToObject(code); - this.Sender = BsonMapper.Global.Deserialize>(sender); + Id = id; + Receiver = receiver; + ContentId = contentId; + Date = date; + Code = BsonMapper.Global.ToObject(code); + Sender = BsonMapper.Global.Deserialize>(sender); // Don't call ReplaceContentURLs here since we're loading the message // from the database and it should already have parsed URL data. - this.Content = BsonMapper.Global.Deserialize>(content); - this.SenderSource = BsonMapper.Global.Deserialize(senderSource); - this.ContentSource = BsonMapper.Global.Deserialize(contentSource); - this.SortCode = BsonMapper.Global.ToObject(sortCode); - this.ExtraChatChannel = this.ExtractExtraChatChannel(); - this.Hash = this.GenerateHash(); + Content = BsonMapper.Global.Deserialize>(content); + SenderSource = BsonMapper.Global.Deserialize(senderSource); + ContentSource = BsonMapper.Global.Deserialize(contentSource); + SortCode = BsonMapper.Global.ToObject(sortCode); + ExtraChatChannel = ExtractExtraChatChannel(); + Hash = GenerateHash(); - foreach (var chunk in this.Sender.Concat(this.Content)) { + foreach (var chunk in Sender.Concat(Content)) { chunk.Message = this; } } internal Message(ObjectId id, ulong receiver, ulong contentId, DateTime date, BsonDocument code, BsonArray sender, BsonArray content, BsonValue senderSource, BsonValue contentSource, BsonDocument sortCode, BsonValue extraChatChannel) { - this.Id = id; - this.Receiver = receiver; - this.ContentId = contentId; - this.Date = date; - this.Code = BsonMapper.Global.ToObject(code); - this.Sender = BsonMapper.Global.Deserialize>(sender); + Id = id; + Receiver = receiver; + ContentId = contentId; + Date = date; + Code = BsonMapper.Global.ToObject(code); + Sender = BsonMapper.Global.Deserialize>(sender); // Don't call ReplaceContentURLs here since we're loading the message // from the database and it should already have parsed URL data. - this.Content = BsonMapper.Global.Deserialize>(content); - this.SenderSource = BsonMapper.Global.Deserialize(senderSource); - this.ContentSource = BsonMapper.Global.Deserialize(contentSource); - this.SortCode = BsonMapper.Global.ToObject(sortCode); - this.ExtraChatChannel = BsonMapper.Global.Deserialize(extraChatChannel); - this.Hash = this.GenerateHash(); + Content = BsonMapper.Global.Deserialize>(content); + SenderSource = BsonMapper.Global.Deserialize(senderSource); + ContentSource = BsonMapper.Global.Deserialize(contentSource); + SortCode = BsonMapper.Global.ToObject(sortCode); + ExtraChatChannel = BsonMapper.Global.Deserialize(extraChatChannel); + Hash = GenerateHash(); - foreach (var chunk in this.Sender.Concat(this.Content)) { + foreach (var chunk in Sender.Concat(Content)) { chunk.Message = this; } } private int GenerateHash() { - return this.SortCode.GetHashCode() - ^ this.ExtraChatChannel.GetHashCode() - ^ string.Join("", this.Sender.Select(c => c.StringValue())).GetHashCode() - ^ string.Join("", this.Content.Select(c => c.StringValue())).GetHashCode(); + return SortCode.GetHashCode() + ^ ExtraChatChannel.GetHashCode() + ^ string.Join("", Sender.Select(c => c.StringValue())).GetHashCode() + ^ string.Join("", Content.Select(c => c.StringValue())).GetHashCode(); } private Guid ExtractExtraChatChannel() { - if (this.ContentSource.Payloads.Count > 0 && this.ContentSource.Payloads[0] is RawPayload raw) { + if (ContentSource.Payloads.Count > 0 && ContentSource.Payloads[0] is RawPayload raw) { // this does an encode and clone every time it's accessed, so cache var data = raw.Data; if (data[1] == 0x27 && data[2] == 18 && data[3] == 0x20) { diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index d6749bd..785641f 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1967,6 +1967,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Override your selected dalamud style with a different one. + /// + internal static string Options_OverrideStyle_Name_Desc { + get { + return ResourceManager.GetString("Options_OverrideStyle_Name_Desc", resourceCulture); + } + } + /// /// Looks up a localized string similar to Styles. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index 5fe2004..80236d4 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -920,4 +920,7 @@ Get help in the discord thread: + + Override your selected dalamud style with a different one + diff --git a/ChatTwo/TextureCache.cs b/ChatTwo/TextureCache.cs index 6c0caad..06ad9a4 100755 --- a/ChatTwo/TextureCache.cs +++ b/ChatTwo/TextureCache.cs @@ -11,17 +11,17 @@ internal class TextureCache : IDisposable { private readonly Dictionary<(uint, bool), IDalamudTextureWrap> _statusIcons = new(); private readonly Dictionary<(uint, bool), IDalamudTextureWrap> _eventItemIcons = new(); - internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> ItemIcons => this._itemIcons; - internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> StatusIcons => this._statusIcons; - internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> EventItemIcons => this._eventItemIcons; + internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> ItemIcons => _itemIcons; + internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> StatusIcons => _statusIcons; + internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> EventItemIcons => _eventItemIcons; internal TextureCache(ITextureProvider textureProvider) { - this.TextureProvider = textureProvider; + TextureProvider = textureProvider; } public void Dispose() { - var allIcons = this.ItemIcons.Values - .Concat(this.StatusIcons.Values); + var allIcons = ItemIcons.Values + .Concat(StatusIcons.Values); foreach (var tex in allIcons) { tex.Dispose(); @@ -34,40 +34,40 @@ internal class TextureCache : IDisposable { } var tex = hq - ? this.TextureProvider.GetIcon(icon, ITextureProvider.IconFlags.ItemHighQuality) - : this.TextureProvider.GetIcon(icon); + ? TextureProvider.GetIcon(icon, ITextureProvider.IconFlags.ItemHighQuality) + : TextureProvider.GetIcon(icon); if (tex != null) { dict[(icon, hq)] = tex; } } internal void AddItem(Item item, bool hq) { - this.AddIcon(this._itemIcons, item.Icon, hq); + AddIcon(_itemIcons, item.Icon, hq); } internal void AddStatus(Status status) { - this.AddIcon(this._statusIcons, status.Icon); + AddIcon(_statusIcons, status.Icon); } internal void AddEventItem(EventItem item) { - this.AddIcon(this._eventItemIcons, item.Icon); + AddIcon(_eventItemIcons, item.Icon); } internal IDalamudTextureWrap? GetItem(Item item, bool hq = false) { - this.AddItem(item, hq); - this.ItemIcons.TryGetValue((item.Icon, hq), out var icon); + AddItem(item, hq); + ItemIcons.TryGetValue((item.Icon, hq), out var icon); return icon; } internal IDalamudTextureWrap? GetStatus(Status status) { - this.AddStatus(status); - this.StatusIcons.TryGetValue((status.Icon, false), out var icon); + AddStatus(status); + StatusIcons.TryGetValue((status.Icon, false), out var icon); return icon; } internal IDalamudTextureWrap? GetEventItem(EventItem item) { - this.AddEventItem(item); - this.EventItemIcons.TryGetValue((item.Icon, false), out var icon); + AddEventItem(item); + EventItemIcons.TryGetValue((item.Icon, false), out var icon); return icon; } } diff --git a/ChatTwo/Ui/AutoCompleteInfo.cs b/ChatTwo/Ui/AutoCompleteInfo.cs index 756c425..e0d7222 100755 --- a/ChatTwo/Ui/AutoCompleteInfo.cs +++ b/ChatTwo/Ui/AutoCompleteInfo.cs @@ -6,8 +6,8 @@ internal class AutoCompleteInfo { internal int EndPos { get; } internal AutoCompleteInfo(string toComplete, int startPos, int endPos) { - this.ToComplete = toComplete; - this.StartPos = startPos; - this.EndPos = endPos; + ToComplete = toComplete; + StartPos = startPos; + EndPos = endPos; } } diff --git a/ChatTwo/Ui/Fonts.cs b/ChatTwo/Ui/Fonts.cs index ceec9e1..2468152 100755 --- a/ChatTwo/Ui/Fonts.cs +++ b/ChatTwo/Ui/Fonts.cs @@ -169,7 +169,7 @@ internal sealed class FaceData { internal byte[] Data { get; } internal FaceData(byte[] data) { - this.Data = data; + Data = data; } } @@ -178,8 +178,8 @@ internal sealed class FontData { internal FaceData? Italic { get; } internal FontData(FaceData regular, FaceData? italic) { - this.Regular = regular; - this.Italic = italic; + Regular = regular; + Italic = italic; } } @@ -189,8 +189,8 @@ internal sealed class Font { internal string ResourcePathItalic { get; } internal Font(string name, string resourcePath, string resourcePathItalic) { - this.Name = name; - this.ResourcePath = resourcePath; - this.ResourcePathItalic = resourcePathItalic; + Name = name; + ResourcePath = resourcePath; + ResourcePathItalic = resourcePathItalic; } } diff --git a/ChatTwo/Ui/SettingsTabs/ChatColours.cs b/ChatTwo/Ui/SettingsTabs/ChatColours.cs index b49b21f..0571895 100755 --- a/ChatTwo/Ui/SettingsTabs/ChatColours.cs +++ b/ChatTwo/Ui/SettingsTabs/ChatColours.cs @@ -13,8 +13,8 @@ internal sealed class ChatColours : ISettingsTab { public string Name => Language.Options_ChatColours_Tab + "###tabs-chat-colours"; internal ChatColours(Configuration mutable, Plugin plugin) { - this.Mutable = mutable; - this.Plugin = plugin; + Mutable = mutable; + Plugin = plugin; #if DEBUG var sortable = ChatTypeExt.SortOrder @@ -36,23 +36,23 @@ internal sealed class ChatColours : ISettingsTab { foreach (var (_, types) in ChatTypeExt.SortOrder) { foreach (var type in types) { if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", Language.Options_ChatColours_Reset)) { - this.Mutable.ChatColours.Remove(type); + Mutable.ChatColours.Remove(type); } ImGui.SameLine(); if (ImGuiUtil.IconButton(FontAwesomeIcon.LongArrowAltDown, $"{type}", Language.Options_ChatColours_Import)) { - var gameColour = this.Plugin.Functions.Chat.GetChannelColour(type); - this.Mutable.ChatColours[type] = gameColour ?? type.DefaultColour() ?? 0; + var gameColour = Plugin.Functions.Chat.GetChannelColour(type); + Mutable.ChatColours[type] = gameColour ?? type.DefaultColour() ?? 0; } ImGui.SameLine(); - var vec = this.Mutable.ChatColours.TryGetValue(type, out var colour) + var vec = Mutable.ChatColours.TryGetValue(type, out var colour) ? ColourUtil.RgbaToVector3(colour) : ColourUtil.RgbaToVector3(type.DefaultColour() ?? 0); if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs)) { - this.Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec); + Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec); } } } diff --git a/ChatTwo/Ui/SettingsTabs/Database.cs b/ChatTwo/Ui/SettingsTabs/Database.cs index 97aa67b..f0814f5 100755 --- a/ChatTwo/Ui/SettingsTabs/Database.cs +++ b/ChatTwo/Ui/SettingsTabs/Database.cs @@ -11,36 +11,36 @@ internal sealed class Database : ISettingsTab { public string Name => Language.Options_Database_Tab + "###tabs-database"; internal Database(Configuration mutable, Store store) { - this.Store = store; - this.Mutable = mutable; + Store = store; + Mutable = mutable; } private bool _showAdvanced; public void Draw(bool changed) { if (changed) { - this._showAdvanced = ImGui.GetIO().KeyShift; + _showAdvanced = ImGui.GetIO().KeyShift; } - ImGuiUtil.OptionCheckbox(ref this.Mutable.DatabaseBattleMessages, Language.Options_DatabaseBattleMessages_Name, Language.Options_DatabaseBattleMessages_Description); + ImGuiUtil.OptionCheckbox(ref Mutable.DatabaseBattleMessages, Language.Options_DatabaseBattleMessages_Name, Language.Options_DatabaseBattleMessages_Description); ImGui.Spacing(); - if (ImGuiUtil.OptionCheckbox(ref this.Mutable.LoadPreviousSession, Language.Options_LoadPreviousSession_Name, Language.Options_LoadPreviousSession_Description)) { - if (this.Mutable.LoadPreviousSession) { - this.Mutable.FilterIncludePreviousSessions = true; + if (ImGuiUtil.OptionCheckbox(ref Mutable.LoadPreviousSession, Language.Options_LoadPreviousSession_Name, Language.Options_LoadPreviousSession_Description)) { + if (Mutable.LoadPreviousSession) { + Mutable.FilterIncludePreviousSessions = true; } } ImGui.Spacing(); - if (ImGuiUtil.OptionCheckbox(ref this.Mutable.FilterIncludePreviousSessions, Language.Options_FilterIncludePreviousSessions_Name, Language.Options_FilterIncludePreviousSessions_Description)) { - if (!this.Mutable.FilterIncludePreviousSessions) { - this.Mutable.LoadPreviousSession = false; + if (ImGuiUtil.OptionCheckbox(ref Mutable.FilterIncludePreviousSessions, Language.Options_FilterIncludePreviousSessions_Name, Language.Options_FilterIncludePreviousSessions_Description)) { + if (!Mutable.FilterIncludePreviousSessions) { + Mutable.LoadPreviousSession = false; } } ImGuiUtil.OptionCheckbox( - ref this.Mutable.SharedMode, + ref Mutable.SharedMode, Language.Options_SharedMode_Name, string.Format(Language.Options_SharedMode_Description, Plugin.PluginName) ); @@ -48,16 +48,16 @@ internal sealed class Database : ISettingsTab { ImGui.Spacing(); - if (this._showAdvanced && ImGui.TreeNodeEx(Language.Options_Database_Advanced)) { + if (_showAdvanced && ImGui.TreeNodeEx(Language.Options_Database_Advanced)) { ImGui.PushTextWrapPos(); ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning); if (ImGui.Button("Checkpoint")) { - this.Store.Database.Checkpoint(); + Store.Database.Checkpoint(); } if (ImGui.Button("Rebuild")) { - this.Store.Database.Rebuild(); + Store.Database.Rebuild(); } ImGui.PopTextWrapPos(); diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs index 662d3a5..2c938c5 100755 --- a/ChatTwo/Ui/SettingsTabs/Display.cs +++ b/ChatTwo/Ui/SettingsTabs/Display.cs @@ -90,7 +90,7 @@ internal sealed class Display : ISettingsTab { ImGuiUtil.OptionCheckbox(ref Mutable.ShowPopOutTitleBar, Language.Options_ShowPopOutTitleBar_Name); ImGui.Spacing(); - ImGui.Checkbox(Language.Options_OverrideStyle_Name, ref Mutable.OverrideStyle); + ImGuiUtil.OptionCheckbox(ref Mutable.OverrideStyle, Language.Options_OverrideStyle_Name, Language.Options_OverrideStyle_Name_Desc); ImGui.Spacing(); if (Mutable.OverrideStyle) @@ -98,7 +98,7 @@ internal sealed class Display : ISettingsTab { var currentStyle = Mutable.ChosenStyle.Equals("") ? StyleModel.GetConfiguredStyle().Name : Mutable.ChosenStyle; if (ImGui.BeginCombo(Language.Options_OverrideStyleDropdown_Name, currentStyle)) { foreach (var style in StyleModel.GetConfiguredStyles()) { - if (ImGui.Selectable(style.Name, this.Mutable.ChosenStyle == style.Name)) { + if (ImGui.Selectable(style.Name, Mutable.ChosenStyle == style.Name)) { Mutable.ChosenStyle = style.Name; } } diff --git a/ChatTwo/Ui/SettingsTabs/Fonts.cs b/ChatTwo/Ui/SettingsTabs/Fonts.cs index db14ee8..95474f4 100755 --- a/ChatTwo/Ui/SettingsTabs/Fonts.cs +++ b/ChatTwo/Ui/SettingsTabs/Fonts.cs @@ -12,45 +12,45 @@ public class Fonts : ISettingsTab { private List JpFonts { get; set; } = new(); internal Fonts(Configuration mutable) { - this.Mutable = mutable; - this.UpdateFonts(); + Mutable = mutable; + UpdateFonts(); } private void UpdateFonts() { - this.GlobalFonts = Ui.Fonts.GetFonts(); - this.JpFonts = Ui.Fonts.GetJpFonts(); + GlobalFonts = Ui.Fonts.GetFonts(); + JpFonts = Ui.Fonts.GetJpFonts(); } public void Draw(bool changed) { if (changed) { - this.UpdateFonts(); + UpdateFonts(); } ImGui.PushTextWrapPos(); - ImGui.Checkbox(Language.Options_FontsEnabled, ref this.Mutable.FontsEnabled); + ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled); ImGui.Spacing(); - if (this.Mutable.FontsEnabled) { - if (ImGuiUtil.BeginComboVertical(Language.Options_Font_Name, this.Mutable.GlobalFont)) { + if (Mutable.FontsEnabled) { + if (ImGuiUtil.BeginComboVertical(Language.Options_Font_Name, Mutable.GlobalFont)) { foreach (var font in Ui.Fonts.GlobalFonts) { - if (ImGui.Selectable(font.Name, this.Mutable.GlobalFont == font.Name)) { - this.Mutable.GlobalFont = font.Name; + if (ImGui.Selectable(font.Name, Mutable.GlobalFont == font.Name)) { + Mutable.GlobalFont = font.Name; } - if (ImGui.IsWindowAppearing() && this.Mutable.GlobalFont == font.Name) { + if (ImGui.IsWindowAppearing() && Mutable.GlobalFont == font.Name) { ImGui.SetScrollHereY(0.5f); } } ImGui.Separator(); - foreach (var name in this.GlobalFonts) { - if (ImGui.Selectable(name, this.Mutable.GlobalFont == name)) { - this.Mutable.GlobalFont = name; + foreach (var name in GlobalFonts) { + if (ImGui.Selectable(name, Mutable.GlobalFont == name)) { + Mutable.GlobalFont = name; } - if (ImGui.IsWindowAppearing() && this.Mutable.GlobalFont == name) { + if (ImGui.IsWindowAppearing() && Mutable.GlobalFont == name) { ImGui.SetScrollHereY(0.5f); } } @@ -62,25 +62,25 @@ public class Fonts : ISettingsTab { ImGuiUtil.WarningText(Language.Options_Font_Warning); ImGui.Spacing(); - if (ImGuiUtil.BeginComboVertical(Language.Options_JapaneseFont_Name, this.Mutable.JapaneseFont)) { + if (ImGuiUtil.BeginComboVertical(Language.Options_JapaneseFont_Name, Mutable.JapaneseFont)) { foreach (var (name, _) in Ui.Fonts.JapaneseFonts) { - if (ImGui.Selectable(name, this.Mutable.JapaneseFont == name)) { - this.Mutable.JapaneseFont = name; + if (ImGui.Selectable(name, Mutable.JapaneseFont == name)) { + Mutable.JapaneseFont = name; } - if (ImGui.IsWindowAppearing() && this.Mutable.JapaneseFont == name) { + if (ImGui.IsWindowAppearing() && Mutable.JapaneseFont == name) { ImGui.SetScrollHereY(0.5f); } } ImGui.Separator(); - foreach (var family in this.JpFonts) { - if (ImGui.Selectable(family, this.Mutable.JapaneseFont == family)) { - this.Mutable.JapaneseFont = family; + foreach (var family in JpFonts) { + if (ImGui.Selectable(family, Mutable.JapaneseFont == family)) { + Mutable.JapaneseFont = family; } - if (ImGui.IsWindowAppearing() && this.Mutable.JapaneseFont == family) { + if (ImGui.IsWindowAppearing() && Mutable.JapaneseFont == family) { ImGui.SetScrollHereY(0.5f); } } @@ -94,12 +94,12 @@ public class Fonts : ISettingsTab { if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name)) { ImGuiUtil.HelpText(string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName)); - var range = (int) this.Mutable.ExtraGlyphRanges; + var range = (int) Mutable.ExtraGlyphRanges; foreach (var extra in Enum.GetValues()) { ImGui.CheckboxFlags(extra.Name(), ref range, (int) extra); } - this.Mutable.ExtraGlyphRanges = (ExtraGlyphRanges) range; + Mutable.ExtraGlyphRanges = (ExtraGlyphRanges) range; } ImGui.Spacing(); @@ -108,9 +108,9 @@ public class Fonts : ISettingsTab { const float speed = .0125f; const float min = 8f; const float max = 36f; - ImGuiUtil.DragFloatVertical(Language.Options_FontSize_Name, ref this.Mutable.FontSize, speed, min, max, $"{this.Mutable.FontSize:N1}"); - ImGuiUtil.DragFloatVertical(Language.Options_JapaneseFontSize_Name, ref this.Mutable.JapaneseFontSize, speed, min, max, $"{this.Mutable.JapaneseFontSize:N1}"); - ImGuiUtil.DragFloatVertical(Language.Options_SymbolsFontSize_Name, ref this.Mutable.SymbolsFontSize, speed, min, max, $"{this.Mutable.SymbolsFontSize:N1}"); + ImGuiUtil.DragFloatVertical(Language.Options_FontSize_Name, ref Mutable.FontSize, speed, min, max, $"{Mutable.FontSize:N1}"); + ImGuiUtil.DragFloatVertical(Language.Options_JapaneseFontSize_Name, ref Mutable.JapaneseFontSize, speed, min, max, $"{Mutable.JapaneseFontSize:N1}"); + ImGuiUtil.DragFloatVertical(Language.Options_SymbolsFontSize_Name, ref Mutable.SymbolsFontSize, speed, min, max, $"{Mutable.SymbolsFontSize:N1}"); ImGuiUtil.HelpText(Language.Options_SymbolsFontSize_Description); ImGui.PopTextWrapPos(); diff --git a/ChatTwo/Ui/SettingsTabs/Miscellaneous.cs b/ChatTwo/Ui/SettingsTabs/Miscellaneous.cs index 5b9dceb..e03a795 100755 --- a/ChatTwo/Ui/SettingsTabs/Miscellaneous.cs +++ b/ChatTwo/Ui/SettingsTabs/Miscellaneous.cs @@ -10,14 +10,14 @@ internal sealed class Miscellaneous : ISettingsTab { public string Name => Language.Options_Miscellaneous_Tab + "###tabs-miscellaneous"; public Miscellaneous(Configuration mutable) { - this.Mutable = mutable; + Mutable = mutable; } public void Draw(bool changed) { - if (ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, this.Mutable.LanguageOverride.Name())) { + if (ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, Mutable.LanguageOverride.Name())) { foreach (var language in Enum.GetValues()) { if (ImGui.Selectable(language.Name())) { - this.Mutable.LanguageOverride = language; + Mutable.LanguageOverride = language; } } @@ -27,10 +27,10 @@ internal sealed class Miscellaneous : ISettingsTab { ImGuiUtil.HelpText(string.Format(Language.Options_Language_Description, Plugin.PluginName)); ImGui.Spacing(); - if (ImGuiUtil.BeginComboVertical(Language.Options_CommandHelpSide_Name, this.Mutable.CommandHelpSide.Name())) { + if (ImGuiUtil.BeginComboVertical(Language.Options_CommandHelpSide_Name, Mutable.CommandHelpSide.Name())) { foreach (var side in Enum.GetValues()) { - if (ImGui.Selectable(side.Name(), this.Mutable.CommandHelpSide == side)) { - this.Mutable.CommandHelpSide = side; + if (ImGui.Selectable(side.Name(), Mutable.CommandHelpSide == side)) { + Mutable.CommandHelpSide = side; } } @@ -40,10 +40,10 @@ internal sealed class Miscellaneous : ISettingsTab { ImGuiUtil.HelpText(string.Format(Language.Options_CommandHelpSide_Description, Plugin.PluginName)); ImGui.Spacing(); - if (ImGuiUtil.BeginComboVertical(Language.Options_KeybindMode_Name, this.Mutable.KeybindMode.Name())) { + if (ImGuiUtil.BeginComboVertical(Language.Options_KeybindMode_Name, Mutable.KeybindMode.Name())) { foreach (var mode in Enum.GetValues()) { - if (ImGui.Selectable(mode.Name(), this.Mutable.KeybindMode == mode)) { - this.Mutable.KeybindMode = mode; + if (ImGui.Selectable(mode.Name(), Mutable.KeybindMode == mode)) { + Mutable.KeybindMode = mode; } if (ImGui.IsItemHovered()) { @@ -59,7 +59,7 @@ internal sealed class Miscellaneous : ISettingsTab { ImGuiUtil.HelpText(string.Format(Language.Options_KeybindMode_Description, Plugin.PluginName)); ImGui.Spacing(); - ImGui.Checkbox(Language.Options_SortAutoTranslate_Name, ref this.Mutable.SortAutoTranslate); + ImGui.Checkbox(Language.Options_SortAutoTranslate_Name, ref Mutable.SortAutoTranslate); ImGuiUtil.HelpText(Language.Options_SortAutoTranslate_Description); ImGui.Spacing(); } diff --git a/ChatTwo/Ui/SettingsTabs/Tabs.cs b/ChatTwo/Ui/SettingsTabs/Tabs.cs index 711cc54..6c238a7 100755 --- a/ChatTwo/Ui/SettingsTabs/Tabs.cs +++ b/ChatTwo/Ui/SettingsTabs/Tabs.cs @@ -15,8 +15,8 @@ internal sealed class Tabs : ISettingsTab { private int _toOpen = -2; internal Tabs(Plugin plugin, Configuration mutable) { - this.Plugin = plugin; - this.Mutable = mutable; + Plugin = plugin; + Mutable = mutable; } public void Draw(bool changed) { @@ -28,29 +28,29 @@ internal sealed class Tabs : ISettingsTab { if (ImGui.BeginPopup(addTabPopup)) { if (ImGui.Selectable(Language.Options_Tabs_NewTab)) { - this.Mutable.Tabs.Add(new Tab()); + Mutable.Tabs.Add(new Tab()); } ImGui.Separator(); if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_General))) { - this.Mutable.Tabs.Add(TabsUtil.VanillaGeneral); + Mutable.Tabs.Add(TabsUtil.VanillaGeneral); } if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_Event))) { - this.Mutable.Tabs.Add(TabsUtil.VanillaEvent); + Mutable.Tabs.Add(TabsUtil.VanillaEvent); } ImGui.EndPopup(); } var toRemove = -1; - var doOpens = this._toOpen > -2; - for (var i = 0; i < this.Mutable.Tabs.Count; i++) { - var tab = this.Mutable.Tabs[i]; + var doOpens = _toOpen > -2; + for (var i = 0; i < Mutable.Tabs.Count; i++) { + var tab = Mutable.Tabs[i]; if (doOpens) { - ImGui.SetNextItemOpen(i == this._toOpen); + ImGui.SetNextItemOpen(i == _toOpen); } if (ImGui.TreeNodeEx($"{tab.Name}###tab-{i}")) { @@ -58,21 +58,21 @@ internal sealed class Tabs : ISettingsTab { if (ImGuiUtil.IconButton(FontAwesomeIcon.TrashAlt, tooltip: Language.Options_Tabs_Delete)) { toRemove = i; - this._toOpen = -1; + _toOpen = -1; } ImGui.SameLine(); if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowUp, tooltip: Language.Options_Tabs_MoveUp) && i > 0) { - (this.Mutable.Tabs[i - 1], this.Mutable.Tabs[i]) = (this.Mutable.Tabs[i], this.Mutable.Tabs[i - 1]); - this._toOpen = i - 1; + (Mutable.Tabs[i - 1], Mutable.Tabs[i]) = (Mutable.Tabs[i], Mutable.Tabs[i - 1]); + _toOpen = i - 1; } ImGui.SameLine(); - if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowDown, tooltip: Language.Options_Tabs_MoveDown) && i < this.Mutable.Tabs.Count - 1) { - (this.Mutable.Tabs[i + 1], this.Mutable.Tabs[i]) = (this.Mutable.Tabs[i], this.Mutable.Tabs[i + 1]); - this._toOpen = i + 1; + if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowDown, tooltip: Language.Options_Tabs_MoveDown) && i < Mutable.Tabs.Count - 1) { + (Mutable.Tabs[i + 1], Mutable.Tabs[i]) = (Mutable.Tabs[i], Mutable.Tabs[i + 1]); + _toOpen = i + 1; } ImGui.InputText(Language.Options_Tabs_Name, ref tab.Name, 512, ImGuiInputTextFlags.EnterReturnsTrue); @@ -160,7 +160,7 @@ internal sealed class Tabs : ISettingsTab { ImGui.TreePop(); } - if (this.Plugin.ExtraChat.ChannelNames.Count > 0 && ImGui.TreeNodeEx(Language.Options_Tabs_ExtraChatChannels)) { + if (Plugin.ExtraChat.ChannelNames.Count > 0 && ImGui.TreeNodeEx(Language.Options_Tabs_ExtraChatChannels)) { ImGui.Checkbox(Language.Options_Tabs_ExtraChatAll, ref tab.ExtraChatAll); ImGui.Separator(); @@ -169,7 +169,7 @@ internal sealed class Tabs : ISettingsTab { ImGui.BeginDisabled(); } - foreach (var (id, name) in this.Plugin.ExtraChat.ChannelNames) { + foreach (var (id, name) in Plugin.ExtraChat.ChannelNames) { var enabled = tab.ExtraChatChannels.Contains(id); if (!ImGui.Checkbox($"{name}##ec-{id}", ref enabled)) { continue; @@ -196,11 +196,11 @@ internal sealed class Tabs : ISettingsTab { } if (toRemove > -1) { - this.Mutable.Tabs.RemoveAt(toRemove); + Mutable.Tabs.RemoveAt(toRemove); } if (doOpens) { - this._toOpen = -2; + _toOpen = -2; } } } diff --git a/ChatTwo/Util/AutoTranslate.cs b/ChatTwo/Util/AutoTranslate.cs index 7c85e26..556521e 100644 --- a/ChatTwo/Util/AutoTranslate.cs +++ b/ChatTwo/Util/AutoTranslate.cs @@ -278,7 +278,7 @@ internal class SingleRow : ISelectorPart { public uint Row { get; } public SingleRow(uint row) { - this.Row = row; + Row = row; } } @@ -287,8 +287,8 @@ internal class IndexRange : ISelectorPart { public uint End { get; } public IndexRange(uint start, uint end) { - this.Start = start; - this.End = end; + Start = start; + End = end; } } @@ -299,7 +299,7 @@ internal class ColumnSpecifier : ISelectorPart { public uint Column { get; } public ColumnSpecifier(uint column) { - this.Column = column; + Column = column; } } @@ -310,9 +310,9 @@ internal class AutoTranslateEntry { internal SeString SeString { get; } public AutoTranslateEntry(uint group, uint row, string str, SeString seStr) { - this.Group = group; - this.Row = row; - this.String = str; - this.SeString = seStr; + Group = group; + Row = row; + String = str; + SeString = seStr; } } diff --git a/ChatTwo/Util/Lender.cs b/ChatTwo/Util/Lender.cs index a833683..456f9f4 100755 --- a/ChatTwo/Util/Lender.cs +++ b/ChatTwo/Util/Lender.cs @@ -6,18 +6,18 @@ internal class Lender { private int _counter; internal Lender(Func ctor) { - this._ctor = ctor; + _ctor = ctor; } internal void ResetCounter() { - this._counter = 0; + _counter = 0; } internal T Borrow() { - if (this._items.Count <= this._counter) { - this._items.Add(this._ctor()); + if (_items.Count <= _counter) { + _items.Add(_ctor()); } - return this._items[this._counter++]; + return _items[_counter++]; } } diff --git a/ChatTwo/Util/Payloads.cs b/ChatTwo/Util/Payloads.cs index 06d7028..92311d2 100755 --- a/ChatTwo/Util/Payloads.cs +++ b/ChatTwo/Util/Payloads.cs @@ -8,7 +8,7 @@ internal class PartyFinderPayload : Payload { internal uint Id { get; } internal PartyFinderPayload(uint id) { - this.Id = id; + Id = id; } protected override byte[] EncodeImpl() { @@ -26,7 +26,7 @@ internal class AchievementPayload : Payload { internal uint Id { get; } internal AchievementPayload(uint id) { - this.Id = id; + Id = id; } protected override byte[] EncodeImpl() { From d2896266e1eeb70add33f3a224b79c409b609efb Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 10 Apr 2024 18:10:23 +0200 Subject: [PATCH 11/12] Use hi-res icons by shifting the texture 340 (170*2)px --- ChatTwo/Ui/ChatLogWindow.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 3c12d81..1df0de7 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -1346,17 +1346,19 @@ public sealed class ChatLogWindow : Window, IUiComponent { private void DrawChunk(Chunk chunk, bool wrap = true, PayloadHandler? handler = null, float lineWidth = 0f) { if (chunk is IconChunk icon && _fontIcon != null) { var bounds = IconUtil.GfdFileView.TryGetEntry((uint) icon.Icon, out var entry); - if (bounds) { - var texSize = new Vector2(_fontIcon.Width, _fontIcon.Height); + if (!bounds) + return; - var sizeRatio = Plugin.Config.FontSize / entry.Height; - var size = new Vector2(entry.Width, entry.Height) * sizeRatio * ImGuiHelpers.GlobalScale; + var texSize = new Vector2(_fontIcon.Width, _fontIcon.Height); - var uv0 = new Vector2(entry.Left, entry.Top) / texSize; - var uv1 = new Vector2(entry.Left + entry.Width, entry.Top + entry.Height) / texSize; - ImGui.Image(_fontIcon.ImGuiHandle, size, uv0, uv1); - ImGuiUtil.PostPayload(chunk, handler); - } + var sizeRatio = Plugin.Config.FontSize / entry.Height; + var size = new Vector2(entry.Width, entry.Height) * sizeRatio * ImGuiHelpers.GlobalScale; + + var uv0 = new Vector2(entry.Left, entry.Top + 170) * 2 / texSize; + var uv1 = new Vector2(entry.Left + entry.Width, entry.Top + entry.Height + 170) * 2 / texSize; + + ImGui.Image(_fontIcon.ImGuiHandle, size, uv0, uv1); + ImGuiUtil.PostPayload(chunk, handler); return; } From 059c7322c8ed588901369c68ae3c81220d39ea60 Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 10 Apr 2024 19:07:35 +0200 Subject: [PATCH 12/12] Add option to hide chat during loading screens [default false] --- ChatTwo/Configuration.cs | 2 ++ ChatTwo/Plugin.cs | 30 +++++++++++++++++--------- ChatTwo/Resources/Language.Designer.cs | 20 ++++++++++++++++- ChatTwo/Resources/Language.resx | 8 ++++++- ChatTwo/Ui/SettingsTabs/Display.cs | 6 ++++++ 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index 6f7a2b0..4cd9a41 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -17,6 +17,7 @@ internal class Configuration : IPluginConfiguration { public bool HideDuringCutscenes = true; public bool HideWhenNotLoggedIn = true; public bool HideWhenUiHidden = true; + public bool HideInLoadingScreens; public bool NativeItemTooltips = true; public bool PrettierTimestamps = true; public bool MoreCompactPretty; @@ -60,6 +61,7 @@ internal class Configuration : IPluginConfiguration { HideDuringCutscenes = other.HideDuringCutscenes; HideWhenNotLoggedIn = other.HideWhenNotLoggedIn; HideWhenUiHidden = other.HideWhenUiHidden; + HideInLoadingScreens = other.HideInLoadingScreens; NativeItemTooltips = other.NativeItemTooltips; PrettierTimestamps = other.PrettierTimestamps; MoreCompactPretty = other.MoreCompactPretty; diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 158f53e..b9b4c8d 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -5,8 +5,8 @@ using ChatTwo.Ipc; using ChatTwo.Resources; using ChatTwo.Ui; using ChatTwo.Util; +using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects; -using Dalamud.Interface.Style; using Dalamud.Interface.Windowing; using Dalamud.IoC; using Dalamud.Plugin; @@ -17,7 +17,8 @@ using XivCommon; namespace ChatTwo; // ReSharper disable once ClassNeverInstantiated.Global -public sealed class Plugin : IDalamudPlugin { +public sealed class Plugin : IDalamudPlugin +{ internal const string PluginName = "Chat 2"; [PluginService] internal static IPluginLog Log { get; private set; } = null!; @@ -63,7 +64,8 @@ public sealed class Plugin : IDalamudPlugin { internal DateTime GameStarted { get; } #pragma warning disable CS8618 - public Plugin() { + public Plugin() + { GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime(); Config = Interface.GetPluginConfig() as Configuration ?? new Configuration(); @@ -112,7 +114,8 @@ public sealed class Plugin : IDalamudPlugin { } #pragma warning restore CS8618 - public void Dispose() { + public void Dispose() + { Interface.LanguageChanged -= LanguageChanged; Interface.UiBuilder.Draw -= Draw; Framework.Update -= FrameworkUpdate; @@ -135,6 +138,9 @@ public sealed class Plugin : IDalamudPlugin { private void Draw() { + if (Config.HideInLoadingScreens && Condition[ConditionFlag.BetweenAreas]) + return; + Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden; ChatLogWindow.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text]; @@ -144,11 +150,13 @@ public sealed class Plugin : IDalamudPlugin { } } - internal void SaveConfig() { + internal void SaveConfig() + { Interface.SavePluginConfig(Config); } - internal void LanguageChanged(string langCode) { + internal void LanguageChanged(string langCode) + { var info = Config.LanguageOverride is LanguageOverride.None ? new CultureInfo(langCode) : new CultureInfo(Config.LanguageOverride.Code()); @@ -156,15 +164,17 @@ public sealed class Plugin : IDalamudPlugin { Language.Culture = info; } - private static readonly string[] ChatAddonNames = { + private static readonly string[] ChatAddonNames = + [ "ChatLog", "ChatLogPanel_0", "ChatLogPanel_1", "ChatLogPanel_2", - "ChatLogPanel_3", - }; + "ChatLogPanel_3" + ]; - private void FrameworkUpdate(IFramework framework) { + private void FrameworkUpdate(IFramework framework) + { if (DeferredSaveFrames >= 0 && DeferredSaveFrames-- == 0) { SaveConfig(); } diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index 785641f..cd67b93 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1770,7 +1770,7 @@ namespace ChatTwo.Resources { } /// - /// Looks up a localized string similar to Hide chat during cutscenes. + /// Looks up a localized string similar to Hide during cutscenes. /// internal static string Options_HideDuringCutscenes_Name { get { @@ -1778,6 +1778,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Hide {0} during loading screens.. + /// + internal static string Options_HideInLoadingScreens_Description { + get { + return ResourceManager.GetString("Options_HideInLoadingScreens_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hide during loading screens. + /// + internal static string Options_HideInLoadingScreens_Name { + get { + return ResourceManager.GetString("Options_HideInLoadingScreens_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide timestamps when previous messages have the same timestamp.. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index 80236d4..3e600c4 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -128,7 +128,7 @@ Hide the in-game chat window when the plugin is active. - Hide chat during cutscenes + Hide during cutscenes Hide {0} during cutscenes. @@ -923,4 +923,10 @@ Override your selected dalamud style with a different one + + Hide during loading screens + + + Hide {0} during loading screens. + diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs index 2c938c5..c127086 100755 --- a/ChatTwo/Ui/SettingsTabs/Display.cs +++ b/ChatTwo/Ui/SettingsTabs/Display.cs @@ -41,6 +41,12 @@ internal sealed class Display : ISettingsTab { ); ImGui.Spacing(); + ImGuiUtil.OptionCheckbox( + ref Mutable.HideInLoadingScreens, + Language.Options_HideInLoadingScreens_Name, + string.Format(Language.Options_HideInLoadingScreens_Description, Plugin.PluginName)); + ImGui.Spacing(); + ImGuiUtil.OptionCheckbox( ref Mutable.NativeItemTooltips, Language.Options_NativeItemTooltips_Name,