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
44QT_FORWARD_DECLARE_CLASS(QWidget)
45
46class FocusLineEdit : public QLineEdit
47{
48public:
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;
56protected:
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
78class tst_QFocusEvent : public QObject
79{
80 Q_OBJECT
81
82public:
83 void initWidget();
84
85private 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
97private:
98 QWidget* testFocusWidget = nullptr;
99 FocusLineEdit* childFocusWidgetOne;
100 FocusLineEdit* childFocusWidgetTwo;
101};
102
103void 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
124void tst_QFocusEvent::cleanupTestCase()
125{
126 delete testFocusWidget;
127}
128
129void tst_QFocusEvent::cleanup()
130{
131 childFocusWidgetTwo->setGeometry( ax: 10, ay: 50, aw: 180, ah: 20 );
132}
133
134void 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
157void 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
174void 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*/
195void 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
217void tst_QFocusEvent::checkReason_Popup()
218{
219 initWidget();
220
221 // Now test the popup reason
222 QMenu* popupMenu = 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
248QT_BEGIN_NAMESPACE
249 extern void qt_set_sequence_auto_mnemonic(bool);
250QT_END_NAMESPACE
251#endif
252
253void 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
291void 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
323void 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
370QTEST_MAIN(tst_QFocusEvent)
371#include "tst_qfocusevent.moc"
372

source code of qtbase/tests/auto/other/qfocusevent/tst_qfocusevent.cpp