Better input preview handling

This commit is contained in:
Infi
2024-05-19 01:51:36 +02:00
parent 31897251f7
commit 23122e924b
4 changed files with 119 additions and 85 deletions
+21 -5
View File
@@ -53,7 +53,7 @@ internal class SortCode {
} }
} }
internal class Message internal partial class Message
{ {
internal Guid Id { get; } = Guid.NewGuid(); internal Guid Id { get; } = Guid.NewGuid();
internal ulong Receiver { get; } internal ulong Receiver { get; }
@@ -257,7 +257,7 @@ internal class Message
} }
var nextIsMatch = false; var nextIsMatch = false;
foreach (var split in Regex.Split(text.Content, "(<item>|<flag>)")) foreach (var split in TextParamRegex().Split(text.Content))
{ {
if (split == "" || !nextIsMatch) if (split == "" || !nextIsMatch)
{ {
@@ -274,6 +274,12 @@ internal class Message
var agentChat = AgentChatLog.Instance(); var agentChat = AgentChatLog.Instance();
var item = *(InventoryItem*)((nint)agentChat + 0x8A0); var item = *(InventoryItem*)((nint)agentChat + 0x8A0);
if (item.ItemID == 0)
{
AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, split));
continue;
}
var kind = item.ItemID switch var kind = item.ItemID switch
{ {
< 500_000 => ItemPayload.ItemKind.Normal, < 500_000 => ItemPayload.ItemKind.Normal,
@@ -291,7 +297,14 @@ internal class Message
} }
else 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 rawX = (int)(MathF.Round(mapCoords.XFloat, 3, MidpointRounding.AwayFromZero) * 1000);
var rawY = (int)(MathF.Round(mapCoords.YFloat, 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)); 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
@"(?<URL>((https?:\/\/|www\.)[a-z0-9-]+(\.[a-z0-9-]+)*|([a-z0-9-]+(\.[a-z0-9-]+)*\.(com|net|org|co|io|app)))(:[\d]{1,5})?(\/[^\s]+)?)", @"(?<URL>((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 RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture
); );
[GeneratedRegex("(<item>|<flag>)")]
private static partial Regex TextParamRegex();
} }
+12 -54
View File
@@ -9,7 +9,6 @@ using ChatTwo.Util;
using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Keys; using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface; using Dalamud.Interface;
@@ -67,10 +66,6 @@ public sealed class ChatLogWindow : Window
private int AutoCompleteSelection; private int AutoCompleteSelection;
private bool AutoCompleteShouldScroll; private bool AutoCompleteShouldScroll;
private int LastLength;
private float PreviewHeight;
private Message? PreviewMessage;
public int CursorPos; public int CursorPos;
public Vector2 LastWindowPos { get; private set; } = Vector2.Zero; public Vector2 LastWindowPos { get; private set; } = Vector2.Zero;
@@ -327,7 +322,9 @@ public sealed class ChatLogWindow : Window
var lineHeight = ImGui.CalcTextSize("A").Y; var lineHeight = ImGui.CalcTextSize("A").Y;
var height = ImGui.GetContentRegionAvail().Y - lineHeight * 2 - ImGui.GetStyle().ItemSpacing.Y - ImGui.GetStyle().FramePadding.Y * 2; 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; return height;
} }
@@ -519,40 +516,9 @@ public sealed class ChatLogWindow : Window
LastViewport = ImGui.GetWindowViewport().NativePtr; LastViewport = ImGui.GetWindowViewport().NativePtr;
WasDocked = ImGui.IsWindowDocked(); WasDocked = ImGui.IsWindowDocked();
// We Predraw this once to get the actual height :HideThePain: var drawPreview = Plugin.Config.PreviewPosition is PreviewPosition.Inside;
PreviewHeight = 0; if (drawPreview)
if (Plugin.Config.PreviewPosition is PreviewPosition.Inside && !string.IsNullOrEmpty(Chat)) Plugin.InputPreview.CalculatePreview();
{
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 currentTab = Plugin.Config.SidebarTabView ? DrawTabSidebar() : DrawTabBar(); var currentTab = Plugin.Config.SidebarTabView ? DrawTabSidebar() : DrawTabBar();
@@ -560,16 +526,8 @@ public sealed class ChatLogWindow : Window
if (currentTab > -1 && currentTab < Plugin.Config.Tabs.Count) if (currentTab > -1 && currentTab < Plugin.Config.Tabs.Count)
activeTab = Plugin.Config.Tabs[currentTab]; activeTab = Plugin.Config.Tabs[currentTab];
if (PreviewMessage != null) if (drawPreview)
{ Plugin.InputPreview.DrawPreview();
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
{
ImGui.TextUnformatted("Text Preview:");
var handler = HandlerLender.Borrow();
DrawChunks(PreviewMessage.Content, true, handler);
handler.Draw();
}
}
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)) using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
{ {
@@ -861,7 +819,7 @@ public sealed class ChatLogWindow : Window
reason = TellReason.Friend; reason = TellReason.Friend;
var tellBytes = Encoding.UTF8.GetBytes(trimmed); 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); 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); var bytes = Encoding.UTF8.GetBytes(trimmed);
AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref bytes); AutoTranslate.ReplaceWithPayload(ref bytes);
Plugin.Common.Functions.Chat.SendMessageUnsafe(bytes); Plugin.Common.Functions.Chat.SendMessageUnsafe(bytes);
} }
@@ -1286,7 +1244,7 @@ public sealed class ChatLogWindow : Window
if (AutoCompleteInfo == null) if (AutoCompleteInfo == null)
return; return;
AutoCompleteList ??= AutoTranslate.Matching(Plugin.DataManager, AutoCompleteInfo.ToComplete, Plugin.Config.SortAutoTranslate); AutoCompleteList ??= AutoTranslate.Matching(AutoCompleteInfo.ToComplete, Plugin.Config.SortAutoTranslate);
if (AutoCompleteOpen) if (AutoCompleteOpen)
{ {
ImGui.OpenPopup(AutoCompleteId); ImGui.OpenPopup(AutoCompleteId);
@@ -1309,7 +1267,7 @@ public sealed class ChatLogWindow : Window
ImGui.SetNextItemWidth(-1); ImGui.SetNextItemWidth(-1);
if (ImGui.InputTextWithHint("##auto-complete-filter", Language.AutoTranslate_Search_Hint, ref AutoCompleteInfo.ToComplete, 256, ImGuiInputTextFlags.CallbackAlways | ImGuiInputTextFlags.CallbackHistory, AutoCompleteCallback)) 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; AutoCompleteSelection = 0;
AutoCompleteShouldScroll = true; AutoCompleteShouldScroll = true;
} }
+63 -10
View File
@@ -1,8 +1,11 @@
using System.Numerics;
using System.Text; using System.Text;
using ChatTwo.Code; using ChatTwo.Code;
using ChatTwo.Resources;
using ChatTwo.Util; using ChatTwo.Util;
using Dalamud.Game.Text; using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using ImGuiNET; using ImGuiNET;
@@ -12,15 +15,17 @@ public class InputPreview : Window
{ {
private ChatLogWindow LogWindow { get; } private ChatLogWindow LogWindow { get; }
private float Height; internal float PreviewHeight;
private int LastLength;
private Message? PreviewMessage;
internal InputPreview(ChatLogWindow logWindow) : base("##chat2-inputpreview") internal InputPreview(ChatLogWindow logWindow) : base("##chat2-inputpreview")
{ {
LogWindow = logWindow; LogWindow = logWindow;
Flags = ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoMove | Flags = ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoMove |
ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoScrollbar;
ImGuiWindowFlags.NoInputs;
RespectCloseHotkey = false; RespectCloseHotkey = false;
DisableWindowSounds = true; DisableWindowSounds = true;
@@ -32,11 +37,11 @@ public class InputPreview : Window
var pos = LogWindow.LastWindowPos; var pos = LogWindow.LastWindowPos;
var size = LogWindow.LastWindowSize; var size = LogWindow.LastWindowSize;
Size = size with { Y = Height }; Size = size with { Y = PreviewHeight };
var y = Plugin.Config.PreviewPosition switch var y = Plugin.Config.PreviewPosition switch
{ {
PreviewPosition.Top => pos.Y - Height, PreviewPosition.Top => pos.Y - PreviewHeight,
PreviewPosition.Bottom => pos.Y + size.Y, PreviewPosition.Bottom => pos.Y + size.Y,
_ => throw new ArgumentOutOfRangeException(nameof(Plugin.Config.PreviewPosition), Plugin.Config.PreviewPosition, null) _ => throw new ArgumentOutOfRangeException(nameof(Plugin.Config.PreviewPosition), Plugin.Config.PreviewPosition, null)
}; };
@@ -52,15 +57,63 @@ public class InputPreview : Window
public override void Draw() public override void Draw()
{ {
CalculatePreview();
DrawPreview();
}
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;
var bytes = Encoding.UTF8.GetBytes(LogWindow.Chat.Trim()); var bytes = Encoding.UTF8.GetBytes(LogWindow.Chat.Trim());
AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref bytes); AutoTranslate.ReplaceWithPayload(ref bytes);
var chunks = ChunkUtil.ToChunks(SeString.Parse(bytes), ChunkSource.Content, ChatType.Say).ToList(); var chunks = ChunkUtil.ToChunks(SeString.Parse(bytes), ChunkSource.Content, ChatType.Say).ToList();
var encodedChunks = Message.FakeMessage(chunks, new ChatCode((ushort) XivChatType.Say)); PreviewMessage = Message.FakeMessage(chunks, new ChatCode((ushort)XivChatType.Say));
PreviewMessage.DecodeTextParam();
}
LogWindow.DrawChunks(encodedChunks.Content); 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);
// WindowPadding applies to top and bottom, so we take it 2 times PreviewHeight = after - before;
Height = ImGui.GetCursorPosY() + ImGui.GetStyle().WindowPadding.Y * 2; 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();
}
} }
} }
+21 -14
View File
@@ -18,7 +18,14 @@ namespace ChatTwo.Util;
internal static class AutoTranslate internal static class AutoTranslate
{ {
private static readonly Dictionary<ClientLanguage, List<AutoTranslateEntry>> Entries = new(); private static readonly Dictionary<ClientLanguage, List<AutoTranslateEntry>> Entries = new();
private static readonly HashSet<(uint, uint)> ValidEntries = new(); private static readonly HashSet<(uint, uint)> ValidEntries = [];
private static readonly ExcelSheet<Completion> CompletionSheet;
static AutoTranslate()
{
CompletionSheet = Plugin.DataManager.GetExcelSheet<Completion>()!;
}
private static Parser<char, (string name, Maybe<IEnumerable<ISelectorPart>> selector)> Parser() private static Parser<char, (string name, Maybe<IEnumerable<ISelectorPart>> selector)> Parser()
{ {
@@ -57,7 +64,7 @@ internal static class AutoTranslate
.Between(Char('['), Char(']')) .Between(Char('['), Char(']'))
.Labelled("selector"); .Labelled("selector");
return Map( return Map(
(name, selector) => (name, selector), (name, sel) => (name, sel),
sheetName, sheetName,
selector.Optional() selector.Optional()
); );
@@ -100,26 +107,26 @@ internal static class AutoTranslate
/// ///
/// This spawns a new thread. /// This spawns a new thread.
/// </summary> /// </summary>
internal static void PreloadCache(IDataManager data) internal static void PreloadCache()
{ {
new Thread(() => new Thread(() =>
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
AllEntries(data); AllEntries();
Plugin.Log.Debug($"Warming up auto-translate took {sw.ElapsedMilliseconds}ms"); Plugin.Log.Debug($"Warming up auto-translate took {sw.ElapsedMilliseconds}ms");
}).Start(); }).Start();
} }
private static List<AutoTranslateEntry> AllEntries(IDataManager data) private static List<AutoTranslateEntry> AllEntries()
{ {
if (Entries.TryGetValue(data.Language, out var entries)) if (Entries.TryGetValue(Plugin.DataManager.Language, out var entries))
return entries; return entries;
var shouldAdd = ValidEntries.Count == 0; var shouldAdd = ValidEntries.Count == 0;
var parser = Parser(); var parser = Parser();
var list = new List<AutoTranslateEntry>(); var list = new List<AutoTranslateEntry>();
foreach (var row in data.GetExcelSheet<Completion>()!) foreach (var row in CompletionSheet)
{ {
var lookup = row.LookupTable.TextValue(); var lookup = row.LookupTable.TextValue();
if (lookup is not ("" or "@")) if (lookup is not ("" or "@"))
@@ -128,11 +135,11 @@ internal static class AutoTranslate
var sheetType = typeof(Completion) var sheetType = typeof(Completion)
.Assembly .Assembly
.GetType($"Lumina.Excel.GeneratedSheets.{sheetName}")!; .GetType($"Lumina.Excel.GeneratedSheets.{sheetName}")!;
var getSheet = data var getSheet = Plugin.DataManager
.GetType() .GetType()
.GetMethod("GetExcelSheet", Type.EmptyTypes)! .GetMethod("GetExcelSheet", Type.EmptyTypes)!
.MakeGenericMethod(sheetType); .MakeGenericMethod(sheetType);
var sheet = (ExcelSheetImpl) getSheet.Invoke(data, null)!; var sheet = (ExcelSheetImpl) getSheet.Invoke(Plugin.DataManager, null)!;
var rowParsers = sheet.GetRowParsers().ToArray(); var rowParsers = sheet.GetRowParsers().ToArray();
var columns = new List<int>(); var columns = new List<int>();
@@ -228,16 +235,16 @@ internal static class AutoTranslate
} }
} }
Entries[data.Language] = list; Entries[Plugin.DataManager.Language] = list;
return list; return list;
} }
internal static List<AutoTranslateEntry> Matching(IDataManager data, string prefix, bool sort) internal static List<AutoTranslateEntry> Matching(string prefix, bool sort)
{ {
var wholeMatches = new List<AutoTranslateEntry>(); var wholeMatches = new List<AutoTranslateEntry>();
var prefixMatches = new List<AutoTranslateEntry>(); var prefixMatches = new List<AutoTranslateEntry>();
var otherMatches = new List<AutoTranslateEntry>(); var otherMatches = new List<AutoTranslateEntry>();
foreach (var entry in AllEntries(data)) foreach (var entry in AllEntries())
{ {
if (entry.String.Equals(prefix, StringComparison.OrdinalIgnoreCase)) if (entry.String.Equals(prefix, StringComparison.OrdinalIgnoreCase))
wholeMatches.Add(entry); wholeMatches.Add(entry);
@@ -264,7 +271,7 @@ internal static class AutoTranslate
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count); 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 = "<at:"u8.ToArray(); var search = "<at:"u8.ToArray();
if (bytes.Length <= search.Length) if (bytes.Length <= search.Length)
@@ -272,7 +279,7 @@ internal static class AutoTranslate
// populate the list of valid entries // populate the list of valid entries
if (ValidEntries.Count == 0) if (ValidEntries.Count == 0)
AllEntries(data); AllEntries();
var start = -1; var start = -1;
for (var i = 0; i < bytes.Length; i++) for (var i = 0; i < bytes.Length; i++)