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