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 {};
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 {};
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)
169{
170 auto __dbg = make_QCScopedDebug("QOhosInputContext::QOhosInputContext");
171
172 QObject::connect(
173 QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
174 this, &QOhosInputContext::onCursorRectangleChanged);
175
176 QGuiApplication::instance()->installEventFilter(this);
177}
178
180
182{
183 if (m_imConnectionState == ImConnectionState::Attached) {
184 setImConnectionState(ImConnectionState::Detached);
185 setImConnectionState(ImConnectionState::Attached);
186 }
187}
188
190{
191 auto __dbg = make_QCScopedDebug("QOhosInputContext::commit");
192
193 const QString phrase = std::exchange(m_pendingPreeditText, {});
194 const auto replaceRange = std::exchange(m_previewReplaceRange, {});
195
196 if (phrase.isEmpty())
197 return;
198
199 QInputMethodEvent event;
200 event.setCommitString(phrase, replaceRange.from, replaceRange.length);
201 sendFocusObjectInputMethodEvent(&event);
202}
203
204void QOhosInputContext::update(Qt::InputMethodQueries queries)
205{
206 if (!focusObjectOrNull()) {
208 return;
209 }
210
211 m_qtImEnabled = queryImEnabled();
212 const auto qtInputMethodHintsValue = queryInputMethodHints();
213 const auto qtEnterKeyTypeValue = queryEnterKeyType();
214
215 const bool imStateChanged =
216 m_qtInputMethodHints != qtInputMethodHintsValue
217 || m_qtEnterKeyType != qtEnterKeyTypeValue;
218
219 auto imAlreadyAttached = m_imConnectionState == ImConnectionState::Attached;
220 if (imAlreadyAttached && imStateChanged)
221 updateInputMethodControllerAttributes(qtInputMethodHintsValue, qtEnterKeyTypeValue);
222
223 m_qtInputMethodHints = qtInputMethodHintsValue;
224 m_qtEnterKeyType = qtEnterKeyTypeValue;
225
226 if (inputMethodAccepted() && m_qtImEnabled) {
227 if (imAlreadyAttached) {
228 pushTextAroundCursorToProxy();
229 updateSelection();
230 }
231
232 auto updateCausedByWidgetTransform = queries == Qt::ImInputItemClipRectangle;
233 auto updateCausedByQueryAllImParameters = queries == Qt::ImQueryAll;
234
235 if (updateCausedByQueryAllImParameters)
236 return;
237
238 if (!updateCausedByWidgetTransform || !imAlreadyAttached) {
240 }
241 } else {
243 }
244}
245
247{
248 auto __dbg = make_QCScopedDebug("QOhosInputContext::invokeAction");
249}
250
252{
253 auto __dbg = make_QCScopedDebug("QOhosInputContext::keyboardRect");
254 return {};
255}
256
258{
259 auto __dbg = make_QCScopedDebug("QOhosInputContext::isAnimating");
260 return {};
261}
262
264{
265 auto __dbg = make_QCScopedDebug("QOhosInputContext::showInputPanel");
266 setImConnectionState(ImConnectionState::Attached);
267}
268
270{
271 auto __dbg = make_QCScopedDebug("QOhosInputContext::hideInputPanel");
272 setImConnectionState(ImConnectionState::Detached);
273}
274
276{
277 auto __dbg = make_QCScopedDebug("QOhosInputContext::isInputPanelVisible");
278 return m_imConnectionRequestedState == ImConnectionState::Attached;
279}
280
281void QOhosInputContext::setFocusObject(QObject *object)
282{
283 if (object == m_focusObject)
284 return;
285
286 const bool wasAttached = m_imConnectionState == ImConnectionState::Attached;
287
288 if (!m_pendingPreeditText.isEmpty())
289 commit();
290
291 m_focusObject = object;
292
293 if (wasAttached && object != nullptr) {
294 m_qtImEnabled = queryImEnabled();
295 if (m_qtImEnabled)
296 reset();
297 }
298}
299
301{
302 return m_focusObject;
303}
304
305void QOhosInputContext::setImConnectionState(ImConnectionState requestedState)
306{
307 if (requestedState != m_imConnectionRequestedState) {
308 setImConnectionStateImpl(requestedState);
309 } else {
310 if (m_imConnectionState == ImConnectionState::Attached && !m_softwareKeyboardVisible)
311 showTextInput();
312 }
313}
314
315void QOhosInputContext::setImConnectionStateImpl(ImConnectionState requestedState)
316{
317 m_imConnectionRequestedState = m_qtImEnabled ? requestedState : ImConnectionState::Detached;
318
319 if (!m_imConnectionRequestActive) {
320 m_imConnectionRequestActive = true;
321
322 dispatchRequestedImStateChange(requestedState);
323 }
324}
325
326void QOhosInputContext::dispatchRequestedImStateChange(ImConnectionState requestedState)
327{
328 auto result = false;
329 if (requestedState == ImConnectionState::Attached)
330 result = attachToInputMethodController();
331 else
332 result = detachFromInputMethodController();
333 handleRequestedImConnectionState(requestedState, result);
334}
335
336bool QOhosInputContext::attachToInputMethodController()
337{
338 class ImCallbacks : public QOhosInputMethodProxy::ClientCallbacks
339 {
340 public:
341 ImCallbacks(QOhosInputContext &inputContext)
342 : m_inputContext(inputContext)
343 {
344 }
345
346 private:
347 void onInsertText(std::string text) override
348 {
349 m_inputContext.sendInsertedTextToQt(std::move(text));
350 }
351
352 void onInsertPreviewText(std::string previewText, int start, int end) override
353 {
354 m_inputContext.sendInsertedPreviewTextToQt(std::move(previewText), start, end);
355 }
356
357 void onFinishPreviewText() override
358 {
359 m_inputContext.commit();
360 }
361
362 void onDeleteForward(int length) override
363 {
364 m_inputContext.sendFocusObjectFunctionalKeyEvent(Qt::Key_Delete, QStringLiteral("\u007F"), length);
365 }
366
367 void onDeleteBackward(int length) override
368 {
369 m_inputContext.sendFocusObjectFunctionalKeyEvent(Qt::Key_Backspace, QStringLiteral("\u0008"), length);
370 }
371
372 void onSendKeyboardStatus(::InputMethod_KeyboardStatus keyboardStatus) override
373 {
374 bool ohosKeyboardShown = keyboardStatus == ::InputMethod_KeyboardStatus::IME_KEYBOARD_STATUS_SHOW;
375 m_inputContext.setSoftwareKeyboardVisibilityStatus(ohosKeyboardShown);
376 }
377
378 void onSendEnterKey(::InputMethod_EnterKeyType) override
379 {
380 m_inputContext.sendFocusObjectFunctionalKeyEvent(Qt::Key_Enter, QStringLiteral("\u000D"));
381 }
382
383 void onMoveCursor(::InputMethod_Direction direction) override
384 {
385 auto qtDirection = tryMapInputMethodDirectionToQt(direction);
386 if (!qtDirection.has_value()) {
387 qOhosPrintfWarning(
388 "got unsupported InputMethod_Direction value (%d), cursor won't be moved!",
389 static_cast<int>(direction));
390 return;
391 }
392
393 m_inputContext.sendCursorMoveToQt(qtDirection.value());
394 }
395
396 QOhosInputContext &m_inputContext;
397 };
398
399 if (m_imProxy)
400 qOhosPrintfWarning("%s: proxy already exists!", Q_FUNC_INFO);
401
402 m_imProxy = std::make_shared<QOhosInputMethodProxy>(
403 std::make_shared<ImCallbacks>(*this),
404 mapQtToOhosImeRequestReason(
405 m_lastInputTypeToTriggerSoftKeyboard.value_or(RequestKeyboardReason::OTHER)));
406
407 if (m_imProxy->hasAttachedSuccessfully()) {
408 pushTextAroundCursorToProxy();
409 m_imProxy->notifyConfigurationChange(
410 mapQtToOhosImeEnterKeyType(m_qtEnterKeyType),
411 mapQtInputMethodHintsToOhosImeTextInputType(m_qtInputMethodHints));
412 return true;
413 }
414 return false;
415}
416
417bool QOhosInputContext::detachFromInputMethodController()
418{
419 if (!m_imProxy)
420 qOhosPrintfWarning("%s: proxy doesn't exist!", Q_FUNC_INFO);
421
422 m_imProxy.reset();
423 return true;
424}
425
426void QOhosInputContext::handleRequestedImConnectionState(ImConnectionState requestedState, bool success)
427{
428 m_imConnectionRequestActive = false;
429
430 if (!success) {
431 qOhosWarning(QtForOhos)
432 << "Cannot "
433 << (requestedState == ImConnectionState::Attached ? "attach to" : "detach from")
434 << " IMC!";
435 return;
436 }
437
438 m_imConnectionState = requestedState;
439
440 if (m_imConnectionRequestedState != m_imConnectionState)
441 setImConnectionStateImpl(m_imConnectionRequestedState);
442
443 onCursorRectangleChanged();
444}
445
446void QOhosInputContext::showTextInput()
447{
448 if (m_imProxy == nullptr) {
449 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
450 return;
451 }
452
453 m_imProxy->showTextInput(mapQtToOhosImeRequestReason(
454 m_lastInputTypeToTriggerSoftKeyboard.value_or(RequestKeyboardReason::OTHER)));
455}
456
458{
459 m_softwareKeyboardVisible = visible;
460}
461
462void QOhosInputContext::setLastInputTypeToTriggerSoftKeyboard(RequestKeyboardReason inputType)
463{
464 m_lastInputTypeToTriggerSoftKeyboard = inputType;
465}
466
467void QOhosInputContext::onCursorRectangleChanged()
468{
469 if (m_imConnectionState == ImConnectionState::Detached) {
470 qOhosDebug(QtForOhos) << "Cursor rectangle changed while detached; deferring update until attach";
471 m_updateCursorRectangleAfterAttaching = true;
472 return;
473 }
474
475 auto *focusedWindow = QGuiApplication::focusWindow();
476 if (focusedWindow == nullptr) {
477 qOhosCritical(QtForOhos)
478 << "Could not retrieve focused window. Updating cursor position isn't possible";
479 return;
480 }
481
482 auto focusedWindowPosition = focusedWindow->mapToGlobal(QPoint(0, 0));
483 QRect cursorRectangle = QGuiApplication::inputMethod()->cursorRectangle().toRect();
484 if (!m_updateCursorRectangleAfterAttaching
485 && cursorRectangle == m_lastCursorRectangle
486 && m_lastFocusedWindowPosition == focusedWindowPosition) {
487 return;
488 }
489
490 m_lastCursorRectangle = cursorRectangle;
491 m_lastFocusedWindowPosition = focusedWindowPosition;
492 auto globalInputItemRectangle =
493 QGuiApplication::inputMethod()->inputItemClipRectangle()
494 .translated(m_lastFocusedWindowPosition)
495 .toRect();
496 auto globalCursorRectangle = m_lastCursorRectangle.translated(m_lastFocusedWindowPosition);
497 auto inputItemClampedCursorPos = clampToRect(
498 {globalCursorRectangle.x(), globalCursorRectangle.y()},
499 globalInputItemRectangle.isValid()
500 ? globalInputItemRectangle
501 : QRect(focusedWindowPosition, focusedWindow->size()));
502 auto nativeCursorPos = QHighDpiScaling::mapPositionToNative(
503 inputItemClampedCursorPos, focusedWindow->screen()->handle());
504 auto screenBasedCursorPos = nativeCursorPos - focusedWindow->screen()->handle()->geometry().topLeft();
505 updateOhosCursor({
506 screenBasedCursorPos.x(), screenBasedCursorPos.y(),
507 globalCursorRectangle.width(), globalCursorRectangle.height()
508 });
509 m_updateCursorRectangleAfterAttaching = false;
510}
511
512void QOhosInputContext::updateInputMethodControllerAttributes(
513 Qt::InputMethodHints qtInputMethodHints, Qt::EnterKeyType qtEnterKeyType)
514{
515 if (m_imProxy == nullptr) {
516 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
517 return;
518 }
519
520 m_imProxy->notifyConfigurationChange(
521 mapQtToOhosImeEnterKeyType(qtEnterKeyType),
522 mapQtInputMethodHintsToOhosImeTextInputType(qtInputMethodHints));
523}
524
525void QOhosInputContext::updateOhosCursor(const QRect &globalCursorRect)
526{
527 if (m_imProxy == nullptr) {
528 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
529 return;
530 }
531
532 m_imProxy->notifyCursorUpdate(globalCursorRect);
533}
534
535void QOhosInputContext::pushTextAroundCursorToProxy()
536{
537 if (m_imProxy == nullptr) {
538 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
539 return;
540 }
541
542 const auto textAroundCursor = queryQtImTextAroundCursorOrEmpty();
543 m_imProxy->setTextAroundCursor(
544 textAroundCursor.first.toStdU16String(),
545 textAroundCursor.second.toStdU16String(),
546 tryQueryCursorPosition().value_or(0));
547}
548
549void QOhosInputContext::updateSelection()
550{
551 if (m_imProxy == nullptr) {
552 qOhosPrintfError("%s: proxy doesn't exist!", Q_FUNC_INFO);
553 return;
554 }
555
556 auto query = tryQueryFocusObjectInputMethod(
557 Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition);
558 if (query.isNull())
559 return;
560
561 const auto surroundingText = query->value(Qt::ImSurroundingText);
562 const auto cursorPosition = query->value(Qt::ImCursorPosition);
563 const auto anchorPosition = query->value(Qt::ImAnchorPosition);
564
565 if (!surroundingText.canConvert<QString>() || !cursorPosition.canConvert<int>()
566 || !anchorPosition.canConvert<int>())
567 return;
568
569 const int cursor = cursorPosition.toInt();
570 const int anchor = anchorPosition.toInt();
571
572 m_imProxy->notifySelectionChange(
573 surroundingText.toString().toStdU16String(), std::min(anchor, cursor), std::max(anchor, cursor));
574}
575
576void QOhosInputContext::handleFocusInEvent(QObject *obj, QFocusEvent *event)
577{
578 if (obj->isWidgetType() && queryImEnabled()) {
579 auto focusReason = event->reason();
580 if (focusReason == Qt::OtherFocusReason || focusReason == Qt::ActiveWindowFocusReason)
581 setLastInputTypeToTriggerSoftKeyboard(RequestKeyboardReason::NONE);
582 }
583}
584
585bool QOhosInputContext::eventFilter(QObject *obj, QEvent *event)
586{
587 if (event->type() == QEvent::FocusIn)
588 handleFocusInEvent(obj, static_cast<QFocusEvent *>(event));
589 return false;
590}
591
592void QOhosInputContext::sendInsertedTextToQt(const std::string &textToInsert)
593{
594 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnabled);
595 if (query.isNull())
596 return;
597
598 if (!query->value(Qt::ImEnabled).toBool()) {
599 qOhosDebug(QtForOhos) << "onInsertText(): focus object is not able to handle InputMethod";
600 return;
601 }
602
603 m_pendingPreeditText.clear();
604 m_previewReplaceRange = {};
605
606 QInputMethodEvent event;
607 event.setCommitString(QString::fromStdString(textToInsert));
608 sendFocusObjectInputMethodEvent(&event);
609}
610
611void QOhosInputContext::sendInsertedPreviewTextToQt(std::string previewText, int start, int end)
612{
613 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnabled);
614 if (query.isNull())
615 return;
616
617 if (!query->value(Qt::ImEnabled).toBool()) {
618 qOhosPrintfDebug("%s: focus object is not able to handle InputMethod", Q_FUNC_INFO);
619 return;
620 }
621
622 m_previewReplaceRange = {};
623 const auto cursor = tryQueryCursorPosition();
624 if (cursor && !(start < 0 && end < 0)) {
625 const int from = qMin(start, end);
626 const int to = qMax(start, end);
627 m_previewReplaceRange = { from - *cursor, to - from };
628 }
629
630 const QString previewString = QString::fromStdString(previewText);
631 QList<QInputMethodEvent::Attribute> imEventAttributes;
632 if (!previewString.isEmpty()) {
633 QTextCharFormat format;
634 format.setFontUnderline(true);
635 imEventAttributes.append(
636 QInputMethodEvent::Attribute(
637 QInputMethodEvent::TextFormat, 0, previewString.length(), format));
638 }
639
640 m_pendingPreeditText = previewString;
641
642 QInputMethodEvent preeditStringEvent(previewString, imEventAttributes);
643 sendFocusObjectInputMethodEvent(&preeditStringEvent);
644}
645
646void QOhosInputContext::sendFocusObjectInputMethodEvent(QInputMethodEvent *event)
647{
648 auto *focusObject = focusObjectOrNull();
649 if (focusObject == nullptr) {
650 qOhosWarning(QtForOhos) << "sendFocusObjectInputMethodEvent(): no focus object to send event to!";
651 return;
652 }
653
654 QCoreApplication::sendEvent(focusObject, event);
655}
656
657void QOhosInputContext::sendCursorMoveToQt(QOhosInputContext::Direction direction)
658{
659 switch (direction) {
660 case QOhosInputContext::Direction::CURSOR_UP:
661 sendFocusObjectFunctionalKeyEvent(Qt::Key_Up);
662 break;
663 case QOhosInputContext::Direction::CURSOR_DOWN:
664 sendFocusObjectFunctionalKeyEvent(Qt::Key_Down);
665 break;
666 case QOhosInputContext::Direction::CURSOR_LEFT:
667 sendFocusObjectFunctionalKeyEvent(Qt::Key_Left);
668 break;
669 case QOhosInputContext::Direction::CURSOR_RIGHT:
670 sendFocusObjectFunctionalKeyEvent(Qt::Key_Right);
671 break;
672 }
673}
674
675void QOhosInputContext::sendFocusObjectFunctionalKeyEvent(Qt::Key key, const QString &keyText, int repeatCount)
676{
677 auto *focusObject = focusObjectOrNull();
678 if (focusObject == nullptr) {
679 qOhosWarning(QtForOhos) << "sendFocusObjectFunctionalKeyEvent(): no focus object to send event to!";
680 return;
681 }
682
683 for (int i = 0; i < repeatCount; ++i) {
684 QGuiApplication::postEvent(focusObject, new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier, keyText));
685 QGuiApplication::postEvent(focusObject, new QKeyEvent(QEvent::KeyRelease, key, Qt::NoModifier, keyText));
686 }
687}
688
689bool QOhosInputContext::queryImEnabled() const
690{
691 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnabled);
692 return !query.isNull() && query->value(Qt::ImEnabled).toBool();
693}
694
695Qt::InputMethodHints QOhosInputContext::queryInputMethodHints() const
696{
697 std::optional<Qt::InputMethodHints> hints;
698 auto query = tryQueryFocusObjectInputMethod(Qt::ImHints);
699 if (!query.isNull()) {
700 auto imHintsInt = tryGetIntPropertyFromQuery(Qt::ImHints, query);
701 if (imHintsInt.has_value())
702 hints = static_cast<Qt::InputMethodHints>(imHintsInt.value());
703 }
704
705 return hints.value_or(defaultInputMethodHints);
706}
707
708Qt::EnterKeyType QOhosInputContext::queryEnterKeyType() const
709{
710 std::optional<Qt::EnterKeyType> enterKeyType;
711 auto query = tryQueryFocusObjectInputMethod(Qt::ImEnterKeyType);
712 if (!query.isNull()) {
713 auto imEnterKeyTypeInt = tryGetIntPropertyFromQuery(Qt::ImEnterKeyType, query);
714 if (imEnterKeyTypeInt.has_value())
715 enterKeyType = static_cast<Qt::EnterKeyType>(imEnterKeyTypeInt.value());
716 }
717
718 return enterKeyType.value_or(defaultEnterKeyType);
719}
720
721std::optional<int> QOhosInputContext::tryQueryCursorPosition() const
722{
723 auto query = tryQueryFocusObjectInputMethod(Qt::ImCursorPosition);
724 return !query.isNull()
725 ? tryGetIntPropertyFromQuery(Qt::ImCursorPosition, query)
726 : std::nullopt;
727}
728
729QSharedPointer<QInputMethodQueryEvent> QOhosInputContext::tryQueryFocusObjectInputMethod(Qt::InputMethodQueries queries) const
730{
731 auto *focusObject = focusObjectOrNull();
732 if (focusObject == nullptr) {
733 qOhosWarning(QtForOhos) << "tryQueryFocusObjectInputMethod(): no focus object to query information from!";
734 return {};
735 }
736
737 auto imqEvent = QSharedPointer<QInputMethodQueryEvent>::create(queries);
738 QCoreApplication::sendEvent(focusObject, imqEvent.data());
739 return imqEvent;
740}
741
742QT_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()