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

source code of qtbase/src/gui/kernel/qinputmethod.cpp