mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
test
This commit is contained in:
parent
8a21fc721f
commit
c326537f9f
6 changed files with 383 additions and 444 deletions
|
|
@ -96,12 +96,6 @@ internal class DalamudCommands : IServiceType
|
|||
ShowInHelp = false,
|
||||
});
|
||||
|
||||
commandManager.AddHandler("/xlime", new CommandInfo(this.OnDebugDrawIMEPanel)
|
||||
{
|
||||
HelpMessage = Loc.Localize("DalamudIMEPanelHelp", "Draw IME panel"),
|
||||
ShowInHelp = false,
|
||||
});
|
||||
|
||||
commandManager.AddHandler("/xllog", new CommandInfo(this.OnOpenLog)
|
||||
{
|
||||
HelpMessage = Loc.Localize("DalamudDevLogHelp", "Open dev log DEBUG"),
|
||||
|
|
@ -308,11 +302,6 @@ internal class DalamudCommands : IServiceType
|
|||
dalamudInterface.ToggleDataWindow(arguments);
|
||||
}
|
||||
|
||||
private void OnDebugDrawIMEPanel(string command, string arguments)
|
||||
{
|
||||
Service<DalamudInterface>.Get().OpenImeWindow();
|
||||
}
|
||||
|
||||
private void OnOpenLog(string command, string arguments)
|
||||
{
|
||||
Service<DalamudInterface>.Get().ToggleLogWindow();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Diagnostics;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
|
@ -17,6 +18,8 @@ using Dalamud.Interface.Utility;
|
|||
|
||||
using ImGuiNET;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
|
@ -26,12 +29,21 @@ namespace Dalamud.Interface.Internal;
|
|||
/// <summary>
|
||||
/// This class handles CJK IME.
|
||||
/// </summary>
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
||||
{
|
||||
private const int CImGuiStbTextCreateUndoOffset = 0xB57A0;
|
||||
private const int CImGuiStbTextUndoOffset = 0xB59C0;
|
||||
|
||||
private const int ImePageSize = 9;
|
||||
|
||||
private static readonly Dictionary<int, string> WmNames =
|
||||
typeof(WM).GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(x => x.IsLiteral && !x.IsInitOnly && x.FieldType == typeof(int))
|
||||
.Select(x => ((int)x.GetRawConstantValue()!, x.Name))
|
||||
.DistinctBy(x => x.Item1)
|
||||
.ToDictionary(x => x.Item1, x => x.Name);
|
||||
|
||||
private static readonly UnicodeRange[] HanRange =
|
||||
{
|
||||
UnicodeRanges.CjkRadicalsSupplement,
|
||||
|
|
@ -57,8 +69,41 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
|
||||
private static readonly delegate* unmanaged<ImGuiInputTextState*, StbTextEditState*, void> StbTextUndo;
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly WndProcHookManager wndProcHookManager = Service<WndProcHookManager>.Get();
|
||||
|
||||
private readonly InterfaceManager interfaceManager;
|
||||
|
||||
private readonly ImGuiSetPlatformImeDataDelegate setPlatformImeDataDelegate;
|
||||
|
||||
/// <summary>The candidates.</summary>
|
||||
private readonly List<string> candidateStrings = new();
|
||||
|
||||
/// <summary>The selected imm component.</summary>
|
||||
private string compositionString = string.Empty;
|
||||
|
||||
/// <summary>The cursor position in screen coordinates.</summary>
|
||||
private Vector2 cursorScreenPos;
|
||||
|
||||
/// <summary>The associated viewport.</summary>
|
||||
private ImGuiViewportPtr associatedViewport;
|
||||
|
||||
/// <summary>The index of the first imm candidate in relation to the full list.</summary>
|
||||
private CANDIDATELIST immCandNative;
|
||||
|
||||
/// <summary>The partial conversion from-range.</summary>
|
||||
private int partialConversionFrom;
|
||||
|
||||
/// <summary>The partial conversion to-range.</summary>
|
||||
private int partialConversionTo;
|
||||
|
||||
/// <summary>The cursor offset in the composition string.</summary>
|
||||
private int compositionCursorOffset;
|
||||
|
||||
/// <summary>The input mode icon from <see cref="SeIconChar"/>.</summary>
|
||||
private char inputModeIcon;
|
||||
|
||||
/// <summary>Undo range for modifying the buffer while composition is in progress.</summary>
|
||||
private (int Start, int End, int Cursor)? temporaryUndoSelection;
|
||||
|
||||
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1003:Symbols should be spaced correctly", Justification = ".")]
|
||||
|
|
@ -87,7 +132,17 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
}
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private DalamudIme() => this.setPlatformImeDataDelegate = this.ImGuiSetPlatformImeData;
|
||||
private DalamudIme(InterfaceManager.InterfaceManagerWithScene imws)
|
||||
{
|
||||
Debug.Assert(ImGuiHelpers.IsImGuiInitialized, "IMWS initialized but IsImGuiInitialized is false?");
|
||||
|
||||
this.interfaceManager = imws.Manager;
|
||||
this.setPlatformImeDataDelegate = this.ImGuiSetPlatformImeData;
|
||||
|
||||
ImGui.GetIO().SetPlatformImeDataFn = Marshal.GetFunctionPointerForDelegate(this.setPlatformImeDataDelegate);
|
||||
this.interfaceManager.Draw += this.Draw;
|
||||
this.wndProcHookManager.PreWndProc += this.WndProcHookManagerOnPreWndProc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="DalamudIme"/> class.
|
||||
|
|
@ -109,7 +164,7 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
/// <summary>
|
||||
/// Gets a value indicating whether to display the cursor in input text. This also deals with blinking.
|
||||
/// </summary>
|
||||
internal static bool ShowCursorInInputText
|
||||
private static bool ShowCursorInInputText
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -126,63 +181,21 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cursor position, in screen coordinates.
|
||||
/// </summary>
|
||||
internal Vector2 CursorPos { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated viewport.
|
||||
/// </summary>
|
||||
internal ImGuiViewportPtr AssociatedViewport { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the first imm candidate in relation to the full list.
|
||||
/// </summary>
|
||||
internal CANDIDATELIST ImmCandNative { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the imm candidates.
|
||||
/// </summary>
|
||||
internal List<string> ImmCand { get; private set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected imm component.
|
||||
/// </summary>
|
||||
internal string ImmComp { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the partial conversion from-range.
|
||||
/// </summary>
|
||||
internal int PartialConversionFrom { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the partial conversion to-range.
|
||||
/// </summary>
|
||||
internal int PartialConversionTo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cursor offset in the composition string.
|
||||
/// </summary>
|
||||
internal int CompositionCursorOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to display partial conversion status.
|
||||
/// </summary>
|
||||
internal bool ShowPartialConversion => this.PartialConversionFrom != 0 ||
|
||||
this.PartialConversionTo != this.ImmComp.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input mode icon from <see cref="SeIconChar"/>.
|
||||
/// </summary>
|
||||
internal char InputModeIcon { get; private set; }
|
||||
|
||||
private static ImGuiInputTextState* TextState =>
|
||||
(ImGuiInputTextState*)(ImGui.GetCurrentContext() + ImGuiContextOffsets.TextStateOffset);
|
||||
|
||||
/// <summary>Gets a value indicating whether to display partial conversion status.</summary>
|
||||
private bool ShowPartialConversion => this.partialConversionFrom != 0 ||
|
||||
this.partialConversionTo != this.compositionString.Length;
|
||||
|
||||
/// <summary>Gets a value indicating whether to draw.</summary>
|
||||
private bool ShouldDraw =>
|
||||
this.candidateStrings.Count != 0 || this.ShowPartialConversion || this.inputModeIcon != default;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.interfaceManager.Draw -= this.Draw;
|
||||
this.ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
|
@ -195,13 +208,13 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
{
|
||||
foreach (var chr in str)
|
||||
{
|
||||
if (HanRange.Any(x => x.FirstCodePoint <= chr && chr < x.FirstCodePoint + x.Length))
|
||||
if (!this.EncounteredHan)
|
||||
{
|
||||
if (Service<FontAtlasFactory>.Get()
|
||||
?.GetFdtReader(GameFontFamilyAndSize.Axis12)
|
||||
.FindGlyph(chr) is null)
|
||||
if (HanRange.Any(x => x.FirstCodePoint <= chr && chr < x.FirstCodePoint + x.Length))
|
||||
{
|
||||
if (!this.EncounteredHan)
|
||||
if (Service<FontAtlasFactory>.Get()
|
||||
?.GetFdtReader(GameFontFamilyAndSize.Axis12)
|
||||
.FindGlyph(chr) is null)
|
||||
{
|
||||
this.EncounteredHan = true;
|
||||
Service<InterfaceManager>.Get().RebuildFonts();
|
||||
|
|
@ -209,9 +222,9 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
}
|
||||
}
|
||||
|
||||
if (HangulRange.Any(x => x.FirstCodePoint <= chr && chr < x.FirstCodePoint + x.Length))
|
||||
if (!this.EncounteredHangul)
|
||||
{
|
||||
if (!this.EncounteredHangul)
|
||||
if (HangulRange.Any(x => x.FirstCodePoint <= chr && chr < x.FirstCodePoint + x.Length))
|
||||
{
|
||||
this.EncounteredHangul = true;
|
||||
Service<InterfaceManager>.Get().RebuildFonts();
|
||||
|
|
@ -220,11 +233,24 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes window messages.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public void ProcessImeMessage(WndProcEventArgs args)
|
||||
private static string ImmGetCompositionString(HIMC hImc, uint comp)
|
||||
{
|
||||
var numBytes = ImmGetCompositionStringW(hImc, comp, null, 0);
|
||||
if (numBytes == 0)
|
||||
return string.Empty;
|
||||
|
||||
var data = stackalloc char[numBytes / 2];
|
||||
_ = ImmGetCompositionStringW(hImc, comp, data, (uint)numBytes);
|
||||
return new(data, 0, numBytes / 2);
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources()
|
||||
{
|
||||
if (ImGuiHelpers.IsImGuiInitialized)
|
||||
ImGui.GetIO().SetPlatformImeDataFn = nint.Zero;
|
||||
}
|
||||
|
||||
private void WndProcHookManagerOnPreWndProc(WndProcEventArgs args)
|
||||
{
|
||||
if (!ImGuiHelpers.IsImGuiInitialized)
|
||||
return;
|
||||
|
|
@ -246,7 +272,7 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
case WM.WM_IME_NOTIFY
|
||||
when (nint)args.WParam is IMN.IMN_OPENCANDIDATE or IMN.IMN_CLOSECANDIDATE
|
||||
or IMN.IMN_CHANGECANDIDATE:
|
||||
this.UpdateImeWindowStatus(hImc);
|
||||
this.UpdateCandidates(hImc);
|
||||
args.SuppressWithValue(0);
|
||||
break;
|
||||
|
||||
|
|
@ -260,22 +286,22 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
else
|
||||
this.ReplaceCompositionString(hImc, (uint)args.LParam);
|
||||
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_COMPOSITION)}({(nint)args.LParam:X}): {this.ImmComp}");
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_COMPOSITION)}({(nint)args.LParam:X}): {this.compositionString}");
|
||||
args.SuppressWithValue(0);
|
||||
break;
|
||||
|
||||
case WM.WM_IME_ENDCOMPOSITION:
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_ENDCOMPOSITION)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.ImmComp}");
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_ENDCOMPOSITION)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.compositionString}");
|
||||
args.SuppressWithValue(0);
|
||||
break;
|
||||
|
||||
case WM.WM_IME_CONTROL:
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_CONTROL)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.ImmComp}");
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_CONTROL)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.compositionString}");
|
||||
args.SuppressWithValue(0);
|
||||
break;
|
||||
|
||||
case WM.WM_IME_REQUEST:
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_REQUEST)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.ImmComp}");
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_REQUEST)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.compositionString}");
|
||||
args.SuppressWithValue(0);
|
||||
break;
|
||||
|
||||
|
|
@ -283,12 +309,12 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
// Hide candidate and composition windows.
|
||||
args.LParam = (LPARAM)((nint)args.LParam & ~(ISC_SHOWUICOMPOSITIONWINDOW | 0xF));
|
||||
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_SETCONTEXT)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.ImmComp}");
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_SETCONTEXT)}({(nint)args.WParam:X}, {(nint)args.LParam:X}): {this.compositionString}");
|
||||
args.SuppressWithDefault();
|
||||
break;
|
||||
|
||||
case WM.WM_IME_NOTIFY:
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_NOTIFY)}({(nint)args.WParam:X}): {this.ImmComp}");
|
||||
// Log.Verbose($"{nameof(WM.WM_IME_NOTIFY)}({(nint)args.WParam:X}): {this.compositionString}");
|
||||
break;
|
||||
|
||||
case WM.WM_KEYDOWN when (int)args.WParam is
|
||||
|
|
@ -302,12 +328,14 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
or VK.VK_RIGHT
|
||||
or VK.VK_DOWN
|
||||
or VK.VK_RETURN:
|
||||
if (this.ImmCand.Count != 0)
|
||||
if (this.candidateStrings.Count != 0)
|
||||
{
|
||||
this.ClearState(hImc);
|
||||
args.WParam = VK.VK_PROCESSKEY;
|
||||
}
|
||||
|
||||
this.UpdateCandidates(hImc);
|
||||
|
||||
break;
|
||||
|
||||
case WM.WM_LBUTTONDOWN:
|
||||
|
|
@ -316,9 +344,15 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
case WM.WM_XBUTTONDOWN:
|
||||
ImmNotifyIME(hImc, NI.NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
||||
break;
|
||||
|
||||
// default:
|
||||
// Log.Verbose($"{(WmNames.TryGetValue((int)args.Message, out var v) ? v : args.Message.ToString())}({(nint)args.WParam:X}, {(nint)args.LParam:X})");
|
||||
// break;
|
||||
}
|
||||
|
||||
this.UpdateInputLanguage(hImc);
|
||||
if (this.inputModeIcon == (char)SeIconChar.ImeKoreanHangul)
|
||||
this.UpdateCandidates(hImc);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -326,23 +360,6 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
}
|
||||
}
|
||||
|
||||
private static string ImmGetCompositionString(HIMC hImc, uint comp)
|
||||
{
|
||||
var numBytes = ImmGetCompositionStringW(hImc, comp, null, 0);
|
||||
if (numBytes == 0)
|
||||
return string.Empty;
|
||||
|
||||
var data = stackalloc char[numBytes / 2];
|
||||
_ = ImmGetCompositionStringW(hImc, comp, data, (uint)numBytes);
|
||||
return new(data, 0, numBytes / 2);
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources()
|
||||
{
|
||||
if (ImGuiHelpers.IsImGuiInitialized)
|
||||
ImGui.GetIO().SetPlatformImeDataFn = nint.Zero;
|
||||
}
|
||||
|
||||
private void UpdateInputLanguage(HIMC hImc)
|
||||
{
|
||||
uint conv, sent;
|
||||
|
|
@ -359,41 +376,39 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
{
|
||||
case LANG.LANG_KOREAN:
|
||||
if (native)
|
||||
this.InputModeIcon = (char)SeIconChar.ImeKoreanHangul;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeKoreanHangul;
|
||||
else if (fullwidth)
|
||||
this.InputModeIcon = (char)SeIconChar.ImeAlphanumeric;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeAlphanumeric;
|
||||
else
|
||||
this.InputModeIcon = (char)SeIconChar.ImeAlphanumericHalfWidth;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeAlphanumericHalfWidth;
|
||||
break;
|
||||
|
||||
case LANG.LANG_JAPANESE:
|
||||
// wtf
|
||||
// see the function called from: 48 8b 0d ?? ?? ?? ?? e8 ?? ?? ?? ?? 8b d8 e9 ?? 00 00 0
|
||||
if (open && native && katakana && fullwidth)
|
||||
this.InputModeIcon = (char)SeIconChar.ImeKatakana;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeKatakana;
|
||||
else if (open && native && katakana)
|
||||
this.InputModeIcon = (char)SeIconChar.ImeKatakanaHalfWidth;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeKatakanaHalfWidth;
|
||||
else if (open && native)
|
||||
this.InputModeIcon = (char)SeIconChar.ImeHiragana;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeHiragana;
|
||||
else if (open && fullwidth)
|
||||
this.InputModeIcon = (char)SeIconChar.ImeAlphanumeric;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeAlphanumeric;
|
||||
else
|
||||
this.InputModeIcon = (char)SeIconChar.ImeAlphanumericHalfWidth;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeAlphanumericHalfWidth;
|
||||
break;
|
||||
|
||||
case LANG.LANG_CHINESE:
|
||||
if (native)
|
||||
this.InputModeIcon = (char)SeIconChar.ImeChineseHan;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeChineseHan;
|
||||
else
|
||||
this.InputModeIcon = (char)SeIconChar.ImeChineseLatin;
|
||||
this.inputModeIcon = (char)SeIconChar.ImeChineseLatin;
|
||||
break;
|
||||
|
||||
default:
|
||||
this.InputModeIcon = default;
|
||||
this.inputModeIcon = default;
|
||||
break;
|
||||
}
|
||||
|
||||
this.UpdateImeWindowStatus(hImc);
|
||||
}
|
||||
|
||||
private void ReplaceCompositionString(HIMC hImc, uint comp)
|
||||
|
|
@ -425,14 +440,14 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
return;
|
||||
}
|
||||
|
||||
this.ImmComp = newString;
|
||||
this.CompositionCursorOffset = ImmGetCompositionStringW(hImc, GCS.GCS_CURSORPOS, null, 0);
|
||||
this.compositionString = newString;
|
||||
this.compositionCursorOffset = ImmGetCompositionStringW(hImc, GCS.GCS_CURSORPOS, null, 0);
|
||||
|
||||
if ((comp & GCS.GCS_COMPATTR) != 0)
|
||||
{
|
||||
var attrLength = ImmGetCompositionStringW(hImc, GCS.GCS_COMPATTR, null, 0);
|
||||
var attrPtr = stackalloc byte[attrLength];
|
||||
var attr = new Span<byte>(attrPtr, Math.Min(this.ImmComp.Length, attrLength));
|
||||
var attr = new Span<byte>(attrPtr, Math.Min(this.compositionString.Length, attrLength));
|
||||
_ = ImmGetCompositionStringW(hImc, GCS.GCS_COMPATTR, attrPtr, (uint)attrLength);
|
||||
var l = 0;
|
||||
while (l < attr.Length && attr[l] is not ATTR_TARGET_CONVERTED and not ATTR_TARGET_NOTCONVERTED)
|
||||
|
|
@ -442,37 +457,37 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
while (r < attr.Length && attr[r] is ATTR_TARGET_CONVERTED or ATTR_TARGET_NOTCONVERTED)
|
||||
r++;
|
||||
|
||||
if (r == 0 || l == this.ImmComp.Length)
|
||||
(l, r) = (0, this.ImmComp.Length);
|
||||
if (r == 0 || l == this.compositionString.Length)
|
||||
(l, r) = (0, this.compositionString.Length);
|
||||
|
||||
(this.PartialConversionFrom, this.PartialConversionTo) = (l, r);
|
||||
(this.partialConversionFrom, this.partialConversionTo) = (l, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.PartialConversionFrom = 0;
|
||||
this.PartialConversionTo = this.ImmComp.Length;
|
||||
this.partialConversionFrom = 0;
|
||||
this.partialConversionTo = this.compositionString.Length;
|
||||
}
|
||||
|
||||
this.UpdateImeWindowStatus(hImc);
|
||||
this.UpdateCandidates(hImc);
|
||||
}
|
||||
|
||||
private void ClearState(HIMC hImc)
|
||||
{
|
||||
this.ImmComp = string.Empty;
|
||||
this.PartialConversionFrom = this.PartialConversionTo = 0;
|
||||
this.CompositionCursorOffset = 0;
|
||||
this.compositionString = string.Empty;
|
||||
this.partialConversionFrom = this.partialConversionTo = 0;
|
||||
this.compositionCursorOffset = 0;
|
||||
this.temporaryUndoSelection = null;
|
||||
TextState->Stb.SelectStart = TextState->Stb.Cursor = TextState->Stb.SelectEnd;
|
||||
ImmNotifyIME(hImc, NI.NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
||||
this.UpdateImeWindowStatus(default);
|
||||
this.UpdateCandidates(default);
|
||||
|
||||
// Log.Information($"{nameof(this.ClearState)}");
|
||||
}
|
||||
|
||||
private void LoadCand(HIMC hImc)
|
||||
private void UpdateCandidates(HIMC hImc)
|
||||
{
|
||||
this.ImmCand.Clear();
|
||||
this.ImmCandNative = default;
|
||||
this.candidateStrings.Clear();
|
||||
this.immCandNative = default;
|
||||
|
||||
if (hImc == default)
|
||||
return;
|
||||
|
|
@ -486,7 +501,7 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
return;
|
||||
|
||||
ref var candlist = ref *(CANDIDATELIST*)pStorage;
|
||||
this.ImmCandNative = candlist;
|
||||
this.immCandNative = candlist;
|
||||
|
||||
if (candlist.dwPageSize == 0 || candlist.dwCount == 0)
|
||||
return;
|
||||
|
|
@ -495,39 +510,250 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType
|
|||
(int)candlist.dwPageStart,
|
||||
(int)Math.Min(candlist.dwCount - candlist.dwPageStart, candlist.dwPageSize)))
|
||||
{
|
||||
this.ImmCand.Add(new((char*)(pStorage + candlist.dwOffset[i])));
|
||||
this.ReflectCharacterEncounters(this.ImmCand[^1]);
|
||||
this.candidateStrings.Add(new((char*)(pStorage + candlist.dwOffset[i])));
|
||||
this.ReflectCharacterEncounters(this.candidateStrings[^1]);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateImeWindowStatus(HIMC hImc)
|
||||
{
|
||||
if (Service<DalamudInterface>.GetNullable() is not { } di)
|
||||
return;
|
||||
|
||||
this.LoadCand(hImc);
|
||||
if (this.ImmCand.Count != 0 || this.ShowPartialConversion || this.InputModeIcon != default)
|
||||
di.OpenImeWindow();
|
||||
else
|
||||
di.CloseImeWindow();
|
||||
}
|
||||
|
||||
private void ImGuiSetPlatformImeData(ImGuiViewportPtr viewport, ImGuiPlatformImeDataPtr data)
|
||||
{
|
||||
this.CursorPos = data.InputPos;
|
||||
this.AssociatedViewport = data.WantVisible ? viewport : default;
|
||||
this.cursorScreenPos = data.InputPos;
|
||||
this.associatedViewport = data.WantVisible ? viewport : default;
|
||||
}
|
||||
|
||||
[ServiceManager.CallWhenServicesReady("Effectively waiting for cimgui context initialization.")]
|
||||
private void ContinueConstruction(InterfaceManager.InterfaceManagerWithScene interfaceManagerWithScene)
|
||||
private void Draw()
|
||||
{
|
||||
if (!ImGuiHelpers.IsImGuiInitialized)
|
||||
if (!this.ShouldDraw)
|
||||
return;
|
||||
|
||||
if (Service<DalamudIme>.GetNullable() is not { } ime)
|
||||
return;
|
||||
|
||||
var viewport = ime.associatedViewport;
|
||||
if (viewport.NativePtr is null)
|
||||
return;
|
||||
|
||||
var drawCand = ime.candidateStrings.Count != 0;
|
||||
var drawConv = drawCand || ime.ShowPartialConversion;
|
||||
var drawIme = ime.inputModeIcon != 0;
|
||||
var imeIconFont = InterfaceManager.DefaultFont;
|
||||
|
||||
var pad = ImGui.GetStyle().WindowPadding;
|
||||
var candTextSize = ImGui.CalcTextSize(ime.compositionString == string.Empty ? " " : ime.compositionString);
|
||||
|
||||
var native = ime.immCandNative;
|
||||
var totalIndex = native.dwSelection + 1;
|
||||
var totalSize = native.dwCount;
|
||||
|
||||
var pageStart = native.dwPageStart;
|
||||
var pageIndex = (pageStart / ImePageSize) + 1;
|
||||
var pageCount = (totalSize / ImePageSize) + 1;
|
||||
var pageInfo = $"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})";
|
||||
|
||||
// Calc the window size.
|
||||
var maxTextWidth = 0f;
|
||||
for (var i = 0; i < ime.candidateStrings.Count; i++)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Expected {nameof(InterfaceManager.InterfaceManagerWithScene)} to have initialized ImGui.");
|
||||
var textSize = ImGui.CalcTextSize($"{i + 1}. {ime.candidateStrings[i]}");
|
||||
maxTextWidth = maxTextWidth > textSize.X ? maxTextWidth : textSize.X;
|
||||
}
|
||||
|
||||
ImGui.GetIO().SetPlatformImeDataFn = Marshal.GetFunctionPointerForDelegate(this.setPlatformImeDataDelegate);
|
||||
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(pageInfo).X ? maxTextWidth : ImGui.CalcTextSize(pageInfo).X;
|
||||
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(ime.compositionString).X
|
||||
? maxTextWidth
|
||||
: ImGui.CalcTextSize(ime.compositionString).X;
|
||||
|
||||
var numEntries = (drawCand ? ime.candidateStrings.Count + 1 : 0) + 1 + (drawIme ? 1 : 0);
|
||||
var spaceY = ImGui.GetStyle().ItemSpacing.Y;
|
||||
var imeWindowHeight = (spaceY * (numEntries - 1)) + (candTextSize.Y * numEntries);
|
||||
var windowSize = new Vector2(maxTextWidth, imeWindowHeight) + (pad * 2);
|
||||
|
||||
// 1. Figure out the expanding direction.
|
||||
var expandUpward = ime.cursorScreenPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y;
|
||||
var windowPos = ime.cursorScreenPos - pad;
|
||||
if (expandUpward)
|
||||
{
|
||||
windowPos.Y -= windowSize.Y - candTextSize.Y - (pad.Y * 2);
|
||||
if (drawIme)
|
||||
windowPos.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (drawIme)
|
||||
windowPos.Y -= candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
// 2. Contain within the viewport. Do not use clamp, as the target window might be too small.
|
||||
if (windowPos.X < viewport.WorkPos.X)
|
||||
windowPos.X = viewport.WorkPos.X;
|
||||
else if (windowPos.X + windowSize.X > viewport.WorkPos.X + viewport.WorkSize.X)
|
||||
windowPos.X = (viewport.WorkPos.X + viewport.WorkSize.X) - windowSize.X;
|
||||
if (windowPos.Y < viewport.WorkPos.Y)
|
||||
windowPos.Y = viewport.WorkPos.Y;
|
||||
else if (windowPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y)
|
||||
windowPos.Y = (viewport.WorkPos.Y + viewport.WorkSize.Y) - windowSize.Y;
|
||||
|
||||
var cursor = windowPos + pad;
|
||||
|
||||
// Draw the ime window.
|
||||
var drawList = ImGui.GetForegroundDrawList(viewport);
|
||||
|
||||
// Draw the background rect for candidates.
|
||||
if (drawCand)
|
||||
{
|
||||
Vector2 candRectLt, candRectRb;
|
||||
if (!expandUpward)
|
||||
{
|
||||
candRectLt = windowPos + candTextSize with { X = 0 } + pad with { X = 0 };
|
||||
candRectRb = windowPos + windowSize;
|
||||
if (drawIme)
|
||||
candRectLt.Y += spaceY + candTextSize.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
candRectLt = windowPos;
|
||||
candRectRb = windowPos + (windowSize - candTextSize with { X = 0 } - pad with { X = 0 });
|
||||
if (drawIme)
|
||||
candRectRb.Y -= spaceY + candTextSize.Y;
|
||||
}
|
||||
|
||||
drawList.AddRectFilled(
|
||||
candRectLt,
|
||||
candRectRb,
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ImGui.GetStyle().WindowRounding);
|
||||
}
|
||||
|
||||
if (!expandUpward && drawIme)
|
||||
{
|
||||
for (var dx = -2; dx <= 2; dx++)
|
||||
{
|
||||
for (var dy = -2; dy <= 2; dy++)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor + new Vector2(dx, dy),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ime.inputModeIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor,
|
||||
ImGui.GetColorU32(ImGuiCol.Text),
|
||||
ime.inputModeIcon);
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (!expandUpward && drawConv)
|
||||
{
|
||||
DrawTextBeingConverted();
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
|
||||
// Add a separator.
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
}
|
||||
|
||||
if (drawCand)
|
||||
{
|
||||
// Add the candidate words.
|
||||
for (var i = 0; i < ime.candidateStrings.Count; i++)
|
||||
{
|
||||
var selected = i == (native.dwSelection % ImePageSize);
|
||||
var color = ImGui.GetColorU32(ImGuiCol.Text);
|
||||
if (selected)
|
||||
color = ImGui.GetColorU32(ImGuiCol.NavHighlight);
|
||||
|
||||
drawList.AddText(cursor, color, $"{i + 1}. {ime.candidateStrings[i]}");
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
// Add a separator
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
|
||||
// Add the pages infomation.
|
||||
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), pageInfo);
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (expandUpward && drawConv)
|
||||
{
|
||||
// Add a separator.
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
|
||||
DrawTextBeingConverted();
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (expandUpward && drawIme)
|
||||
{
|
||||
for (var dx = -2; dx <= 2; dx++)
|
||||
{
|
||||
for (var dy = -2; dy <= 2; dy++)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor + new Vector2(dx, dy),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ime.inputModeIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor,
|
||||
ImGui.GetColorU32(ImGuiCol.Text),
|
||||
ime.inputModeIcon);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void DrawTextBeingConverted()
|
||||
{
|
||||
// Draw the text background.
|
||||
drawList.AddRectFilled(
|
||||
cursor - (pad / 2),
|
||||
cursor + candTextSize + (pad / 2),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg));
|
||||
|
||||
// If only a part of the full text is marked for conversion, then draw background for the part being edited.
|
||||
if (ime.partialConversionFrom != 0 || ime.partialConversionTo != ime.compositionString.Length)
|
||||
{
|
||||
var part1 = ime.compositionString[..ime.partialConversionFrom];
|
||||
var part2 = ime.compositionString[..ime.partialConversionTo];
|
||||
var size1 = ImGui.CalcTextSize(part1);
|
||||
var size2 = ImGui.CalcTextSize(part2);
|
||||
drawList.AddRectFilled(
|
||||
cursor + size1 with { Y = 0 },
|
||||
cursor + size2,
|
||||
ImGui.GetColorU32(ImGuiCol.TextSelectedBg));
|
||||
}
|
||||
|
||||
// Add the text being converted.
|
||||
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), ime.compositionString);
|
||||
|
||||
// Draw the caret inside the composition string.
|
||||
if (DalamudIme.ShowCursorInInputText)
|
||||
{
|
||||
var partBeforeCaret = ime.compositionString[..ime.compositionCursorOffset];
|
||||
var sizeBeforeCaret = ImGui.CalcTextSize(partBeforeCaret);
|
||||
drawList.AddLine(
|
||||
cursor + sizeBeforeCaret with { Y = 0 },
|
||||
cursor + sizeBeforeCaret,
|
||||
ImGui.GetColorU32(ImGuiCol.Text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
private readonly ComponentDemoWindow componentDemoWindow;
|
||||
private readonly DataWindow dataWindow;
|
||||
private readonly GamepadModeNotifierWindow gamepadModeNotifierWindow;
|
||||
private readonly DalamudImeWindow imeWindow;
|
||||
private readonly ConsoleWindow consoleWindow;
|
||||
private readonly PluginStatWindow pluginStatWindow;
|
||||
private readonly PluginInstallerWindow pluginWindow;
|
||||
|
|
@ -114,7 +113,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
|
||||
this.dataWindow = new DataWindow() { IsOpen = false };
|
||||
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow() { IsOpen = false };
|
||||
this.imeWindow = new DalamudImeWindow() { IsOpen = false };
|
||||
this.consoleWindow = new ConsoleWindow(configuration) { IsOpen = configuration.LogOpenAtStartup };
|
||||
this.pluginStatWindow = new PluginStatWindow() { IsOpen = false };
|
||||
this.pluginWindow = new PluginInstallerWindow(pluginImageCache, configuration) { IsOpen = false };
|
||||
|
|
@ -142,7 +140,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
this.WindowSystem.AddWindow(this.componentDemoWindow);
|
||||
this.WindowSystem.AddWindow(this.dataWindow);
|
||||
this.WindowSystem.AddWindow(this.gamepadModeNotifierWindow);
|
||||
this.WindowSystem.AddWindow(this.imeWindow);
|
||||
this.WindowSystem.AddWindow(this.consoleWindow);
|
||||
this.WindowSystem.AddWindow(this.pluginStatWindow);
|
||||
this.WindowSystem.AddWindow(this.pluginWindow);
|
||||
|
|
@ -265,11 +262,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
/// </summary>
|
||||
public void OpenGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.IsOpen = true;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the <see cref="DalamudImeWindow"/>.
|
||||
/// </summary>
|
||||
public void OpenImeWindow() => this.imeWindow.IsOpen = true;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the <see cref="ConsoleWindow"/>.
|
||||
/// </summary>
|
||||
|
|
@ -365,11 +357,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
|
||||
#region Close
|
||||
|
||||
/// <summary>
|
||||
/// Closes the <see cref="DalamudImeWindow"/>.
|
||||
/// </summary>
|
||||
public void CloseImeWindow() => this.imeWindow.IsOpen = false;
|
||||
|
||||
/// <summary>
|
||||
/// Closes the <see cref="GamepadModeNotifierWindow"/>.
|
||||
/// </summary>
|
||||
|
|
@ -417,11 +404,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
/// </summary>
|
||||
public void ToggleGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.Toggle();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the <see cref="DalamudImeWindow"/>.
|
||||
/// </summary>
|
||||
public void ToggleImeWindow() => this.imeWindow.Toggle();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the <see cref="ConsoleWindow"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -67,9 +67,6 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly WndProcHookManager wndProcHookManager = Service<WndProcHookManager>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DalamudIme dalamudIme = Service<DalamudIme>.Get();
|
||||
|
||||
private readonly SwapChainVtableResolver address = new();
|
||||
private readonly Hook<SetCursorDelegate> setCursorHook;
|
||||
|
|
@ -627,8 +624,6 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
var r = this.scene?.ProcessWndProcW(args.Hwnd, (User32.WindowMessage)args.Message, args.WParam, args.LParam);
|
||||
if (r is not null)
|
||||
args.SuppressWithValue(r.Value);
|
||||
|
||||
this.dalamudIme.ProcessImeMessage(args);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,266 +0,0 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Dalamud.Interface.Windowing;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Internal.Windows;
|
||||
|
||||
/// <summary>
|
||||
/// A window for displaying IME details.
|
||||
/// </summary>
|
||||
internal unsafe class DalamudImeWindow : Window
|
||||
{
|
||||
private const int ImePageSize = 9;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudImeWindow"/> class.
|
||||
/// </summary>
|
||||
public DalamudImeWindow()
|
||||
: base(
|
||||
"Dalamud IME",
|
||||
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoBackground)
|
||||
{
|
||||
this.Size = default(Vector2);
|
||||
|
||||
this.RespectCloseHotkey = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Draw()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void PostDraw()
|
||||
{
|
||||
if (Service<DalamudIme>.GetNullable() is not { } ime)
|
||||
return;
|
||||
|
||||
var viewport = ime.AssociatedViewport;
|
||||
if (viewport.NativePtr is null)
|
||||
return;
|
||||
|
||||
var drawCand = ime.ImmCand.Count != 0;
|
||||
var drawConv = drawCand || ime.ShowPartialConversion;
|
||||
var drawIme = ime.InputModeIcon != 0;
|
||||
var imeIconFont = InterfaceManager.DefaultFont;
|
||||
|
||||
var pad = ImGui.GetStyle().WindowPadding;
|
||||
var candTextSize = ImGui.CalcTextSize(ime.ImmComp == string.Empty ? " " : ime.ImmComp);
|
||||
|
||||
var native = ime.ImmCandNative;
|
||||
var totalIndex = native.dwSelection + 1;
|
||||
var totalSize = native.dwCount;
|
||||
|
||||
var pageStart = native.dwPageStart;
|
||||
var pageIndex = (pageStart / ImePageSize) + 1;
|
||||
var pageCount = (totalSize / ImePageSize) + 1;
|
||||
var pageInfo = $"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})";
|
||||
|
||||
// Calc the window size.
|
||||
var maxTextWidth = 0f;
|
||||
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 numEntries = (drawCand ? ime.ImmCand.Count + 1 : 0) + 1 + (drawIme ? 1 : 0);
|
||||
var spaceY = ImGui.GetStyle().ItemSpacing.Y;
|
||||
var imeWindowHeight = (spaceY * (numEntries - 1)) + (candTextSize.Y * numEntries);
|
||||
var windowSize = new Vector2(maxTextWidth, imeWindowHeight) + (pad * 2);
|
||||
|
||||
// 1. Figure out the expanding direction.
|
||||
var expandUpward = ime.CursorPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y;
|
||||
var windowPos = ime.CursorPos - pad;
|
||||
if (expandUpward)
|
||||
{
|
||||
windowPos.Y -= windowSize.Y - candTextSize.Y - (pad.Y * 2);
|
||||
if (drawIme)
|
||||
windowPos.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (drawIme)
|
||||
windowPos.Y -= candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
// 2. Contain within the viewport. Do not use clamp, as the target window might be too small.
|
||||
if (windowPos.X < viewport.WorkPos.X)
|
||||
windowPos.X = viewport.WorkPos.X;
|
||||
else if (windowPos.X + windowSize.X > viewport.WorkPos.X + viewport.WorkSize.X)
|
||||
windowPos.X = (viewport.WorkPos.X + viewport.WorkSize.X) - windowSize.X;
|
||||
if (windowPos.Y < viewport.WorkPos.Y)
|
||||
windowPos.Y = viewport.WorkPos.Y;
|
||||
else if (windowPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y)
|
||||
windowPos.Y = (viewport.WorkPos.Y + viewport.WorkSize.Y) - windowSize.Y;
|
||||
|
||||
var cursor = windowPos + pad;
|
||||
|
||||
// Draw the ime window.
|
||||
var drawList = ImGui.GetForegroundDrawList(viewport);
|
||||
|
||||
// Draw the background rect for candidates.
|
||||
if (drawCand)
|
||||
{
|
||||
Vector2 candRectLt, candRectRb;
|
||||
if (!expandUpward)
|
||||
{
|
||||
candRectLt = windowPos + candTextSize with { X = 0 } + pad with { X = 0 };
|
||||
candRectRb = windowPos + windowSize;
|
||||
if (drawIme)
|
||||
candRectLt.Y += spaceY + candTextSize.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
candRectLt = windowPos;
|
||||
candRectRb = windowPos + (windowSize - candTextSize with { X = 0 } - pad with { X = 0 });
|
||||
if (drawIme)
|
||||
candRectRb.Y -= spaceY + candTextSize.Y;
|
||||
}
|
||||
|
||||
drawList.AddRectFilled(
|
||||
candRectLt,
|
||||
candRectRb,
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ImGui.GetStyle().WindowRounding);
|
||||
}
|
||||
|
||||
if (!expandUpward && drawIme)
|
||||
{
|
||||
for (var dx = -2; dx <= 2; dx++)
|
||||
{
|
||||
for (var dy = -2; dy <= 2; dy++)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor + new Vector2(dx, dy),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ime.InputModeIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor,
|
||||
ImGui.GetColorU32(ImGuiCol.Text),
|
||||
ime.InputModeIcon);
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (!expandUpward && drawConv)
|
||||
{
|
||||
DrawTextBeingConverted();
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
|
||||
// Add a separator.
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
}
|
||||
|
||||
if (drawCand)
|
||||
{
|
||||
// Add the candidate words.
|
||||
for (var i = 0; i < ime.ImmCand.Count; i++)
|
||||
{
|
||||
var selected = i == (native.dwSelection % ImePageSize);
|
||||
var color = ImGui.GetColorU32(ImGuiCol.Text);
|
||||
if (selected)
|
||||
color = ImGui.GetColorU32(ImGuiCol.NavHighlight);
|
||||
|
||||
drawList.AddText(cursor, color, $"{i + 1}. {ime.ImmCand[i]}");
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
// Add a separator
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
|
||||
// Add the pages infomation.
|
||||
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), pageInfo);
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (expandUpward && drawConv)
|
||||
{
|
||||
// Add a separator.
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
|
||||
DrawTextBeingConverted();
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (expandUpward && drawIme)
|
||||
{
|
||||
for (var dx = -2; dx <= 2; dx++)
|
||||
{
|
||||
for (var dy = -2; dy <= 2; dy++)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor + new Vector2(dx, dy),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ime.InputModeIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor,
|
||||
ImGui.GetColorU32(ImGuiCol.Text),
|
||||
ime.InputModeIcon);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void DrawTextBeingConverted()
|
||||
{
|
||||
// Draw the text background.
|
||||
drawList.AddRectFilled(
|
||||
cursor - (pad / 2),
|
||||
cursor + candTextSize + (pad / 2),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg));
|
||||
|
||||
// If only a part of the full text is marked for conversion, then draw background for the part being edited.
|
||||
if (ime.PartialConversionFrom != 0 || ime.PartialConversionTo != ime.ImmComp.Length)
|
||||
{
|
||||
var part1 = ime.ImmComp[..ime.PartialConversionFrom];
|
||||
var part2 = ime.ImmComp[..ime.PartialConversionTo];
|
||||
var size1 = ImGui.CalcTextSize(part1);
|
||||
var size2 = ImGui.CalcTextSize(part2);
|
||||
drawList.AddRectFilled(
|
||||
cursor + size1 with { Y = 0 },
|
||||
cursor + size2,
|
||||
ImGui.GetColorU32(ImGuiCol.TextSelectedBg));
|
||||
}
|
||||
|
||||
// Add the text being converted.
|
||||
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), ime.ImmComp);
|
||||
|
||||
// Draw the caret inside the composition string.
|
||||
if (DalamudIme.ShowCursorInInputText)
|
||||
{
|
||||
var partBeforeCaret = ime.ImmComp[..ime.CompositionCursorOffset];
|
||||
var sizeBeforeCaret = ImGui.CalcTextSize(partBeforeCaret);
|
||||
drawList.AddLine(
|
||||
cursor + sizeBeforeCaret with { Y = 0 },
|
||||
cursor + sizeBeforeCaret,
|
||||
ImGui.GetColorU32(ImGuiCol.Text));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -165,6 +165,7 @@ internal static class ServiceManager
|
|||
|
||||
var earlyLoadingServices = new HashSet<Type>();
|
||||
var blockingEarlyLoadingServices = new HashSet<Type>();
|
||||
var providedServices = new HashSet<Type>();
|
||||
|
||||
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
|
||||
var getAsyncTaskMap = new Dictionary<Type, Task>();
|
||||
|
|
@ -197,7 +198,10 @@ internal static class ServiceManager
|
|||
|
||||
// We don't actually need to load provided services, something else does
|
||||
if (serviceKind.HasFlag(ServiceKind.ProvidedService))
|
||||
{
|
||||
providedServices.Add(serviceType);
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(
|
||||
serviceKind.HasFlag(ServiceKind.EarlyLoadedService) ||
|
||||
|
|
@ -340,7 +344,16 @@ internal static class ServiceManager
|
|||
}
|
||||
|
||||
if (!tasks.Any())
|
||||
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||
{
|
||||
// No more services we can start loading for now.
|
||||
// Either we're waiting for provided services, or there's a dependency cycle.
|
||||
providedServices.RemoveWhere(x => getAsyncTaskMap[x].IsCompleted);
|
||||
if (providedServices.Any())
|
||||
await Task.WhenAny(providedServices.Select(x => getAsyncTaskMap[x]));
|
||||
else
|
||||
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (servicesToLoad.Any())
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue