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 QArkUi::callArkUiOrFailOnErrorResult(
292 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetGetTextIndexAtCursorFunc),
293 textEditorProxy.get(),
294 [](::InputMethod_TextEditorProxy *) {
298 QArkUi::callArkUiOrFailOnErrorResult(
299 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetReceivePrivateCommandFunc),
300 textEditorProxy.get(),
301 [](::InputMethod_TextEditorProxy *, ::InputMethod_PrivateCommand **,
unsigned long) {
305 registrationHandles.push_back(
306 registerTextEditorProxyCallback(
307 textEditorProxy.get(),
308 Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetSetPreviewTextFunc),
309 [weakClientCallbacks](
const char16_t *text,
std::size_t length,
std::int32_t,
std::int32_t) {
310 std::u16string utf16Text(text, length);
312 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onInsertPreviewText,
317 registrationHandles.push_back(
318 registerTextEditorProxyCallback(
319 textEditorProxy.get(), Q_OHOS_NAMED_FUNC(::OH_TextEditorProxy_SetFinishTextPreviewFunc),
320 [weakClientCallbacks]() {
322 weakClientCallbacks, &QOhosInputMethodProxy::ClientCallbacks::onFinishPreviewText);
325 return QtOhos::moveToSharedPtr(std::move(registrationHandles));
328QOhosInputMethodProxy::QOhosInputMethodProxy(
329 std::shared_ptr<ClientCallbacks> clientCallbacks, ::InputMethod_RequestKeyboardReason requestKeyboardReason)
330 : m_clientCallbacks(clientCallbacks)
331 , m_textAroundCursor(std::make_shared<QOhosMutexProtectedValue<TextAroundCursor>>())
333 auto weakClientCallbacks = QtOhos::makeWeakPtr(clientCallbacks);
334 m_jsScopeData = QtOhos::evalInJsThread(
335 [&](
auto &) -> std::shared_ptr<JsScopeData> {
336 auto textEditorProxyData = std::make_shared<JsScopeData::JsTextEditorProxyData>();
337 auto textEditorProxy = makeTextEditorProxy();
338 auto callbacksRegistrationHandle = registerCallbacks(
339 textEditorProxy, textEditorProxyData, m_textAroundCursor, weakClientCallbacks);
341 auto showKeyboard =
true;
342 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
344 auto inputMethodProxy = tryMakeInputMethodProxy(textEditorProxy, attachOptions);
345 if (!inputMethodProxy) {
346 qOhosPrintfError(
"%s: inputMethodProxy is nullptr!", Q_FUNC_INFO);
350 return QtOhos::makeProxyWithJsThreadDeleter(
351 QtOhos::moveToSharedPtr(
353 .textEditorProxy = textEditorProxy,
354 .textEditorProxyData = textEditorProxyData,
355 .inputMethodProxy = inputMethodProxy,
356 .callbacksRegistrationHandle = callbacksRegistrationHandle,
362bool QOhosInputMethodProxy::hasAttachedSuccessfully()
364 return m_jsScopeData !=
nullptr;
367void QOhosInputMethodProxy::showTextInput(::InputMethod_RequestKeyboardReason requestKeyboardReason)
369 if (!hasAttachedSuccessfully()) {
370 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
376 if (m_jsScopeData->textEditorProxyData->textInputShown) {
377 qOhosPrintfDebug(
"%s: text input already shown, skip showing it again", Q_FUNC_INFO);
381 auto showKeyboard =
true;
382 auto attachOptions = makeAttachOptions(showKeyboard, requestKeyboardReason);
384 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
385 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_ShowTextInput),
386 m_jsScopeData->inputMethodProxy.get(),
387 attachOptions.get());
388 m_jsScopeData->textEditorProxyData->textInputShown = errcode == ::InputMethod_ErrorCode::IME_ERR_OK;
389 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
390 qOhosPrintfError(
"%s: OH_InputMethodProxy_ShowTextInput failed!", Q_FUNC_INFO);
395void QOhosInputMethodProxy::notifyConfigurationChange(
396 ::InputMethod_EnterKeyType enterKeyType, ::InputMethod_TextInputType textInputType)
398 if (!hasAttachedSuccessfully()) {
399 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
405 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
406 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyConfigurationChange),
407 m_jsScopeData->inputMethodProxy.get(),
408 enterKeyType, textInputType);
409 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
410 qOhosPrintfError(
"%s: OH_InputMethodProxy_NotifyConfigurationChange failed!", Q_FUNC_INFO);
415void QOhosInputMethodProxy::notifyCursorUpdate(
const QRectF &cursorRect)
417 if (!hasAttachedSuccessfully()) {
418 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
422 double x = cursorRect.x();
423 double y = cursorRect.y();
424 double width = cursorRect.width();
425 double height = cursorRect.height();
429 auto ohCursorInfo = makeCursorInfo(x, y, width, height);
431 ::InputMethod_ErrorCode errcode =
QArkUi::callArkUi(
432 Q_OHOS_NAMED_FUNC(::OH_InputMethodProxy_NotifyCursorUpdate),
433 m_jsScopeData->inputMethodProxy.get(), ohCursorInfo.get());
434 if (errcode != ::InputMethod_ErrorCode::IME_ERR_OK)
435 qOhosPrintfError(
"%s: OH_InputMethodProxy_NotifyCursorUpdate failed!", Q_FUNC_INFO);
440void QOhosInputMethodProxy::setTextAroundCursor(
std::u16string leftText,
std::u16string rightText)
442 if (!hasAttachedSuccessfully()) {
443 qOhosPrintfError(
"%s: operation aborted, IMC not attached.", Q_FUNC_INFO);
447 m_textAroundCursor->processValue(
448 [&](TextAroundCursor &textAroundCursor) {
449 textAroundCursor.leftText = std::move(leftText);
450 textAroundCursor.rightText = std::move(rightText);
454QOhosInputMethodProxy::ClientCallbacks::ClientCallbacks() =
default;
456QOhosInputMethodProxy::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={})