5#include <QtCore/private/qohoslogger_p.h>
6#include <QtCore/private/qstringconverter_p.h>
10#include <qarkui/qarkuiutils.h>
11#include <qohosplugincore.h>
12#include <qohosutils.h>
20#ifdef __cpp_lib_string_resize_and_overwrite
24 const auto maxBytes = utf16String.size() * 3;
25 utf8Str.resize_and_overwrite(maxBytes, [&] (
char *dst, size_t)
noexcept {
26 return QUtf8::convertFromUnicode(dst, QStringView{utf16String}) - dst;
31QT_WARNING_DISABLE_DEPRECATED
32 return std::wstring_convert<std::codecvt_utf8_utf16<
char16_t>,
char16_t>{}.to_bytes(utf16String);
39 return std::shared_ptr<::InputMethod_TextEditorProxy>(
40 QArkUi::callArkUiOrFailOnNullResult(
41 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_Create)),
42 [](::InputMethod_TextEditorProxy *textEditorProxy) {
44 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_Destroy),
50 bool showKeyboard, ::InputMethod_RequestKeyboardReason requestKeyboardReason)
52 return std::shared_ptr<::InputMethod_AttachOptions>(
53 QArkUi::callArkUiOrFailOnNullResult(
54 Q_OHOS_NAMED_FUNC(::OH_AttachOptions_CreateWithRequestKeyboardReason),
55 showKeyboard, requestKeyboardReason),
56 [](::InputMethod_AttachOptions *attachOptions) {
58 Q_OHOS_NAMED_FUNC(::OH_AttachOptions_Destroy),
64 std::shared_ptr<::InputMethod_TextEditorProxy> textEditorProxy,
65 std::shared_ptr<::InputMethod_AttachOptions> attachOptions)
67 ::InputMethod_InputMethodProxy *inputMethodProxyPtr =
nullptr;
68 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
69 Q_OHOS_NAMED_FUNC(::OH_InputMethodController_Attach),
70 textEditorProxy.get(), attachOptions.get(), &inputMethodProxyPtr);
71 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK) {
72 qOhosPrintfError(
"%s: OH_InputMethodController_Attach failed!", Q_FUNC_INFO);
76 return std::shared_ptr<::InputMethod_InputMethodProxy>(
77 inputMethodProxyPtr, [](::InputMethod_InputMethodProxy *proxy) {
78 QArkUi::callArkUiOrFailOnErrorResult(
79 Q_OHOS_NAMED_FUNC(::OH_InputMethodController_Detach), proxy);
84 double x,
double y,
double width,
double height)
86 return std::shared_ptr<::InputMethod_CursorInfo>(
87 QArkUi::callArkUiOrFailOnNullResult(
88 Q_OHOS_NAMED_FUNC(::OH_CursorInfo_Create),
90 [](::InputMethod_CursorInfo *cursorInfo) {
92 Q_OHOS_NAMED_FUNC(::OH_CursorInfo_Destroy), cursorInfo);
96template<
typename CallbackResult,
typename... CallbackArgs>
99 typename CallbackResult,
100 typename... CallbackArgs,
101 ::InputMethod_ErrorCode rawSetCallbackFunc(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>)>
103 ::InputMethod_TextEditorProxy *textEditorProxy,
104 QOhosNamedFunc<::InputMethod_ErrorCode(*)(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>), rawSetCallbackFunc> setCallbackFunc,
105 std::function<CallbackResult(CallbackArgs...)> callback)
107 static std::map<::InputMethod_TextEditorProxy *, std::shared_ptr<std::function<CallbackResult(CallbackArgs...)>>> callbacksMap;
109 bool callbackRegistered;
110 std::tie(std::ignore, callbackRegistered) = callbacksMap.emplace(
112 std::make_shared<std::function<CallbackResult(CallbackArgs...)>>(std::move(callback)));
114 if (!callbackRegistered)
115 qOhosReportFatalErrorAndAbort(Q_FUNC_INFO,
"encountered duplicate callback registration for %p", textEditorProxy);
117 QArkUi::callArkUiOrFailOnErrorResult(
118 setCallbackFunc, textEditorProxy,
119 [](::InputMethod_TextEditorProxy *textEditorProxy, CallbackArgs ...callbackArgs) {
120 auto callbackIter = callbacksMap.find(textEditorProxy);
121 if (callbackIter != callbacksMap.end()) {
122 auto sharedCallback = callbackIter->second;
123 return (*sharedCallback)(callbackArgs...);
125 return CallbackResult();
129 return QtOhos::makeDestroyNotifier(
130 [textEditorProxy]() {
131 callbacksMap.erase(textEditorProxy);
136 typename CallbackResult,
137 typename... CallbackArgs,
138 ::InputMethod_ErrorCode rawSetCallbackFunc(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>),
141 ::InputMethod_TextEditorProxy *textEditorProxy,
142 QOhosNamedFunc<::InputMethod_ErrorCode(*)(::InputMethod_TextEditorProxy *, TextEditorProxyCallbackFunc<CallbackResult, CallbackArgs...>), rawSetCallbackFunc> setCallbackFunc,
145 static_assert(
std::is_assignable<
std::function<CallbackResult(CallbackArgs...)>, Callback>::value,
"");
146 return registerTextEditorProxyCallbackImpl<CallbackResult>(
147 textEditorProxy, setCallbackFunc,
std::function<CallbackResult(CallbackArgs...)>(
std::move(callback)));
150template<
typename... CallbackArgs>
152 std::weak_ptr<QOhosInputMethodProxy::ClientCallbacks> weakClientCallbacks,
153 void (QOhosInputMethodProxy::ClientCallbacks::*callbackMemPtr)(CallbackArgs...),
154 CallbackArgs ...args)
156 QtOhos::invokeInQtThread(
157 [weakClientCallbacks, callbackMemPtr, args...]() {
158 auto clientCallbacks = weakClientCallbacks.lock();
160 (clientCallbacks.get()->*callbackMemPtr)(args...);
166std::shared_ptr<
void> QOhosInputMethodProxy::registerCallbacks(
167 std::shared_ptr<::InputMethod_TextEditorProxy> textEditorProxy,
168 std::shared_ptr<JsScopeData::JsTextEditorProxyData> textEditorProxyData,
169 std::shared_ptr<QOhosMutexProtectedValue<TextAroundCursor>> textAroundCursor,
170 std::weak_ptr<QOhosInputMethodProxy::ClientCallbacks> weakClientCallbacks)
172 std::vector<std::shared_ptr<
void>> registrationHandles;
174 registrationHandles.push_back(
175 registerTextEditorProxyCallback(
176 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetTextConfigFunc),
177 [](::InputMethod_TextConfig *config) {
178 QArkUi::callArkUiOrFailOnErrorResult(
179 Q_OHOS_NAMED_FUNC(::OH_TextConfig_SetPreviewTextSupport),
183 registrationHandles.push_back(
184 registerTextEditorProxyCallback(
185 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetInsertTextFunc),
186 [weakClientCallbacks](
const char16_t *text, size_t length) {
187 std::u16string utf16Text(text, length);
189 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onInsertText,
193 registrationHandles.push_back(
194 registerTextEditorProxyCallback(
195 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetDeleteForwardFunc),
196 [weakClientCallbacks](
std::int32_t length) {
198 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onDeleteForward,
202 registrationHandles.push_back(
203 registerTextEditorProxyCallback(
204 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetDeleteBackwardFunc),
205 [weakClientCallbacks](
std::int32_t length) {
207 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onDeleteBackward,
211 registrationHandles.push_back(
212 registerTextEditorProxyCallback(
213 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetSendKeyboardStatusFunc),
214 [weakClientCallbacks, weakTextEditorProxyData = QtOhos::makeWeakPtr(textEditorProxyData)](::InputMethod_KeyboardStatus keyboardStatus) {
215 auto sharedTextEditorProxyData = weakTextEditorProxyData.lock();
216 if (sharedTextEditorProxyData)
217 sharedTextEditorProxyData->textInputShown = keyboardStatus == ::IME_KEYBOARD_STATUS_SHOW;
220 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onSendKeyboardStatus,
224 registrationHandles.push_back(
225 registerTextEditorProxyCallback(
226 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetSendEnterKeyFunc),
227 [weakClientCallbacks](::InputMethod_EnterKeyType enterKeyType) {
229 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onSendEnterKey,
233 registrationHandles.push_back(
234 registerTextEditorProxyCallback(
235 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetMoveCursorFunc),
236 [weakClientCallbacks](::InputMethod_Direction direction) {
238 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onMoveCursor,
242 registrationHandles.push_back(
243 registerTextEditorProxyCallback(
244 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetHandleSetSelectionFunc),
248 registrationHandles.push_back(
249 registerTextEditorProxyCallback(
250 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetHandleExtendActionFunc),
254 auto makeGetTextOfCursorCallback = [weakTextAroundCursor = QtOhos::makeWeakPtr(textAroundCursor)](
255 std::function<
std::u16string(
const TextAroundCursor &,
std::size_t maxLength)> selectFromTextAroundCursorFunc) {
256 return [weakTextAroundCursor, selectFromTextAroundCursorFunc = std::move(selectFromTextAroundCursorFunc)](
257 int32_t maxLength,
char16_t *outBuffer,
std::size_t *outLength) {
258 auto protectedTextAroundCursor = weakTextAroundCursor.lock();
259 if (!protectedTextAroundCursor) {
260 qOhosPrintfDebug(
"%s: cannot get text of cursor", Q_FUNC_INFO);
265 const auto selectedText = protectedTextAroundCursor->evalWithValue(
266 [maxLength, &selectFromTextAroundCursorFunc](
const TextAroundCursor &textAroundCursor) {
267 return selectFromTextAroundCursorFunc(textAroundCursor,
static_cast<std::size_t>(maxLength));
269 *outLength = selectedText.copy(outBuffer, selectedText.size());
273 registrationHandles.push_back(
274 registerTextEditorProxyCallback(
275 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetLeftTextOfCursorFunc),
276 makeGetTextOfCursorCallback(
277 [](
const TextAroundCursor &textAroundCursor,
std::size_t maxLength) {
278 const auto &leftText = textAroundCursor.leftText;
279 return leftText.substr(leftText.size() - std::min(maxLength, leftText.size()));
282 registrationHandles.push_back(
283 registerTextEditorProxyCallback(
284 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetRightTextOfCursorFunc),
285 makeGetTextOfCursorCallback(
286 [](
const TextAroundCursor &textAroundCursor,
std::size_t maxLength) {
287 const auto &rightText = textAroundCursor.rightText;
288 return rightText.substr(0, std::min(maxLength, rightText.size()));
291 registrationHandles.push_back(
292 registerTextEditorProxyCallback(
293 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetTextIndexAtCursorFunc),
294 [weakTextAroundCursor = QtOhos::makeWeakPtr(textAroundCursor)]() {
295 auto protectedTextAroundCursor = weakTextAroundCursor.lock();
296 if (!protectedTextAroundCursor) {
297 qOhosPrintfWarning(
"%s: cannot get text index at cursor", Q_FUNC_INFO);
301 return protectedTextAroundCursor->evalWithValue(
302 [](
const TextAroundCursor &textAroundCursor) {
303 return textAroundCursor.cursorIndex;
307 QArkUi::callArkUiOrFailOnErrorResult(
308 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetReceivePrivateCommandFunc),
309 textEditorProxy.get(),
310 [](::InputMethod_TextEditorProxy *, ::InputMethod_PrivateCommand **,
unsigned long) {
314 registrationHandles.push_back(
315 registerTextEditorProxyCallback(
316 textEditorProxy.get(),
317 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetSetPreviewTextFunc),
318 [weakClientCallbacks](
const char16_t *text,
std::size_t length,
std::int32_t,
std::int32_t) {
319 std::u16string utf16Text(text, length);
321 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onInsertPreviewText,
326 registrationHandles.push_back(
327 registerTextEditorProxyCallback(
328 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetFinishTextPreviewFunc),
329 [weakClientCallbacks]() {
331 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onFinishPreviewText);
334 return QtOhos::moveToSharedPtr(std::move(registrationHandles));
337QOhosInputMethodProxy::QOhosInputMethodProxy(
338 std::shared_ptr<ClientCallbacks> clientCallbacks, ::InputMethod_RequestKeyboardReason requestKeyboardReason)
339 : m_clientCallbacks(clientCallbacks)
340 , m_textAroundCursor(std::make_shared<QOhosMutexProtectedValue<TextAroundCursor>>())
342 auto weakClientCallbacks = QtOhos::makeWeakPtr(clientCallbacks);
343 m_jsScopeData = QtOhos::evalInJsThread(
344 [&](
auto &) -> std::shared_ptr<JsScopeData> {
345 auto textEditorProxyData = std::make_shared<JsScopeData::JsTextEditorProxyData>();
346 auto textEditorProxy = makeTextEditorProxy();
347 auto callbacksRegistrationHandle = registerCallbacks(
348 textEditorProxy, textEditorProxyData, m_textAroundCursor, weakClientCallbacks);
350 auto showKeyboard =
true;
351 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
353 auto inputMethodProxy = tryMakeInputMethodProxy(textEditorProxy, attachOptions);
354 if (!inputMethodProxy) {
355 qOhosPrintfError(
"%s: inputMethodProxy is nullptr!", Q_FUNC_INFO);
359 return QtOhos::makeProxyWithJsThreadDeleter(
360 QtOhos::moveToSharedPtr(
362 .textEditorProxy = textEditorProxy,
363 .textEditorProxyData = textEditorProxyData,
364 .inputMethodProxy = inputMethodProxy,
365 .callbacksRegistrationHandle = callbacksRegistrationHandle,
371bool QOhosInputMethodProxy::hasAttachedSuccessfully()
373 return m_jsScopeData !=
nullptr;
376void QOhosInputMethodProxy::showTextInput(::InputMethod_RequestKeyboardReason requestKeyboardReason)
378 if (!hasAttachedSuccessfully()) {
379 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
385 if (m_jsScopeData->textEditorProxyData->textInputShown) {
386 qOhosPrintfDebug(
"%s: text input already shown, skip showing it again", Q_FUNC_INFO);
390 auto showKeyboard =
true;
391 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
393 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
394 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_ShowTextInput),
395 m_jsScopeData->inputMethodProxy.get(),
396 attachOptions.get());
397 m_jsScopeData->textEditorProxyData->textInputShown = errcode == ::InputMethod_ErrorCode::IME_ERR_OK;
398 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
399 qOhosPrintfError(
"%s: OH_InputMethodProxy_ShowTextInput failed!", Q_FUNC_INFO);
404void QOhosInputMethodProxy::notifyConfigurationChange(
405 ::InputMethod_EnterKeyType enterKeyType, ::InputMethod_TextInputType textInputType)
407 if (!hasAttachedSuccessfully()) {
408 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
414 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
415 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyConfigurationChange),
416 m_jsScopeData->inputMethodProxy.get(),
417 enterKeyType, textInputType);
418 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
419 qOhosPrintfError(
"%s: OH_InputMethodProxy_NotifyConfigurationChange failed!", Q_FUNC_INFO);
424void QOhosInputMethodProxy::notifyCursorUpdate(
const QRectF &cursorRect)
426 if (!hasAttachedSuccessfully()) {
427 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
431 double x = cursorRect.x();
432 double y = cursorRect.y();
433 double width = cursorRect.width();
434 double height = cursorRect.height();
438 auto ohCursorInfo = makeCursorInfo(x, y, width, height);
440 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
441 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyCursorUpdate),
442 m_jsScopeData->inputMethodProxy.get(), ohCursorInfo.get());
443 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
444 qOhosPrintfError(
"%s: OH_InputMethodProxy_NotifyCursorUpdate failed!", Q_FUNC_INFO);
449void QOhosInputMethodProxy::notifySelectionChange(
std::u16string text,
int start,
int end)
451 if (!hasAttachedSuccessfully()) {
452 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
458 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
459 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifySelectionChange),
460 m_jsScopeData->inputMethodProxy.get(), &text[0], text.size(), start, end);
461 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
463 "%s: OH_InputMethodProxy_NotifySelectionChange failed with error code: %d", Q_FUNC_INFO,
469void QOhosInputMethodProxy::setTextAroundCursor(
470 std::u16string leftText,
std::u16string rightText,
int cursorIndex)
472 if (!hasAttachedSuccessfully()) {
473 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
477 m_textAroundCursor->processValue(
478 [&](TextAroundCursor &textAroundCursor) {
479 textAroundCursor.leftText = std::move(leftText);
480 textAroundCursor.rightText = std::move(rightText);
481 textAroundCursor.cursorIndex = cursorIndex;
485QOhosInputMethodProxy::ClientCallbacks::ClientCallbacks() =
default;
487QOhosInputMethodProxy::ClientCallbacks::~ClientCallbacks() =
default;
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={})