1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <qinputmethod.h>
41#include <private/qinputmethod_p.h>
42#include <qguiapplication.h>
43#include <qtimer.h>
44#include <qpa/qplatforminputcontext_p.h>
45
46#include <QDebug>
47
48QT_BEGIN_NAMESPACE
49
50/*!
51 \internal
52*/
53QInputMethod::QInputMethod()
54 : QObject(*new QInputMethodPrivate)
55{
56}
57
58/*!
59 \internal
60*/
61QInputMethod::~QInputMethod()
62{
63}
64
65/*!
66 \class QInputMethod
67 \brief The QInputMethod class provides access to the active text input method.
68 \inmodule QtGui
69
70 QInputMethod is used by the text editors for integrating to the platform text input
71 methods and more commonly by application views for querying various text input method-related
72 information like virtual keyboard visibility and keyboard dimensions.
73
74 Qt Quick also provides access to QInputMethod in QML through \l{QmlGlobalQtObject}{Qt global object}
75 as \c Qt.inputMethod property.
76*/
77
78/*!
79 Returns the transformation from input item coordinates to the window coordinates.
80*/
81QTransform QInputMethod::inputItemTransform() const
82{
83 Q_D(const QInputMethod);
84 return d->inputItemTransform;
85}
86
87/*!
88 Sets the transformation from input item coordinates to window coordinates to be \a transform.
89 Item transform needs to be updated by the focused window like QQuickCanvas whenever
90 item is moved inside the scene.
91*/
92void QInputMethod::setInputItemTransform(const QTransform &transform)
93{
94 Q_D(QInputMethod);
95 if (d->inputItemTransform == transform)
96 return;
97
98 d->inputItemTransform = transform;
99 emit cursorRectangleChanged();
100 emit anchorRectangleChanged();
101}
102
103
104/*!
105 \since 5.1
106
107 Returns the input item's geometry in input item coordinates.
108
109 \sa setInputItemRectangle()
110*/
111QRectF QInputMethod::inputItemRectangle() const
112{
113 Q_D(const QInputMethod);
114 return d->inputRectangle;
115}
116
117/*!
118 \since 5.1
119
120 Sets the input item's geometry to be \a rect, in input item coordinates.
121 This needs to be updated by the focused window like QQuickCanvas whenever
122 item is moved inside the scene, or focus is changed.
123*/
124void QInputMethod::setInputItemRectangle(const QRectF &rect)
125{
126 Q_D(QInputMethod);
127 d->inputRectangle = rect;
128}
129
130static QRectF inputMethodQueryRectangle_helper(Qt::InputMethodQuery imquery, const QTransform &xform)
131{
132 QRectF r;
133 if (QObject *focusObject = qGuiApp->focusObject()) {
134 QInputMethodQueryEvent query(imquery);
135 QGuiApplication::sendEvent(focusObject, &query);
136 r = query.value(imquery).toRectF();
137 if (r.isValid())
138 r = xform.mapRect(r);
139 }
140 return r;
141}
142
143/*!
144 \property QInputMethod::cursorRectangle
145 \brief Input item's cursor rectangle in window coordinates.
146
147 Cursor rectangle is often used by various text editing controls
148 like text prediction popups for following the text being typed.
149*/
150QRectF QInputMethod::cursorRectangle() const
151{
152 Q_D(const QInputMethod);
153 return inputMethodQueryRectangle_helper(Qt::ImCursorRectangle, d->inputItemTransform);
154}
155
156/*!
157 \property QInputMethod::anchorRectangle
158 \brief Input item's anchor rectangle in window coordinates.
159
160 Anchor rectangle is often used by various text editing controls
161 like text prediction popups for following the text selection.
162*/
163QRectF QInputMethod::anchorRectangle() const
164{
165 Q_D(const QInputMethod);
166 return inputMethodQueryRectangle_helper(Qt::ImAnchorRectangle, d->inputItemTransform);
167}
168
169/*!
170 \property QInputMethod::keyboardRectangle
171 \brief Virtual keyboard's geometry in window coordinates.
172
173 This might be an empty rectangle if it is not possible to know the geometry
174 of the keyboard. This is the case for a floating keyboard on android.
175*/
176QRectF QInputMethod::keyboardRectangle() const
177{
178 Q_D(const QInputMethod);
179 QPlatformInputContext *ic = d->platformInputContext();
180 if (ic)
181 return ic->keyboardRect();
182 return QRectF();
183}
184
185/*!
186 \property QInputMethod::inputItemClipRectangle
187 \brief Input item's clipped rectangle in window coordinates.
188
189 The clipped input rectangle is often used by various input methods to determine
190 how much screen real estate is available for the input method (e.g. Virtual Keyboard).
191*/
192QRectF QInputMethod::inputItemClipRectangle() const
193{
194 Q_D(const QInputMethod);
195 return inputMethodQueryRectangle_helper(Qt::ImInputItemClipRectangle, d->inputItemTransform);
196}
197/*!
198 Requests virtual keyboard to open. If the platform
199 doesn't provide virtual keyboard the visibility
200 remains false.
201
202 Normally applications should not need to call this
203 function, keyboard should automatically open when
204 the text editor gains focus.
205*/
206void QInputMethod::show()
207{
208 Q_D(QInputMethod);
209 QPlatformInputContext *ic = d->platformInputContext();
210 if (ic)
211 ic->showInputPanel();
212}
213
214/*!
215 Requests virtual keyboard to close.
216
217 Normally applications should not need to call this function,
218 keyboard should automatically close when the text editor loses
219 focus, for example when the parent view is closed.
220*/
221void QInputMethod::hide()
222{
223 Q_D(QInputMethod);
224 QPlatformInputContext *ic = d->platformInputContext();
225 if (ic)
226 ic->hideInputPanel();
227}
228
229/*!
230 \property QInputMethod::visible
231 \brief Virtual keyboard's visibility on the screen
232
233 Input method visibility remains false for devices
234 with no virtual keyboards.
235
236 \sa show(), hide()
237*/
238bool QInputMethod::isVisible() const
239{
240 Q_D(const QInputMethod);
241 QPlatformInputContext *ic = d->platformInputContext();
242 if (ic)
243 return ic->isInputPanelVisible();
244 return false;
245}
246
247/*!
248 Controls the keyboard visibility. Equivalent
249 to calling show() (if \a visible is \c true)
250 or hide() (if \a visible is \c false).
251
252 \sa show(), hide()
253*/
254void QInputMethod::setVisible(bool visible)
255{
256 visible ? show() : hide();
257}
258
259/*!
260 \property QInputMethod::animating
261 \brief True when the virtual keyboard is being opened or closed.
262
263 Animating is false when keyboard is fully open or closed.
264 When \c animating is \c true and \c visibility is \c true keyboard
265 is being opened. When \c animating is \c true and \c visibility is
266 false keyboard is being closed.
267*/
268
269bool QInputMethod::isAnimating() const
270{
271 Q_D(const QInputMethod);
272 QPlatformInputContext *ic = d->platformInputContext();
273 if (ic)
274 return ic->isAnimating();
275 return false;
276}
277
278/*!
279 \property QInputMethod::locale
280 \brief Current input locale.
281*/
282QLocale QInputMethod::locale() const
283{
284 Q_D(const QInputMethod);
285 QPlatformInputContext *ic = d->platformInputContext();
286 if (ic)
287 return ic->locale();
288 return QLocale::c();
289}
290
291/*!
292 \property QInputMethod::inputDirection
293 \brief Current input direction.
294*/
295Qt::LayoutDirection QInputMethod::inputDirection() const
296{
297 Q_D(const QInputMethod);
298 QPlatformInputContext *ic = d->platformInputContext();
299 if (ic)
300 return ic->inputDirection();
301 return Qt::LeftToRight;
302}
303
304/*!
305 Called by the input item to inform the platform input methods when there has been
306 state changes in editor's input method query attributes. When calling the function
307 \a queries parameter has to be used to tell what has changes, which input method
308 can use to make queries for attributes it's interested with QInputMethodQueryEvent.
309
310 In particular calling update whenever the cursor position changes is important as
311 that often causes other query attributes like surrounding text and text selection
312 to change as well. The attributes that often change together with cursor position
313 have been grouped in Qt::ImQueryInput value for convenience.
314*/
315void QInputMethod::update(Qt::InputMethodQueries queries)
316{
317 Q_D(QInputMethod);
318
319 if (queries & Qt::ImEnabled) {
320 QObject *focus = qApp->focusObject();
321 bool enabled = d->objectAcceptsInputMethod(focus);
322 QPlatformInputContextPrivate::setInputMethodAccepted(enabled);
323 }
324
325 QPlatformInputContext *ic = d->platformInputContext();
326 if (ic)
327 ic->update(queries);
328
329 if (queries & Qt::ImCursorRectangle)
330 emit cursorRectangleChanged();
331
332 if (queries & (Qt::ImAnchorRectangle))
333 emit anchorRectangleChanged();
334
335 if (queries & (Qt::ImInputItemClipRectangle))
336 emit inputItemClipRectangleChanged();
337}
338
339/*!
340 Resets the input method state. For example, a text editor normally calls
341 this method before inserting a text to make widget ready to accept a text.
342
343 Input method resets automatically when the focused editor changes.
344*/
345void QInputMethod::reset()
346{
347 Q_D(QInputMethod);
348 QPlatformInputContext *ic = d->platformInputContext();
349 if (ic)
350 ic->reset();
351}
352
353/*!
354 Commits the word user is currently composing to the editor. The function is
355 mostly needed by the input methods with text prediction features and by the
356 methods where the script used for typing characters is different from the
357 script that actually gets appended to the editor. Any kind of action that
358 interrupts the text composing needs to flush the composing state by calling the
359 commit() function, for example when the cursor is moved elsewhere.
360*/
361void QInputMethod::commit()
362{
363 Q_D(QInputMethod);
364 QPlatformInputContext *ic = d->platformInputContext();
365 if (ic)
366 ic->commit();
367}
368
369/*!
370 \enum QInputMethod::Action
371
372 Indicates the kind of action performed by the user.
373
374 \value Click A normal click/tap
375 \value ContextMenu A context menu click/tap (e.g. right-button or tap-and-hold)
376
377 \sa invokeAction()
378*/
379
380/*!
381 Called by the input item when the word currently being composed is tapped by
382 the user, as indicated by the action \a a and the given \a cursorPosition.
383 Input methods often use this information to offer more word suggestions to the user.
384*/
385void QInputMethod::invokeAction(Action a, int cursorPosition)
386{
387 Q_D(QInputMethod);
388 QPlatformInputContext *ic = d->platformInputContext();
389 if (ic)
390 ic->invokeAction(a, cursorPosition);
391}
392
393static inline bool platformSupportsHiddenText()
394{
395 const QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
396 return inputContext && inputContext->hasCapability(QPlatformInputContext::HiddenTextCapability);
397}
398
399bool QInputMethodPrivate::objectAcceptsInputMethod(QObject *object)
400{
401 bool enabled = false;
402 if (object) {
403 // If the platform does not support hidden text, query the hints
404 // in addition and disable in case of ImhHiddenText.
405 static const bool supportsHiddenText = platformSupportsHiddenText();
406 QInputMethodQueryEvent query(supportsHiddenText
407 ? Qt::InputMethodQueries(Qt::ImEnabled)
408 : Qt::InputMethodQueries(Qt::ImEnabled | Qt::ImHints));
409 QGuiApplication::sendEvent(object, &query);
410 enabled = query.value(Qt::ImEnabled).toBool();
411 if (enabled && !supportsHiddenText
412 && Qt::InputMethodHints(query.value(Qt::ImHints).toInt()).testFlag(Qt::ImhHiddenText)) {
413 enabled = false;
414 }
415 }
416 return enabled;
417}
418
419/*!
420 Send \a query to the current focus object with parameters \a argument and return the result.
421 */
422QVariant QInputMethod::queryFocusObject(Qt::InputMethodQuery query, const QVariant &argument)
423{
424 QVariant retval;
425 QObject *focusObject = qGuiApp->focusObject();
426 if (!focusObject)
427 return retval;
428
429 bool newMethodWorks = QMetaObject::invokeMethod(focusObject, "inputMethodQuery",
430 Qt::DirectConnection,
431 Q_RETURN_ARG(QVariant, retval),
432 Q_ARG(Qt::InputMethodQuery, query),
433 Q_ARG(QVariant, argument));
434 if (newMethodWorks)
435 return retval;
436
437 QInputMethodQueryEvent queryEvent(query);
438 QCoreApplication::sendEvent(focusObject, &queryEvent);
439 return queryEvent.value(query);
440}
441
442QT_END_NAMESPACE
443
444#include "moc_qinputmethod.cpp"
445