mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
Better IMEWindow (#841)
This commit is contained in:
parent
e1df496e2f
commit
a241e646b6
2 changed files with 101 additions and 21 deletions
|
|
@ -1,8 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
@ -14,7 +18,7 @@ namespace Dalamud.Game.Gui.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class handles IME for non-English users.
|
/// This class handles IME for non-English users.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class DalamudIME : IDisposable
|
internal unsafe class DalamudIME : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("IME");
|
private static readonly ModuleLog Log = new("IME");
|
||||||
|
|
||||||
|
|
@ -22,6 +26,8 @@ namespace Dalamud.Game.Gui.Internal
|
||||||
private IntPtr wndProcPtr;
|
private IntPtr wndProcPtr;
|
||||||
private IntPtr oldWndProcPtr;
|
private IntPtr oldWndProcPtr;
|
||||||
private WndProcDelegate wndProcDelegate;
|
private WndProcDelegate wndProcDelegate;
|
||||||
|
private AsmHook imguiTextInputCursorHook;
|
||||||
|
private Vector2* cursorPos;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DalamudIME"/> class.
|
/// Initializes a new instance of the <see cref="DalamudIME"/> class.
|
||||||
|
|
@ -60,6 +66,18 @@ namespace Dalamud.Game.Gui.Internal
|
||||||
SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.oldWndProcPtr);
|
SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.oldWndProcPtr);
|
||||||
this.oldWndProcPtr = IntPtr.Zero;
|
this.oldWndProcPtr = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.imguiTextInputCursorHook?.Dispose();
|
||||||
|
Marshal.FreeHGlobal((IntPtr)this.cursorPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the position of the cursor.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The position of the cursor.</returns>
|
||||||
|
internal Vector2 GetCursorPos()
|
||||||
|
{
|
||||||
|
return new Vector2(this.cursorPos->X, this.cursorPos->Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -73,6 +91,31 @@ namespace Dalamud.Game.Gui.Internal
|
||||||
this.interfaceHandle = Service<InterfaceManager>.Get().WindowHandlePtr;
|
this.interfaceHandle = Service<InterfaceManager>.Get().WindowHandlePtr;
|
||||||
this.wndProcPtr = Marshal.GetFunctionPointerForDelegate(this.wndProcDelegate);
|
this.wndProcPtr = Marshal.GetFunctionPointerForDelegate(this.wndProcDelegate);
|
||||||
this.oldWndProcPtr = SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.wndProcPtr);
|
this.oldWndProcPtr = SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.wndProcPtr);
|
||||||
|
|
||||||
|
var module = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().First(m => m.ModuleName == "cimgui.dll");
|
||||||
|
var scanner = new SigScanner(module);
|
||||||
|
var cursorDrawingPtr = scanner.ScanModule("F3 0F 11 75 ?? 0F 28 CF");
|
||||||
|
Log.Debug($"Found cursorDrawingPtr at {cursorDrawingPtr:X}");
|
||||||
|
|
||||||
|
this.cursorPos = (Vector2*)Marshal.AllocHGlobal(sizeof(Vector2));
|
||||||
|
this.cursorPos->X = 0f;
|
||||||
|
this.cursorPos->Y = 0f;
|
||||||
|
|
||||||
|
var asm = new[]
|
||||||
|
{
|
||||||
|
"use64",
|
||||||
|
$"push rax",
|
||||||
|
$"mov rax, {(IntPtr)this.cursorPos + sizeof(float)}",
|
||||||
|
$"movss [rax],xmm7",
|
||||||
|
$"mov rax, {(IntPtr)this.cursorPos}",
|
||||||
|
$"movss [rax],xmm6",
|
||||||
|
$"pop rax",
|
||||||
|
};
|
||||||
|
|
||||||
|
Log.Debug($"Asm Code:\n{string.Join("\n", asm)}");
|
||||||
|
this.imguiTextInputCursorHook = new AsmHook(cursorDrawingPtr, asm, "ImguiTextInputCursorHook");
|
||||||
|
this.imguiTextInputCursorHook?.Enable();
|
||||||
|
|
||||||
this.IsEnabled = true;
|
this.IsEnabled = true;
|
||||||
Log.Information("Enabled!");
|
Log.Information("Enabled!");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Dalamud.Game.ClientState.Keys;
|
||||||
using Dalamud.Game.Gui.Internal;
|
using Dalamud.Game.Gui.Internal;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A window for displaying IME details.
|
/// A window for displaying IME details.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class IMEWindow : Window
|
internal unsafe class IMEWindow : Window
|
||||||
{
|
{
|
||||||
private const int ImePageSize = 9;
|
private const int ImePageSize = 9;
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
/// Initializes a new instance of the <see cref="IMEWindow"/> class.
|
/// Initializes a new instance of the <see cref="IMEWindow"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IMEWindow()
|
public IMEWindow()
|
||||||
: base("Dalamud IME", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.AlwaysAutoResize)
|
: base("Dalamud IME", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoBackground)
|
||||||
{
|
{
|
||||||
this.Size = new Vector2(100, 200);
|
this.Size = new Vector2(100, 200);
|
||||||
this.SizeCondition = ImGuiCond.FirstUseEver;
|
this.SizeCondition = ImGuiCond.FirstUseEver;
|
||||||
|
|
@ -29,6 +29,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
|
if (this.IsOpen && Service<KeyState>.Get()[VirtualKey.SHIFT]) Service<DalamudInterface>.Get().CloseIMEWindow();
|
||||||
var ime = Service<DalamudIME>.GetNullable();
|
var ime = Service<DalamudIME>.GetNullable();
|
||||||
|
|
||||||
if (ime == null || !ime.IsEnabled)
|
if (ime == null || !ime.IsEnabled)
|
||||||
|
|
@ -36,34 +37,70 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
ImGui.Text("IME is unavailable.");
|
ImGui.Text("IME is unavailable.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.Text(ime.ImmComp);
|
/// <inheritdoc/>
|
||||||
|
public override void PostDraw()
|
||||||
|
{
|
||||||
|
if (this.IsOpen && Service<KeyState>.Get()[VirtualKey.SHIFT]) Service<DalamudInterface>.Get().CloseIMEWindow();
|
||||||
|
var ime = Service<DalamudIME>.GetNullable();
|
||||||
|
|
||||||
ImGui.Separator();
|
if (ime == null || !ime.IsEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var cursorPos = ime.GetCursorPos();
|
||||||
|
|
||||||
|
var nextDrawPosY = cursorPos.Y;
|
||||||
|
var maxTextWidth = 0f;
|
||||||
|
var textHeight = ImGui.CalcTextSize(ime.ImmComp).Y;
|
||||||
|
var drawAreaPosX = cursorPos.X + ImGui.GetStyle().WindowPadding.X;
|
||||||
|
|
||||||
var native = ime.ImmCandNative;
|
var native = ime.ImmCandNative;
|
||||||
for (var i = 0; i < ime.ImmCand.Count; i++)
|
|
||||||
{
|
|
||||||
var selected = i == (native.Selection % ImePageSize);
|
|
||||||
|
|
||||||
if (selected)
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
|
|
||||||
|
|
||||||
ImGui.Text($"{i + 1}. {ime.ImmCand[i]}");
|
|
||||||
|
|
||||||
if (selected)
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalIndex = native.Selection + 1;
|
var totalIndex = native.Selection + 1;
|
||||||
var totalSize = native.Count;
|
var totalSize = native.Count;
|
||||||
|
|
||||||
var pageStart = native.PageStart;
|
var pageStart = native.PageStart;
|
||||||
var pageIndex = (pageStart / ImePageSize) + 1;
|
var pageIndex = (pageStart / ImePageSize) + 1;
|
||||||
var pageCount = (totalSize / ImePageSize) + 1;
|
var pageCount = (totalSize / ImePageSize) + 1;
|
||||||
|
var pageInfo = $"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})";
|
||||||
|
|
||||||
ImGui.Separator();
|
// Calc the window size
|
||||||
ImGui.Text($"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})");
|
for (var i = 0; i < ime.ImmCand.Count; i++)
|
||||||
|
{
|
||||||
|
var textSize = ImGui.CalcTextSize($"{i + 1}. {ime.ImmCand[i]}");
|
||||||
|
maxTextWidth = maxTextWidth > textSize.X ? maxTextWidth : textSize.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(pageInfo).X ? maxTextWidth : ImGui.CalcTextSize(pageInfo).X;
|
||||||
|
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(ime.ImmComp).X ? maxTextWidth : ImGui.CalcTextSize(ime.ImmComp).X;
|
||||||
|
|
||||||
|
var imeWindowMinPos = new Vector2(cursorPos.X, cursorPos.Y);
|
||||||
|
var imeWindowMaxPos = new Vector2(cursorPos.X + maxTextWidth + (2 * ImGui.GetStyle().WindowPadding.X), cursorPos.Y + (textHeight * (ime.ImmCand.Count + 2)) + (5 * (ime.ImmCand.Count - 1)) + (2 * ImGui.GetStyle().WindowPadding.Y));
|
||||||
|
|
||||||
|
var drawList = ImGui.GetForegroundDrawList();
|
||||||
|
// Draw the background rect
|
||||||
|
drawList.AddRectFilled(imeWindowMinPos, imeWindowMaxPos, ImGui.GetColorU32(ImGuiCol.WindowBg), ImGui.GetStyle().WindowRounding);
|
||||||
|
// Add component text
|
||||||
|
drawList.AddText(new Vector2(drawAreaPosX, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Text), ime.ImmComp);
|
||||||
|
nextDrawPosY += textHeight + ImGui.GetStyle().ItemSpacing.Y;
|
||||||
|
// Add separator
|
||||||
|
drawList.AddLine(new Vector2(drawAreaPosX, nextDrawPosY), new Vector2(drawAreaPosX + maxTextWidth, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||||
|
// Add candidate words
|
||||||
|
for (var i = 0; i < ime.ImmCand.Count; i++)
|
||||||
|
{
|
||||||
|
var selected = i == (native.Selection % ImePageSize);
|
||||||
|
var color = ImGui.GetColorU32(ImGuiCol.Text);
|
||||||
|
if (selected)
|
||||||
|
color = ImGui.GetColorU32(ImGuiCol.NavHighlight);
|
||||||
|
|
||||||
|
drawList.AddText(new Vector2(drawAreaPosX, nextDrawPosY), color, $"{i + 1}. {ime.ImmCand[i]}");
|
||||||
|
nextDrawPosY += textHeight + ImGui.GetStyle().ItemSpacing.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add separator
|
||||||
|
drawList.AddLine(new Vector2(drawAreaPosX, nextDrawPosY), new Vector2(drawAreaPosX + maxTextWidth, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||||
|
// Add pages infomation
|
||||||
|
drawList.AddText(new Vector2(drawAreaPosX, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Text), pageInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue