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
38QT_BEGIN_NAMESPACE
39bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMethodEvent::Attribute &attribute2);
40
41namespace QtVirtualKeyboard {
42
43class ShadowInputContextPrivate : public QObjectPrivate
44{
45public:
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
66ShadowInputContext::ShadowInputContext(QObject *parent) :
67 QObject(*new ShadowInputContextPrivate(), parent)
68{
69}
70
71void ShadowInputContext::setInputContext(QVirtualKeyboardInputContext *inputContext)
72{
73 Q_D(ShadowInputContext);
74 d->inputContext = inputContext;
75}
76
77QObject *ShadowInputContext::inputItem() const
78{
79 Q_D(const ShadowInputContext);
80 return d->inputItem.data();
81}
82
83void 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
93QRectF ShadowInputContext::anchorRectangle() const
94{
95 Q_D(const ShadowInputContext);
96 return d->anchorRectangle;
97}
98
99QRectF ShadowInputContext::cursorRectangle() const
100{
101 Q_D(const ShadowInputContext);
102 return d->cursorRectangle;
103}
104
105bool ShadowInputContext::anchorRectIntersectsClipRect() const
106{
107 Q_D(const ShadowInputContext);
108 return d->anchorRectIntersectsClipRect;
109}
110
111bool ShadowInputContext::cursorRectIntersectsClipRect() const
112{
113 Q_D(const ShadowInputContext);
114 return d->cursorRectIntersectsClipRect;
115}
116
117bool ShadowInputContext::selectionControlVisible() const
118{
119 Q_D(const ShadowInputContext);
120 return d->selectionControlVisible;
121}
122
123void 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
144void 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
188void 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
231QVariant 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
253QT_END_NAMESPACE
254

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