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 [](auto...) {
294 }));
295
296 return QtOhos::moveToSharedPtr(std::move(registrationHandles));
297}
298
299QOhosInputMethodProxy::QOhosInputMethodProxy(
300 std::shared_ptr<ClientCallbacks> clientCallbacks, ::InputMethod_RequestKeyboardReason requestKeyboardReason)
301 : m_clientCallbacks(clientCallbacks)
302{
303 auto weakClientCallbacks = QtOhos::makeWeakPtr(clientCallbacks);
304 m_jsScopeData = QtOhos::evalInJsThread(
305 [&](auto &) -> std::shared_ptr<JsScopeData> {
306 auto textEditorProxyData = std::make_shared<JsScopeData::JsTextEditorProxyData>();
307 auto textEditorProxy = makeTextEditorProxy();
308 auto callbacksRegistrationHandle = registerCallbacks(textEditorProxy, textEditorProxyData, weakClientCallbacks);
309
310 auto showKeyboard = true;
311 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
312
313 auto inputMethodProxy = tryMakeInputMethodProxy(textEditorProxy, attachOptions);
314 if (!inputMethodProxy) {
315 qOhosPrintfError("%s: inputMethodProxy is nullptr!", Q_FUNC_INFO);
316 return nullptr;
317 }
318
319 return QtOhos::makeProxyWithJsThreadDeleter(
320 QtOhos::moveToSharedPtr(
321 JsScopeData {
322 .textEditorProxy = textEditorProxy,
323 .textEditorProxyData = textEditorProxyData,
324 .inputMethodProxy = inputMethodProxy,
325 .callbacksRegistrationHandle = callbacksRegistrationHandle,
326 }));
327 });
328}
329
330bool QOhosInputMethodProxy::hasAttachedSuccessfully()
331{
332 return m_jsScopeData != nullptr;
333}
334
335void QOhosInputMethodProxy::showTextInput(::InputMethod_RequestKeyboardReason requestKeyboardReason)
336{
337 if (!hasAttachedSuccessfully()) {
338 qOhosPrintfError("%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
339 return;
340 }
341
343 [&](QtOhos::JsState &) {
344 if (m_jsScopeData->textEditorProxyData->textInputShown) {
345 qOhosPrintfDebug("%s: text input already shown, skip showing it again", Q_FUNC_INFO);
346 return;
347 }
348
349 auto showKeyboard = true;
350 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
351
352 ::InputMethod_ErrorCode errcode = QArkUi::callArkUi(
353 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_ShowTextInput),
354 m_jsScopeData->inputMethodProxy.get(),
355 attachOptions.get());
356 m_jsScopeData->textEditorProxyData->textInputShown = errcode == ::InputMethod_ErrorCode::IME_ERR_OK;
357 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
358 qOhosPrintfError("%s: OH_InputMethodProxy_ShowTextInput failed!", Q_FUNC_INFO);
359 });
360}
361
362void QOhosInputMethodProxy::notifyConfigurationChange(
363 ::InputMethod_EnterKeyType enterKeyType, ::InputMethod_TextInputType textInputType)
364{
365 if (!hasAttachedSuccessfully()) {
366 qOhosPrintfError("%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
367 return;
368 }
369
371 [&](QtOhos::JsState &) {
372 ::InputMethod_ErrorCode errcode = QArkUi::callArkUi(
373 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyConfigurationChange),
374 m_jsScopeData->inputMethodProxy.get(),
375 enterKeyType, textInputType);
376 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
377 qOhosPrintfError("%s: OH_InputMethodProxy_NotifyConfigurationChange failed!", Q_FUNC_INFO);
378 });
379}
380
381void QOhosInputMethodProxy::notifyCursorUpdate(const QRectF &cursorRect)
382{
383 if (!hasAttachedSuccessfully()) {
384 qOhosPrintfError("%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
385 return;
386 }
387
388 double x = cursorRect.x();
389 double y = cursorRect.y();
390 double width = cursorRect.width();
391 double height = cursorRect.height();
392
394 [&](QtOhos::JsState &) {
395 auto ohCursorInfo = makeCursorInfo(x, y, width, height);
396
397 ::InputMethod_ErrorCode errcode = QArkUi::callArkUi(
398 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyCursorUpdate),
399 m_jsScopeData->inputMethodProxy.get(), ohCursorInfo.get());
400 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
401 qOhosPrintfError("%s: OH_InputMethodProxy_NotifyCursorUpdate failed!", Q_FUNC_INFO);
402 });
403}
404
405QOhosInputMethodProxy::ClientCallbacks::ClientCallbacks() = default;
406
407QOhosInputMethodProxy::ClientCallbacks::~ClientCallbacks() = default;
408
409QT_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)