1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtVirtualKeyboard/private/qvirtualkeyboardinputcontext_p.h>
31#include <QtVirtualKeyboard/private/platforminputcontext_p.h>
32#include <QtVirtualKeyboard/private/settings_p.h>
33#include <QtVirtualKeyboard/private/shifthandler_p.h>
34#include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h>
35#include <QtVirtualKeyboard/private/enterkeyaction_p.h>
36#include <QtVirtualKeyboard/qvirtualkeyboardinputengine.h>
37
38#include <QGuiApplication>
39#include <QtQuick/qquickitem.h>
40#include <QtQuick/qquickwindow.h>
41#include <QtGui/qpa/qplatformintegration.h>
42#include <QtGui/private/qguiapplication_p.h>
43
44QT_BEGIN_NAMESPACE
45
46bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMethodEvent::Attribute &attribute2)
47{
48 return attribute1.start == attribute2.start &&
49 attribute1.length == attribute2.length &&
50 attribute1.type == attribute2.type &&
51 attribute1.value == attribute2.value;
52}
53
54using namespace QtVirtualKeyboard;
55
56const bool QtVirtualKeyboard::QT_VIRTUALKEYBOARD_FORCE_EVENTS_WITHOUT_FOCUS = qEnvironmentVariableIsSet(varName: "QT_VIRTUALKEYBOARD_FORCE_EVENTS_WITHOUT_FOCUS");
57
58QVirtualKeyboardInputContextPrivate::QVirtualKeyboardInputContextPrivate(QVirtualKeyboardInputContext *q_ptr) :
59 QObject(nullptr),
60 q_ptr(q_ptr),
61 platformInputContext(nullptr),
62 inputEngine(nullptr),
63 _shiftHandler(nullptr),
64 keyboardRect(),
65 previewRect(),
66 _previewVisible(false),
67 animating(false),
68 _focus(false),
69 cursorPosition(0),
70 anchorPosition(0),
71 forceAnchorPosition(-1),
72 _forceCursorPosition(-1),
73 inputMethodHints(Qt::ImhNone),
74 preeditText(),
75 preeditTextAttributes(),
76 surroundingText(),
77 selectedText(),
78 anchorRectangle(),
79 cursorRectangle(),
80 selectionControlVisible(false),
81 anchorRectIntersectsClipRect(false),
82 cursorRectIntersectsClipRect(false)
83#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION
84 , activeNavigationKeys()
85#endif
86{
87}
88
89void QVirtualKeyboardInputContextPrivate::init()
90{
91 Q_Q(QVirtualKeyboardInputContext);
92 QGuiApplicationPrivate *guiApplicationPrivate = QGuiApplicationPrivate::instance();
93 QPlatformIntegration *platformIntegration = guiApplicationPrivate->platformIntegration();
94 QPlatformInputContext *unknownPlatformInputContext = platformIntegration->inputContext();
95 platformInputContext = qobject_cast<PlatformInputContext *>(object: unknownPlatformInputContext);
96 inputEngine = new QVirtualKeyboardInputEngine(q);
97 _shiftHandler = new ShiftHandler(q);
98 inputEngine->init();
99 _shiftHandler->init();
100 _shadow.setInputContext(q);
101 if (platformInputContext) {
102 platformInputContext->setInputContext(q);
103 QObject::connect(sender: platformInputContext, signal: &PlatformInputContext::focusObjectChanged, receiver: this, slot: &QVirtualKeyboardInputContextPrivate::onInputItemChanged);
104 QObject::connect(sender: platformInputContext, signal: &PlatformInputContext::focusObjectChanged, receiver: this, slot: &QVirtualKeyboardInputContextPrivate::inputItemChanged);
105 }
106}
107
108QVirtualKeyboardInputContextPrivate::~QVirtualKeyboardInputContextPrivate()
109{
110}
111
112bool QVirtualKeyboardInputContextPrivate::focus() const
113{
114 return _focus;
115}
116
117void QVirtualKeyboardInputContextPrivate::setFocus(bool focus)
118{
119 if (_focus != focus) {
120 VIRTUALKEYBOARD_DEBUG() << "QVirtualKeyboardInputContextPrivate::setFocus():" << focus;
121 _focus = focus;
122 emit focusChanged();
123 }
124}
125
126QRectF QVirtualKeyboardInputContextPrivate::keyboardRectangle() const
127{
128 return keyboardRect;
129}
130
131void QVirtualKeyboardInputContextPrivate::setKeyboardRectangle(QRectF rectangle)
132{
133 if (keyboardRect != rectangle) {
134 keyboardRect = rectangle;
135 emit keyboardRectangleChanged();
136 platformInputContext->emitKeyboardRectChanged();
137 }
138}
139
140QRectF QVirtualKeyboardInputContextPrivate::previewRectangle() const
141{
142 return previewRect;
143}
144
145void QVirtualKeyboardInputContextPrivate::setPreviewRectangle(QRectF rectangle)
146{
147 if (previewRect != rectangle) {
148 previewRect = rectangle;
149 emit previewRectangleChanged();
150 }
151}
152
153bool QVirtualKeyboardInputContextPrivate::previewVisible() const
154{
155 return _previewVisible;
156}
157
158void QVirtualKeyboardInputContextPrivate::setPreviewVisible(bool visible)
159{
160 if (_previewVisible != visible) {
161 _previewVisible = visible;
162 emit previewVisibleChanged();
163 }
164}
165
166QString QVirtualKeyboardInputContextPrivate::locale() const
167{
168 return platformInputContext ? platformInputContext->locale().name() : QString();
169}
170
171void QVirtualKeyboardInputContextPrivate::setLocale(const QString &locale)
172{
173 VIRTUALKEYBOARD_DEBUG() << "QVirtualKeyboardInputContextPrivate::setLocale():" << locale;
174 QLocale newLocale(locale);
175 if (newLocale != platformInputContext->locale()) {
176 platformInputContext->setLocale(newLocale);
177 platformInputContext->setInputDirection(newLocale.textDirection());
178 emit localeChanged();
179 }
180}
181
182QObject *QVirtualKeyboardInputContextPrivate::inputItem() const
183{
184 return platformInputContext ? platformInputContext->focusObject() : nullptr;
185}
186
187ShiftHandler *QVirtualKeyboardInputContextPrivate::shiftHandler() const
188{
189 return _shiftHandler;
190}
191
192ShadowInputContext *QVirtualKeyboardInputContextPrivate::shadow() const
193{
194 return const_cast<ShadowInputContext *>(&_shadow);
195}
196
197QStringList QVirtualKeyboardInputContextPrivate::inputMethods() const
198{
199 return platformInputContext ? platformInputContext->inputMethods() : QStringList();
200}
201
202bool QVirtualKeyboardInputContextPrivate::fileExists(const QUrl &fileUrl)
203{
204 QString fileName;
205 if (fileUrl.scheme() == QLatin1String("qrc")) {
206 fileName = QLatin1Char(':') + fileUrl.path();
207 } else {
208 fileName = fileUrl.toLocalFile();
209 }
210 return !fileName.isEmpty() && QFile::exists(fileName);
211}
212
213bool QVirtualKeyboardInputContextPrivate::hasEnterKeyAction(QObject *item) const
214{
215 return item != nullptr && qmlAttachedPropertiesObject<EnterKeyAction>(obj: item, create: false);
216}
217
218void QVirtualKeyboardInputContextPrivate::registerInputPanel(QObject *inputPanel)
219{
220 VIRTUALKEYBOARD_DEBUG() << "QVirtualKeyboardInputContextPrivate::registerInputPanel():" << inputPanel;
221 Q_ASSERT(!this->inputPanel);
222 this->inputPanel = inputPanel;
223}
224
225void QVirtualKeyboardInputContextPrivate::hideInputPanel()
226{
227 platformInputContext->hideInputPanel();
228}
229
230void QVirtualKeyboardInputContextPrivate::updateAvailableLocales(const QStringList &availableLocales)
231{
232 Settings *settings = Settings::instance();
233 if (settings)
234 settings->setAvailableLocales(availableLocales);
235}
236
237void QVirtualKeyboardInputContextPrivate::forceCursorPosition(int anchorPosition, int cursorPosition)
238{
239 if (!_shadow.inputItem())
240 return;
241 if (!platformInputContext->m_visible)
242 return;
243 if (testState(state: State::Reselect))
244 return;
245 if (testState(state: State::SyncShadowInput))
246 return;
247
248 VIRTUALKEYBOARD_DEBUG() << "QVirtualKeyboardInputContextPrivate::forceCursorPosition():" << cursorPosition << "anchorPosition:" << anchorPosition;
249 if (!preeditText.isEmpty()) {
250 forceAnchorPosition = -1;
251 _forceCursorPosition = cursorPosition;
252 if (cursorPosition > this->cursorPosition)
253 _forceCursorPosition += preeditText.length();
254 commit();
255 } else {
256 forceAnchorPosition = anchorPosition;
257 _forceCursorPosition = cursorPosition;
258 Q_Q(QVirtualKeyboardInputContext);
259 q->setPreeditText(text: QString());
260 if (!inputMethodHints.testFlag(flag: Qt::ImhNoPredictiveText) &&
261 cursorPosition > 0 && selectedText.isEmpty()) {
262 QVirtualKeyboardScopedState reselectState(this, State::Reselect);
263 if (inputEngine->reselect(cursorPosition, reselectFlags: QVirtualKeyboardInputEngine::ReselectFlag::WordAtCursor))
264 setState(State::InputMethodClick);
265 }
266 }
267}
268
269void QVirtualKeyboardInputContextPrivate::onInputItemChanged()
270{
271 if (QObject *item = inputItem()) {
272 if (QQuickItem *vkbPanel = qobject_cast<QQuickItem*>(object: inputPanel)) {
273 if (QQuickItem *quickItem = qobject_cast<QQuickItem*>(object: item)) {
274 const QVariant isDesktopPanel = vkbPanel->property(name: "desktopPanel");
275 /*
276 For integrated keyboards, make sure it's a sibling to the overlay. The
277 high z-order will make sure it gets events also during a modal session.
278 */
279 if (isDesktopPanel.isValid() && !isDesktopPanel.toBool()) {
280 if (QQuickWindow *quickWindow = quickItem->window()) {
281 QQuickItem *overlay = quickWindow->property(name: "_q_QQuickOverlay").value<QQuickItem*>();
282 if (overlay && overlay->isVisible()) {
283 if (vkbPanel->parentItem() != overlay->parentItem()) {
284 inputPanelParentItem = vkbPanel->parentItem();
285 inputPanelZ = vkbPanel->z();
286 vkbPanel->setParentItem(overlay->parentItem());
287 vkbPanel->setZ(overlay->z() + 1);
288 }
289 } else {
290 if (QQuickItem *oldParentItem = qobject_cast<QQuickItem *>(object: inputPanelParentItem.data())) {
291 vkbPanel->setParentItem(oldParentItem);
292 vkbPanel->setZ(inputPanelZ);
293 inputPanelParentItem = nullptr;
294 }
295 }
296 }
297 }
298 }
299 }
300 } else {
301 if (!activeKeys.isEmpty()) {
302 // After losing keyboard focus it is impossible to track pressed keys
303 activeKeys.clear();
304 clearState(state: State::KeyEvent);
305 }
306 }
307 clearState(state: State::InputMethodClick);
308}
309
310void QVirtualKeyboardInputContextPrivate::sendPreedit(const QString &text, const QList<QInputMethodEvent::Attribute> &attributes, int replaceFrom, int replaceLength)
311{
312 VIRTUALKEYBOARD_DEBUG() << "QVirtualKeyboardInputContextPrivate::sendPreedit()"
313#ifdef SENSITIVE_DEBUG
314 << text << replaceFrom << replaceLength
315#endif
316 ;
317
318 bool textChanged = preeditText != text;
319 bool attributesChanged = preeditTextAttributes != attributes;
320
321 if (textChanged || attributesChanged) {
322 preeditText = text;
323 preeditTextAttributes = attributes;
324
325 if (platformInputContext) {
326 QInputMethodEvent event(text, attributes);
327 const bool replace = replaceFrom != 0 || replaceLength > 0;
328 if (replace)
329 event.setCommitString(commitString: QString(), replaceFrom, replaceLength);
330
331 sendInputMethodEvent(event: &event);
332
333 // Send also to shadow input if only attributes changed.
334 // In this case the update() may not be called, so the shadow
335 // input may be out of sync.
336 if (_shadow.inputItem() && !replace && !text.isEmpty() &&
337 !textChanged && attributesChanged) {
338 VIRTUALKEYBOARD_DEBUG() << "QVirtualKeyboardInputContextPrivate::sendPreedit(shadow)"
339#ifdef SENSITIVE_DEBUG
340 << text << replaceFrom << replaceLength
341#endif
342 ;
343 event.setAccepted(true);
344 QGuiApplication::sendEvent(receiver: _shadow.inputItem(), event: &event);
345 }
346 }
347
348 if (textChanged) {
349 Q_Q(QVirtualKeyboardInputContext);
350 emit q->preeditTextChanged();
351 }
352 }
353
354 if (preeditText.isEmpty())
355 preeditTextAttributes.clear();
356}
357
358void QVirtualKeyboardInputContextPrivate::sendInputMethodEvent(QInputMethodEvent *event)
359{
360 QVirtualKeyboardScopedState inputMethodEventState(this, State::InputMethodEvent);
361 platformInputContext->sendEvent(event);
362}
363
364void QVirtualKeyboardInputContextPrivate::reset()
365{
366 inputEngine->reset();
367}
368
369void QVirtualKeyboardInputContextPrivate::commit()
370{
371 inputEngine->update();
372}
373
374void QVirtualKeyboardInputContextPrivate::update(Qt::InputMethodQueries queries)
375{
376 Q_Q(QVirtualKeyboardInputContext);
377
378 // No need to fetch input clip rectangle during animation
379 if (!(queries & ~Qt::ImInputItemClipRectangle) && animating)
380 return;
381
382 // fetch
383 QInputMethodQueryEvent imQueryEvent(Qt::InputMethodQueries(Qt::ImHints |
384 Qt::ImQueryInput | Qt::ImInputItemClipRectangle));
385 platformInputContext->sendEvent(event: &imQueryEvent);
386 Qt::InputMethodHints inputMethodHints = Qt::InputMethodHints(imQueryEvent.value(query: Qt::ImHints).toInt());
387 const int cursorPosition = imQueryEvent.value(query: Qt::ImCursorPosition).toInt();
388 const int anchorPosition = imQueryEvent.value(query: Qt::ImAnchorPosition).toInt();
389 QRectF anchorRectangle;
390 QRectF cursorRectangle;
391 if (const QGuiApplication *app = qApp) {
392 anchorRectangle = app->inputMethod()->anchorRectangle();
393 cursorRectangle = app->inputMethod()->cursorRectangle();
394 } else {
395 anchorRectangle = this->anchorRectangle;
396 cursorRectangle = this->cursorRectangle;
397 }
398 QString surroundingText = imQueryEvent.value(query: Qt::ImSurroundingText).toString();
399 QString selectedText = imQueryEvent.value(query: Qt::ImCurrentSelection).toString();
400
401 // check against changes
402 bool newInputMethodHints = inputMethodHints != this->inputMethodHints;
403 bool newSurroundingText = surroundingText != this->surroundingText;
404 bool newSelectedText = selectedText != this->selectedText;
405 bool newAnchorPosition = anchorPosition != this->anchorPosition;
406 bool newCursorPosition = cursorPosition != this->cursorPosition;
407 bool newAnchorRectangle = anchorRectangle != this->anchorRectangle;
408 bool newCursorRectangle = cursorRectangle != this->cursorRectangle;
409 bool selectionControlVisible = platformInputContext->evaluateInputPanelVisible() && (cursorPosition != anchorPosition) && !inputMethodHints.testFlag(flag: Qt::ImhNoTextHandles);
410 bool newSelectionControlVisible = selectionControlVisible != this->selectionControlVisible;
411
412 QRectF inputItemClipRect = imQueryEvent.value(query: Qt::ImInputItemClipRectangle).toRectF();
413 QRectF anchorRect = imQueryEvent.value(query: Qt::ImAnchorRectangle).toRectF();
414 QRectF cursorRect = imQueryEvent.value(query: Qt::ImCursorRectangle).toRectF();
415
416 bool anchorRectIntersectsClipRect = inputItemClipRect.intersects(r: anchorRect);
417 bool newAnchorRectIntersectsClipRect = anchorRectIntersectsClipRect != this->anchorRectIntersectsClipRect;
418
419 bool cursorRectIntersectsClipRect = inputItemClipRect.intersects(r: cursorRect);
420 bool newCursorRectIntersectsClipRect = cursorRectIntersectsClipRect != this->cursorRectIntersectsClipRect;
421
422 // update
423 this->inputMethodHints = inputMethodHints;
424 this->surroundingText = surroundingText;
425 this->selectedText = selectedText;
426 this->anchorPosition = anchorPosition;
427 this->cursorPosition = cursorPosition;
428 this->anchorRectangle = anchorRectangle;
429 this->cursorRectangle = cursorRectangle;
430 this->selectionControlVisible = selectionControlVisible;
431 this->anchorRectIntersectsClipRect = anchorRectIntersectsClipRect;
432 this->cursorRectIntersectsClipRect = cursorRectIntersectsClipRect;
433
434 // update input engine
435 if ((newSurroundingText || newCursorPosition) &&
436 !testState(state: State::InputMethodEvent)) {
437 commit();
438 }
439 if (newInputMethodHints) {
440 reset();
441 }
442
443 // notify
444 if (newInputMethodHints) {
445 emit q->inputMethodHintsChanged();
446 }
447 if (newSurroundingText) {
448 emit q->surroundingTextChanged();
449 }
450 if (newSelectedText) {
451 emit q->selectedTextChanged();
452 }
453 if (newAnchorPosition) {
454 emit q->anchorPositionChanged();
455 }
456 if (newCursorPosition) {
457 emit q->cursorPositionChanged();
458 }
459 if (newAnchorRectangle) {
460 emit q->anchorRectangleChanged();
461 }
462 if (newCursorRectangle) {
463 emit q->cursorRectangleChanged();
464 }
465 if (newSelectionControlVisible) {
466 emit q->selectionControlVisibleChanged();
467 }
468 if (newAnchorRectIntersectsClipRect) {
469 emit q->anchorRectIntersectsClipRectChanged();
470 }
471 if (newCursorRectIntersectsClipRect) {
472 emit q->cursorRectIntersectsClipRectChanged();
473 }
474
475 // word reselection
476 if (newInputMethodHints || newSurroundingText || newSelectedText)
477 clearState(state: State::InputMethodClick);
478 if ((newSurroundingText || newCursorPosition) && !newSelectedText && isEmptyState() &&
479 !inputMethodHints.testFlag(flag: Qt::ImhNoPredictiveText) &&
480 cursorPosition > 0 && this->selectedText.isEmpty()) {
481 QVirtualKeyboardScopedState reselectState(this, State::Reselect);
482 if (inputEngine->reselect(cursorPosition, reselectFlags: QVirtualKeyboardInputEngine::ReselectFlag::WordAtCursor))
483 setState(State::InputMethodClick);
484 }
485
486 if (!testState(state: State::SyncShadowInput)) {
487 QVirtualKeyboardScopedState syncShadowInputState(this, State::SyncShadowInput);
488 _shadow.update(queries);
489 }
490}
491
492void QVirtualKeyboardInputContextPrivate::invokeAction(QInputMethod::Action action, int cursorPosition)
493{
494 switch (action) {
495 case QInputMethod::Click:
496 if (isEmptyState()) {
497 if (inputEngine->clickPreeditText(cursorPosition))
498 break;
499
500 bool reselect = !inputMethodHints.testFlag(flag: Qt::ImhNoPredictiveText) && selectedText.isEmpty() && cursorPosition < preeditText.length();
501 if (reselect) {
502 QVirtualKeyboardScopedState reselectState(this, State::Reselect);
503 _forceCursorPosition = this->cursorPosition + cursorPosition;
504 commit();
505 inputEngine->reselect(cursorPosition: this->cursorPosition, reselectFlags: QVirtualKeyboardInputEngine::ReselectFlag::WordBeforeCursor);
506 } else if (!preeditText.isEmpty() && cursorPosition == preeditText.length()) {
507 commit();
508 }
509 }
510 clearState(state: State::InputMethodClick);
511 break;
512
513 case QInputMethod::ContextMenu:
514 break;
515 }
516}
517
518bool QVirtualKeyboardInputContextPrivate::filterEvent(const QEvent *event)
519{
520 QEvent::Type type = event->type();
521 if (type == QEvent::KeyPress || type == QEvent::KeyRelease) {
522 const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
523 const int key = keyEvent->key();
524
525 // Keep track of pressed keys update key event state
526 if (type == QEvent::KeyPress)
527 activeKeys += keyEvent->nativeScanCode();
528 else if (type == QEvent::KeyRelease)
529 activeKeys -= keyEvent->nativeScanCode();
530
531 if (activeKeys.isEmpty())
532 clearState(state: State::KeyEvent);
533 else
534 setState(State::KeyEvent);
535
536#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION
537 if ((key >= Qt::Key_Left && key <= Qt::Key_Down) || key == Qt::Key_Return) {
538 if (type == QEvent::KeyPress && platformInputContext->isInputPanelVisible()) {
539 activeNavigationKeys += key;
540 emit navigationKeyPressed(key, keyEvent->isAutoRepeat());
541 return true;
542 } else if (type == QEvent::KeyRelease && activeNavigationKeys.contains(key)) {
543 activeNavigationKeys -= key;
544 emit navigationKeyReleased(key, keyEvent->isAutoRepeat());
545 return true;
546 }
547 }
548#endif
549
550 // Break composing text since the virtual keyboard does not support hard keyboard events
551 if (!preeditText.isEmpty()) {
552 if (type == QEvent::KeyPress && (key == Qt::Key_Delete || key == Qt::Key_Backspace)) {
553 reset();
554 Q_Q(QVirtualKeyboardInputContext);
555 q->clear();
556 return true;
557 } else {
558 commit();
559 }
560 }
561 }
562#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION
563 else if (type == QEvent::ShortcutOverride) {
564 const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
565 int key = keyEvent->key();
566 if ((key >= Qt::Key_Left && key <= Qt::Key_Down) || key == Qt::Key_Return)
567 return true;
568 }
569#endif
570
571 return false;
572}
573
574void QVirtualKeyboardInputContextPrivate::addSelectionAttribute(QList<QInputMethodEvent::Attribute> &attributes)
575{
576 if (!testAttribute(attributes, attributeType: QInputMethodEvent::Selection)) {
577 // Convert Cursor attribute to Selection attribute.
578 // In this case the cursor is set in pre-edit text, but
579 // the cursor is not being forced to specific location.
580 if (_forceCursorPosition == -1) {
581 int cursorAttributeIndex = findAttribute(attributes: preeditTextAttributes, attributeType: QInputMethodEvent::Cursor);
582 if (cursorAttributeIndex != -1 && preeditTextAttributes[cursorAttributeIndex].length > 0)
583 _forceCursorPosition = cursorPosition + preeditTextAttributes[cursorAttributeIndex].start;
584 forceAnchorPosition = -1;
585 }
586
587 if (_forceCursorPosition != -1) {
588 if (forceAnchorPosition != -1)
589 attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::Selection, forceAnchorPosition, _forceCursorPosition - forceAnchorPosition, QVariant()));
590 else
591 attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::Selection, _forceCursorPosition, 0, QVariant()));
592 }
593 }
594 forceAnchorPosition = -1;
595 _forceCursorPosition = -1;
596}
597
598bool QVirtualKeyboardInputContextPrivate::testAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const
599{
600 for (const QInputMethodEvent::Attribute &attribute : qAsConst(t: attributes)) {
601 if (attribute.type == attributeType)
602 return true;
603 }
604 return false;
605}
606
607int QVirtualKeyboardInputContextPrivate::findAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const
608{
609 const int count = attributes.count();
610 for (int i = 0; i < count; ++i) {
611 if (attributes.at(i).type == attributeType)
612 return i;
613 }
614 return -1;
615}
616
617QT_END_NAMESPACE
618

source code of qtvirtualkeyboard/src/virtualkeyboard/qvirtualkeyboardinputcontext_p.cpp