Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RDY] win32 ime #6223

Closed
wants to merge 14 commits into from
34 changes: 5 additions & 29 deletions Avalonia.sln
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sandbox", "samples\Sandbox\Sandbox.csproj", "{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroComGenerator", "src\tools\MicroComGenerator\MicroComGenerator.csproj", "{AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroComGenerator", "src\tools\MicroComGenerator\MicroComGenerator.csproj", "{AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.MicroCom", "src\Avalonia.MicroCom\Avalonia.MicroCom.csproj", "{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MicroCom", "src\Avalonia.MicroCom\Avalonia.MicroCom.csproj", "{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Expand Down Expand Up @@ -326,8 +326,8 @@ Global
{42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhone.Build.0 = Release|Any CPU
{42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -2020,30 +2020,6 @@ Global
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhone.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhone.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|Any CPU.Build.0 = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.ActiveCfg = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.Build.0 = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public virtual void SetCursor(ICursorImpl cursor)
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { }

public WindowTransparencyLevel TransparencyLevel { get; private set; }
public Action InputMethodUpdated { get; set; }

public IPopupImpl CreatePopup() => null;
}
Expand Down
6 changes: 6 additions & 0 deletions src/Avalonia.Controls/Platform/ITopLevelImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,11 @@ public interface ITopLevelImpl : IDisposable
/// Gets the <see cref="AcrylicPlatformCompensationLevels"/> for the platform.
/// </summary>
AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }


/// <summary>
/// Called when the input method is changed for the current top level object.
/// </summary>
Action InputMethodUpdated { get; set; }
}
}
6 changes: 6 additions & 0 deletions src/Avalonia.Controls/TopLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver dependencyResolv
impl.Resized = HandleResized;
impl.ScalingChanged = HandleScalingChanged;
impl.TransparencyLevelChanged = HandleTransparencyLevelChanged;
impl.InputMethodUpdated = HandleInputMethodUpdated;

_keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this);
Expand Down Expand Up @@ -187,6 +188,11 @@ public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver dependencyResolv
impl.LostFocus += PlatformImpl_LostFocus;
}

private void HandleInputMethodUpdated()
{
KeyboardDevice.Instance.NotifyInputMethodUpdated(this);
}

/// <summary>
/// Fired when the window is opened.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.DesignerSupport/Remote/Stubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ public void SetWindowManagerAddShadowHint(bool enabled)
public bool NeedsManagedDecorations => false;

public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
public Action InputMethodUpdated { get; set; }
}

class ClipboardStub : IClipboard
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Headless/HeadlessWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,5 +336,7 @@ public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight)
{

}

public Action InputMethodUpdated { get; set; }
}
}
8 changes: 7 additions & 1 deletion src/Avalonia.Input/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ MembersMustExist : Member 'public Avalonia.Platform.IPlatformHandle Avalonia.Inp
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.Gestures.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.Gestures.RightTappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.Gestures.TappedEvent' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.IKeyboardDevice.NotifyInputMethodUpdated(Avalonia.Input.TextInput.ITextInputMethodRoot)' is present in the implementation but not in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.InputElement.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.InputElement.TappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_DoubleTapped(System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_Tapped(System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Input.TextInput.ITextInputMethodClient.ActiveState' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.EventHandler Avalonia.Input.TextInput.ITextInputMethodClient.ActiveStateChanged' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Input.TextInput.ITextInputMethodClient.ActiveState.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodClient.add_ActiveStateChanged(System.EventHandler)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodClient.remove_ActiveStateChanged(System.EventHandler)' is present in the implementation but not in the contract.
TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract.
Total Issues: 11
Total Issues: 17
2 changes: 2 additions & 0 deletions src/Avalonia.Input/IKeyboardDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@ void SetFocusedElement(
IInputElement? element,
NavigationMethod method,
KeyModifiers modifiers);

void NotifyInputMethodUpdated(TextInput.ITextInputMethodRoot? root);
}
}
5 changes: 5 additions & 0 deletions src/Avalonia.Input/KeyboardDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,5 +265,10 @@ public void ProcessRawEvent(RawInputEventArgs e)
e.Handled = ev.Handled;
}
}

public void NotifyInputMethodUpdated(ITextInputMethodRoot? root)
{
_textInputManager.NotifyInputMethodUpdated(root);
}
}
}
28 changes: 18 additions & 10 deletions src/Avalonia.Input/TextInput/InputMethodManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,27 +75,35 @@ public void SetFocusedElement(IInputElement? element)
if(_focusedElement == element)
return;
_focusedElement = element;

var inputMethod = (element?.VisualRoot as ITextInputMethodRoot)?.InputMethod;

var root = element?.VisualRoot as ITextInputMethodRoot;

NotifyInputMethodUpdated(root);
}

public void NotifyInputMethodUpdated(ITextInputMethodRoot? root)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why exactly do we need this? Is it needed to handle keyboard layout switch between basic and IME-enabled languages?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if that should be handled on the xplat layer though. I'd expect that state to be tracked by the platform code that encapsulates such switches.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how other users do this, but for me I heavily rely on win+space to switch between en/zh on the fly.
The ime manager queries client capabilities when it gets focus (or init?), but doesn't handle IME switch when the control is focused.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I'd expect the WindowImpl.cs to always have an active Imm32InputMethod instance (just like X11Window does) that maintains its state rather than going through the requery process. Your approach also adds more ties between input method handling and toplevels while we are kinda planning to add built-in OSK support.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WindowImpl always have that. InputMethodManager doesn't.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial idea was that InputMethodManager synchronizes the control with the native window implementation. The native window implementation is supposed to keep track of what was previously passed to it rather than requery that information on demand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Maybe it's possible to put the mutable states in IMM32InputMethod.

{
if (_focusedElement?.VisualRoot != root)
{
return;
}
var inputMethod = root?.InputMethod;
if(_im != inputMethod)
_im?.SetActive(false);

_im = inputMethod;

if (_focusedElement == null || _im == null)
{
Client = null;
_im?.SetActive(false);
return;
}

var clientQuery = new TextInputMethodClientRequestedEventArgs
{
RoutedEvent = InputElement.TextInputMethodClientRequestedEvent
};

_focusedElement.RaiseEvent(clientQuery);
_focusedElement?.RaiseEvent(clientQuery);
Client = clientQuery.Client;
if (Client == null)
{
_im?.SetActive(false);
}
}
}
}
2 changes: 2 additions & 0 deletions src/Avalonia.Native/WindowImplBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -478,5 +478,7 @@ public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0);

public IPlatformHandle Handle { get; private set; }

public Action InputMethodUpdated { get; set; }
}
}
2 changes: 2 additions & 0 deletions src/Avalonia.X11/X11Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1165,5 +1165,7 @@ public void SetWindowManagerAddShadowHint(bool enabled)
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0.8);

public bool NeedsManagedDecorations => false;

public Action InputMethodUpdated { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,7 @@ public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
public WindowTransparencyLevel TransparencyLevel { get; private set; }

public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);

public Action InputMethodUpdated { get; set; }
}
}
2 changes: 2 additions & 0 deletions src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,5 +259,7 @@ public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
public WindowTransparencyLevel TransparencyLevel { get; private set; }

public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);

public Action InputMethodUpdated { get; set; }
}
}
166 changes: 166 additions & 0 deletions src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Text;

using Avalonia.Input.TextInput;
using Avalonia.Threading;

using static Avalonia.Win32.Interop.UnmanagedMethods;

namespace Avalonia.Win32.Input
{
/// <summary>
/// A Windows input method editor based on Windows Input Method Manager (IMM32).
/// </summary>
class Imm32InputMethod : ITextInputMethodImpl, IDisposable
{
private bool _disposedValue;
private IntPtr _hwnd;
private WindowImpl _parent;
private bool _active;
private bool _systemCaret;
private bool _showCompositionWindow;
private bool _showCandidateList;
private ushort _langId;
private const int _caretMargin = 1;

public Imm32InputMethod(WindowImpl parent, IntPtr hwnd, IntPtr HKL)
{
_parent = parent;
_hwnd = hwnd;
_disposedValue = false;
_active = false;
_langId = PRIMARYLANGID(LGID(HKL));
_systemCaret = (_langId == LANG_ZH || _langId == LANG_JA);
_showCompositionWindow = true;
_showCandidateList = true;
if (_systemCaret)
{
CreateCaret(_hwnd, IntPtr.Zero, 1, 1);
}
IsComposing = false;
}

public void Reset()
{
if (IsComposing)
{
Dispatcher.UIThread.Post(() =>
{
IntPtr himc = ImmGetContext(_hwnd);
ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
ImmReleaseContext(_hwnd, himc);
});
}
}

public void SetActive(bool active)
{
_active = active;
Dispatcher.UIThread.Post(() =>
{
IntPtr himc = ImmGetContext(_hwnd);
ImmSetActiveContext(himc, active);
ImmSetOpenStatus(himc, active);
ImmReleaseContext(_hwnd, himc);
});
}

public void SetCursorRect(Rect rect)
{
var focused = GetActiveWindow() == _hwnd;
if (!focused)
{
return;
}
Dispatcher.UIThread.Post(() =>
{
IntPtr himc = ImmGetContext(_hwnd);
if (himc == IntPtr.Zero)
{
return;
}

// see: https://chromium.googlesource.com/experimental/chromium/src/+/bf09a5036ccfb77d2277247c66dc55daf41df3fe/chrome/browser/ime_input.cc
// see: https://engine.chinmaygarde.com/window__win32_8cc_source.html

var p1 = rect.TopLeft;
var p2 = rect.BottomRight;
var s = _parent.DesktopScaling;
var (x1, y1, x2, y2) = ((int)(p1.X * s), (int)(p1.Y * s), (int)(p2.X * s), (int)(p2.Y * s));

if (_showCompositionWindow)
{
var compForm = new COMPOSITIONFORM
{
dwStyle = CFS_POINT,
ptCurrentPos = new POINT { X = x1, Y = y1 },
};
ImmSetCompositionWindow(himc, ref compForm);
}

if (_showCandidateList)
{
var candidateForm = new CANDIDATEFORM
{
dwIndex = 0,
dwStyle = CFS_CANDIDATEPOS,
ptCurrentPos = new POINT { X = x2, Y = y2 }
};
ImmSetCandidateWindow(himc, ref candidateForm);

if (_systemCaret)
{
SetCaretPos(x2, y2);
}

if (_langId == LANG_KO)
{
y2 += _caretMargin;
}

candidateForm = new CANDIDATEFORM
{
dwIndex = 0,
dwStyle = CFS_EXCLUDE,
ptCurrentPos = new POINT { X = x2, Y = y2 },
rcArea = new RECT { left = x2, top = y2, right = x2, bottom = y2 + _caretMargin}
};
}

ImmReleaseContext(_hwnd, himc);
});
}

public void SetOptions(TextInputOptionsQueryEventArgs options)
{
// ???
}

protected void _dispose()
{
if (!_disposedValue)
{
_disposedValue = true;
if (_systemCaret)
{
_systemCaret = false;
DestroyCaret();
}
}
}

public bool IsComposing { get; set; }

~Imm32InputMethod()
{
_dispose();
}

public void Dispose()
{
_dispose();
GC.SuppressFinalize(this);
}
}
}
Loading