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 | |
29 | |
30 | #include <QtTest/QtTest> |
31 | #include <qapplication.h> |
32 | #include <qlineedit.h> |
33 | #include <qmenu.h> |
34 | #include <qlabel.h> |
35 | #include <qdialog.h> |
36 | #include <qevent.h> |
37 | #include <qlineedit.h> |
38 | #include <QBoxLayout> |
39 | #include <QSysInfo> |
40 | |
41 | #include <qpa/qplatformintegration.h> |
42 | #include <private/qguiapplication_p.h> |
43 | |
44 | QT_FORWARD_DECLARE_CLASS(QWidget) |
45 | |
46 | class FocusLineEdit : public QLineEdit |
47 | { |
48 | public: |
49 | FocusLineEdit( QWidget* parent = 0, const char* name = 0 ) : QLineEdit(name, parent) {} |
50 | int focusInEventReason; |
51 | int focusOutEventReason; |
52 | bool focusInEventRecieved; |
53 | bool focusInEventGotFocus; |
54 | bool focusOutEventRecieved; |
55 | bool focusOutEventLostFocus; |
56 | protected: |
57 | virtual void keyPressEvent( QKeyEvent *e ) |
58 | { |
59 | // qDebug( QString("keyPressEvent: %1").arg(e->key()) ); |
60 | QLineEdit::keyPressEvent( e ); |
61 | } |
62 | void focusInEvent( QFocusEvent* e ) |
63 | { |
64 | QLineEdit::focusInEvent( e ); |
65 | focusInEventReason = e->reason(); |
66 | focusInEventGotFocus = e->gotFocus(); |
67 | focusInEventRecieved = true; |
68 | } |
69 | void focusOutEvent( QFocusEvent* e ) |
70 | { |
71 | QLineEdit::focusOutEvent( e ); |
72 | focusOutEventReason = e->reason(); |
73 | focusOutEventLostFocus = !e->gotFocus(); |
74 | focusOutEventRecieved = true; |
75 | } |
76 | }; |
77 | |
78 | class tst_QFocusEvent : public QObject |
79 | { |
80 | Q_OBJECT |
81 | |
82 | public: |
83 | void initWidget(); |
84 | |
85 | private slots: |
86 | void initTestCase(); |
87 | void cleanupTestCase(); |
88 | void cleanup(); |
89 | void checkReason_Tab(); |
90 | void checkReason_ShiftTab(); |
91 | void checkReason_BackTab(); |
92 | void checkReason_Popup(); |
93 | void checkReason_focusWidget(); |
94 | void checkReason_Shortcut(); |
95 | void checkReason_ActiveWindow(); |
96 | |
97 | private: |
98 | QWidget* testFocusWidget = nullptr; |
99 | FocusLineEdit* childFocusWidgetOne; |
100 | FocusLineEdit* childFocusWidgetTwo; |
101 | }; |
102 | |
103 | void tst_QFocusEvent::initTestCase() |
104 | { |
105 | if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) |
106 | QSKIP("QWindow::requestActivate() is not supported on this platform." ); |
107 | |
108 | testFocusWidget = new QWidget( 0 ); |
109 | childFocusWidgetOne = new FocusLineEdit( testFocusWidget ); |
110 | childFocusWidgetOne->setGeometry( ax: 10, ay: 10, aw: 180, ah: 20 ); |
111 | childFocusWidgetTwo = new FocusLineEdit( testFocusWidget ); |
112 | childFocusWidgetTwo->setGeometry( ax: 10, ay: 50, aw: 180, ah: 20 ); |
113 | |
114 | //qApp->setMainWidget( testFocusWidget ); Qt4 |
115 | testFocusWidget->resize( w: 200,h: 100 ); |
116 | testFocusWidget->show(); |
117 | QVERIFY(QTest::qWaitForWindowExposed(testFocusWidget)); |
118 | // Applications don't get focus when launched from the command line on Mac. |
119 | #ifdef Q_OS_MAC |
120 | testFocusWidget->raise(); |
121 | #endif |
122 | } |
123 | |
124 | void tst_QFocusEvent::cleanupTestCase() |
125 | { |
126 | delete testFocusWidget; |
127 | } |
128 | |
129 | void tst_QFocusEvent::cleanup() |
130 | { |
131 | childFocusWidgetTwo->setGeometry( ax: 10, ay: 50, aw: 180, ah: 20 ); |
132 | } |
133 | |
134 | void tst_QFocusEvent::initWidget() |
135 | { |
136 | // On X11 we have to ensure the event was processed before doing any checking, on Windows |
137 | // this is processed straight away. |
138 | QApplication::setActiveWindow(testFocusWidget); |
139 | childFocusWidgetOne->setFocus(); // The first lineedit should have focus |
140 | QVERIFY(QTest::qWaitForWindowActive(testFocusWidget)); |
141 | QTRY_VERIFY(childFocusWidgetOne->hasFocus()); |
142 | |
143 | childFocusWidgetOne->focusInEventRecieved = false; |
144 | childFocusWidgetOne->focusInEventGotFocus = false; |
145 | childFocusWidgetOne->focusInEventReason = -1; |
146 | childFocusWidgetOne->focusOutEventRecieved = false; |
147 | childFocusWidgetOne->focusOutEventLostFocus = false; |
148 | childFocusWidgetOne->focusOutEventReason = -1; |
149 | childFocusWidgetTwo->focusInEventRecieved = false; |
150 | childFocusWidgetTwo->focusInEventGotFocus = false; |
151 | childFocusWidgetTwo->focusInEventReason = -1; |
152 | childFocusWidgetTwo->focusOutEventRecieved = false; |
153 | childFocusWidgetTwo->focusOutEventLostFocus = false; |
154 | childFocusWidgetTwo->focusOutEventReason = -1; |
155 | } |
156 | |
157 | void tst_QFocusEvent::checkReason_Tab() |
158 | { |
159 | initWidget(); |
160 | |
161 | // Now test the tab key |
162 | QTest::keyClick( widget: childFocusWidgetOne, key: Qt::Key_Tab ); |
163 | |
164 | QVERIFY(childFocusWidgetOne->focusOutEventRecieved); |
165 | QVERIFY(childFocusWidgetTwo->focusInEventRecieved); |
166 | QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); |
167 | QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); |
168 | |
169 | QVERIFY( childFocusWidgetTwo->hasFocus() ); |
170 | QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int) Qt::TabFocusReason ); |
171 | QCOMPARE( childFocusWidgetTwo->focusInEventReason, (int) Qt::TabFocusReason ); |
172 | } |
173 | |
174 | void tst_QFocusEvent::checkReason_ShiftTab() |
175 | { |
176 | initWidget(); |
177 | |
178 | // Now test the shift + tab key |
179 | QTest::keyClick( widget: childFocusWidgetOne, key: Qt::Key_Tab, modifier: Qt::ShiftModifier ); |
180 | |
181 | QVERIFY(childFocusWidgetOne->focusOutEventRecieved); |
182 | QVERIFY(childFocusWidgetTwo->focusInEventRecieved); |
183 | QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); |
184 | QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); |
185 | |
186 | QVERIFY( childFocusWidgetTwo->hasFocus() ); |
187 | QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int)Qt::BacktabFocusReason ); |
188 | QCOMPARE( childFocusWidgetTwo->focusInEventReason, (int)Qt::BacktabFocusReason ); |
189 | |
190 | } |
191 | |
192 | /*! |
193 | In this test we verify that the Qt::KeyBacktab key is handled in a qfocusevent |
194 | */ |
195 | void tst_QFocusEvent::checkReason_BackTab() |
196 | { |
197 | #ifdef Q_OS_WIN32 // key is not supported on Windows |
198 | QSKIP( "Backtab is not supported on Windows" ); |
199 | #else |
200 | initWidget(); |
201 | QVERIFY( childFocusWidgetOne->hasFocus() ); |
202 | |
203 | // Now test the backtab key |
204 | QTest::keyClick( widget: childFocusWidgetOne, key: Qt::Key_Backtab ); |
205 | |
206 | QTRY_VERIFY(childFocusWidgetOne->focusOutEventRecieved); |
207 | QVERIFY(childFocusWidgetTwo->focusInEventRecieved); |
208 | QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); |
209 | QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); |
210 | |
211 | QVERIFY( childFocusWidgetTwo->hasFocus() ); |
212 | QCOMPARE( childFocusWidgetOne->focusOutEventReason, int(Qt::BacktabFocusReason) ); |
213 | QCOMPARE( childFocusWidgetTwo->focusInEventReason, int(Qt::BacktabFocusReason) ); |
214 | #endif |
215 | } |
216 | |
217 | void tst_QFocusEvent::() |
218 | { |
219 | initWidget(); |
220 | |
221 | // Now test the popup reason |
222 | QMenu* = new QMenu( testFocusWidget ); |
223 | popupMenu->addMenu( title: "Test" ); |
224 | popupMenu->popup( pos: QPoint(0,0) ); |
225 | |
226 | QTRY_VERIFY(childFocusWidgetOne->focusOutEventLostFocus); |
227 | |
228 | QTRY_VERIFY( childFocusWidgetOne->hasFocus() ); |
229 | QVERIFY( !childFocusWidgetOne->focusInEventRecieved ); |
230 | QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); |
231 | QVERIFY( !childFocusWidgetTwo->focusInEventRecieved ); |
232 | QVERIFY( !childFocusWidgetTwo->focusOutEventRecieved ); |
233 | QCOMPARE( childFocusWidgetOne->focusOutEventReason, int(Qt::PopupFocusReason)); |
234 | |
235 | popupMenu->hide(); |
236 | |
237 | QVERIFY(childFocusWidgetOne->focusInEventRecieved); |
238 | QVERIFY(childFocusWidgetOne->focusInEventGotFocus); |
239 | |
240 | QVERIFY( childFocusWidgetOne->hasFocus() ); |
241 | QVERIFY( childFocusWidgetOne->focusInEventRecieved ); |
242 | QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); |
243 | QVERIFY( !childFocusWidgetTwo->focusInEventRecieved ); |
244 | QVERIFY( !childFocusWidgetTwo->focusOutEventRecieved ); |
245 | } |
246 | |
247 | #ifdef Q_OS_MAC |
248 | QT_BEGIN_NAMESPACE |
249 | extern void qt_set_sequence_auto_mnemonic(bool); |
250 | QT_END_NAMESPACE |
251 | #endif |
252 | |
253 | void tst_QFocusEvent::checkReason_Shortcut() |
254 | { |
255 | initWidget(); |
256 | #ifdef Q_OS_MAC |
257 | qt_set_sequence_auto_mnemonic(true); |
258 | #endif |
259 | QLabel* label = new QLabel( "&Test" , testFocusWidget ); |
260 | label->setBuddy( childFocusWidgetTwo ); |
261 | label->setGeometry( ax: 10, ay: 50, aw: 90, ah: 20 ); |
262 | childFocusWidgetTwo->setGeometry( ax: 105, ay: 50, aw: 95, ah: 20 ); |
263 | label->show(); |
264 | |
265 | QVERIFY( childFocusWidgetOne->hasFocus() ); |
266 | QVERIFY( !childFocusWidgetTwo->hasFocus() ); |
267 | |
268 | QTest::keyClick( widget: label, key: Qt::Key_T, modifier: Qt::AltModifier ); |
269 | |
270 | QVERIFY(childFocusWidgetOne->focusOutEventRecieved); |
271 | QVERIFY(childFocusWidgetTwo->focusInEventRecieved); |
272 | QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); |
273 | QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); |
274 | |
275 | QVERIFY( childFocusWidgetTwo->hasFocus() ); |
276 | QVERIFY( !childFocusWidgetOne->focusInEventRecieved ); |
277 | QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); |
278 | QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int)Qt::ShortcutFocusReason ); |
279 | QVERIFY( childFocusWidgetTwo->focusInEventRecieved ); |
280 | QCOMPARE( childFocusWidgetTwo->focusInEventReason, (int)Qt::ShortcutFocusReason ); |
281 | QVERIFY( !childFocusWidgetTwo->focusOutEventRecieved ); |
282 | |
283 | label->hide(); |
284 | QVERIFY( childFocusWidgetTwo->hasFocus() ); |
285 | QVERIFY( !childFocusWidgetOne->hasFocus() ); |
286 | #ifdef Q_OS_MAC |
287 | qt_set_sequence_auto_mnemonic(false); |
288 | #endif |
289 | } |
290 | |
291 | void tst_QFocusEvent::checkReason_focusWidget() |
292 | { |
293 | // This test checks that a widget doesn't loose |
294 | // its focuswidget just because the focuswidget loses focus. |
295 | QWidget window1; |
296 | QWidget frame1; |
297 | QWidget frame2; |
298 | QLineEdit edit1; |
299 | QLineEdit edit2; |
300 | |
301 | QVBoxLayout outerLayout; |
302 | outerLayout.addWidget(&frame1); |
303 | outerLayout.addWidget(&frame2); |
304 | window1.setLayout(&outerLayout); |
305 | |
306 | QVBoxLayout leftLayout; |
307 | QVBoxLayout rightLayout; |
308 | leftLayout.addWidget(&edit1); |
309 | rightLayout.addWidget(&edit2); |
310 | frame1.setLayout(&leftLayout); |
311 | frame2.setLayout(&rightLayout); |
312 | window1.show(); |
313 | QVERIFY(QTest::qWaitForWindowActive(&window1)); |
314 | |
315 | edit1.setFocus(); |
316 | QTRY_VERIFY(edit1.hasFocus()); |
317 | edit2.setFocus(); |
318 | |
319 | QVERIFY(frame1.focusWidget() != 0); |
320 | QVERIFY(frame2.focusWidget() != 0); |
321 | } |
322 | |
323 | void tst_QFocusEvent::checkReason_ActiveWindow() |
324 | { |
325 | initWidget(); |
326 | |
327 | QDialog* d = new QDialog( testFocusWidget ); |
328 | d->show(); |
329 | QVERIFY(QTest::qWaitForWindowExposed(d)); |
330 | |
331 | d->activateWindow(); // ### CDE |
332 | QApplication::setActiveWindow(d); |
333 | QVERIFY(QTest::qWaitForWindowActive(d)); |
334 | |
335 | QTRY_VERIFY(childFocusWidgetOne->focusOutEventRecieved); |
336 | QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); |
337 | |
338 | #if defined(Q_OS_WIN) |
339 | if (QSysInfo::kernelVersion() == "10.0.15063" ) { |
340 | // Activate window of testFocusWidget, focus in that window goes to childFocusWidgetOne |
341 | QWARN("Windows 10 Creators Update (10.0.15063) requires explicit activateWindow()" ); |
342 | testFocusWidget->activateWindow(); |
343 | } |
344 | #endif |
345 | |
346 | QVERIFY( !childFocusWidgetOne->focusInEventRecieved ); |
347 | QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); |
348 | QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int)Qt::ActiveWindowFocusReason); |
349 | QVERIFY( !childFocusWidgetOne->hasFocus() ); |
350 | |
351 | d->hide(); |
352 | |
353 | if (!QGuiApplication::platformName().compare(other: QLatin1String("offscreen" ), cs: Qt::CaseInsensitive) |
354 | || !QGuiApplication::platformName().compare(other: QLatin1String("minimal" ), cs: Qt::CaseInsensitive) |
355 | || !QGuiApplication::platformName().compare(other: QLatin1String("winrt" ), cs: Qt::CaseInsensitive)) { |
356 | // Activate window of testFocusWidget, focus in that window goes to childFocusWidgetOne |
357 | QWARN("Platforms offscreen, minimal, and winrt require explicit activateWindow()" ); |
358 | testFocusWidget->activateWindow(); |
359 | } |
360 | |
361 | QTRY_VERIFY(childFocusWidgetOne->focusInEventRecieved); |
362 | QVERIFY(childFocusWidgetOne->focusInEventGotFocus); |
363 | |
364 | QVERIFY( childFocusWidgetOne->hasFocus() ); |
365 | QVERIFY( childFocusWidgetOne->focusInEventRecieved ); |
366 | QCOMPARE( childFocusWidgetOne->focusInEventReason, (int)Qt::ActiveWindowFocusReason); |
367 | } |
368 | |
369 | |
370 | QTEST_MAIN(tst_QFocusEvent) |
371 | #include "tst_qfocusevent.moc" |
372 | |