diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index e1196d6..b21fee4 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -53,7 +53,7 @@ internal class SortCode { } } -internal class Message +internal partial class Message { internal Guid Id { get; } = Guid.NewGuid(); internal ulong Receiver { get; } @@ -257,7 +257,7 @@ internal class Message } var nextIsMatch = false; - foreach (var split in Regex.Split(text.Content, "(|)")) + foreach (var split in TextParamRegex().Split(text.Content)) { if (split == "" || !nextIsMatch) { @@ -274,6 +274,12 @@ internal class Message var agentChat = AgentChatLog.Instance(); var item = *(InventoryItem*)((nint)agentChat + 0x8A0); + if (item.ItemID == 0) + { + AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, split)); + continue; + } + var kind = item.ItemID switch { < 500_000 => ItemPayload.ItemKind.Normal, @@ -291,7 +297,14 @@ internal class Message } else { - var mapCoords = AgentMap.Instance()->FlagMapMarker; + var agentMap = AgentMap.Instance(); + if (agentMap->IsFlagMarkerSet == 0) + { + AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, split)); + continue; + } + + var mapCoords = agentMap->FlagMapMarker; var rawX = (int)(MathF.Round(mapCoords.XFloat, 3, MidpointRounding.AwayFromZero) * 1000); var rawY = (int)(MathF.Round(mapCoords.YFloat, 3, MidpointRounding.AwayFromZero) * 1000); @@ -300,10 +313,10 @@ internal class Message } } - catch (UriFormatException) + catch (Exception) { AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, split)); - Plugin.Log.Debug($"Invalid URL accepted by Regex but failed URI parsing: '{split}'"); + Plugin.Log.Debug($"Failed to parse the text param: '{split}'"); } } } @@ -327,4 +340,7 @@ internal class Message @"(?((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 | RegexOptions.ExplicitCapture ); + + [GeneratedRegex("(|)")] + private static partial Regex TextParamRegex(); } diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 449dde3..9d3f80c 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -9,7 +9,6 @@ using ChatTwo.Util; using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Keys; -using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; @@ -67,10 +66,6 @@ public sealed class ChatLogWindow : Window private int AutoCompleteSelection; private bool AutoCompleteShouldScroll; - private int LastLength; - private float PreviewHeight; - private Message? PreviewMessage; - public int CursorPos; public Vector2 LastWindowPos { get; private set; } = Vector2.Zero; @@ -327,7 +322,9 @@ public sealed class ChatLogWindow : Window var lineHeight = ImGui.CalcTextSize("A").Y; var height = ImGui.GetContentRegionAvail().Y - lineHeight * 2 - ImGui.GetStyle().ItemSpacing.Y - ImGui.GetStyle().FramePadding.Y * 2; - height -= PreviewHeight; + if (Plugin.Config.PreviewPosition is PreviewPosition.Inside) + height -= Plugin.InputPreview.PreviewHeight; + return height; } @@ -519,40 +516,9 @@ public sealed class ChatLogWindow : Window LastViewport = ImGui.GetWindowViewport().NativePtr; WasDocked = ImGui.IsWindowDocked(); - // We Predraw this once to get the actual height :HideThePain: - PreviewHeight = 0; - if (Plugin.Config.PreviewPosition is PreviewPosition.Inside && !string.IsNullOrEmpty(Chat)) - { - if (PreviewMessage == null || LastLength != Chat.Length) - { - LastLength = Chat.Length; - - var bytes = Encoding.UTF8.GetBytes(Chat.Trim()); - AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref bytes); - - var chunks = ChunkUtil.ToChunks(SeString.Parse(bytes), ChunkSource.Content, ChatType.Say).ToList(); - PreviewMessage = Message.FakeMessage(chunks, new ChatCode((ushort)XivChatType.Say)); - PreviewMessage.DecodeTextParam(); - } - - var pos = ImGui.GetCursorPos(); - ImGui.SetCursorPos(new Vector2(-500, -500)); - var before = ImGui.GetCursorPosY(); - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)) - { - ImGui.TextUnformatted(Language.Options_Preview_Header); - DrawChunks(PreviewMessage.Content); - } - var after = ImGui.GetCursorPosY(); - ImGui.SetCursorPos(pos); - - PreviewHeight = after - before; - } - else - { - LastLength = 0; - PreviewMessage = null; - } + var drawPreview = Plugin.Config.PreviewPosition is PreviewPosition.Inside; + if (drawPreview) + Plugin.InputPreview.CalculatePreview(); var currentTab = Plugin.Config.SidebarTabView ? DrawTabSidebar() : DrawTabBar(); @@ -560,16 +526,8 @@ public sealed class ChatLogWindow : Window if (currentTab > -1 && currentTab < Plugin.Config.Tabs.Count) activeTab = Plugin.Config.Tabs[currentTab]; - if (PreviewMessage != null) - { - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)) - { - ImGui.TextUnformatted("Text Preview:"); - var handler = HandlerLender.Borrow(); - DrawChunks(PreviewMessage.Content, true, handler); - handler.Draw(); - } - } + if (drawPreview) + Plugin.InputPreview.DrawPreview(); using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)) { @@ -861,7 +819,7 @@ public sealed class ChatLogWindow : Window reason = TellReason.Friend; var tellBytes = Encoding.UTF8.GetBytes(trimmed); - AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref tellBytes); + AutoTranslate.ReplaceWithPayload(ref tellBytes); Plugin.Functions.Chat.SendTell(reason, target.ContentId, target.Name, (ushort) world.RowId, tellBytes); } @@ -881,7 +839,7 @@ public sealed class ChatLogWindow : Window } var bytes = Encoding.UTF8.GetBytes(trimmed); - AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref bytes); + AutoTranslate.ReplaceWithPayload(ref bytes); Plugin.Common.Functions.Chat.SendMessageUnsafe(bytes); } @@ -1286,7 +1244,7 @@ public sealed class ChatLogWindow : Window if (AutoCompleteInfo == null) return; - AutoCompleteList ??= AutoTranslate.Matching(Plugin.DataManager, AutoCompleteInfo.ToComplete, Plugin.Config.SortAutoTranslate); + AutoCompleteList ??= AutoTranslate.Matching(AutoCompleteInfo.ToComplete, Plugin.Config.SortAutoTranslate); if (AutoCompleteOpen) { ImGui.OpenPopup(AutoCompleteId); @@ -1309,7 +1267,7 @@ public sealed class ChatLogWindow : Window ImGui.SetNextItemWidth(-1); if (ImGui.InputTextWithHint("##auto-complete-filter", Language.AutoTranslate_Search_Hint, ref AutoCompleteInfo.ToComplete, 256, ImGuiInputTextFlags.CallbackAlways | ImGuiInputTextFlags.CallbackHistory, AutoCompleteCallback)) { - AutoCompleteList = AutoTranslate.Matching(Plugin.DataManager, AutoCompleteInfo.ToComplete, Plugin.Config.SortAutoTranslate); + AutoCompleteList = AutoTranslate.Matching(AutoCompleteInfo.ToComplete, Plugin.Config.SortAutoTranslate); AutoCompleteSelection = 0; AutoCompleteShouldScroll = true; } diff --git a/ChatTwo/Ui/InputPreview.cs b/ChatTwo/Ui/InputPreview.cs index ca274b3..726eca5 100644 --- a/ChatTwo/Ui/InputPreview.cs +++ b/ChatTwo/Ui/InputPreview.cs @@ -1,8 +1,11 @@ +using System.Numerics; using System.Text; using ChatTwo.Code; +using ChatTwo.Resources; using ChatTwo.Util; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using ImGuiNET; @@ -12,15 +15,17 @@ public class InputPreview : Window { private ChatLogWindow LogWindow { get; } - private float Height; + internal float PreviewHeight; + + private int LastLength; + private Message? PreviewMessage; internal InputPreview(ChatLogWindow logWindow) : base("##chat2-inputpreview") { LogWindow = logWindow; Flags = ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoMove | - ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoScrollbar | - ImGuiWindowFlags.NoInputs; + ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoScrollbar; RespectCloseHotkey = false; DisableWindowSounds = true; @@ -32,11 +37,11 @@ public class InputPreview : Window var pos = LogWindow.LastWindowPos; var size = LogWindow.LastWindowSize; - Size = size with { Y = Height }; + Size = size with { Y = PreviewHeight }; var y = Plugin.Config.PreviewPosition switch { - PreviewPosition.Top => pos.Y - Height, + PreviewPosition.Top => pos.Y - PreviewHeight, PreviewPosition.Bottom => pos.Y + size.Y, _ => throw new ArgumentOutOfRangeException(nameof(Plugin.Config.PreviewPosition), Plugin.Config.PreviewPosition, null) }; @@ -52,15 +57,63 @@ public class InputPreview : Window public override void Draw() { - var bytes = Encoding.UTF8.GetBytes(LogWindow.Chat.Trim()); - AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref bytes); + CalculatePreview(); + DrawPreview(); + } - var chunks = ChunkUtil.ToChunks(SeString.Parse(bytes), ChunkSource.Content, ChatType.Say).ToList(); - var encodedChunks = Message.FakeMessage(chunks, new ChatCode((ushort) XivChatType.Say)); + internal void CalculatePreview() + { + // We Pre-draw this once to get the actual height :HideThePain: + PreviewHeight = 0; + if (!string.IsNullOrEmpty(LogWindow.Chat)) + { + if (PreviewMessage == null || LastLength != LogWindow.Chat.Length) + { + LastLength = LogWindow.Chat.Length; - LogWindow.DrawChunks(encodedChunks.Content); + var bytes = Encoding.UTF8.GetBytes(LogWindow.Chat.Trim()); + AutoTranslate.ReplaceWithPayload(ref bytes); - // WindowPadding applies to top and bottom, so we take it 2 times - Height = ImGui.GetCursorPosY() + ImGui.GetStyle().WindowPadding.Y * 2; + var chunks = ChunkUtil.ToChunks(SeString.Parse(bytes), ChunkSource.Content, ChatType.Say).ToList(); + PreviewMessage = Message.FakeMessage(chunks, new ChatCode((ushort)XivChatType.Say)); + PreviewMessage.DecodeTextParam(); + } + + var pos = ImGui.GetCursorPos(); + ImGui.SetCursorPos(new Vector2(-500, -500)); + var before = ImGui.GetCursorPosY(); + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)) + { + ImGui.TextUnformatted(Language.Options_Preview_Header); + LogWindow.DrawChunks(PreviewMessage.Content); + } + var after = ImGui.GetCursorPosY(); + ImGui.SetCursorPos(pos); + + PreviewHeight = after - before; + PreviewHeight += Plugin.Config.PreviewPosition is not PreviewPosition.Inside + ? ImGui.GetStyle().WindowPadding.Y * 2 + : 0; + } + else + { + LastLength = 0; + PreviewMessage = null; + } + } + + internal void DrawPreview() + { + if (PreviewMessage == null) + return; + + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)) + { + ImGui.TextUnformatted(Language.Options_Preview_Header); + + var handler = LogWindow.HandlerLender.Borrow(); + LogWindow.DrawChunks(PreviewMessage.Content, true, handler); + handler.Draw(); + } } } diff --git a/ChatTwo/Util/AutoTranslate.cs b/ChatTwo/Util/AutoTranslate.cs index d7b48a3..385977f 100644 --- a/ChatTwo/Util/AutoTranslate.cs +++ b/ChatTwo/Util/AutoTranslate.cs @@ -18,7 +18,14 @@ namespace ChatTwo.Util; internal static class AutoTranslate { private static readonly Dictionary> Entries = new(); - private static readonly HashSet<(uint, uint)> ValidEntries = new(); + private static readonly HashSet<(uint, uint)> ValidEntries = []; + + private static readonly ExcelSheet CompletionSheet; + + static AutoTranslate() + { + CompletionSheet = Plugin.DataManager.GetExcelSheet()!; + } private static Parser> selector)> Parser() { @@ -57,7 +64,7 @@ internal static class AutoTranslate .Between(Char('['), Char(']')) .Labelled("selector"); return Map( - (name, selector) => (name, selector), + (name, sel) => (name, sel), sheetName, selector.Optional() ); @@ -100,26 +107,26 @@ internal static class AutoTranslate /// /// This spawns a new thread. /// - internal static void PreloadCache(IDataManager data) + internal static void PreloadCache() { new Thread(() => { var sw = Stopwatch.StartNew(); - AllEntries(data); + AllEntries(); Plugin.Log.Debug($"Warming up auto-translate took {sw.ElapsedMilliseconds}ms"); }).Start(); } - private static List AllEntries(IDataManager data) + private static List AllEntries() { - if (Entries.TryGetValue(data.Language, out var entries)) + if (Entries.TryGetValue(Plugin.DataManager.Language, out var entries)) return entries; var shouldAdd = ValidEntries.Count == 0; var parser = Parser(); var list = new List(); - foreach (var row in data.GetExcelSheet()!) + foreach (var row in CompletionSheet) { var lookup = row.LookupTable.TextValue(); if (lookup is not ("" or "@")) @@ -128,11 +135,11 @@ internal static class AutoTranslate var sheetType = typeof(Completion) .Assembly .GetType($"Lumina.Excel.GeneratedSheets.{sheetName}")!; - var getSheet = data + var getSheet = Plugin.DataManager .GetType() .GetMethod("GetExcelSheet", Type.EmptyTypes)! .MakeGenericMethod(sheetType); - var sheet = (ExcelSheetImpl) getSheet.Invoke(data, null)!; + var sheet = (ExcelSheetImpl) getSheet.Invoke(Plugin.DataManager, null)!; var rowParsers = sheet.GetRowParsers().ToArray(); var columns = new List(); @@ -228,16 +235,16 @@ internal static class AutoTranslate } } - Entries[data.Language] = list; + Entries[Plugin.DataManager.Language] = list; return list; } - internal static List Matching(IDataManager data, string prefix, bool sort) + internal static List Matching(string prefix, bool sort) { var wholeMatches = new List(); var prefixMatches = new List(); var otherMatches = new List(); - foreach (var entry in AllEntries(data)) + foreach (var entry in AllEntries()) { if (entry.String.Equals(prefix, StringComparison.OrdinalIgnoreCase)) wholeMatches.Add(entry); @@ -264,7 +271,7 @@ internal static class AutoTranslate [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] private static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count); - internal static void ReplaceWithPayload(IDataManager data, ref byte[] bytes) + internal static void ReplaceWithPayload(ref byte[] bytes) { var search = "