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
qohosinputcontext.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/qnapi_p.h>
6#include <qohosjsenv_p.h>
7#include <QtCore/private/qohoslogger_p.h>
8#include <QtCore/qnamespace.h>
9#include <QtGui/private/qhighdpiscaling_p.h>
10#include "qohosjsmain.h"
13#include <QtCore/qrect.h>
14#include <QtGui/qinputdevice.h>
15#include <QtGui/qguiapplication.h>
16#include <QtGui/qtextformat.h>
17#include <algorithm>
18#include <qohosjsutils.h>
19#include <inputmethod/inputmethod_controller_capi.h>
20
22
23namespace {
24
26const Qt::EnterKeyType defaultEnterKeyType = Qt::EnterKeyDefault;
27
28using InsertedTextId = QtOhos::TypedId<std::uint64_t, struct InsertedTextIdTag>;
29
31{
32public:
33 QOhosInsertedText(const std::string &text, const InsertedTextId &id);
34
35 InsertedTextId id() const;
36 std::string text() const;
37
38private:
39 InsertedTextId m_id;
40 std::string m_text;
41};
42
44{
45public:
47
52
53 std::optional<InsertedTextId> lastId() const;
55
56private:
57 JsInputMethodInsertedTextComposer() = default;
58 void initOrIncrementId();
59
60 std::optional<InsertedTextId> m_id;
61};
62
63QOhosInsertedText::QOhosInsertedText(const std::string &text, const InsertedTextId &id)
64 : m_id(id)
65 , m_text(text)
66{}
67
68InsertedTextId QOhosInsertedText::id() const
69{
70 return m_id;
71}
72
73std::string QOhosInsertedText::text() const
74{
75 return m_text;
76}
77
83
84std::optional<InsertedTextId> JsInputMethodInsertedTextComposer::lastId() const
85{
86 return m_id;
87}
88
89void JsInputMethodInsertedTextComposer::initOrIncrementId()
90{
91 if (!m_id.has_value()) {
92 m_id = InsertedTextId(0);
93 } else {
94 auto oldValue = m_id.value().value();
95 m_id = InsertedTextId(++oldValue);
96 }
97}
98
100{
101 initOrIncrementId();
102 return QOhosInsertedText(text, m_id.value());
103}
104
106{
107 using TextInputType = ::InputMethod_TextInputType;
108 if (hints & Qt::ImhMultiLine) {
109 return TextInputType::IME_TEXT_INPUT_TYPE_MULTILINE;
110 } else if (hints & Qt::ImhDigitsOnly) {
111 return (hints & Qt::ImhHiddenText)
112 ? TextInputType::IME_TEXT_INPUT_TYPE_NUMBER_PASSWORD
113 : TextInputType::IME_TEXT_INPUT_TYPE_NUMBER;
114 } else if (hints & Qt::ImhDialableCharactersOnly) {
115 return TextInputType::IME_TEXT_INPUT_TYPE_PHONE;
116 } else if (hints & (Qt::ImhDate | Qt::ImhTime)) {
117 return TextInputType::IME_TEXT_INPUT_TYPE_DATETIME;
118 } else if (hints & Qt::ImhEmailCharactersOnly) {
119 return TextInputType::IME_TEXT_INPUT_TYPE_EMAIL_ADDRESS;
120 } else if (hints & Qt::ImhUrlCharactersOnly) {
121 return TextInputType::IME_TEXT_INPUT_TYPE_URL;
122 } else if (hints & Qt::ImhHiddenText) {
123 return TextInputType::IME_TEXT_INPUT_TYPE_VISIBLE_PASSWORD;
124 } else {
125 return TextInputType::IME_TEXT_INPUT_TYPE_TEXT;
126 }
127}
128
129std::optional<QOhosInputContext::Direction> tryMapInputMethodDirectionToQt(::InputMethod_Direction direction)
130{
131 switch (direction) {
132 case ::InputMethod_Direction::IME_DIRECTION_NONE:
133 return {};
134 case ::InputMethod_Direction::IME_DIRECTION_UP:
136 case ::InputMethod_Direction::IME_DIRECTION_DOWN:
138 case ::InputMethod_Direction::IME_DIRECTION_LEFT:
140 case ::InputMethod_Direction::IME_DIRECTION_RIGHT:
142 }
143 return {};
144}
145
146::InputMethod_EnterKeyType mapQtToOhosImeEnterKeyType(Qt::EnterKeyType qtEnterKeyType)
147{
148 switch (qtEnterKeyType) {
149 case Qt::EnterKeyType::EnterKeyReturn:
150 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_NEWLINE;
151 case Qt::EnterKeyType::EnterKeyDone:
152 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_DONE;
153 case Qt::EnterKeyType::EnterKeyGo:
154 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_GO;
155 case Qt::EnterKeyType::EnterKeySend:
156 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_SEND;
157 case Qt::EnterKeyType::EnterKeySearch:
158 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_SEARCH;
159 case Qt::EnterKeyType::EnterKeyNext:
160 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_NEXT;
161 case Qt::EnterKeyType::EnterKeyPrevious:
162 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_PREVIOUS;
163 case Qt::EnterKeyType::EnterKeyDefault:
164 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_NONE;
165 }
166 qOhosPrintfError(
167 "%s: Cannot map Qt::EnterKeyType to OHOS value. Returning ::InputMethod_EnterKeyType::IME_ENTER_KEY_UNSPECIFIED",
168 Q_FUNC_INFO);
169 return ::InputMethod_EnterKeyType::IME_ENTER_KEY_UNSPECIFIED;
170}
171
173 QOhosInputContext::RequestKeyboardReason qtRequestKeyboardReason)
174{
175 switch (qtRequestKeyboardReason) {
176 case QOhosInputContext::RequestKeyboardReason::NONE:
177 return ::InputMethod_RequestKeyboardReason::IME_REQUEST_REASON_NONE;
178 case QOhosInputContext::RequestKeyboardReason::MOUSE:
179 return ::InputMethod_RequestKeyboardReason::IME_REQUEST_REASON_MOUSE;
180 case QOhosInputContext::RequestKeyboardReason::TOUCH:
181 return ::InputMethod_RequestKeyboardReason::IME_REQUEST_REASON_TOUCH;
182 case QOhosInputContext::RequestKeyboardReason::OTHER:
183 return ::InputMethod_RequestKeyboardReason::IME_REQUEST_REASON_OTHER;
184 }
185 qOhosPrintfError(
186 "%s: Cannot map QOhosInputContext::RequestKeyboardReason to OHOS value. Returning ::InputMethod_RequestKeyboardReason::IME_REQUEST_REASON_NONE",
187 Q_FUNC_INFO);
188 return ::InputMethod_RequestKeyboardReason::IME_REQUEST_REASON_NONE;
189}
190
191std::optional<int> tryGetIntPropertyFromQuery(Qt::InputMethodQuery property, QSharedPointer<QInputMethodQueryEvent> query)
192{
193 bool converted;
194 const auto value = query->value(property).toInt(&converted);
195 return converted ? std::optional(value) : std::nullopt;
196}
197
198void notifyOhosInputMethodAboutPossibleAutocorrection(const QOhosInsertedText &insertedText, int cursorPosition)
199{
200 auto lastInsertedTextId = JsInputMethodInsertedTextComposer::instance().lastId();
201 if (!lastInsertedTextId.has_value()) {
202 qOhosPrintfError("%s: JsInputMethodInsertedTextComposer has no last inserted text ID", Q_FUNC_INFO);
203 return;
204 }
205
206 auto currentInsertedTextId = insertedText.id();
207 if (currentInsertedTextId != lastInsertedTextId.value()) {
208 qOhosPrintfWarning(
209 "%s: inserted text and one currently processed differ from each other, system won't be notified with changeSelection()", Q_FUNC_INFO);
210 return;
211 }
212
214 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
215 auto startPosition = cursorPosition;
216 auto endPosition = cursorPosition + insertedText.text().length();
217 jsState.evalToPromiseOrRejectOnThrow(
218 "@ohos.inputMethod.getController().changeSelection(*)", {insertedText.text(), startPosition, endPosition})
219 .onCatch(QtOhos::makeErrorLoggingJsCallback("changeSelection()"))
220 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
221 },
222 Q_FUNC_INFO);
223}
224
225inline QPoint clampToRect(const QPoint &p, const QRect &rect)
226{
227 int x = qBound(rect.left(), p.x(), rect.right());
228 int y = qBound(rect.top(), p.y(), rect.bottom());
229 return QPoint(x, y);
230}
231
233{
234 auto *focusObject = QGuiApplication::focusObject();
235 if (!focusObject)
236 return {};
237
238 QInputMethodQueryEvent directEvent(Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor);
239 QCoreApplication::sendEvent(focusObject, &directEvent);
240
241 const auto textBeforeCursorResult = directEvent.value(Qt::ImTextBeforeCursor);
242 const auto textAfterCursorResult = directEvent.value(Qt::ImTextAfterCursor);
243 if (textBeforeCursorResult.canConvert<QString>() && textAfterCursorResult.canConvert<QString>())
244 return {textBeforeCursorResult.toString(), textAfterCursorResult.toString()};
245
246 QInputMethodQueryEvent fallbackEvent(Qt::ImSurroundingText | Qt::ImCursorPosition);
247 QCoreApplication::sendEvent(focusObject, &fallbackEvent);
248
249 const auto surroundingTextResult = fallbackEvent.value(Qt::ImSurroundingText);
250 const auto cursorPositionResult = fallbackEvent.value(Qt::ImCursorPosition);
251
252 if (!surroundingTextResult.canConvert<QString>() || !cursorPositionResult.canConvert<int>())
253 return {};
254
255 const auto cursorPos = cursorPositionResult.toInt();
256 if (cursorPos < 0)
257 return {};
258
259 const QString text = surroundingTextResult.toString();
260 return {text.left(cursorPos), text.mid(cursorPos)};
261}
262
263}
264
267 , m_lastCursorRectangle(0, 0, 0, 0)
268 , m_qtImEnabled(false)
274{
275 auto __dbg = make_QCScopedDebug("QOhosInputContext::QOhosInputContext");
276
277 QObject::connect(
278 QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
279 this, &QOhosInputContext::onCursorRectangleChanged);
280
281 QGuiApplication::instance()->installEventFilter(this);
282}
283
285
287{
288 if (m_imConnectionState == ImConnectionState::Attached) {
289 setImConnectionState(ImConnectionState::Detached);
290 setImConnectionState(ImConnectionState::Attached);
291 }
292}
293
295{
296 auto __dbg = make_QCScopedDebug("QOhosInputContext::commit");
297
298 auto preeditText = std::exchange(m_pendingPreeditText, {});
299
300 QInputMethodEvent event;
301 event.setCommitString(preeditText);
302 sendFocusObjectInputMethodEvent(&event);
303}
304
305void QOhosInputContext::update(Qt::InputMethodQueries queries)
306{
307 if (!focusObjectOrNull()) {
309 return;
310 }
311
312 m_qtImEnabled = queryImEnabled();
313 const auto qtInputMethodHintsValue = queryInputMethodHints();
314 const auto qtEnterKeyTypeValue = queryEnterKeyType();
315
316 const bool imStateChanged =
317 m_qtInputMethodHints != qtInputMethodHintsValue
318 || m_qtEnterKeyType != qtEnterKeyTypeValue;
319
320 auto imAlreadyAttached = m_imConnectionState == ImConnectionState::Attached;
321 if (imAlreadyAttached && imStateChanged)
322 updateInputMethodControllerAttributes(qtInputMethodHintsValue, qtEnterKeyTypeValue);
323
324 m_qtInputMethodHints = qtInputMethodHintsValue;
325 m_qtEnterKeyType = qtEnterKeyTypeValue;
326
327 if (inputMethodAccepted() && m_qtImEnabled) {
328 if (imAlreadyAttached)
329 pushTextAroundCursorToProxy();
330
331 auto updateCausedByWidgetTransform = queries == Qt::ImInputItemClipRectangle;
332 auto updateCausedByQueryAllImParameters = queries == Qt::ImQueryAll;
333
334 if (updateCausedByQueryAllImParameters)
335 return;
336
337 if (!updateCausedByWidgetTransform || !imAlreadyAttached) {
339 }
340 } else {
342 }
343}
344
346{
347 auto __dbg = make_QCScopedDebug("QOhosInputContext::invokeAction");
348}
349
351{
352 auto __dbg = make_QCScopedDebug("QOhosInputContext::keyboardRect");
353 return {};
354}
355
357{
358 auto __dbg = make_QCScopedDebug("QOhosInputContext::isAnimating");
359 return {};
360}
361
363{
364 auto __dbg = make_QCScopedDebug("QOhosInputContext::showInputPanel");
365 setImConnectionState(ImConnectionState::Attached);
366}
367
369{
370 auto __dbg = make_QCScopedDebug("QOhosInputContext::hideInputPanel");
371 setImConnectionState(ImConnectionState::Detached);
372}
373
375{
376 auto __dbg = make_QCScopedDebug("QOhosInputContext::isInputPanelVisible");
377 return m_imConnectionRequestedState == ImConnectionState::Attached;
378}
379
380void QOhosInputContext::setFocusObject(QObject *object)
381{
382 m_focusObject = object;
383}
384
386{
387 return m_focusObject;
388}
389
390void QOhosInputContext::setImConnectionState(ImConnectionState requestedState)
391{
392 if (requestedState != m_imConnectionRequestedState) {
393 setImConnectionStateImpl(requestedState);
394 } else {
395 if (m_imConnectionState == ImConnectionState::Attached && !m_softwareKeyboardVisible)
396 showTextInput();
397 }
398}
399
400void QOhosInputContext::setImConnectionStateImpl(ImConnectionState requestedState)
401{
402 m_imConnectionRequestedState = m_qtImEnabled ? requestedState : ImConnectionState::Detached;
403
404 if (!m_imConnectionRequestActive) {
405 m_imConnectionRequestActive = true;
406
407 dispatchRequestedImStateChange(requestedState);
408 }
409}
410
411void QOhosInputContext::dispatchRequestedImStateChange(ImConnectionState requestedState)
412{
413 auto result = false;
414 if (requestedState == ImConnectionState::Attached)
415 result = attachToInputMethodController();
416 else
417 result = detachFromInputMethodController();
418 handleRequestedImConnectionState(requestedState, result);
419}
420
421bool QOhosInputContext::attachToInputMethodController()
422{
423 class ImCallbacks : public QOhosInputMethodProxy::ClientCallbacks
424 {
425 public:
426 ImCallbacks(QOhosInputContext &inputContext)
427 : m_inputContext(inputContext)
428 {
429 }
430
431 private:
432 void onInsertText(std::string text) override
433 {
434 m_inputContext.sendInsertedTextToQt(std::move(text));
435 }
436
437 void onInsertPreviewText(std::string previewText) override
438 {
439 m_inputContext.sendInsertedPreviewTextToQt(std::move(previewText));
440 }
441
442 void onFinishPreviewText() override
443 {
444 m_inputContext.commit();
445 }
446
447 void onDeleteForward(int length) override
448 {
449 m_inputContext.sendFocusObjectFunctionalKeyEvent(Qt::Key_Delete, QStringLiteral("\u007F"), length);
450 }
451
452 void onDeleteBackward(int length) override
453 {
454 m_inputContext.sendFocusObjectFunctionalKeyEvent(Qt::Key_Backspace, QStringLiteral("\u0008"), length);
455 }
456
457 void onSendKeyboardStatus(::InputMethod_KeyboardStatus keyboardStatus) override
458 {
459 bool ohosKeyboardShown = keyboardStatus == ::InputMethod_KeyboardStatus::IME_KEYBOARD_STATUS_SHOW;
460 m_inputContext.setSoftwareKeyboardVisibilityStatus(ohosKeyboardShown);
461 }
462
463 void onSendEnterKey(::InputMethod_EnterKeyType) override
464 {
465 m_inputContext.sendFocusObjectFunctionalKeyEvent(Qt::Key_Enter, QStringLiteral("\u000D"));
466 }
467
468 void onMoveCursor(::InputMethod_Direction direction) override
469 {
470 auto qtDirection = tryMapInputMethodDirectionToQt(direction);
471 if (!qtDirection.has_value()) {
472 qOhosPrintfWarning(
473 "got unsupported InputMethod_Direction value (%d), cursor won't be moved!",
474 static_cast<int>(direction));
475 return;
476 }
477
478 m_inputContext.sendCursorMoveToQt(qtDirection.value());
479 }
480
481 QOhosInputContext &m_inputContext;
482 };
483
484 if (m_imProxy)
485 qOhosPrintfWarning("%s: proxy already exists!", Q_FUNC_INFO);
486
487 m_imProxy = std::make_shared<QOhosInputMethodProxy>(
488 std::make_shared<ImCallbacks>(*this),
489 mapQtToOhosImeRequestReason(
490 m_lastInputTypeToTriggerSoftKeyboard.value_or(RequestKeyboardReason::OTHER)));
491
492 if (m_imProxy->hasAttachedSuccessfully()) {
493 pushTextAroundCursorToProxy();
494 m_imProxy->notifyConfigurationChange(
495 mapQtToOhosImeEnterKeyType(m_qtEnterKeyType),
496 mapQtInputMethodHintsToOhosImeTextInputType(m_qtInputMethodHints));
497 return true;
498 }
499 return false;
500}
501
502bool QOhosInputContext::detachFromInputMethodController()
503{
504 if (!m_imProxy)
505 qOhosPrintfWarning("%s: proxy doesn't exist!", Q_FUNC_INFO);
506
507 m_imProxy.reset();
508 return true;
509}
510
511void QOhosInputContext::handleRequestedImConnectionState(ImConnectionState requestedState, bool success)
512{
513 m_imConnectionRequestActive = false;
514
515 if (!success) {
516 qOhosWarning(QtForOhos)
517 << "Cannot "
518 << (requestedState == ImConnectionState::Attached ? "attach to" : "detach from")
519 << " IMC!";
520 return;
521 }
522
523 m_imConnectionState = requestedState;
524
525 if (m_imConnectionRequestedState != m_imConnectionState)
526 setImConnectionStateImpl(m_imConnectionRequestedState);
527
528 onCursorRectangleChanged();
529}
530
531void QOhosInputContext::showTextInput()
532{
533 if (m_imProxy == nullptr) {
534 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
535 return;
536 }
537
538 m_imProxy->showTextInput(mapQtToOhosImeRequestReason(
539 m_lastInputTypeToTriggerSoftKeyboard.value_or(RequestKeyboardReason::OTHER)));
540}
541
543{
544 m_softwareKeyboardVisible = visible;
545}
546
547void QOhosInputContext::setLastInputTypeToTriggerSoftKeyboard(RequestKeyboardReason inputType)
548{
549 m_lastInputTypeToTriggerSoftKeyboard = inputType;
550}
551
552void QOhosInputContext::onCursorRectangleChanged()
553{
554 if (m_imConnectionState == ImConnectionState::Detached) {
555 qOhosDebug(QtForOhos) << "Cursor rectangle changed while detached; deferring update until attach";
556 m_updateCursorRectangleAfterAttaching = true;
557 return;
558 }
559
560 auto *focusedWindow = QGuiApplication::focusWindow();
561 if (focusedWindow == nullptr) {
562 qOhosCritical(QtForOhos)
563 << "Could not retrieve focused window. Updating cursor position isn't possible";
564 return;
565 }
566
567 auto focusedWindowPosition = focusedWindow->mapToGlobal(QPoint(0, 0));
568 QRect cursorRectangle = QGuiApplication::inputMethod()->cursorRectangle().toRect();
569 if (!m_updateCursorRectangleAfterAttaching
570 && cursorRectangle == m_lastCursorRectangle
571 && m_lastFocusedWindowPosition == focusedWindowPosition) {
572 return;
573 }
574
575 m_lastCursorRectangle = cursorRectangle;
576 m_lastFocusedWindowPosition = focusedWindowPosition;
577 auto globalInputItemRectangle =
578 QGuiApplication::inputMethod()->inputItemClipRectangle()
579 .translated(m_lastFocusedWindowPosition)
580 .toRect();
581 auto globalCursorRectangle = m_lastCursorRectangle.translated(m_lastFocusedWindowPosition);
582 auto inputItemClampedCursorPos = clampToRect(
583 {globalCursorRectangle.x(), globalCursorRectangle.y()},
584 globalInputItemRectangle.isValid()
585 ? globalInputItemRectangle
586 : QRect(focusedWindowPosition, focusedWindow->size()));
587 auto nativeCursorPos = QHighDpiScaling::mapPositionToNative(
588 inputItemClampedCursorPos, focusedWindow->screen()->handle());
589 auto screenBasedCursorPos = nativeCursorPos - focusedWindow->screen()->handle()->geometry().topLeft();
590 updateOhosCursor({
591 screenBasedCursorPos.x(), screenBasedCursorPos.y(),
592 globalCursorRectangle.width(), globalCursorRectangle.height()
593 });
594 m_updateCursorRectangleAfterAttaching = false;
595}
596
597void QOhosInputContext::updateInputMethodControllerAttributes(
598 Qt::InputMethodHints qtInputMethodHints, Qt::EnterKeyType qtEnterKeyType)
599{
600 if (m_imProxy == nullptr) {
601 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
602 return;
603 }
604
605 m_imProxy->notifyConfigurationChange(
606 mapQtToOhosImeEnterKeyType(qtEnterKeyType),
607 mapQtInputMethodHintsToOhosImeTextInputType(qtInputMethodHints));
608}
609
610void QOhosInputContext::updateOhosCursor(const QRect &globalCursorRect)
611{
612 if (m_imProxy == nullptr) {
613 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
614 return;
615 }
616
617 m_imProxy->notifyCursorUpdate(globalCursorRect);
618}
619
620void QOhosInputContext::pushTextAroundCursorToProxy()
621{
622 if (m_imProxy == nullptr) {
623 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
624 return;
625 }
626
627 const auto textAroundCursor = queryQtImTextAroundCursorOrEmpty();
628 m_imProxy->setTextAroundCursor(
629 textAroundCursor.first.toStdU16String(),
630 textAroundCursor.second.toStdU16String());
631}
632
633void QOhosInputContext::handleFocusInEvent(QObject *obj, QFocusEvent *event)
634{
635 if (obj->isWidgetType() && queryImEnabled()) {
636 auto focusReason = event->reason();
637 if (focusReason == Qt::OtherFocusReason || focusReason == Qt::ActiveWindowFocusReason)
638 setLastInputTypeToTriggerSoftKeyboard(RequestKeyboardReason::NONE);
639 }
640}
641
642bool QOhosInputContext::eventFilter(QObject *obj, QEvent *event)
643{
644 if (event->type() == QEvent::FocusIn)
645 handleFocusInEvent(obj, static_cast<QFocusEvent *>(event));
646 return false;
647}
648
649void QOhosInputContext::sendInsertedTextToQt(const std::string &textToInsert)
650{
652
653 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnabled);
654 if (query.isNull())
655 return;
656
657 if (!query->value(Qt::ImEnabled).toBool()) {
658 qOhosDebug(QtForOhos) << "onInsertText(): focus object is not able to handle InputMethod";
659 return;
660 }
661
662 m_pendingPreeditText.clear();
663
664 QInputMethodEvent event;
665 event.setCommitString(QString::fromStdString(insertedText.text()));
666 sendFocusObjectInputMethodEvent(&event);
667
668 auto cursorPosition = tryQueryCursorPosition();
669 if (cursorPosition.has_value())
670 notifyOhosInputMethodAboutPossibleAutocorrection(insertedText, cursorPosition.value());
671 else
672 qOhosWarning(QtForOhos) << "Couldn't obtain IM cursorPosition";
673}
674
675void QOhosInputContext::sendInsertedPreviewTextToQt(std::string previewText)
676{
677 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnabled);
678 if (query.isNull())
679 return;
680
681 if (!query->value(Qt::ImEnabled).toBool()) {
682 qOhosPrintfDebug("%s: focus object is not able to handle InputMethod", Q_FUNC_INFO);
683 return;
684 }
685
686 const QString previewString = QString::fromStdString(previewText);
687 QList<QInputMethodEvent::Attribute> imEventAttributes;
688 if (!previewString.isEmpty()) {
689 QTextCharFormat format;
690 format.setFontUnderline(true);
691 imEventAttributes.append(
692 QInputMethodEvent::Attribute(
693 QInputMethodEvent::TextFormat, 0, previewString.length(), format));
694 }
695
696 m_pendingPreeditText = previewString;
697
698 QInputMethodEvent preeditStringEvent(previewString, imEventAttributes);
699 sendFocusObjectInputMethodEvent(&preeditStringEvent);
700}
701
702void QOhosInputContext::sendFocusObjectInputMethodEvent(QInputMethodEvent *event)
703{
704 auto *focusObject = focusObjectOrNull();
705 if (focusObject == nullptr) {
706 qOhosWarning(QtForOhos) << "sendFocusObjectInputMethodEvent(): no focus object to send event to!";
707 return;
708 }
709
710 QCoreApplication::sendEvent(focusObject, event);
711}
712
713void QOhosInputContext::sendCursorMoveToQt(QOhosInputContext::Direction direction)
714{
715 switch (direction) {
716 case QOhosInputContext::Direction::CURSOR_UP:
717 sendFocusObjectFunctionalKeyEvent(Qt::Key_Up);
718 break;
719 case QOhosInputContext::Direction::CURSOR_DOWN:
720 sendFocusObjectFunctionalKeyEvent(Qt::Key_Down);
721 break;
722 case QOhosInputContext::Direction::CURSOR_LEFT:
723 sendFocusObjectFunctionalKeyEvent(Qt::Key_Left);
724 break;
725 case QOhosInputContext::Direction::CURSOR_RIGHT:
726 sendFocusObjectFunctionalKeyEvent(Qt::Key_Right);
727 break;
728 }
729}
730
731void QOhosInputContext::sendFocusObjectFunctionalKeyEvent(Qt::Key key, const QString &keyText, int repeatCount)
732{
733 auto *focusObject = focusObjectOrNull();
734 if (focusObject == nullptr) {
735 qOhosWarning(QtForOhos) << "sendFocusObjectFunctionalKeyEvent(): no focus object to send event to!";
736 return;
737 }
738
739 for (int i = 0; i < repeatCount; ++i) {
740 QGuiApplication::postEvent(focusObject, new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier, keyText));
741 QGuiApplication::postEvent(focusObject, new QKeyEvent(QEvent::KeyRelease, key, Qt::NoModifier, keyText));
742 }
743}
744
745bool QOhosInputContext::queryImEnabled() const
746{
747 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnabled);
748 return !query.isNull() && query->value(Qt::ImEnabled).toBool();
749}
750
751Qt::InputMethodHints QOhosInputContext::queryInputMethodHints() const
752{
753 std::optional<Qt::InputMethodHints> hints;
754 auto query = tryQueryFocusObjectInputMethod(Qt::ImHints);
755 if (!query.isNull()) {
756 auto imHintsInt = tryGetIntPropertyFromQuery(Qt::ImHints, query);
757 if (imHintsInt.has_value())
758 hints = static_cast<Qt::InputMethodHints>(imHintsInt.value());
759 }
760
761 return hints.value_or(defaultInputMethodHints);
762}
763
764Qt::EnterKeyType QOhosInputContext::queryEnterKeyType() const
765{
766 std::optional<Qt::EnterKeyType> enterKeyType;
767 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnterKeyType);
768 if (!query.isNull()) {
769 auto imEnterKeyTypeInt = tryGetIntPropertyFromQuery(Qt::ImEnterKeyType, query);
770 if (imEnterKeyTypeInt.has_value())
771 enterKeyType = static_cast<Qt::EnterKeyType>(imEnterKeyTypeInt.value());
772 }
773
774 return enterKeyType.value_or(defaultEnterKeyType);
775}
776
777std::optional<int> QOhosInputContext::tryQueryCursorPosition() const
778{
779 auto query = tryQueryFocusObjectInputMethod(Qt::ImCursorPosition);
780 return !query.isNull()
781 ? tryGetIntPropertyFromQuery(Qt::ImCursorPosition, query)
782 : std::nullopt;
783}
784
785QSharedPointer<QInputMethodQueryEvent> QOhosInputContext::tryQueryFocusObjectInputMethod(Qt::InputMethodQueries queries) const
786{
787 auto *focusObject = focusObjectOrNull();
788 if (focusObject == nullptr) {
789 qOhosWarning(QtForOhos) << "tryQueryFocusObjectInputMethod(): no focus object to query information from!";
790 return {};
791 }
792
793 auto imqEvent = QSharedPointer<QInputMethodQueryEvent>::create(queries);
794 QCoreApplication::sendEvent(focusObject, imqEvent.data());
795 return imqEvent;
796}
797
798QT_END_NAMESPACE
void update(Qt::InputMethodQueries queries) override
Notification on editor updates.
QRectF keyboardRect() const override
This function can be reimplemented to return virtual keyboard rectangle in currently active window co...
void setSoftwareKeyboardVisibilityStatus(bool visible)
void showInputPanel() override
Request to show input panel.
void invokeAction(QInputMethod::Action action, int cursorPosition) override
Called when the word currently being composed in the input item is tapped by the user.
void setLastInputTypeToTriggerSoftKeyboard(RequestKeyboardReason inputType)
void reset() override
Method to be called when input method needs to be reset.
void hideInputPanel() override
Request to hide input panel.
bool eventFilter(QObject *obj, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
bool isInputPanelVisible() const override
Returns input panel visibility status.
QObject * focusObjectOrNull() const
void setFocusObject(QObject *object) override
This virtual method gets called to notify updated focus to object.
bool isAnimating() const override
This function can be reimplemented to return true whenever input method is animating shown or hidden.
QtOhos::enums::ohos::inputMethod::Direction Direction
JsInputMethodInsertedTextComposer & operator=(const JsInputMethodInsertedTextComposer &)=delete
JsInputMethodInsertedTextComposer(JsInputMethodInsertedTextComposer &&)=delete
static JsInputMethodInsertedTextComposer & instance()
JsInputMethodInsertedTextComposer & operator=(JsInputMethodInsertedTextComposer &&)=delete
std::optional< InsertedTextId > lastId() const
JsInputMethodInsertedTextComposer(const JsInputMethodInsertedTextComposer &)=delete
QOhosInsertedText makeInsertedText(const std::string &text)
QOhosInsertedText(const std::string &text, const InsertedTextId &id)
Combined button and popup list for selecting options.
::InputMethod_TextInputType mapQtInputMethodHintsToOhosImeTextInputType(Qt::InputMethodHints hints)
::InputMethod_EnterKeyType mapQtToOhosImeEnterKeyType(Qt::EnterKeyType qtEnterKeyType)
std::optional< int > tryGetIntPropertyFromQuery(Qt::InputMethodQuery property, QSharedPointer< QInputMethodQueryEvent > query)
const Qt::EnterKeyType defaultEnterKeyType
std::optional< QOhosInputContext::Direction > tryMapInputMethodDirectionToQt(::InputMethod_Direction direction)
void notifyOhosInputMethodAboutPossibleAutocorrection(const QOhosInsertedText &insertedText, int cursorPosition)
const Qt::InputMethodHints defaultInputMethodHints
QPoint clampToRect(const QPoint &p, const QRect &rect)
::InputMethod_RequestKeyboardReason mapQtToOhosImeRequestReason(QOhosInputContext::RequestKeyboardReason qtRequestKeyboardReason)
std::pair< QString, QString > queryQtImTextAroundCursorOrEmpty()
void invokeInJsThreadAndWaitForContinue(std::function< void(JsState &, QOhosTaskPromise<>)> &&task, std::string callerContextName={})