Add test for QQuickControl::focusReason to the focus test case

That feature is quite broken at the moment for controls that use a
contentItem that takes focus; document that using QEXPECT_FAIL.

Also, building custom items based on control does not work as
expected; controls don't get tab focus even with activeFocusOnTab
set, focusPolicy on the control seems to be ignored.

Replace the old test, which didn't really test anything other than
that transferring focus programmatically works.

Task-number: QTBUG-75862
Pick-to: 6.2
Change-Id: I801a48d8b6a8aeb41c98e5aa68b13ca426412fed
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Volker Hilsheimer 2021-11-19 15:50:33 +01:00
parent 063c5c7fae
commit 670cd949d3
2 changed files with 290 additions and 39 deletions

View File

@ -0,0 +1,99 @@
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Item {
Component.onCompleted: control.focus = true
width: 640
height: 480
Column {
anchors.top: parent.top
anchors.topMargin: 10
spacing: 10
Control {
id: control
implicitWidth: 100
implicitHeight: 20
objectName: "control"
activeFocusOnTab: true
Menu {
id: contextMenu
objectName: "contextMenu"
MenuItem {
text: "Hello"
}
MenuItem {
text: "World"
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: function onClicked(mouseEvent) {
if (mouseEvent.button == Qt.RightButton)
contextMenu.visible = true
}
}
}
ComboBox {
id: combobox
objectName: "combobox"
model: ["Banana", "Apple", "Coconut"]
activeFocusOnTab: true
}
ComboBox {
id: editcombo
objectName: "editcombo"
editable: true
model: ["Kiwi", "Mango", "Pomelo"]
activeFocusOnTab: true
}
SpinBox {
id: spinbox
objectName: "spinbox"
from: 0
to: 100
value: 20
editable: true
activeFocusOnTab: true
}
Control {
id: customText
objectName: "customText"
implicitWidth: 100
implicitHeight: 50
contentItem: TextInput {
text: parent.visualFocus ? "focus" : "no focus"
}
activeFocusOnTab: true
}
Control {
id: customItem
objectName: "customItem"
implicitWidth: 100
implicitHeight: 50
contentItem: Rectangle {
anchors.fill: parent
color: parent.activeFocus ? "red" : "blue"
opacity: 0.3
}
focusPolicy: Qt.WheelFocus
activeFocusOnTab: true
}
TextField {
id: textfield
objectName: "textfield"
text: "test123"
activeFocusOnTab: true
}
}
}

View File

@ -62,7 +62,6 @@ private slots:
void policy_data();
void policy();
void reason_data();
void reason();
void visualFocus();
@ -240,59 +239,212 @@ void tst_focus::policy()
QVERIFY(!control->hasVisualFocus());
}
void tst_focus::reason_data()
{
QTest::addColumn<QString>("name");
QTest::newRow("Control") << "Control";
QTest::newRow("TextField") << "TextField";
QTest::newRow("TextArea") << "TextArea";
QTest::newRow("SpinBox") << "SpinBox";
QTest::newRow("ComboBox") << "ComboBox";
}
void tst_focus::reason()
{
QFETCH(QString, name);
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls);
auto resetTabFocusBehavior = qScopeGuard([]{
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusBehavior(-1));
});
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QString("import QtQuick.Controls; ApplicationWindow { width: 100; height: 100; %1 { anchors.fill: parent } }").arg(name).toUtf8(), QUrl());
QQuickView view;
view.setSource(testFileUrl("focusReason.qml"));
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(component.create()));
QVERIFY(window.data());
QQuickControl *control = view.findChild<QQuickControl *>("control");
QQuickControl *combobox = view.findChild<QQuickControl *>("combobox");
QQuickControl *editcombo = view.findChild<QQuickControl *>("editcombo");
QQuickControl *spinbox = view.findChild<QQuickControl *>("spinbox");
QQuickControl *customText = view.findChild<QQuickControl *>("customText");
QQuickControl *customItem = view.findChild<QQuickControl *>("customItem");
// not a QQuickControl subclass
QQuickItem *textfield = view.findChild<QQuickItem *>("textfield");
// helper for clicking into a control
const auto itemCenter = [](const QQuickItem *item) -> QPoint {
return item->mapToScene(item->clipRect().center()).toPoint();
};
QQuickItem *control = window->contentItem()->childItems().first();
QVERIFY(control);
QVERIFY(combobox);
QVERIFY(editcombo);
QVERIFY(spinbox);
QVERIFY(textfield);
QVERIFY(customText);
QVERIFY(customItem);
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window.data()));
// setting focusPolicy to Strong/WheelFocus doesn't implicitly turn on event delivery
customText->setAcceptedMouseButtons(Qt::LeftButton);
customItem->setAcceptedMouseButtons(Qt::LeftButton);
customItem->setWheelEnabled(true);
QCOMPARE(control->property("focusReason").toInt(), int(Qt::OtherFocusReason));
control->forceActiveFocus(Qt::MouseFocusReason);
// window activation -> ActiveWindowFocusReason
QVERIFY(control->hasFocus());
QVERIFY(control->hasActiveFocus());
QCOMPARE(control->property("focusReason").toInt(), int(Qt::MouseFocusReason));
if (control->focusReason() != Qt::ActiveWindowFocusReason
&& QStringList{"windows", "offscreen"}.contains(QGuiApplication::platformName())) {
QEXPECT_FAIL("", "On Windows and offscreen platforms, window activation does not set focus reason", Continue);
}
QCOMPARE(control->focusReason(), Qt::ActiveWindowFocusReason);
QEXPECT_FAIL("TextArea", "TODO: TextArea::visualFocus?", Continue);
QEXPECT_FAIL("TextField", "TODO: TextField::visualFocus?", Continue);
QCOMPARE(control->property("visualFocus"), QVariant(false));
// test setter/getter
control->setFocus(false, Qt::MouseFocusReason);
QCOMPARE(control->focusReason(), Qt::MouseFocusReason);
control->setFocus(true, Qt::TabFocusReason);
QCOMPARE(control->focusReason(), Qt::TabFocusReason);
control->setFocus(false, Qt::BacktabFocusReason);
QCOMPARE(control->focusReason(), Qt::BacktabFocusReason);
control->forceActiveFocus(Qt::ShortcutFocusReason);
QCOMPARE(control->focusReason(), Qt::ShortcutFocusReason);
control->setFocusReason(Qt::PopupFocusReason);
QCOMPARE(control->focusReason(), Qt::PopupFocusReason);
window->contentItem()->setFocus(false, Qt::TabFocusReason);
QVERIFY(!control->hasActiveFocus());
QCOMPARE(control->property("focusReason").toInt(), int(Qt::TabFocusReason));
// programmatic focus changes
combobox->setFocus(true, Qt::OtherFocusReason);
QCOMPARE(control->focusReason(), Qt::OtherFocusReason);
QEXPECT_FAIL("TextArea", "", Continue);
QEXPECT_FAIL("TextField", "", Continue);
QCOMPARE(control->property("visualFocus"), QVariant(false));
QVERIFY(combobox->hasFocus());
QVERIFY(combobox->hasActiveFocus());
QCOMPARE(combobox->focusReason(), Qt::OtherFocusReason);
control->forceActiveFocus(Qt::TabFocusReason);
// tab focus -> TabFocusReason
QTest::keyClick(&view, Qt::Key_Tab);
QVERIFY(editcombo->hasFocus());
QVERIFY(editcombo->hasActiveFocus());
QCOMPARE(qApp->focusObject(), editcombo->contentItem());
QCOMPARE(combobox->focusReason(), Qt::TabFocusReason);
QCOMPARE(editcombo->focusReason(), Qt::TabFocusReason);
editcombo->setFocusReason(Qt::NoFocusReason); // reset so that we can verify that focusOut sets it
QTest::keyClick(&view, Qt::Key_Tab);
QVERIFY(spinbox->hasFocus());
QVERIFY(spinbox->hasActiveFocus());
QCOMPARE(qApp->focusObject(), spinbox->contentItem());
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(editcombo->focusReason(), Qt::TabFocusReason);
QCOMPARE(spinbox->focusReason(), Qt::TabFocusReason);
spinbox->setFocusReason(Qt::NoFocusReason);
QTest::keyClick(&view, Qt::Key_Tab);
QVERIFY(customText->hasFocus());
QVERIFY(customText->hasActiveFocus());
QCOMPARE(qApp->focusObject(), customText);
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(spinbox->focusReason(), Qt::TabFocusReason);
QCOMPARE(customText->focusReason(), Qt::TabFocusReason);
customText->setFocusReason(Qt::NoFocusReason);
QTest::keyClick(&view, Qt::Key_Tab);
QCOMPARE(qApp->focusObject(), customItem);
QVERIFY(customItem->hasFocus());
QVERIFY(customItem->hasActiveFocus());
QCOMPARE(customText->focusReason(), Qt::TabFocusReason);
QCOMPARE(customItem->focusReason(), Qt::TabFocusReason);
customItem->setFocusReason(Qt::NoFocusReason);
QTest::keyClick(&view, Qt::Key_Tab);
QVERIFY(textfield->hasFocus());
QVERIFY(textfield->hasActiveFocus());
QCOMPARE(qApp->focusObject(), textfield);
QCOMPARE(customItem->focusReason(), Qt::TabFocusReason);
QTest::keyClick(&view, Qt::Key_Tab);
QVERIFY(control->hasFocus());
QVERIFY(control->hasActiveFocus());
QCOMPARE(control->property("focusReason").toInt(), int(Qt::TabFocusReason));
QCOMPARE(control->focusReason(), Qt::TabFocusReason);
QEXPECT_FAIL("TextArea", "", Continue);
QEXPECT_FAIL("TextField", "", Continue);
QCOMPARE(control->property("visualFocus"), QVariant(true));
// backtab -> BacktabFocusReason
QTest::keyClick(&view, Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(textfield->hasFocus());
QCOMPARE(control->focusReason(), Qt::BacktabFocusReason);
QTest::keyClick(&view, Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(customItem->hasFocus());
QCOMPARE(customItem->focusReason(), Qt::BacktabFocusReason);
QTest::keyClick(&view, Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(customText->hasFocus());
QCOMPARE(customText->focusReason(), Qt::BacktabFocusReason);
QTest::keyClick(&view, Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(spinbox->hasFocus());
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(spinbox->focusReason(), Qt::BacktabFocusReason);
QTest::keyClick(&view, Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(editcombo->hasFocus());
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(editcombo->focusReason(), Qt::BacktabFocusReason);
QTest::keyClick(&view, Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(combobox->hasFocus());
QCOMPARE(combobox->focusReason(), Qt::BacktabFocusReason);
// click focus -> MouseFocusReason
QTest::mouseClick(&view, Qt::LeftButton, {}, itemCenter(editcombo));
QTRY_VERIFY(editcombo->hasFocus());
QVERIFY(editcombo->contentItem()->hasFocus());
QVERIFY(editcombo->hasActiveFocus());
QVERIFY(editcombo->contentItem()->hasActiveFocus());
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(editcombo->focusReason(), Qt::MouseFocusReason);
editcombo->setFocusReason(Qt::NoFocusReason);
QTest::mouseClick(&view, Qt::LeftButton, {}, itemCenter(combobox)); // opens popup
QTest::mouseClick(&view, Qt::LeftButton, {}, itemCenter(combobox)); // closes popup
QVERIFY(combobox->hasFocus());
QVERIFY(combobox->hasActiveFocus());
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(editcombo->focusReason(), Qt::MouseFocusReason);
QCOMPARE(combobox->focusReason(), Qt::MouseFocusReason);
combobox->setFocusReason(Qt::NoFocusReason);
QTest::mouseClick(&view, Qt::LeftButton, {}, itemCenter(spinbox));
QVERIFY(spinbox->hasFocus());
QVERIFY(spinbox->hasActiveFocus());
QCOMPARE(combobox->focusReason(), Qt::MouseFocusReason);
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(spinbox->focusReason(), Qt::MouseFocusReason);
spinbox->setFocusReason(Qt::NoFocusReason);
QTest::mouseClick(&view, Qt::LeftButton, {}, itemCenter(customText));
QTRY_VERIFY2(customText->contentItem()->hasFocus(), qPrintable(qApp->focusObject()->objectName()));
QVERIFY(customText->contentItem()->hasActiveFocus());
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(spinbox->focusReason(), Qt::MouseFocusReason);
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(customText->focusReason(), Qt::MouseFocusReason);
customText->setFocusReason(Qt::NoFocusReason);
QTest::mouseClick(&view, Qt::LeftButton, {}, itemCenter(customItem));
QVERIFY(customItem->hasFocus());
QVERIFY(customItem->hasActiveFocus());
QEXPECT_FAIL("", "Events on content item not filtered - QTBUG-75862", Continue);
QCOMPARE(customText->focusReason(), Qt::MouseFocusReason);
QCOMPARE(customItem->focusReason(), Qt::MouseFocusReason);
customItem->setFocusReason(Qt::NoFocusReason);
QTest::mouseClick(&view, Qt::LeftButton, {}, itemCenter(textfield));
QCOMPARE(customItem->focusReason(), Qt::MouseFocusReason);
customItem->setFocusReason(Qt::NoFocusReason);
customText->setFocusReason(Qt::NoFocusReason);
// Wheel focus -> MouseFocusReason
QWheelEvent wheelEvent(QPointF(customItem->width() / 2, customItem->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
Qt::NoScrollPhase, false);
QGuiApplication::sendEvent(customItem, &wheelEvent);
QVERIFY(customItem->hasActiveFocus());
QCOMPARE(customItem->focusReason(), Qt::MouseFocusReason);
// Popup opens -> PopupFocusReason
QTest::mouseClick(&view, Qt::RightButton, {}, itemCenter(control));
QTRY_VERIFY(!customItem->hasActiveFocus());
QEXPECT_FAIL("", "Popup opening doesn't set the focus reason", Continue);
QCOMPARE(customItem->focusReason(), Qt::PopupFocusReason);
QTest::keyClick(&view, Qt::Key_Escape); // close the popup
}
void tst_focus::visualFocus()