Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosinputmethodproxy.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtCore/private/qohoslogger_p.h>
6#include <QtCore/private/qstringconverter_p.h>
7#include <codecvt>
8#include <locale>
9#include <qarkui/qarkuiutils.h>
10#include <qohosplugincore.h>
11#include <qohosutils.h>
12
14
15namespace {
16
17std::string convertUtf16ToUtf8(const std::u16string &utf16String)
18{
19#ifdef __cpp_lib_string_resize_and_overwrite
20 std::string ut8Str;
21 // In the worst case, we need atleast 3 bytes when converting from utf16
22 // to utf8
23 const auto maxBytes = utf16String.size() * 3;
24 utf8Str.resize_and_overwrite(maxBytes, [&] (char *dst, size_t) noexcept {
25 return QUtf8::convertFromUnicode(dst, QStringView{utf16String}) - dst;
26 });
27 return utf8Str;
28#else
29QT_WARNING_PUSH
30QT_WARNING_DISABLE_DEPRECATED
31 return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(utf16String);
32QT_WARNING_POP
33#endif
34}
35
37{
38 return std::shared_ptr<::InputMethod_TextEditorProxy>(
39 QArkUi::callArkUiOrFailOnNullResult(
40 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_Create)),
41 [](::InputMethod_TextEditorProxy *textEditorProxy) {
42 QArkUi::callArkUi(
43 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_Destroy),
44 textEditorProxy);
45 });
46}
47
49 bool showKeyboard, ::InputMethod_RequestKeyboardReason requestKeyboardReason)
50{
51 return std::shared_ptr<::InputMethod_AttachOptions>(
52 QArkUi::callArkUiOrFailOnNullResult(
53 Q_OHOS_NAMED_FUNC(::OH_AttachOptions_CreateWithRequestKeyboardReason),
54 showKeyboard, requestKeyboardReason),
55 [](::InputMethod_AttachOptions *attachOptions) {
56 QArkUi::callArkUi(
57 Q_OHOS_NAMED_FUNC(::OH_AttachOptions_Destroy),
58 attachOptions);
59 });
60}
61
63 std::shared_ptr<::InputMethod_TextEditorProxy> textEditorProxy,
64 std::shared_ptr<::InputMethod_AttachOptions> attachOptions)
65{
66 ::InputMethod_InputMethodProxy *inputMethodProxyPtr = nullptr;
67 ::InputMethod_ErrorCode errcode = QArkUi::callArkUi(
68 Q_OHOS_NAMED_FUNC(::OH_InputMethodController_Attach),
69 textEditorProxy.get(), attachOptions.get(), &inputMethodProxyPtr);
70 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK) {
71 qOhosPrintfError("%s: OH_InputMethodController_Attach failed!", Q_FUNC_INFO);
72 return {};
73 }
74
75 return std::shared_ptr<::InputMethod_InputMethodProxy>(
76 inputMethodProxyPtr, [](::InputMethod_InputMethodProxy *proxy) {
77 QArkUi::callArkUiOrFailOnErrorResult(
78 Q_OHOS_NAMED_FUNC(::OH_InputMethodController_Detach), proxy);
79 });
80}
81
83 double x, double y, double width, double height)
84{
85 return std::shared_ptr<::InputMethod_CursorInfo>(
86 QArkUi::callArkUiOrFailOnNullResult(
87 Q_OHOS_NAMED_FUNC(::OH_CursorInfo_Create),
88 x, y, width, height),
89 [](::InputMethod_CursorInfo *cursorInfo) {
90 QArkUi::callArkUi(
91 Q_OHOS_NAMED_FUNC(::OH_CursorInfo_Destroy), cursorInfo);
92 });
93}
94
95template<typename CallbackResult, typename... CallbackArgs>
97template<
98 typename CallbackResult,
99 typename... CallbackArgs,
100 ::InputMethod_ErrorCode rawSetCallbackFunc(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>)>
102 ::InputMethod_TextEditorProxy *textEditorProxy,
103 QOhosNamedFunc<::InputMethod_ErrorCode(*)(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>), rawSetCallbackFunc> setCallbackFunc,
104 std::function<CallbackResult(CallbackArgs...)> callback)
105{
106 static std::map<::InputMethod_TextEditorProxy *, std::shared_ptr<std::function<CallbackResult(CallbackArgs...)>>> callbacksMap;
107
108 bool callbackRegistered;
109 std::tie(std::ignore, callbackRegistered) = callbacksMap.emplace(
110 textEditorProxy,
111 std::make_shared<std::function<CallbackResult(CallbackArgs...)>>(std::move(callback)));
112
113 if (!callbackRegistered)
114 qOhosReportFatalErrorAndAbort(Q_FUNC_INFO, "encountered duplicate callback registration for %p", textEditorProxy);
115
116 QArkUi::callArkUiOrFailOnErrorResult(
117 setCallbackFunc, textEditorProxy,
118 [](::InputMethod_TextEditorProxy *textEditorProxy, CallbackArgs ...callbackArgs) {
119 auto callbackIter = callbacksMap.find(textEditorProxy);
120 if (callbackIter != callbacksMap.end()) {
121 auto sharedCallback = callbackIter->second;
122 return (*sharedCallback)(callbackArgs...);
123 } else {
124 return CallbackResult();
125 }
126 });
127
128 return QtOhos::makeDestroyNotifier(
129 [textEditorProxy]() {
130 callbacksMap.erase(textEditorProxy);
131 });
132}
133
134template<
135 typename CallbackResult,
136 typename... CallbackArgs,
137 ::InputMethod_ErrorCode rawSetCallbackFunc(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>),
138 typename Callback>
140 ::InputMethod_TextEditorProxy *textEditorProxy,
141 QOhosNamedFunc<::InputMethod_ErrorCode(*)(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>), rawSetCallbackFunc> setCallbackFunc,
142 Callback callback)
143{
144 static_assert(std::is_assignable<std::function<CallbackResult(CallbackArgs...)>, Callback>::value, "");
145 return registerTextEditorProxyCallbackImpl<CallbackResult>(
146 textEditorProxy, setCallbackFunc, std::function<CallbackResult(CallbackArgs...)>(std::move(callback)));
147}
148
149template<typename... CallbackArgs>
151 std::weak_ptr<QOhosInputMethodProxy::ClientCallbacks> weakClientCallbacks,
152 void (QOhosInputMethodProxy::ClientCallbacks::*callbackMemPtr)(CallbackArgs...),
153 CallbackArgs ...args)
154{
155 QtOhos::invokeInQtThread(
156 [weakClientCallbacks, callbackMemPtr, args...]() {
157 auto clientCallbacks = weakClientCallbacks.lock();
158 if (clientCallbacks)
159 (clientCallbacks.get()->*callbackMemPtr)(args...);
160 });
161}
162
163}
164
165std::shared_ptr<void> QOhosInputMethodProxy::registerCallbacks(
166 std::shared_ptr<::InputMethod_TextEditorProxy> textEditorProxy,
167 std::shared_ptr<JsScopeData::JsTextEditorProxyData> textEditorProxyData,
168 std::weak_ptr<QOhosInputMethodProxy::ClientCallbacks> weakClientCallbacks)
169{
170 std::vector<std::shared_ptr<void>> registrationHandles;
171
172 registrationHandles.push_back(
173 registerTextEditorProxyCallback(
174 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetTextConfigFunc),
175 [](::InputMethod_TextConfig *config) {
176 QArkUi::callArkUiOrFailOnErrorResult(
177 Q_OHOS_NAMED_FUNC(::OH_TextConfig_SetPreviewTextSupport),
178 config, true);
179 }));
180
181 registrationHandles.push_back(
182 registerTextEditorProxyCallback(
183 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetInsertTextFunc),
184 [weakClientCallbacks](const char16_t *text, size_t length) {
185 std::u16string utf16Text(text, length);
186 callInQtThread(
187 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onInsertText,
188 convertUtf16ToUtf8(utf16Text));
189 }));
190
191 registrationHandles.push_back(
192 registerTextEditorProxyCallback(
193 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetDeleteForwardFunc),
194 [weakClientCallbacks](std::int32_t length) {
195 callInQtThread(
196 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onDeleteForward,
197 length);
198 }));
199
200 registrationHandles.push_back(
201 registerTextEditorProxyCallback(
202 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetDeleteBackwardFunc),
203 [weakClientCallbacks](std::int32_t length) {
204 callInQtThread(
205 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onDeleteBackward,
206 length);
207 }));
208
209 registrationHandles.push_back(
210 registerTextEditorProxyCallback(
211 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetSendKeyboardStatusFunc),
212 [weakClientCallbacks, weakTextEditorProxyData = QtOhos::makeWeakPtr(textEditorProxyData)](::InputMethod_KeyboardStatus keyboardStatus) {
213 auto sharedTextEditorProxyData = weakTextEditorProxyData.lock();
214 if (sharedTextEditorProxyData)
215 sharedTextEditorProxyData->textInputShown = keyboardStatus == ::IME_KEYBOARD_STATUS_SHOW;
216
217 callInQtThread(
218 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onSendKeyboardStatus,
219 keyboardStatus);
220 }));
221
222 registrationHandles.push_back(
223 registerTextEditorProxyCallback(
224 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetSendEnterKeyFunc),
225 [weakClientCallbacks](::InputMethod_EnterKeyType enterKeyType) {
226 callInQtThread(
227 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onSendEnterKey,
228 enterKeyType);
229 }));
230
231 registrationHandles.push_back(
232 registerTextEditorProxyCallback(
233 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetMoveCursorFunc),
234 [weakClientCallbacks](::InputMethod_Direction direction) {
235 callInQtThread(
236 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onMoveCursor,
237 direction);
238 }));
239
240 registrationHandles.push_back(
241 registerTextEditorProxyCallback(
242 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetHandleSetSelectionFunc),
243 [](auto...) {
244 }));
245
246 registrationHandles.push_back(
247 registerTextEditorProxyCallback(
248 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetHandleExtendActionFunc),
249 [](auto...) {
250 }));
251
252 registrationHandles.push_back(
253 registerTextEditorProxyCallback(
254 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetLeftTextOfCursorFunc),
255 [](auto...) {
256 }));
257
258 registrationHandles.push_back(
259 registerTextEditorProxyCallback(
260 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetRightTextOfCursorFunc),
261 [](auto...) {
262 }));
263
264 QArkUi::callArkUiOrFailOnErrorResult(
265 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetTextIndexAtCursorFunc),
266 textEditorProxy.get(),
267 [](::InputMethod_TextEditorProxy *) {
268 return 0;
269 });
270
271 QArkUi::callArkUiOrFailOnErrorResult(
272 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetReceivePrivateCommandFunc),
273 textEditorProxy.get(),
274 [](::InputMethod_TextEditorProxy *, ::InputMethod_PrivateCommand **, unsigned long) {
275 return 0;
276 });
277
278 registrationHandles.push_back(
279 registerTextEditorProxyCallback(
280 textEditorProxy.get(),
281 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetSetPreviewTextFunc),
282 [weakClientCallbacks](const char16_t *text, std::size_t length, std::int32_t, std::int32_t) {
283 std::u16string utf16Text(text, length);
284 callInQtThread(
285 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onInsertPreviewText,
286 convertUtf16ToUtf8(utf16Text));
287 return 0;
288 }));
289
290 registrationHandles.push_back(
291 registerTextEditorProxyCallback(
292 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetFinishTextPreviewFunc),
293 [weakClientCallbacks]() {
294 callInQtThread(
295 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onFinishPreviewText);
296 }));
297
298 return QtOhos::moveToSharedPtr(std::move(registrationHandles));
299}
300
301QOhosInputMethodProxy::QOhosInputMethodProxy(
302 std::shared_ptr<ClientCallbacks> clientCallbacks, ::InputMethod_RequestKeyboardReason requestKeyboardReason)
303 : m_clientCallbacks(clientCallbacks)
304{
305 auto weakClientCallbacks = QtOhos::makeWeakPtr(clientCallbacks);
306 m_jsScopeData = QtOhos::evalInJsThread(
307 [&](auto &) -> std::shared_ptr<JsScopeData> {
308 auto textEditorProxyData = std::make_shared<JsScopeData::JsTextEditorProxyData>();
309 auto textEditorProxy = makeTextEditorProxy();
310 auto callbacksRegistrationHandle = registerCallbacks(textEditorProxy, textEditorProxyData, weakClientCallbacks);
311
312 auto showKeyboard = true;
313 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
314
315 auto inputMethodProxy = tryMakeInputMethodProxy(textEditorProxy, attachOptions);
316 if (!inputMethodProxy) {
317 qOhosPrintfError("%s: inputMethodProxy is nullptr!", Q_FUNC_INFO);
318 return nullptr;
319 }
320
321 return QtOhos::makeProxyWithJsThreadDeleter(
322 QtOhos::moveToSharedPtr(
323 JsScopeData {
324 .textEditorProxy = textEditorProxy,
325 .textEditorProxyData = textEditorProxyData,
326 .inputMethodProxy = inputMethodProxy,
327 .callbacksRegistrationHandle = callbacksRegistrationHandle,
328 }));
329 },
330 Q_FUNC_INFO);
331}
332
333bool QOhosInputMethodProxy::hasAttachedSuccessfully()
334{
335 return m_jsScopeData != nullptr;
336}
337
338void QOhosInputMethodProxy::showTextInput(::InputMethod_RequestKeyboardReason requestKeyboardReason)
339{
340 if (!hasAttachedSuccessfully()) {
341 qOhosPrintfError("%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
342 return;
343 }
344
346 [&](QtOhos::JsState &) {
347 if (m_jsScopeData->textEditorProxyData->textInputShown) {
348 qOhosPrintfDebug("%s: text input already shown, skip showing it again", Q_FUNC_INFO);
349 return;
350 }
351
352 auto showKeyboard = true;
353 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
354
355 ::InputMethod_ErrorCode errcode = QArkUi::callArkUi(
356 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_ShowTextInput),
357 m_jsScopeData->inputMethodProxy.get(),
358 attachOptions.get());
359 m_jsScopeData->textEditorProxyData->textInputShown = errcode == ::InputMethod_ErrorCode::IME_ERR_OK;
360 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
361 qOhosPrintfError("%s: OH_InputMethodProxy_ShowTextInput failed!", Q_FUNC_INFO);
362 },
363 Q_FUNC_INFO);
364}
365
366void QOhosInputMethodProxy::notifyConfigurationChange(
367 ::InputMethod_EnterKeyType enterKeyType, ::InputMethod_TextInputType textInputType)
368{
369 if (!hasAttachedSuccessfully()) {
370 qOhosPrintfError("%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
371 return;
372 }
373
375 [&](QtOhos::JsState &) {
376 ::InputMethod_ErrorCode errcode = QArkUi::callArkUi(
377 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyConfigurationChange),
378 m_jsScopeData->inputMethodProxy.get(),
379 enterKeyType, textInputType);
380 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
381 qOhosPrintfError("%s: OH_InputMethodProxy_NotifyConfigurationChange failed!", Q_FUNC_INFO);
382 },
383 Q_FUNC_INFO);
384}
385
386void QOhosInputMethodProxy::notifyCursorUpdate(const QRectF &cursorRect)
387{
388 if (!hasAttachedSuccessfully()) {
389 qOhosPrintfError("%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
390 return;
391 }
392
393 double x = cursorRect.x();
394 double y = cursorRect.y();
395 double width = cursorRect.width();
396 double height = cursorRect.height();
397
399 [&](QtOhos::JsState &) {
400 auto ohCursorInfo = makeCursorInfo(x, y, width, height);
401
402 ::InputMethod_ErrorCode errcode = QArkUi::callArkUi(
403 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyCursorUpdate),
404 m_jsScopeData->inputMethodProxy.get(), ohCursorInfo.get());
405 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
406 qOhosPrintfError("%s: OH_InputMethodProxy_NotifyCursorUpdate failed!", Q_FUNC_INFO);
407 },
408 Q_FUNC_INFO);
409}
410
411QOhosInputMethodProxy::ClientCallbacks::ClientCallbacks() = default;
412
413QOhosInputMethodProxy::ClientCallbacks::~ClientCallbacks() = default;
414
415QT_END_NAMESPACE
Combined button and popup list for selecting options.
std::shared_ptr< void > registerTextEditorProxyCallback(::InputMethod_TextEditorProxy *textEditorProxy, QOhosNamedFunc<::InputMethod_ErrorCode(*)(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc< CallbackResult, CallbackArgs... >), rawSetCallbackFunc > setCallbackFunc, Callback callback)
void callInQtThread(std::weak_ptr< QOhosInputMethodProxy::ClientCallbacks > weakClientCallbacks, void(QOhosInputMethodProxy::ClientCallbacks::*callbackMemPtr)(CallbackArgs...), CallbackArgs ...args)
std::shared_ptr<::InputMethod_AttachOptions > makeAttachOptions(bool showKeyboard, ::InputMethod_RequestKeyboardReason requestKeyboardReason)
std::shared_ptr<::InputMethod_CursorInfo > makeCursorInfo(double x, double y, double width, double height)
std::shared_ptr< void > registerTextEditorProxyCallbackImpl(::InputMethod_TextEditorProxy *textEditorProxy, QOhosNamedFunc<::InputMethod_ErrorCode(*)(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc< CallbackResult, CallbackArgs... >), rawSetCallbackFunc > setCallbackFunc, std::function< CallbackResult(CallbackArgs...)> callback)
CallbackResult(::InputMethod_TextEditorProxy *, CallbackArgs...) TextEditorProxyCallbackFunc
std::string convertUtf16ToUtf8(const std::u16string &utf16String)
std::shared_ptr<::InputMethod_TextEditorProxy > makeTextEditorProxy()
std::shared_ptr<::InputMethod_InputMethodProxy > tryMakeInputMethodProxy(std::shared_ptr<::InputMethod_TextEditorProxy > textEditorProxy, std::shared_ptr<::InputMethod_AttachOptions > attachOptions)
void runInJsThreadAndWait(const std::function< void(JsState &)> &task, std::string callerContextName={})