IM: Don't let all widgets support IM by default

A new property Qt::ImEnabled was added in Qt 5.3.
Since the already existing widgets with IM support
(3rd party included) didn't implement this property,
QWidget got the fall back logic that if a widget
was queried for Qt::ImEnabled, and the returned QVariant
was invalid (the widget didn't implement it), we
would, for backwards compatibility with Qt 4, return "true"
(meaning that the widget supports IM).

But a side effect from this fallback logic, is that now
any widget that doesn't implement ImEnabled (or input
methods at all) report that they support IM. This will
confuse platforms like iOS, which uses ImEnabled to decide
if the input panel should show, and if text selection tools
should be enabled. The result is therefore that if you click
on a QPushButton, the input panel will open.

This patch will implement a more careful strategy to check if
a widget implements IM, if ImEnabled is missing. Rather than
saying that all widgets that don't implement ImEnabled supports
IM, we now require that the widget also returns a valid QVariant
for Qt::ImSurroundingText. We assume then, that a widget that
doesn't do so will anyway not be in need of input method support
from the platform.

Fixes: QTBUG-104527
Pick-to: 6.4 6.3 6.2
Change-Id: Ib391fd1daae92c4325e9ccb59730fbdd7c9328fc
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Richard Moe Gustavsen 2022-06-22 13:58:38 +02:00
parent 1ffe49dd19
commit 3b12305575
3 changed files with 48 additions and 3 deletions

View File

@ -399,7 +399,11 @@ QVariant QInputMethod::queryFocusObject(Qt::InputMethodQuery query, const QVaria
Q_ARG(Qt::InputMethodQuery, query),
Q_ARG(QVariant, argument));
Q_ASSERT(ok);
return retval;
if (retval.isValid())
return retval;
// If the new API didn't have an answer to the query, we fall
// back to use the old event-based API.
}
QInputMethodQueryEvent queryEvent(query);

View File

@ -8896,8 +8896,18 @@ bool QWidget::event(QEvent *event)
Qt::InputMethodQuery q = (Qt::InputMethodQuery)(int)(queries & (1<<i));
if (q) {
QVariant v = inputMethodQuery(q);
if (q == Qt::ImEnabled && !v.isValid() && isEnabled())
v = QVariant(true); // special case for Qt4 compatibility
if (q == Qt::ImEnabled && !v.isValid() && isEnabled()) {
// Qt:ImEnabled was added in Qt 5.3. So not all widgets support it, even
// if they implement IM otherwise (and override inputMethodQuery()).
// So for legacy reasons, we need to check by other means if IM is supported when
// Qt::ImEnabled is not implemented (the query returns an invalid QVariant).
// Since QWidget implements inputMethodQuery(), and return valid values for
// some of the IM properties, we cannot just query for Qt::ImQueryAll.
// Instead we assume that if a widget supports IM, it will implement
// Qt::ImSurroundingText (which is not implemented by QWidget).
const bool imEnabledFallback = inputMethodQuery(Qt::ImSurroundingText).isValid();
v = QVariant(imEnabledFallback);
}
query->setValue(q, v);
}
}

View File

@ -369,6 +369,8 @@ private slots:
void focusProxy();
void focusProxyAndInputMethods();
void imEnabledNotImplemented();
#ifdef QT_BUILD_INTERNAL
void scrollWithoutBackingStore();
#endif
@ -10982,6 +10984,35 @@ void tst_QWidget::focusProxyAndInputMethods()
QCOMPARE(qApp->focusObject(), toplevel.data());
}
void tst_QWidget::imEnabledNotImplemented()
{
// Check that a plain widget doesn't report that it supports IM. Only
// widgets that implements either Qt::ImEnabled, or the Qt4 backup
// solution, Qt::ImSurroundingText, should do so.
QWidget topLevel;
QWidget plain(&topLevel);
QLineEdit edit(&topLevel);
topLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
QApplication::setActiveWindow(&topLevel);
QVERIFY(QTest::qWaitForWindowActive(&topLevel));
// A plain widget should return false for ImEnabled
plain.setFocus(Qt::OtherFocusReason);
QCOMPARE(QApplication::focusWidget(), &plain);
QVariant imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant());
QVERIFY(imEnabled.isValid());
QVERIFY(!imEnabled.toBool());
// But a lineedit should return true
edit.setFocus(Qt::OtherFocusReason);
QCOMPARE(QApplication::focusWidget(), &edit);
imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant());
QVERIFY(imEnabled.isValid());
QVERIFY(imEnabled.toBool());
}
#ifdef QT_BUILD_INTERNAL
class scrollWidgetWBS : public QWidget
{