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 | |