| 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 test suite of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 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 as published by the Free Software |
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 21 | ** included in the packaging of this file. Please review the following |
| 22 | ** information to ensure the GNU General Public License requirements will |
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 24 | ** |
| 25 | ** $QT_END_LICENSE$ |
| 26 | ** |
| 27 | ****************************************************************************/ |
| 28 | #include <qtest.h> |
| 29 | #include <QSignalSpy> |
| 30 | #include <QtQml/qqmlengine.h> |
| 31 | #include <QtQml/qqmlcomponent.h> |
| 32 | #include <QtQuick/qquickview.h> |
| 33 | #include <QtQuick/private/qquickrectangle_p.h> |
| 34 | #include <private/qquicktextedit_p.h> |
| 35 | #include <QtQuick/private/qquicktext_p.h> |
| 36 | #include <QtQuick/private/qquickfocusscope_p.h> |
| 37 | #include "../../shared/util.h" |
| 38 | #include "../shared/visualtestutil.h" |
| 39 | |
| 40 | using namespace QQuickVisualTestUtil; |
| 41 | |
| 42 | class tst_qquickfocusscope : public QQmlDataTest |
| 43 | { |
| 44 | Q_OBJECT |
| 45 | public: |
| 46 | tst_qquickfocusscope() {} |
| 47 | |
| 48 | private slots: |
| 49 | void basic(); |
| 50 | void nested(); |
| 51 | void noFocus(); |
| 52 | void textEdit(); |
| 53 | void forceFocus(); |
| 54 | void noParentFocus(); |
| 55 | void signalEmission(); |
| 56 | void qtBug13380(); |
| 57 | void forceActiveFocus(); |
| 58 | void canvasFocus(); |
| 59 | }; |
| 60 | |
| 61 | void tst_qquickfocusscope::basic() |
| 62 | { |
| 63 | QQuickView *view = new QQuickView; |
| 64 | view->setSource(testFileUrl(fileName: "test.qml" )); |
| 65 | |
| 66 | QQuickFocusScope *item0 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item0" )); |
| 67 | QQuickRectangle *item1 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item1" )); |
| 68 | QQuickRectangle *item2 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item2" )); |
| 69 | QQuickRectangle *item3 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item3" )); |
| 70 | QVERIFY(item0 != nullptr); |
| 71 | QVERIFY(item1 != nullptr); |
| 72 | QVERIFY(item2 != nullptr); |
| 73 | QVERIFY(item3 != nullptr); |
| 74 | |
| 75 | view->show(); |
| 76 | view->requestActivate(); |
| 77 | |
| 78 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 79 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 80 | |
| 81 | QVERIFY(view->isTopLevel()); |
| 82 | QVERIFY(item0->hasActiveFocus()); |
| 83 | QVERIFY(item1->hasActiveFocus()); |
| 84 | QVERIFY(!item2->hasActiveFocus()); |
| 85 | QVERIFY(!item3->hasActiveFocus()); |
| 86 | |
| 87 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 88 | QTest::qWait(ms: 50); |
| 89 | QVERIFY(item0->hasActiveFocus()); |
| 90 | QVERIFY(!item1->hasActiveFocus()); |
| 91 | QVERIFY(item2->hasActiveFocus()); |
| 92 | QVERIFY(!item3->hasActiveFocus()); |
| 93 | |
| 94 | QTest::keyClick(window: view, key: Qt::Key_Down); |
| 95 | QTest::qWait(ms: 50); |
| 96 | QVERIFY(!item0->hasActiveFocus()); |
| 97 | QVERIFY(!item1->hasActiveFocus()); |
| 98 | QVERIFY(!item2->hasActiveFocus()); |
| 99 | QVERIFY(item3->hasActiveFocus()); |
| 100 | |
| 101 | delete view; |
| 102 | } |
| 103 | |
| 104 | void tst_qquickfocusscope::nested() |
| 105 | { |
| 106 | QQuickView *view = new QQuickView; |
| 107 | view->setSource(testFileUrl(fileName: "test2.qml" )); |
| 108 | |
| 109 | QQuickFocusScope *item1 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item1" )); |
| 110 | QQuickFocusScope *item2 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item2" )); |
| 111 | QQuickFocusScope *item3 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item3" )); |
| 112 | QQuickFocusScope *item4 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item4" )); |
| 113 | QQuickFocusScope *item5 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item5" )); |
| 114 | QVERIFY(item1 != nullptr); |
| 115 | QVERIFY(item2 != nullptr); |
| 116 | QVERIFY(item3 != nullptr); |
| 117 | QVERIFY(item4 != nullptr); |
| 118 | QVERIFY(item5 != nullptr); |
| 119 | |
| 120 | view->show(); |
| 121 | view->requestActivate(); |
| 122 | |
| 123 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 124 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 125 | |
| 126 | QVERIFY(item1->hasActiveFocus()); |
| 127 | QVERIFY(item2->hasActiveFocus()); |
| 128 | QVERIFY(item3->hasActiveFocus()); |
| 129 | QVERIFY(item4->hasActiveFocus()); |
| 130 | QVERIFY(item5->hasActiveFocus()); |
| 131 | delete view; |
| 132 | } |
| 133 | |
| 134 | void tst_qquickfocusscope::noFocus() |
| 135 | { |
| 136 | QQuickView *view = new QQuickView; |
| 137 | view->setSource(testFileUrl(fileName: "test4.qml" )); |
| 138 | |
| 139 | QQuickRectangle *item0 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item0" )); |
| 140 | QQuickRectangle *item1 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item1" )); |
| 141 | QQuickRectangle *item2 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item2" )); |
| 142 | QQuickRectangle *item3 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item3" )); |
| 143 | QVERIFY(item0 != nullptr); |
| 144 | QVERIFY(item1 != nullptr); |
| 145 | QVERIFY(item2 != nullptr); |
| 146 | QVERIFY(item3 != nullptr); |
| 147 | |
| 148 | view->show(); |
| 149 | view->requestActivate(); |
| 150 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 151 | QCOMPARE(view, qGuiApp->focusWindow()); |
| 152 | |
| 153 | QVERIFY(!item0->hasActiveFocus()); |
| 154 | QVERIFY(!item1->hasActiveFocus()); |
| 155 | QVERIFY(!item2->hasActiveFocus()); |
| 156 | QVERIFY(!item3->hasActiveFocus()); |
| 157 | |
| 158 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 159 | QVERIFY(!item0->hasActiveFocus()); |
| 160 | QVERIFY(!item1->hasActiveFocus()); |
| 161 | QVERIFY(!item2->hasActiveFocus()); |
| 162 | QVERIFY(!item3->hasActiveFocus()); |
| 163 | |
| 164 | QTest::keyClick(window: view, key: Qt::Key_Down); |
| 165 | QVERIFY(!item0->hasActiveFocus()); |
| 166 | QVERIFY(!item1->hasActiveFocus()); |
| 167 | QVERIFY(!item2->hasActiveFocus()); |
| 168 | QVERIFY(!item3->hasActiveFocus()); |
| 169 | |
| 170 | delete view; |
| 171 | } |
| 172 | |
| 173 | void tst_qquickfocusscope::textEdit() |
| 174 | { |
| 175 | QQuickView *view = new QQuickView; |
| 176 | view->setSource(testFileUrl(fileName: "test5.qml" )); |
| 177 | |
| 178 | QQuickFocusScope *item0 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item0" )); |
| 179 | QQuickTextEdit *item1 = findItem<QQuickTextEdit>(parent: view->rootObject(), objectName: QLatin1String("item1" )); |
| 180 | QQuickRectangle *item2 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item2" )); |
| 181 | QQuickTextEdit *item3 = findItem<QQuickTextEdit>(parent: view->rootObject(), objectName: QLatin1String("item3" )); |
| 182 | QVERIFY(item0 != nullptr); |
| 183 | QVERIFY(item1 != nullptr); |
| 184 | QVERIFY(item2 != nullptr); |
| 185 | QVERIFY(item3 != nullptr); |
| 186 | |
| 187 | view->show(); |
| 188 | view->requestActivate(); |
| 189 | |
| 190 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 191 | |
| 192 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 193 | QVERIFY(item0->hasActiveFocus()); |
| 194 | QVERIFY(item1->hasActiveFocus()); |
| 195 | QVERIFY(!item2->hasActiveFocus()); |
| 196 | QVERIFY(!item3->hasActiveFocus()); |
| 197 | |
| 198 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 199 | QVERIFY(item0->hasActiveFocus()); |
| 200 | QVERIFY(item1->hasActiveFocus()); |
| 201 | QVERIFY(!item2->hasActiveFocus()); |
| 202 | QVERIFY(!item3->hasActiveFocus()); |
| 203 | |
| 204 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 205 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 206 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 207 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 208 | QTest::keyClick(window: view, key: Qt::Key_Right); |
| 209 | QVERIFY(item0->hasActiveFocus()); |
| 210 | QVERIFY(!item1->hasActiveFocus()); |
| 211 | QVERIFY(item2->hasActiveFocus()); |
| 212 | QVERIFY(!item3->hasActiveFocus()); |
| 213 | |
| 214 | QTest::keyClick(window: view, key: Qt::Key_Down); |
| 215 | QVERIFY(!item0->hasActiveFocus()); |
| 216 | QVERIFY(!item1->hasActiveFocus()); |
| 217 | QVERIFY(!item2->hasActiveFocus()); |
| 218 | QVERIFY(item3->hasActiveFocus()); |
| 219 | |
| 220 | delete view; |
| 221 | } |
| 222 | |
| 223 | void tst_qquickfocusscope::forceFocus() |
| 224 | { |
| 225 | QQuickView *view = new QQuickView; |
| 226 | view->setSource(testFileUrl(fileName: "forcefocus.qml" )); |
| 227 | |
| 228 | QQuickFocusScope *item0 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item0" )); |
| 229 | QQuickRectangle *item1 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item1" )); |
| 230 | QQuickRectangle *item2 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item2" )); |
| 231 | QQuickFocusScope *item3 = findItem<QQuickFocusScope>(parent: view->rootObject(), objectName: QLatin1String("item3" )); |
| 232 | QQuickRectangle *item4 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item4" )); |
| 233 | QQuickRectangle *item5 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item5" )); |
| 234 | QVERIFY(item0 != nullptr); |
| 235 | QVERIFY(item1 != nullptr); |
| 236 | QVERIFY(item2 != nullptr); |
| 237 | QVERIFY(item3 != nullptr); |
| 238 | QVERIFY(item4 != nullptr); |
| 239 | QVERIFY(item5 != nullptr); |
| 240 | |
| 241 | view->show(); |
| 242 | view->requestActivate(); |
| 243 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 244 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 245 | |
| 246 | QVERIFY(item0->hasActiveFocus()); |
| 247 | QVERIFY(item1->hasActiveFocus()); |
| 248 | QVERIFY(!item2->hasActiveFocus()); |
| 249 | QVERIFY(!item3->hasActiveFocus()); |
| 250 | QVERIFY(!item4->hasActiveFocus()); |
| 251 | QVERIFY(!item5->hasActiveFocus()); |
| 252 | |
| 253 | QTest::keyClick(window: view, key: Qt::Key_4); |
| 254 | QVERIFY(item0->hasActiveFocus()); |
| 255 | QVERIFY(item1->hasActiveFocus()); |
| 256 | QVERIFY(!item2->hasActiveFocus()); |
| 257 | QVERIFY(!item3->hasActiveFocus()); |
| 258 | QVERIFY(!item4->hasActiveFocus()); |
| 259 | QVERIFY(!item5->hasActiveFocus()); |
| 260 | |
| 261 | QTest::keyClick(window: view, key: Qt::Key_5); |
| 262 | QVERIFY(!item0->hasActiveFocus()); |
| 263 | QVERIFY(!item1->hasActiveFocus()); |
| 264 | QVERIFY(!item2->hasActiveFocus()); |
| 265 | QVERIFY(item3->hasActiveFocus()); |
| 266 | QVERIFY(!item4->hasActiveFocus()); |
| 267 | QVERIFY(item5->hasActiveFocus()); |
| 268 | |
| 269 | delete view; |
| 270 | } |
| 271 | |
| 272 | void tst_qquickfocusscope::noParentFocus() |
| 273 | { |
| 274 | QQuickView *view = new QQuickView; |
| 275 | view->setSource(testFileUrl(fileName: "chain.qml" )); |
| 276 | QVERIFY(view->rootObject()); |
| 277 | |
| 278 | view->show(); |
| 279 | view->requestActivate(); |
| 280 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 281 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 282 | |
| 283 | QVERIFY(!view->rootObject()->property("focus1" ).toBool()); |
| 284 | QVERIFY(!view->rootObject()->property("focus2" ).toBool()); |
| 285 | QVERIFY(view->rootObject()->property("focus3" ).toBool()); |
| 286 | QVERIFY(view->rootObject()->property("focus4" ).toBool()); |
| 287 | QVERIFY(view->rootObject()->property("focus5" ).toBool()); |
| 288 | |
| 289 | delete view; |
| 290 | } |
| 291 | |
| 292 | void tst_qquickfocusscope::signalEmission() |
| 293 | { |
| 294 | QQuickView *view = new QQuickView; |
| 295 | view->setSource(testFileUrl(fileName: "signalEmission.qml" )); |
| 296 | |
| 297 | QQuickRectangle *item1 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item1" )); |
| 298 | QQuickRectangle *item2 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item2" )); |
| 299 | QQuickRectangle *item3 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item3" )); |
| 300 | QQuickRectangle *item4 = findItem<QQuickRectangle>(parent: view->rootObject(), objectName: QLatin1String("item4" )); |
| 301 | QVERIFY(item1 != nullptr); |
| 302 | QVERIFY(item2 != nullptr); |
| 303 | QVERIFY(item3 != nullptr); |
| 304 | QVERIFY(item4 != nullptr); |
| 305 | |
| 306 | view->show(); |
| 307 | view->requestActivate(); |
| 308 | |
| 309 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 310 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 311 | |
| 312 | QVariant blue(QColor("blue" )); |
| 313 | QVariant red(QColor("red" )); |
| 314 | |
| 315 | item1->setFocus(true); |
| 316 | QCOMPARE(item1->property("color" ), red); |
| 317 | QCOMPARE(item2->property("color" ), blue); |
| 318 | QCOMPARE(item3->property("color" ), blue); |
| 319 | QCOMPARE(item4->property("color" ), blue); |
| 320 | |
| 321 | item2->setFocus(true); |
| 322 | QCOMPARE(item1->property("color" ), blue); |
| 323 | QCOMPARE(item2->property("color" ), red); |
| 324 | QCOMPARE(item3->property("color" ), blue); |
| 325 | QCOMPARE(item4->property("color" ), blue); |
| 326 | |
| 327 | item3->setFocus(true); |
| 328 | QCOMPARE(item1->property("color" ), blue); |
| 329 | QCOMPARE(item2->property("color" ), red); |
| 330 | QCOMPARE(item3->property("color" ), red); |
| 331 | QCOMPARE(item4->property("color" ), blue); |
| 332 | |
| 333 | item4->setFocus(true); |
| 334 | QCOMPARE(item1->property("color" ), blue); |
| 335 | QCOMPARE(item2->property("color" ), red); |
| 336 | QCOMPARE(item3->property("color" ), blue); |
| 337 | QCOMPARE(item4->property("color" ), red); |
| 338 | |
| 339 | item4->setFocus(false); |
| 340 | QCOMPARE(item1->property("color" ), blue); |
| 341 | QCOMPARE(item2->property("color" ), red); |
| 342 | QCOMPARE(item3->property("color" ), blue); |
| 343 | QCOMPARE(item4->property("color" ), blue); |
| 344 | |
| 345 | delete view; |
| 346 | } |
| 347 | |
| 348 | void tst_qquickfocusscope::qtBug13380() |
| 349 | { |
| 350 | QQuickView *view = new QQuickView; |
| 351 | view->setSource(testFileUrl(fileName: "qtBug13380.qml" )); |
| 352 | |
| 353 | view->show(); |
| 354 | QVERIFY(view->rootObject()); |
| 355 | view->requestActivate(); |
| 356 | qApp->processEvents(); |
| 357 | |
| 358 | QVERIFY(QTest::qWaitForWindowExposed(view)); |
| 359 | |
| 360 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 361 | QVERIFY(view->rootObject()->property("noFocus" ).toBool()); |
| 362 | |
| 363 | view->rootObject()->setProperty(name: "showRect" , value: true); |
| 364 | QVERIFY(view->rootObject()->property("noFocus" ).toBool()); |
| 365 | |
| 366 | delete view; |
| 367 | } |
| 368 | |
| 369 | void tst_qquickfocusscope::forceActiveFocus() |
| 370 | { |
| 371 | QQuickView *view = new QQuickView; |
| 372 | view->setSource(testFileUrl(fileName: "forceActiveFocus.qml" )); |
| 373 | |
| 374 | view->show(); |
| 375 | view->requestActivate(); |
| 376 | QVERIFY(QTest::qWaitForWindowExposed(view)); |
| 377 | QTRY_COMPARE(view, qGuiApp->focusWindow()); |
| 378 | |
| 379 | QQuickItem *rootObject = view->rootObject(); |
| 380 | QVERIFY(rootObject); |
| 381 | |
| 382 | QQuickItem *scope = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("scope" )); |
| 383 | QQuickItem *itemA1 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("item-a1" )); |
| 384 | QQuickItem *scopeA = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("scope-a" )); |
| 385 | QQuickItem *itemA2 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("item-a2" )); |
| 386 | QQuickItem *itemB1 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("item-b1" )); |
| 387 | QQuickItem *scopeB = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("scope-b" )); |
| 388 | QQuickItem *itemB2 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("item-b2" )); |
| 389 | |
| 390 | QVERIFY(scope); |
| 391 | QVERIFY(itemA1); |
| 392 | QVERIFY(scopeA); |
| 393 | QVERIFY(itemA2); |
| 394 | QVERIFY(itemB1); |
| 395 | QVERIFY(scopeB); |
| 396 | QVERIFY(itemB2); |
| 397 | |
| 398 | QSignalSpy rootSpy(rootObject, SIGNAL(activeFocusChanged(bool))); |
| 399 | QSignalSpy scopeSpy(scope, SIGNAL(activeFocusChanged(bool))); |
| 400 | QSignalSpy scopeASpy(scopeA, SIGNAL(activeFocusChanged(bool))); |
| 401 | QSignalSpy scopeBSpy(scopeB, SIGNAL(activeFocusChanged(bool))); |
| 402 | |
| 403 | // First, walk the focus from item-a1 down to item-a2 and back again |
| 404 | itemA1->forceActiveFocus(); |
| 405 | QVERIFY(itemA1->hasActiveFocus()); |
| 406 | QVERIFY(!rootObject->hasActiveFocus()); |
| 407 | QCOMPARE(rootSpy.count(), 0); |
| 408 | QCOMPARE(scopeSpy.count(), 1); |
| 409 | |
| 410 | scopeA->forceActiveFocus(); |
| 411 | QVERIFY(!itemA1->hasActiveFocus()); |
| 412 | QVERIFY(scopeA->hasActiveFocus()); |
| 413 | QCOMPARE(scopeASpy.count(), 1); |
| 414 | QCOMPARE(rootSpy.count(), 0); |
| 415 | QCOMPARE(scopeSpy.count(), 1); |
| 416 | |
| 417 | itemA2->forceActiveFocus(); |
| 418 | QVERIFY(!itemA1->hasActiveFocus()); |
| 419 | QVERIFY(itemA2->hasActiveFocus()); |
| 420 | QVERIFY(scopeA->hasActiveFocus()); |
| 421 | QCOMPARE(scopeASpy.count(), 1); |
| 422 | QCOMPARE(rootSpy.count(), 0); |
| 423 | QCOMPARE(scopeSpy.count(), 1); |
| 424 | |
| 425 | scopeA->forceActiveFocus(); |
| 426 | QVERIFY(!itemA1->hasActiveFocus()); |
| 427 | QVERIFY(itemA2->hasActiveFocus()); |
| 428 | QVERIFY(scopeA->hasActiveFocus()); |
| 429 | QCOMPARE(scopeASpy.count(), 1); |
| 430 | QCOMPARE(rootSpy.count(), 0); |
| 431 | QCOMPARE(scopeSpy.count(), 1); |
| 432 | |
| 433 | itemA1->forceActiveFocus(); |
| 434 | QVERIFY(itemA1->hasActiveFocus()); |
| 435 | QVERIFY(!scopeA->hasActiveFocus()); |
| 436 | QVERIFY(!itemA2->hasActiveFocus()); |
| 437 | QCOMPARE(scopeASpy.count(), 2); |
| 438 | QCOMPARE(rootSpy.count(), 0); |
| 439 | QCOMPARE(scopeSpy.count(), 1); |
| 440 | |
| 441 | // Then jump back and forth between branch 'a' and 'b' |
| 442 | itemB1->forceActiveFocus(); |
| 443 | QVERIFY(itemB1->hasActiveFocus()); |
| 444 | QCOMPARE(rootSpy.count(), 0); |
| 445 | QCOMPARE(scopeSpy.count(), 1); |
| 446 | |
| 447 | scopeA->forceActiveFocus(); |
| 448 | QVERIFY(!itemA1->hasActiveFocus()); |
| 449 | QVERIFY(!itemB1->hasActiveFocus()); |
| 450 | QVERIFY(scopeA->hasActiveFocus()); |
| 451 | QCOMPARE(scopeASpy.count(), 3); |
| 452 | QCOMPARE(rootSpy.count(), 0); |
| 453 | QCOMPARE(scopeSpy.count(), 1); |
| 454 | |
| 455 | scopeB->forceActiveFocus(); |
| 456 | QVERIFY(!scopeA->hasActiveFocus()); |
| 457 | QVERIFY(!itemB1->hasActiveFocus()); |
| 458 | QVERIFY(scopeB->hasActiveFocus()); |
| 459 | QCOMPARE(scopeASpy.count(), 4); |
| 460 | QCOMPARE(scopeBSpy.count(), 1); |
| 461 | QCOMPARE(rootSpy.count(), 0); |
| 462 | QCOMPARE(scopeSpy.count(), 1); |
| 463 | |
| 464 | itemA2->forceActiveFocus(); |
| 465 | QVERIFY(!scopeB->hasActiveFocus()); |
| 466 | QVERIFY(itemA2->hasActiveFocus()); |
| 467 | QCOMPARE(scopeASpy.count(), 5); |
| 468 | QCOMPARE(scopeBSpy.count(), 2); |
| 469 | QCOMPARE(rootSpy.count(), 0); |
| 470 | QCOMPARE(scopeSpy.count(), 1); |
| 471 | |
| 472 | itemB2->forceActiveFocus(); |
| 473 | QVERIFY(!itemA2->hasActiveFocus()); |
| 474 | QVERIFY(itemB2->hasActiveFocus()); |
| 475 | QCOMPARE(scopeASpy.count(), 6); |
| 476 | QCOMPARE(scopeBSpy.count(), 3); |
| 477 | QCOMPARE(rootSpy.count(), 0); |
| 478 | QCOMPARE(scopeSpy.count(), 1); |
| 479 | |
| 480 | delete view; |
| 481 | } |
| 482 | |
| 483 | void tst_qquickfocusscope::canvasFocus() |
| 484 | { |
| 485 | QQuickView *view = new QQuickView; |
| 486 | view->setSource(testFileUrl(fileName: "canvasFocus.qml" )); |
| 487 | |
| 488 | QQuickView alternateView; |
| 489 | |
| 490 | QQuickItem *rootObject = view->rootObject(); |
| 491 | QVERIFY(rootObject); |
| 492 | |
| 493 | QQuickItem *rootItem = view->contentItem(); |
| 494 | QQuickItem *scope1 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("scope1" )); |
| 495 | QQuickItem *item1 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("item1" )); |
| 496 | QQuickItem *scope2 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("scope2" )); |
| 497 | QQuickItem *item2 = findItem<QQuickItem>(parent: rootObject, objectName: QLatin1String("item2" )); |
| 498 | |
| 499 | QVERIFY(scope1); |
| 500 | QVERIFY(item1); |
| 501 | QVERIFY(scope2); |
| 502 | QVERIFY(item2); |
| 503 | |
| 504 | QSignalSpy rootFocusSpy(rootItem, SIGNAL(focusChanged(bool))); |
| 505 | QSignalSpy scope1FocusSpy(scope1, SIGNAL(focusChanged(bool))); |
| 506 | QSignalSpy item1FocusSpy(item1, SIGNAL(focusChanged(bool))); |
| 507 | QSignalSpy scope2FocusSpy(scope2, SIGNAL(focusChanged(bool))); |
| 508 | QSignalSpy item2FocusSpy(item2, SIGNAL(focusChanged(bool))); |
| 509 | QSignalSpy rootActiveFocusSpy(rootItem, SIGNAL(activeFocusChanged(bool))); |
| 510 | QSignalSpy scope1ActiveFocusSpy(scope1, SIGNAL(activeFocusChanged(bool))); |
| 511 | QSignalSpy item1ActiveFocusSpy(item1, SIGNAL(activeFocusChanged(bool))); |
| 512 | QSignalSpy scope2ActiveFocusSpy(scope2, SIGNAL(activeFocusChanged(bool))); |
| 513 | QSignalSpy item2ActiveFocusSpy(item2, SIGNAL(activeFocusChanged(bool))); |
| 514 | |
| 515 | QCOMPARE(rootItem->hasFocus(), false); |
| 516 | QCOMPARE(rootItem->hasActiveFocus(), false); |
| 517 | QCOMPARE(scope1->hasFocus(), true); |
| 518 | QCOMPARE(scope1->hasActiveFocus(), false); |
| 519 | QCOMPARE(item1->hasFocus(), true); |
| 520 | QCOMPARE(item1->hasActiveFocus(), false); |
| 521 | QCOMPARE(scope2->hasFocus(), false); |
| 522 | QCOMPARE(scope2->hasActiveFocus(), false); |
| 523 | QCOMPARE(item2->hasFocus(), false); |
| 524 | QCOMPARE(item2->hasActiveFocus(), false); |
| 525 | |
| 526 | view->show(); |
| 527 | view->requestActivate(); |
| 528 | |
| 529 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 530 | QCOMPARE(view, qGuiApp->focusWindow()); |
| 531 | |
| 532 | // Now the window has focus, active focus given to item1 |
| 533 | QCOMPARE(rootItem->hasFocus(), true); |
| 534 | QCOMPARE(rootItem->hasActiveFocus(), true); |
| 535 | QCOMPARE(scope1->hasFocus(), true); |
| 536 | QCOMPARE(scope1->hasActiveFocus(), true); |
| 537 | QCOMPARE(item1->hasFocus(), true); |
| 538 | QCOMPARE(item1->hasActiveFocus(), true); |
| 539 | QCOMPARE(scope2->hasFocus(), false); |
| 540 | QCOMPARE(scope2->hasActiveFocus(), false); |
| 541 | QCOMPARE(item2->hasFocus(), false); |
| 542 | QCOMPARE(item2->hasActiveFocus(), false); |
| 543 | |
| 544 | QCOMPARE(rootFocusSpy.count(), 1); |
| 545 | QCOMPARE(rootActiveFocusSpy.count(), 1); |
| 546 | QCOMPARE(scope1FocusSpy.count(), 0); |
| 547 | QCOMPARE(scope1ActiveFocusSpy.count(), 1); |
| 548 | QCOMPARE(item1FocusSpy.count(), 0); |
| 549 | QCOMPARE(item1ActiveFocusSpy.count(), 1); |
| 550 | |
| 551 | |
| 552 | // view->hide(); // seemingly doesn't remove focus, so have an another view steal it. |
| 553 | alternateView.show(); |
| 554 | alternateView.requestActivate(); |
| 555 | QVERIFY(QTest::qWaitForWindowActive(&alternateView)); |
| 556 | QCOMPARE(QGuiApplication::focusWindow(), &alternateView); |
| 557 | |
| 558 | QCOMPARE(rootItem->hasFocus(), false); |
| 559 | QCOMPARE(rootItem->hasActiveFocus(), false); |
| 560 | QCOMPARE(scope1->hasFocus(), true); |
| 561 | QCOMPARE(scope1->hasActiveFocus(), false); |
| 562 | QCOMPARE(item1->hasFocus(), true); |
| 563 | QCOMPARE(item1->hasActiveFocus(), false); |
| 564 | |
| 565 | QCOMPARE(rootFocusSpy.count(), 2); |
| 566 | QCOMPARE(rootActiveFocusSpy.count(), 2); |
| 567 | QCOMPARE(scope1FocusSpy.count(), 0); |
| 568 | QCOMPARE(scope1ActiveFocusSpy.count(), 2); |
| 569 | QCOMPARE(item1FocusSpy.count(), 0); |
| 570 | QCOMPARE(item1ActiveFocusSpy.count(), 2); |
| 571 | |
| 572 | |
| 573 | // window does not have focus, so item2 will not get active focus |
| 574 | item2->forceActiveFocus(); |
| 575 | |
| 576 | QCOMPARE(rootItem->hasFocus(), false); |
| 577 | QCOMPARE(rootItem->hasActiveFocus(), false); |
| 578 | QCOMPARE(scope1->hasFocus(), false); |
| 579 | QCOMPARE(scope1->hasActiveFocus(), false); |
| 580 | QCOMPARE(item1->hasFocus(), true); |
| 581 | QCOMPARE(item1->hasActiveFocus(), false); |
| 582 | QCOMPARE(scope2->hasFocus(), true); |
| 583 | QCOMPARE(scope2->hasActiveFocus(), false); |
| 584 | QCOMPARE(item2->hasFocus(), true); |
| 585 | QCOMPARE(item2->hasActiveFocus(), false); |
| 586 | |
| 587 | QCOMPARE(rootFocusSpy.count(), 2); |
| 588 | QCOMPARE(rootActiveFocusSpy.count(), 2); |
| 589 | QCOMPARE(scope1FocusSpy.count(), 1); |
| 590 | QCOMPARE(scope1ActiveFocusSpy.count(), 2); |
| 591 | QCOMPARE(item1FocusSpy.count(), 0); |
| 592 | QCOMPARE(item1ActiveFocusSpy.count(), 2); |
| 593 | QCOMPARE(scope2FocusSpy.count(), 1); |
| 594 | QCOMPARE(scope2ActiveFocusSpy.count(), 0); |
| 595 | QCOMPARE(item2FocusSpy.count(), 1); |
| 596 | QCOMPARE(item2ActiveFocusSpy.count(), 0); |
| 597 | |
| 598 | // give the window focus, and item2 will get active focus |
| 599 | view->show(); |
| 600 | view->requestActivate(); |
| 601 | QVERIFY(QTest::qWaitForWindowActive(view)); |
| 602 | QCOMPARE(QGuiApplication::focusWindow(), view); |
| 603 | |
| 604 | QCOMPARE(rootItem->hasFocus(), true); |
| 605 | QCOMPARE(rootItem->hasActiveFocus(), true); |
| 606 | QCOMPARE(scope2->hasFocus(), true); |
| 607 | QCOMPARE(scope2->hasActiveFocus(), true); |
| 608 | QCOMPARE(item2->hasFocus(), true); |
| 609 | QCOMPARE(item2->hasActiveFocus(), true); |
| 610 | QCOMPARE(rootFocusSpy.count(), 3); |
| 611 | QCOMPARE(rootActiveFocusSpy.count(), 3); |
| 612 | QCOMPARE(scope2FocusSpy.count(), 1); |
| 613 | QCOMPARE(scope2ActiveFocusSpy.count(), 1); |
| 614 | QCOMPARE(item2FocusSpy.count(), 1); |
| 615 | QCOMPARE(item2ActiveFocusSpy.count(), 1); |
| 616 | |
| 617 | delete view; |
| 618 | } |
| 619 | |
| 620 | QTEST_MAIN(tst_qquickfocusscope) |
| 621 | |
| 622 | #include "tst_qquickfocusscope.moc" |
| 623 | |