| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2017 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/shadowinputcontext_p.h> | 
| 31 | #include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h> | 
| 32 | #include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h> | 
| 33 |  | 
| 34 | #include <QtCore/private/qobject_p.h> | 
| 35 | #include <QGuiApplication> | 
| 36 | #include <QQuickItem> | 
| 37 |  | 
| 38 | QT_BEGIN_NAMESPACE | 
| 39 | bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMethodEvent::Attribute &attribute2); | 
| 40 |  | 
| 41 | namespace QtVirtualKeyboard { | 
| 42 |  | 
| 43 | class ShadowInputContextPrivate : public QObjectPrivate | 
| 44 | { | 
| 45 | public: | 
| 46 |     ShadowInputContextPrivate() : | 
| 47 |         QObjectPrivate(), | 
| 48 |         inputContext(nullptr), | 
| 49 |         anchorRectIntersectsClipRect(false), | 
| 50 |         cursorRectIntersectsClipRect(false), | 
| 51 |         selectionControlVisible(false) | 
| 52 |     { | 
| 53 |     } | 
| 54 |  | 
| 55 |     QVirtualKeyboardInputContext *inputContext; | 
| 56 |     QPointer<QObject> inputItem; | 
| 57 |     QString preeditText; | 
| 58 |     QList<QInputMethodEvent::Attribute> preeditTextAttributes; | 
| 59 |     QRectF anchorRectangle; | 
| 60 |     QRectF cursorRectangle; | 
| 61 |     bool anchorRectIntersectsClipRect; | 
| 62 |     bool cursorRectIntersectsClipRect; | 
| 63 |     bool selectionControlVisible; | 
| 64 | }; | 
| 65 |  | 
| 66 | ShadowInputContext::ShadowInputContext(QObject *parent) : | 
| 67 |     QObject(*new ShadowInputContextPrivate(), parent) | 
| 68 | { | 
| 69 | } | 
| 70 |  | 
| 71 | void ShadowInputContext::setInputContext(QVirtualKeyboardInputContext *inputContext) | 
| 72 | { | 
| 73 |     Q_D(ShadowInputContext); | 
| 74 |     d->inputContext = inputContext; | 
| 75 | } | 
| 76 |  | 
| 77 | QObject *ShadowInputContext::inputItem() const | 
| 78 | { | 
| 79 |     Q_D(const ShadowInputContext); | 
| 80 |     return d->inputItem.data(); | 
| 81 | } | 
| 82 |  | 
| 83 | void ShadowInputContext::setInputItem(QObject *inputItem) | 
| 84 | { | 
| 85 |     Q_D(ShadowInputContext); | 
| 86 |     if (d->inputItem != inputItem) { | 
| 87 |         d->inputItem = inputItem; | 
| 88 |         emit inputItemChanged(); | 
| 89 |         update(queries: Qt::ImQueryAll); | 
| 90 |     } | 
| 91 | } | 
| 92 |  | 
| 93 | QRectF ShadowInputContext::anchorRectangle() const | 
| 94 | { | 
| 95 |     Q_D(const ShadowInputContext); | 
| 96 |     return d->anchorRectangle; | 
| 97 | } | 
| 98 |  | 
| 99 | QRectF ShadowInputContext::cursorRectangle() const | 
| 100 | { | 
| 101 |     Q_D(const ShadowInputContext); | 
| 102 |     return d->cursorRectangle; | 
| 103 | } | 
| 104 |  | 
| 105 | bool ShadowInputContext::anchorRectIntersectsClipRect() const | 
| 106 | { | 
| 107 |     Q_D(const ShadowInputContext); | 
| 108 |     return d->anchorRectIntersectsClipRect; | 
| 109 | } | 
| 110 |  | 
| 111 | bool ShadowInputContext::cursorRectIntersectsClipRect() const | 
| 112 | { | 
| 113 |     Q_D(const ShadowInputContext); | 
| 114 |     return d->cursorRectIntersectsClipRect; | 
| 115 | } | 
| 116 |  | 
| 117 | bool ShadowInputContext::selectionControlVisible() const | 
| 118 | { | 
| 119 |     Q_D(const ShadowInputContext); | 
| 120 |     return d->selectionControlVisible; | 
| 121 | } | 
| 122 |  | 
| 123 | void ShadowInputContext::setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos) | 
| 124 | { | 
| 125 |     Q_D(ShadowInputContext); | 
| 126 |     QObject *focus = d->inputItem; | 
| 127 |     if (!focus) | 
| 128 |         return; | 
| 129 |  | 
| 130 |     QQuickItem *quickItem = qobject_cast<QQuickItem *>(object: d->inputItem); | 
| 131 |     bool success; | 
| 132 |     int anchor = queryFocusObject(query: Qt::ImCursorPosition, argument: quickItem ? quickItem->mapFromScene(point: anchorPos) : anchorPos).toInt(ok: &success); | 
| 133 |     if (success) { | 
| 134 |         int cursor = queryFocusObject(query: Qt::ImCursorPosition, argument: quickItem ? quickItem->mapFromScene(point: cursorPos) : cursorPos).toInt(ok: &success); | 
| 135 |         if (success) { | 
| 136 |             QList<QInputMethodEvent::Attribute> imAttributes; | 
| 137 |             imAttributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant())); | 
| 138 |             QInputMethodEvent event(QString(), imAttributes); | 
| 139 |             QGuiApplication::sendEvent(receiver: QGuiApplication::focusObject(), event: &event); | 
| 140 |         } | 
| 141 |     } | 
| 142 | } | 
| 143 |  | 
| 144 | void ShadowInputContext::updateSelectionProperties() | 
| 145 | { | 
| 146 |     Q_D(ShadowInputContext); | 
| 147 |     if (!d->inputItem) | 
| 148 |         return; | 
| 149 |  | 
| 150 |     QInputMethodQueryEvent imQueryEvent(Qt::ImAnchorRectangle | | 
| 151 |                                         Qt::ImCursorRectangle | | 
| 152 |                                         Qt::ImInputItemClipRectangle); | 
| 153 |     QGuiApplication::sendEvent(receiver: d->inputItem, event: &imQueryEvent); | 
| 154 |     QQuickItem *quickItem = qobject_cast<QQuickItem *>(object: d->inputItem); | 
| 155 |     const QRectF anchorRect = imQueryEvent.value(query: Qt::ImAnchorRectangle).toRectF(); | 
| 156 |     const QRectF cursorRect = imQueryEvent.value(query: Qt::ImCursorRectangle).toRectF(); | 
| 157 |     const QRectF anchorRectangle = quickItem ? quickItem->mapRectToScene(rect: anchorRect) : anchorRect; | 
| 158 |     const QRectF cursorRectangle = quickItem ? quickItem->mapRectToScene(rect: cursorRect) : cursorRect; | 
| 159 |     const QRectF inputItemClipRect = imQueryEvent.value(query: Qt::ImInputItemClipRectangle).toRectF(); | 
| 160 |     const bool anchorRectIntersectsClipRect = inputItemClipRect.intersects(r: anchorRect); | 
| 161 |     const bool cursorRectIntersectsClipRect = inputItemClipRect.intersects(r: cursorRect); | 
| 162 |     const bool selectionControlVisible = d->inputContext->isSelectionControlVisible(); | 
| 163 |  | 
| 164 |     const bool newAnchorRectangle = anchorRectangle != d->anchorRectangle; | 
| 165 |     const bool newCursorRectangle = cursorRectangle != d->cursorRectangle; | 
| 166 |     const bool newAnchorRectIntersectsClipRect = anchorRectIntersectsClipRect != d->anchorRectIntersectsClipRect; | 
| 167 |     const bool newCursorRectIntersectsClipRect = cursorRectIntersectsClipRect != d->cursorRectIntersectsClipRect; | 
| 168 |     const bool newSelectionControlVisible = selectionControlVisible != d->selectionControlVisible; | 
| 169 |  | 
| 170 |     d->anchorRectangle = anchorRectangle; | 
| 171 |     d->cursorRectangle = cursorRectangle; | 
| 172 |     d->anchorRectIntersectsClipRect = anchorRectIntersectsClipRect; | 
| 173 |     d->cursorRectIntersectsClipRect = cursorRectIntersectsClipRect; | 
| 174 |     d->selectionControlVisible = selectionControlVisible; | 
| 175 |  | 
| 176 |     if (newAnchorRectangle) | 
| 177 |         emit anchorRectangleChanged(); | 
| 178 |     if (newCursorRectangle) | 
| 179 |         emit cursorRectangleChanged(); | 
| 180 |     if (newAnchorRectIntersectsClipRect) | 
| 181 |         emit anchorRectIntersectsClipRectChanged(); | 
| 182 |     if (newCursorRectIntersectsClipRect) | 
| 183 |         emit cursorRectIntersectsClipRectChanged(); | 
| 184 |     if (newSelectionControlVisible) | 
| 185 |         emit selectionControlVisibleChanged(); | 
| 186 | } | 
| 187 |  | 
| 188 | void ShadowInputContext::update(Qt::InputMethodQueries queries) | 
| 189 | { | 
| 190 |     Q_UNUSED(queries) | 
| 191 |     Q_D(ShadowInputContext); | 
| 192 |     if (!d->inputItem) | 
| 193 |         return; | 
| 194 |  | 
| 195 |     QInputMethodQueryEvent imQueryEvent(Qt::ImQueryInput); | 
| 196 |     QGuiApplication::sendEvent(receiver: d->inputItem, event: &imQueryEvent); | 
| 197 |  | 
| 198 |     const QString surroundingText = imQueryEvent.value(query: Qt::ImSurroundingText).toString(); | 
| 199 |     const int cursorPosition = imQueryEvent.value(query: Qt::ImCursorPosition).toInt(); | 
| 200 |     const int anchorPosition = imQueryEvent.value(query: Qt::ImAnchorPosition).toInt(); | 
| 201 |  | 
| 202 |     const QString newSurroundingText = d->inputContext->surroundingText(); | 
| 203 |     const int newCursorPosition = d->inputContext->cursorPosition(); | 
| 204 |     const int newAnchorPosition = d->inputContext->anchorPosition(); | 
| 205 |  | 
| 206 |     bool updateSurroundingText = newSurroundingText != surroundingText; | 
| 207 |     bool updateSelection = newCursorPosition != cursorPosition || newAnchorPosition != anchorPosition; | 
| 208 |     if (updateSurroundingText || updateSelection) { | 
| 209 |         QList<QInputMethodEvent::Attribute> attributes; | 
| 210 |         attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::Selection, | 
| 211 |                                                        newAnchorPosition, | 
| 212 |                                                        newCursorPosition - newAnchorPosition, QVariant())); | 
| 213 |         QInputMethodEvent inputEvent(QString(), attributes); | 
| 214 |         if (updateSurroundingText) | 
| 215 |             inputEvent.setCommitString(commitString: newSurroundingText, replaceFrom: -cursorPosition, replaceLength: surroundingText.length()); | 
| 216 |         QGuiApplication::sendEvent(receiver: d->inputItem, event: &inputEvent); | 
| 217 |     } | 
| 218 |  | 
| 219 |     const QString newPreeditText = d->inputContext->preeditText(); | 
| 220 |     const QList<QInputMethodEvent::Attribute> newPreeditAttributes = d->inputContext->preeditTextAttributes(); | 
| 221 |     if (d->preeditText != newPreeditText || d->preeditTextAttributes != newPreeditAttributes) { | 
| 222 |         d->preeditText = newPreeditText; | 
| 223 |         d->preeditTextAttributes = newPreeditAttributes; | 
| 224 |         QInputMethodEvent inputEvent(d->preeditText, d->preeditTextAttributes); | 
| 225 |         QGuiApplication::sendEvent(receiver: d->inputItem, event: &inputEvent); | 
| 226 |     } | 
| 227 |  | 
| 228 |     updateSelectionProperties(); | 
| 229 | } | 
| 230 |  | 
| 231 | QVariant ShadowInputContext::queryFocusObject(Qt::InputMethodQuery query, QVariant argument) | 
| 232 | { | 
| 233 |     Q_D(ShadowInputContext); | 
| 234 |     QVariant retval; | 
| 235 |     QObject *focusObject = d->inputItem; | 
| 236 |     if (!focusObject) | 
| 237 |         return retval; | 
| 238 |  | 
| 239 |     bool newMethodWorks = QMetaObject::invokeMethod(obj: focusObject, member: "inputMethodQuery" , | 
| 240 |                                                     Qt::DirectConnection, | 
| 241 |                                                     Q_RETURN_ARG(QVariant, retval), | 
| 242 |                                                     Q_ARG(Qt::InputMethodQuery, query), | 
| 243 |                                                     Q_ARG(QVariant, argument)); | 
| 244 |     if (newMethodWorks) | 
| 245 |         return retval; | 
| 246 |  | 
| 247 |     QInputMethodQueryEvent queryEvent(query); | 
| 248 |     QCoreApplication::sendEvent(receiver: focusObject, event: &queryEvent); | 
| 249 |     return queryEvent.value(query); | 
| 250 | } | 
| 251 |  | 
| 252 | } // namespace QtVirtualKeyboard | 
| 253 | QT_END_NAMESPACE | 
| 254 |  |