1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
#include "cru/win/native/input_method.hpp"
#include "cru/common/logger.hpp"
#include "cru/win/exception.hpp"
#include "cru/win/native/window.hpp"
#include "cru/win/string.hpp"
#include "dpi_util.hpp"
#include <vector>
namespace cru::platform::native::win {
WinInputMethodContextRef::WinInputMethodContextRef(WinNativeWindow* window)
: window_(window) {
Expects(window);
window_handle_ = window->GetWindowHandle();
handle_ = ::ImmGetContext(window_handle_);
// TODO: Events
event_revoker_guards_.push_back(
EventRevokerGuard(window->NativeMessageEvent()->AddHandler(
std::bind(&WinInputMethodContextRef::OnWindowNativeMessage, this,
std::placeholders::_1))));
}
WinInputMethodContextRef::~WinInputMethodContextRef() {
::ImmReleaseContext(window_handle_, handle_);
}
void WinInputMethodContextRef::Reset() {
wchar_t s[1] = {L'\0'};
if (!::ImmSetCompositionStringW(handle_, SCS_SETSTR, static_cast<LPVOID>(s),
sizeof(s), static_cast<LPVOID>(s),
sizeof(s))) {
log::Warn(
"WinInputMethodContextRef: Failed to reset input method context.");
}
}
std::string WinInputMethodContextRef::GetCompositionText() {
const auto length =
::ImmGetCompositionStringW(handle_, GCS_RESULTREADSTR, NULL, 0) +
sizeof(wchar_t);
std::vector<std::byte> data(length);
const auto result = ::ImmGetCompositionStringW(
handle_, GCS_RESULTREADSTR, static_cast<LPVOID>(data.data()), length);
if (result == IMM_ERROR_NODATA) {
return std::string{};
} else if (result == IMM_ERROR_GENERAL) {
throw new platform::win::Win32Error(::GetLastError(),
"Failed to get composition string.");
}
return platform::win::ToUtf8String(
std::wstring_view(reinterpret_cast<wchar_t*>(data.data())));
}
void WinInputMethodContextRef::SetCandidateWindowPosition(const Point& point) {
::CANDIDATEFORM form;
form.dwIndex = 1;
form.dwStyle = CFS_CANDIDATEPOS;
form.ptCurrentPos = DipToPi(point);
if (!::ImmSetCandidateWindow(handle_, &form))
log::Debug(
"WinInputMethodContextRef: Failed to set input method candidate window "
"position.");
}
IEvent<std::nullptr_t>* WinInputMethodContextRef::CompositionStartEvent() {
return &composition_start_event_;
}
IEvent<std::nullptr_t>* WinInputMethodContextRef::CompositionEndEvent() {
return &composition_end_event_;
};
IEvent<std::string>* WinInputMethodContextRef::CompositionTextChangeEvent() {
return &composition_text_change_event_;
}
void WinInputMethodContextRef::OnWindowNativeMessage(
WindowNativeMessageEventArgs& args) {
const auto message = args.GetWindowMessage();
switch (message.msg) {
case WM_IME_COMPOSITION: {
composition_text_change_event_.Raise(this->GetCompositionText());
args.HandleWithResult(0);
break;
}
case WM_IME_STARTCOMPOSITION: {
composition_start_event_.Raise(nullptr);
args.HandleWithResult(0);
break;
}
case WM_IME_ENDCOMPOSITION: {
composition_end_event_.Raise(nullptr);
args.HandleWithResult(0);
break;
}
case WM_IME_SETCONTEXT: {
const auto new_l_param =
message.l_param & (~static_cast<LPARAM>(ISC_SHOWUICOMPOSITIONWINDOW));
args.HandleWithResult(::DefWindowProcW(message.hwnd, message.msg,
message.w_param, new_l_param));
}
}
}
} // namespace cru::platform::native::win
|