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 | |
32 | |
33 | #include "qcommandlinkbutton.h" |
34 | #include <qapplication.h> |
35 | |
36 | #include <qcommandlinkbutton.h> |
37 | #include <qmenu.h> |
38 | #include <qtimer.h> |
39 | #include <QDialog> |
40 | #include <QGridLayout> |
41 | #include <QPainter> |
42 | |
43 | class tst_QCommandLinkButton : public QObject |
44 | { |
45 | Q_OBJECT |
46 | |
47 | private slots: |
48 | void initTestCase(); |
49 | void cleanupTestCase(); |
50 | void init(); |
51 | |
52 | void getSetCheck(); |
53 | void pressed(); |
54 | void setAccel(); |
55 | void isCheckable(); |
56 | void setDown(); |
57 | void popupCrash(); |
58 | void isChecked(); |
59 | void animateClick(); |
60 | void toggle(); |
61 | void clicked(); |
62 | void toggled(); |
63 | void defaultAndAutoDefault(); |
64 | void setAutoRepeat(); |
65 | void heightForWithWithIcon(); |
66 | |
67 | protected slots: |
68 | void resetCounters(); |
69 | void onClicked(); |
70 | void onToggled( bool on ); |
71 | void onPressed(); |
72 | void onReleased(); |
73 | void helperSlotDelete(); |
74 | |
75 | private: |
76 | uint click_count; |
77 | uint toggle_count; |
78 | uint press_count; |
79 | uint release_count; |
80 | |
81 | QCommandLinkButton *testWidget; |
82 | }; |
83 | |
84 | // Testing get/set functions |
85 | void tst_QCommandLinkButton::getSetCheck() |
86 | { |
87 | QCommandLinkButton obj1; |
88 | |
89 | QString text("mytext" ); |
90 | QVERIFY(obj1.description().isEmpty()); |
91 | obj1.setDescription(text); |
92 | QCOMPARE(obj1.description(), text); |
93 | |
94 | QVERIFY(obj1.text().isEmpty()); |
95 | obj1.setText(text); |
96 | QCOMPARE(obj1.text(), text); |
97 | |
98 | QMenu *var1 = new QMenu; |
99 | obj1.setMenu(var1); |
100 | QCOMPARE(var1, obj1.menu()); |
101 | obj1.setMenu((QMenu *)0); |
102 | QCOMPARE((QMenu *)0, obj1.menu()); |
103 | delete var1; |
104 | } |
105 | |
106 | void tst_QCommandLinkButton::initTestCase() |
107 | { |
108 | // Create the test class |
109 | testWidget = new QCommandLinkButton( "&Start" , 0 ); |
110 | testWidget->setObjectName("testWidget" ); |
111 | testWidget->resize( w: 200, h: 200 ); |
112 | testWidget->show(); |
113 | if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland" ), cs: Qt::CaseInsensitive)) |
114 | QSKIP("Wayland: This fails. Figure out why." ); |
115 | QVERIFY(QTest::qWaitForWindowActive(testWidget)); |
116 | |
117 | connect( sender: testWidget, SIGNAL(clicked()), receiver: this, SLOT(onClicked()) ); |
118 | connect( sender: testWidget, SIGNAL(pressed()), receiver: this, SLOT(onPressed()) ); |
119 | connect( sender: testWidget, SIGNAL(released()), receiver: this, SLOT(onReleased()) ); |
120 | connect( sender: testWidget, SIGNAL(toggled(bool)), receiver: this, SLOT(onToggled(bool)) ); |
121 | } |
122 | |
123 | void tst_QCommandLinkButton::cleanupTestCase() |
124 | { |
125 | delete testWidget; |
126 | testWidget = 0; |
127 | } |
128 | |
129 | void tst_QCommandLinkButton::init() |
130 | { |
131 | testWidget->setAutoRepeat( false ); |
132 | testWidget->setDown( false ); |
133 | testWidget->setText("Test" ); |
134 | testWidget->setDescription("Description text." ); |
135 | testWidget->setEnabled( true ); |
136 | QKeySequence seq; |
137 | testWidget->setShortcut( seq ); |
138 | |
139 | resetCounters(); |
140 | } |
141 | |
142 | void tst_QCommandLinkButton::resetCounters() |
143 | { |
144 | toggle_count = 0; |
145 | press_count = 0; |
146 | release_count = 0; |
147 | click_count = 0; |
148 | } |
149 | |
150 | void tst_QCommandLinkButton::onClicked() |
151 | { |
152 | click_count++; |
153 | } |
154 | |
155 | void tst_QCommandLinkButton::onToggled( bool /*on*/ ) |
156 | { |
157 | toggle_count++; |
158 | } |
159 | |
160 | void tst_QCommandLinkButton::onPressed() |
161 | { |
162 | press_count++; |
163 | } |
164 | |
165 | void tst_QCommandLinkButton::onReleased() |
166 | { |
167 | release_count++; |
168 | } |
169 | |
170 | void tst_QCommandLinkButton::setAutoRepeat() |
171 | { |
172 | // Give the last tests time to finish - i.e., wait for the window close and |
173 | // deactivate to avoid a race condition here. We can't add this to the end |
174 | // of the defaultAndAutoDefault test, since any failure in that test will |
175 | // return out of that function. |
176 | QTest::qWait(ms: 1000); |
177 | |
178 | // If this changes, this test must be completely revised. |
179 | QVERIFY( !testWidget->isCheckable() ); |
180 | |
181 | // verify autorepeat is off by default. |
182 | QCommandLinkButton tmp( 0 ); |
183 | tmp.setObjectName("tmp" ); |
184 | QVERIFY( !tmp.autoRepeat() ); |
185 | |
186 | // check if we can toggle the mode |
187 | testWidget->setAutoRepeat( true ); |
188 | QVERIFY( testWidget->autoRepeat() ); |
189 | |
190 | testWidget->setAutoRepeat( false ); |
191 | QVERIFY( !testWidget->autoRepeat() ); |
192 | |
193 | resetCounters(); |
194 | |
195 | // check that the button is down if we press space and not in autorepeat |
196 | testWidget->setDown( false ); |
197 | testWidget->setAutoRepeat( false ); |
198 | QTest::keyPress( widget: testWidget, key: Qt::Key_Space ); |
199 | |
200 | QVERIFY( testWidget->isDown() ); |
201 | QVERIFY( toggle_count == 0 ); |
202 | QVERIFY( press_count == 1 ); |
203 | QVERIFY( release_count == 0 ); |
204 | QVERIFY( click_count == 0 ); |
205 | |
206 | QTest::keyRelease( widget: testWidget, key: Qt::Key_Space ); |
207 | resetCounters(); |
208 | |
209 | // check that the button is down if we press space while in autorepeat |
210 | |
211 | testWidget->setDown( false ); |
212 | testWidget->setAutoRepeat( true ); |
213 | QTest::keyPress( widget: testWidget, key: Qt::Key_Space ); |
214 | QTRY_VERIFY(press_count > 10); |
215 | QVERIFY( testWidget->isDown() ); |
216 | QVERIFY( toggle_count == 0 ); |
217 | QTest::keyRelease( widget: testWidget, key: Qt::Key_Space ); |
218 | QCOMPARE(press_count, release_count); |
219 | QCOMPARE(release_count, click_count); |
220 | |
221 | // #### shouldn't I check here to see if multiple signals have been fired??? |
222 | |
223 | // check that pressing ENTER has no effect |
224 | resetCounters(); |
225 | testWidget->setDown( false ); |
226 | testWidget->setAutoRepeat( false ); |
227 | QTest::keyPress( widget: testWidget, key: Qt::Key_Enter ); |
228 | |
229 | QVERIFY( !testWidget->isDown() ); |
230 | QVERIFY( toggle_count == 0 ); |
231 | QVERIFY( press_count == 0 ); |
232 | QVERIFY( release_count == 0 ); |
233 | QVERIFY( click_count == 0 ); |
234 | QTest::keyRelease( widget: testWidget, key: Qt::Key_Enter ); |
235 | |
236 | // check that pressing ENTER has no effect |
237 | resetCounters(); |
238 | testWidget->setDown( false ); |
239 | testWidget->setAutoRepeat( true ); |
240 | QTest::keyClick( widget: testWidget, key: Qt::Key_Enter ); |
241 | QVERIFY( !testWidget->isDown() ); |
242 | QVERIFY( toggle_count == 0 ); |
243 | QVERIFY( press_count == 0 ); |
244 | QVERIFY( release_count == 0 ); |
245 | QVERIFY( click_count == 0 ); |
246 | } |
247 | |
248 | void tst_QCommandLinkButton::pressed() |
249 | { |
250 | QTest::keyPress( widget: testWidget, key: ' ' ); |
251 | QCOMPARE( press_count, (uint)1 ); |
252 | QCOMPARE( release_count, (uint)0 ); |
253 | |
254 | QTest::keyRelease( widget: testWidget, key: ' ' ); |
255 | QCOMPARE( press_count, (uint)1 ); |
256 | QCOMPARE( release_count, (uint)1 ); |
257 | |
258 | QTest::keyPress( widget: testWidget,key: Qt::Key_Enter ); |
259 | QCOMPARE( press_count, (uint)1 ); |
260 | QCOMPARE( release_count, (uint)1 ); |
261 | |
262 | testWidget->setAutoDefault(true); |
263 | QTest::keyPress( widget: testWidget,key: Qt::Key_Enter ); |
264 | QCOMPARE( press_count, (uint)2 ); |
265 | QCOMPARE( release_count, (uint)2 ); |
266 | testWidget->setAutoDefault(false); |
267 | } |
268 | |
269 | void tst_QCommandLinkButton::isCheckable() |
270 | { |
271 | QVERIFY( !testWidget->isCheckable() ); |
272 | } |
273 | |
274 | void tst_QCommandLinkButton::setDown() |
275 | { |
276 | testWidget->setDown( false ); |
277 | QVERIFY( !testWidget->isDown() ); |
278 | |
279 | testWidget->setDown( true ); |
280 | QVERIFY( testWidget->isDown() ); |
281 | |
282 | testWidget->setDown( true ); |
283 | QTest::keyClick( widget: testWidget, key: Qt::Key_Escape ); |
284 | QVERIFY( !testWidget->isDown() ); |
285 | } |
286 | |
287 | void tst_QCommandLinkButton::isChecked() |
288 | { |
289 | testWidget->setDown( false ); |
290 | QVERIFY( !testWidget->isChecked() ); |
291 | |
292 | testWidget->setDown( true ); |
293 | QVERIFY( !testWidget->isChecked() ); |
294 | |
295 | testWidget->setDown( false ); |
296 | testWidget->toggle(); |
297 | QVERIFY( testWidget->isChecked() == testWidget->isCheckable() ); |
298 | } |
299 | |
300 | void tst_QCommandLinkButton::toggle() |
301 | { |
302 | // the pushbutton shouldn't toggle the button. |
303 | testWidget->toggle(); |
304 | QVERIFY(!testWidget->isChecked()); |
305 | } |
306 | |
307 | void tst_QCommandLinkButton::toggled() |
308 | { |
309 | // the pushbutton shouldn't send a toggled signal when we call the toggle slot. |
310 | QVERIFY( !testWidget->isCheckable() ); |
311 | |
312 | testWidget->toggle(); |
313 | QVERIFY( toggle_count == 0 ); |
314 | |
315 | // do it again, just to be sure |
316 | resetCounters(); |
317 | testWidget->toggle(); |
318 | QVERIFY( toggle_count == 0 ); |
319 | |
320 | // finally check that we can toggle using the mouse |
321 | resetCounters(); |
322 | QTest::mousePress( widget: testWidget, button: Qt::LeftButton ); |
323 | QVERIFY( toggle_count == 0 ); |
324 | QVERIFY( click_count == 0 ); |
325 | |
326 | QTest::mouseRelease( widget: testWidget, button: Qt::LeftButton ); |
327 | QVERIFY( click_count == 1 ); |
328 | } |
329 | |
330 | /* |
331 | If we press an accelerator key we ONLY get a pressed signal and |
332 | NOT a released or clicked signal. |
333 | */ |
334 | |
335 | void tst_QCommandLinkButton::setAccel() |
336 | { |
337 | testWidget->setText("&AccelTest" ); |
338 | QKeySequence seq( Qt::ALT + Qt::Key_A ); |
339 | testWidget->setShortcut( seq ); |
340 | |
341 | // The shortcut will not be activated unless the button is in a active |
342 | // window and has focus |
343 | testWidget->setFocus(); |
344 | QVERIFY(QTest::qWaitForWindowActive(testWidget)); |
345 | |
346 | QTest::keyClick( widget: testWidget, key: 'A', modifier: Qt::AltModifier ); |
347 | QTest::qWait( ms: 500 ); |
348 | QVERIFY( click_count == 1 ); |
349 | QVERIFY( press_count == 1 ); |
350 | QVERIFY( release_count == 1 ); |
351 | QVERIFY( toggle_count == 0 ); |
352 | |
353 | // wait 200 ms because setAccel uses animateClick. |
354 | // if we don't wait this may screw up a next test. |
355 | QTest::qWait(ms: 200); |
356 | } |
357 | |
358 | void tst_QCommandLinkButton::animateClick() |
359 | { |
360 | QVERIFY( !testWidget->isDown() ); |
361 | testWidget->animateClick(); |
362 | QVERIFY( testWidget->isDown() ); |
363 | QTest::qWait( ms: 200 ); |
364 | QVERIFY( !testWidget->isDown() ); |
365 | |
366 | QVERIFY( click_count == 1 ); |
367 | QVERIFY( press_count == 1 ); |
368 | QVERIFY( release_count == 1 ); |
369 | QVERIFY( toggle_count == 0 ); |
370 | } |
371 | |
372 | void tst_QCommandLinkButton::clicked() |
373 | { |
374 | QTest::mousePress( widget: testWidget, button: Qt::LeftButton ); |
375 | QVERIFY( press_count == 1 ); |
376 | QVERIFY( release_count == 0 ); |
377 | |
378 | QTest::mouseRelease( widget: testWidget, button: Qt::LeftButton ); |
379 | QCOMPARE( press_count, (uint)1 ); |
380 | QCOMPARE( release_count, (uint)1 ); |
381 | |
382 | press_count = 0; |
383 | release_count = 0; |
384 | testWidget->setDown(false); |
385 | for (uint i=0; i<10; i++) |
386 | QTest::mouseClick( widget: testWidget, button: Qt::LeftButton ); |
387 | QCOMPARE( press_count, (uint)10 ); |
388 | QCOMPARE( release_count, (uint)10 ); |
389 | } |
390 | |
391 | QCommandLinkButton *pb = 0; |
392 | void tst_QCommandLinkButton::helperSlotDelete() |
393 | { |
394 | delete pb; |
395 | pb = 0; |
396 | } |
397 | |
398 | void tst_QCommandLinkButton::popupCrash() |
399 | { |
400 | pb = new QCommandLinkButton("foo" , "description" ); |
401 | QMenu * = new QMenu("bar" , pb); |
402 | pb->setMenu(menu); |
403 | QTimer::singleShot(msec: 1000, receiver: this, SLOT(helperSlotDelete())); |
404 | pb->show(); |
405 | pb->click(); |
406 | } |
407 | |
408 | void tst_QCommandLinkButton::defaultAndAutoDefault() |
409 | { |
410 | { |
411 | // Adding buttons directly to QDialog |
412 | QDialog dialog; |
413 | |
414 | QCommandLinkButton button1(&dialog); |
415 | QVERIFY(button1.autoDefault()); |
416 | QVERIFY(!button1.isDefault()); |
417 | |
418 | QCommandLinkButton button2(&dialog); |
419 | QVERIFY(button2.autoDefault()); |
420 | QVERIFY(!button2.isDefault()); |
421 | |
422 | button1.setDefault(true); |
423 | QVERIFY(button1.autoDefault()); |
424 | QVERIFY(button1.isDefault()); |
425 | QVERIFY(button2.autoDefault()); |
426 | QVERIFY(!button2.isDefault()); |
427 | |
428 | dialog.show(); |
429 | QVERIFY(dialog.isVisible()); |
430 | |
431 | QObject::connect(sender: &button1, SIGNAL(clicked()), receiver: &dialog, SLOT(hide())); |
432 | QTest::keyClick(widget: &dialog, key: Qt::Key_Return); |
433 | QVERIFY(!dialog.isVisible()); |
434 | } |
435 | |
436 | { |
437 | // Adding buttons to QDialog through a layout |
438 | QDialog dialog; |
439 | |
440 | QCommandLinkButton button3; |
441 | button3.setAutoDefault(false); |
442 | |
443 | QCommandLinkButton button1; |
444 | QVERIFY(!button1.autoDefault()); |
445 | QVERIFY(!button1.isDefault()); |
446 | |
447 | QCommandLinkButton button2; |
448 | QVERIFY(!button2.autoDefault()); |
449 | QVERIFY(!button2.isDefault()); |
450 | |
451 | button1.setDefault(true); |
452 | QVERIFY(!button1.autoDefault()); |
453 | QVERIFY(button1.isDefault()); |
454 | QVERIFY(!button2.autoDefault()); |
455 | QVERIFY(!button2.isDefault()); |
456 | |
457 | QGridLayout layout; |
458 | layout.addWidget(&button3, row: 0, column: 3); |
459 | layout.addWidget(&button2, row: 0, column: 2); |
460 | layout.addWidget(&button1, row: 0, column: 1); |
461 | dialog.setLayout(&layout); |
462 | button3.setFocus(); |
463 | QVERIFY(button1.autoDefault()); |
464 | QVERIFY(button1.isDefault()); |
465 | QVERIFY(button2.autoDefault()); |
466 | QVERIFY(!button2.isDefault()); |
467 | |
468 | dialog.show(); |
469 | QVERIFY(dialog.isVisible()); |
470 | |
471 | QObject::connect(sender: &button1, SIGNAL(clicked()), receiver: &dialog, SLOT(hide())); |
472 | QTest::keyClick(widget: &dialog, key: Qt::Key_Return); |
473 | QVERIFY(!dialog.isVisible()); |
474 | } |
475 | |
476 | { |
477 | // autoDefault behavior. |
478 | QDialog dialog; |
479 | QCommandLinkButton button2(&dialog); |
480 | QCommandLinkButton button1(&dialog); |
481 | dialog.show(); |
482 | QVERIFY(dialog.isVisible()); |
483 | |
484 | // No default button is set, and button2 is the first autoDefault button |
485 | // that is next in the tab order |
486 | QObject::connect(sender: &button2, SIGNAL(clicked()), receiver: &dialog, SLOT(hide())); |
487 | QTest::keyClick(widget: &dialog, key: Qt::Key_Return); |
488 | QVERIFY(!dialog.isVisible()); |
489 | |
490 | // Reparenting |
491 | QVERIFY(button2.autoDefault()); |
492 | button2.setParent(0); |
493 | QVERIFY(!button2.autoDefault()); |
494 | button2.setAutoDefault(false); |
495 | button2.setParent(&dialog); |
496 | QVERIFY(!button2.autoDefault()); |
497 | |
498 | button1.setAutoDefault(true); |
499 | button1.setParent(0); |
500 | QVERIFY(button1.autoDefault()); |
501 | } |
502 | } |
503 | |
504 | void tst_QCommandLinkButton::heightForWithWithIcon() |
505 | { |
506 | QWidget mainWin; |
507 | |
508 | QPixmap pixmap(64, 64); |
509 | { |
510 | pixmap.fill(fillColor: Qt::white); |
511 | QPainter painter(&pixmap); |
512 | painter.setBrush(Qt::black); |
513 | painter.drawEllipse(x: 0, y: 0, w: 63, h: 63); |
514 | } |
515 | |
516 | QCommandLinkButton *largeIconButton = new QCommandLinkButton(QString("Large Icon" ), |
517 | QString("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Mauris nibh lectus, adipiscing eu." ), |
518 | &mainWin); |
519 | largeIconButton->setIconSize(QSize(64, 64)); |
520 | largeIconButton->setIcon(pixmap); |
521 | |
522 | QVBoxLayout *layout = new QVBoxLayout(); |
523 | layout->addWidget(largeIconButton); |
524 | layout->addStretch(); |
525 | mainWin.setLayout(layout); |
526 | mainWin.showMaximized(); |
527 | QVERIFY(QTest::qWaitForWindowExposed(&mainWin)); |
528 | QVERIFY(largeIconButton->height() > 68); //enough room for the icon |
529 | |
530 | } |
531 | |
532 | QTEST_MAIN(tst_QCommandLinkButton) |
533 | #include "tst_qcommandlinkbutton.moc" |
534 | |