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 <qtabbar.h>
33
34#include <qpushbutton.h>
35#include <qstyle.h>
36#include <qstyleoption.h>
37
38class TabBar;
39
40class tst_QTabBar : public QObject
41{
42 Q_OBJECT
43
44public:
45 tst_QTabBar();
46 virtual ~tst_QTabBar();
47
48public slots:
49 void initTestCase();
50 void cleanupTestCase();
51 void init();
52
53private slots:
54 void getSetCheck();
55 void setIconSize();
56 void setIconSize_data();
57
58 void testCurrentChanged_data();
59 void testCurrentChanged();
60
61 void insertAtCurrentIndex();
62 void insertAfterCurrentIndex();
63
64 void removeTab_data();
65 void removeTab();
66
67 void hideTab_data();
68 void hideTab();
69 void hideAllTabs();
70
71 void setElideMode_data();
72 void setElideMode();
73 void sizeHints();
74
75 void setUsesScrollButtons_data();
76 void setUsesScrollButtons();
77
78 void removeLastTab();
79
80 void closeButton();
81
82 void tabButton_data();
83 void tabButton();
84
85 void selectionBehaviorOnRemove_data();
86 void selectionBehaviorOnRemove();
87
88 void moveTab_data();
89 void moveTab();
90
91 void task251184_removeTab();
92 void changeTitleWhileDoubleClickingTab();
93
94 void taskQTBUG_10052_widgetLayoutWhenMoving();
95
96 void tabBarClicked();
97 void autoHide();
98
99 void mouseReleaseOutsideTabBar();
100
101 void scrollButtons_data();
102 void scrollButtons();
103
104private:
105 void checkPositions(const TabBar &tabbar, const QList<int> &positions);
106};
107
108// Testing get/set functions
109void tst_QTabBar::getSetCheck()
110{
111 QTabBar obj1;
112 obj1.addTab(text: "Tab1");
113 obj1.addTab(text: "Tab2");
114 obj1.addTab(text: "Tab3");
115 obj1.addTab(text: "Tab4");
116 obj1.addTab(text: "Tab5");
117 // Shape QTabBar::shape()
118 // void QTabBar::setShape(Shape)
119 obj1.setShape(QTabBar::Shape(QTabBar::RoundedNorth));
120 QCOMPARE(QTabBar::Shape(QTabBar::RoundedNorth), obj1.shape());
121 obj1.setShape(QTabBar::Shape(QTabBar::RoundedSouth));
122 QCOMPARE(QTabBar::Shape(QTabBar::RoundedSouth), obj1.shape());
123 obj1.setShape(QTabBar::Shape(QTabBar::RoundedWest));
124 QCOMPARE(QTabBar::Shape(QTabBar::RoundedWest), obj1.shape());
125 obj1.setShape(QTabBar::Shape(QTabBar::RoundedEast));
126 QCOMPARE(QTabBar::Shape(QTabBar::RoundedEast), obj1.shape());
127 obj1.setShape(QTabBar::Shape(QTabBar::TriangularNorth));
128 QCOMPARE(QTabBar::Shape(QTabBar::TriangularNorth), obj1.shape());
129 obj1.setShape(QTabBar::Shape(QTabBar::TriangularSouth));
130 QCOMPARE(QTabBar::Shape(QTabBar::TriangularSouth), obj1.shape());
131 obj1.setShape(QTabBar::Shape(QTabBar::TriangularWest));
132 QCOMPARE(QTabBar::Shape(QTabBar::TriangularWest), obj1.shape());
133 obj1.setShape(QTabBar::Shape(QTabBar::TriangularEast));
134 QCOMPARE(QTabBar::Shape(QTabBar::TriangularEast), obj1.shape());
135
136 // bool QTabBar::drawBase()
137 // void QTabBar::setDrawBase(bool)
138 obj1.setDrawBase(false);
139 QCOMPARE(false, obj1.drawBase());
140 obj1.setDrawBase(true);
141 QCOMPARE(true, obj1.drawBase());
142
143 // int QTabBar::currentIndex()
144 // void QTabBar::setCurrentIndex(int)
145 obj1.setCurrentIndex(0);
146 QCOMPARE(0, obj1.currentIndex());
147 obj1.setCurrentIndex(INT_MIN);
148 QCOMPARE(0, obj1.currentIndex());
149 obj1.setCurrentIndex(INT_MAX);
150 QCOMPARE(0, obj1.currentIndex());
151 obj1.setCurrentIndex(4);
152 QCOMPARE(4, obj1.currentIndex());
153}
154
155tst_QTabBar::tst_QTabBar()
156{
157}
158
159tst_QTabBar::~tst_QTabBar()
160{
161}
162
163void tst_QTabBar::initTestCase()
164{
165}
166
167void tst_QTabBar::cleanupTestCase()
168{
169}
170
171void tst_QTabBar::init()
172{
173}
174
175void tst_QTabBar::setIconSize_data()
176{
177 QTest::addColumn<int>(name: "sizeToSet");
178 QTest::addColumn<int>(name: "expectedWidth");
179
180 const int iconDefault = qApp->style()->pixelMetric(metric: QStyle::PM_TabBarIconSize);
181 const int smallIconSize = qApp->style()->pixelMetric(metric: QStyle::PM_SmallIconSize);
182 const int largeIconSize = qApp->style()->pixelMetric(metric: QStyle::PM_LargeIconSize);
183 QTest::newRow(dataTag: "default") << -1 << iconDefault;
184 QTest::newRow(dataTag: "zero") << 0 << 0;
185 QTest::newRow(dataTag: "same as default") << iconDefault << iconDefault;
186 QTest::newRow(dataTag: "large") << largeIconSize << largeIconSize;
187 QTest::newRow(dataTag: "small") << smallIconSize << smallIconSize;
188}
189
190void tst_QTabBar::setIconSize()
191{
192 QFETCH(int, sizeToSet);
193 QFETCH(int, expectedWidth);
194 QTabBar tabBar;
195 tabBar.setIconSize(QSize(sizeToSet, sizeToSet));
196 QCOMPARE(tabBar.iconSize().width(), expectedWidth);
197}
198
199void tst_QTabBar::testCurrentChanged_data()
200{
201 QTest::addColumn<int>(name: "tabToSet");
202 QTest::addColumn<int>(name: "expectedCount");
203
204 QTest::newRow(dataTag: "pressAntotherTab") << 1 << 2;
205 QTest::newRow(dataTag: "pressTheSameTab") << 0 << 1;
206}
207
208void tst_QTabBar::testCurrentChanged()
209{
210 QFETCH(int, tabToSet);
211 QFETCH(int, expectedCount);
212 QTabBar tabBar;
213 QSignalSpy spy(&tabBar, SIGNAL(currentChanged(int)));
214 tabBar.addTab(text: "Tab1");
215 tabBar.addTab(text: "Tab2");
216 QCOMPARE(tabBar.currentIndex(), 0);
217 tabBar.setCurrentIndex(tabToSet);
218 QCOMPARE(tabBar.currentIndex(), tabToSet);
219 QCOMPARE(spy.count(), expectedCount);
220}
221
222class TabBar : public QTabBar
223{
224public:
225 using QTabBar::initStyleOption;
226 using QTabBar::moveTab;
227 using QTabBar::QTabBar;
228};
229
230void tst_QTabBar::insertAtCurrentIndex()
231{
232 QTabBar tabBar;
233 tabBar.addTab(text: "Tab1");
234 QCOMPARE(tabBar.currentIndex(), 0);
235 tabBar.insertTab(index: 0, text: "Tab2");
236 QCOMPARE(tabBar.currentIndex(), 1);
237 tabBar.insertTab(index: 0, text: "Tab3");
238 QCOMPARE(tabBar.currentIndex(), 2);
239 tabBar.insertTab(index: 2, text: "Tab4");
240 QCOMPARE(tabBar.currentIndex(), 3);
241}
242
243void tst_QTabBar::insertAfterCurrentIndex()
244{
245 TabBar tabBar;
246
247 tabBar.addTab(text: "Tab10");
248 checkPositions(tabbar: tabBar, positions: { QStyleOptionTab::OnlyOneTab });
249
250 tabBar.addTab(text: "Tab20");
251 checkPositions(tabbar: tabBar, positions: { QStyleOptionTab::Beginning, QStyleOptionTab::End });
252
253 tabBar.insertTab(index: 1, text: "Tab15");
254 checkPositions(tabbar: tabBar,
255 positions: { QStyleOptionTab::Beginning, QStyleOptionTab::Middle, QStyleOptionTab::End });
256
257 tabBar.insertTab(index: 3, text: "Tab30");
258 checkPositions(tabbar: tabBar,
259 positions: { QStyleOptionTab::Beginning, QStyleOptionTab::Middle, QStyleOptionTab::Middle,
260 QStyleOptionTab::End });
261
262 tabBar.insertTab(index: 3, text: "Tab25");
263 checkPositions(tabbar: tabBar,
264 positions: { QStyleOptionTab::Beginning, QStyleOptionTab::Middle, QStyleOptionTab::Middle,
265 QStyleOptionTab::Middle, QStyleOptionTab::End });
266}
267
268void tst_QTabBar::removeTab_data()
269{
270 QTest::addColumn<int>(name: "currentIndex");
271 QTest::addColumn<int>(name: "deleteIndex");
272 QTest::addColumn<int>(name: "spyCount");
273 QTest::addColumn<int>(name: "finalIndex");
274
275 QTest::newRow(dataTag: "deleteEnd") << 0 << 2 << 0 << 0;
276 QTest::newRow(dataTag: "deleteEndWithIndexOnEnd") << 2 << 2 << 1 << 1;
277 QTest::newRow(dataTag: "deleteMiddle") << 2 << 1 << 1 << 1;
278 QTest::newRow(dataTag: "deleteMiddleOnMiddle") << 1 << 1 << 1 << 1;
279}
280void tst_QTabBar::removeTab()
281{
282 QTabBar tabbar;
283
284 QFETCH(int, currentIndex);
285 QFETCH(int, deleteIndex);
286 tabbar.addTab(text: "foo");
287 tabbar.addTab(text: "bar");
288 tabbar.addTab(text: "baz");
289 tabbar.setCurrentIndex(currentIndex);
290 QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int)));
291 tabbar.removeTab(index: deleteIndex);
292 QTEST(spy.count(), "spyCount");
293 QTEST(tabbar.currentIndex(), "finalIndex");
294}
295
296void tst_QTabBar::hideTab_data()
297{
298 QTest::addColumn<int>(name: "currentIndex");
299 QTest::addColumn<int>(name: "hideIndex");
300 QTest::addColumn<int>(name: "spyCount");
301 QTest::addColumn<int>(name: "finalIndex");
302
303 QTest::newRow(dataTag: "hideEnd") << 0 << 2 << 0 << 0;
304 QTest::newRow(dataTag: "hideEndWithIndexOnEnd") << 2 << 2 << 1 << 1;
305 QTest::newRow(dataTag: "hideMiddle") << 2 << 1 << 0 << 2;
306 QTest::newRow(dataTag: "hideMiddleOnMiddle") << 1 << 1 << 1 << 2;
307 QTest::newRow(dataTag: "hideFirst") << 2 << 0 << 0 << 2;
308 QTest::newRow(dataTag: "hideFirstOnFirst") << 0 << 0 << 1 << 1;
309}
310
311void tst_QTabBar::hideTab()
312{
313 QTabBar tabbar;
314
315 QFETCH(int, currentIndex);
316 QFETCH(int, hideIndex);
317 tabbar.addTab(text: "foo");
318 tabbar.addTab(text: "bar");
319 tabbar.addTab(text: "baz");
320 tabbar.setCurrentIndex(currentIndex);
321 QSignalSpy spy(&tabbar, &QTabBar::currentChanged);
322 tabbar.setTabVisible(index: hideIndex, visible: false);
323 QTEST(spy.count(), "spyCount");
324 QTEST(tabbar.currentIndex(), "finalIndex");
325}
326
327void tst_QTabBar::hideAllTabs()
328{
329 TabBar tabbar;
330
331 tabbar.addTab(text: "foo");
332 tabbar.addTab(text: "bar");
333 tabbar.addTab(text: "baz");
334 tabbar.setCurrentIndex(0);
335 checkPositions(tabbar,
336 positions: { QStyleOptionTab::Beginning, QStyleOptionTab::Middle, QStyleOptionTab::End });
337
338 // Check we don't crash trying to hide an unexistant tab
339 QSize prevSizeHint = tabbar.sizeHint();
340 tabbar.setTabVisible(index: 3, visible: false);
341 checkPositions(tabbar,
342 positions: { QStyleOptionTab::Beginning, QStyleOptionTab::Middle, QStyleOptionTab::End });
343 QCOMPARE(tabbar.currentIndex(), 0);
344 QSize sizeHint = tabbar.sizeHint();
345 QVERIFY(sizeHint.width() == prevSizeHint.width());
346 prevSizeHint = sizeHint;
347
348 tabbar.setTabVisible(index: 1, visible: false);
349 checkPositions(tabbar, positions: { QStyleOptionTab::Beginning, QStyleOptionTab::End });
350 QCOMPARE(tabbar.currentIndex(), 0);
351 sizeHint = tabbar.sizeHint();
352 QVERIFY(sizeHint.width() < prevSizeHint.width());
353 prevSizeHint = sizeHint;
354
355 tabbar.setTabVisible(index: 2, visible: false);
356 checkPositions(tabbar, positions: { QStyleOptionTab::OnlyOneTab });
357 QCOMPARE(tabbar.currentIndex(), 0);
358 sizeHint = tabbar.sizeHint();
359 QVERIFY(sizeHint.width() < prevSizeHint.width());
360 prevSizeHint = sizeHint;
361
362 tabbar.setTabVisible(index: 0, visible: false);
363 // We cannot set currentIndex < 0
364 QCOMPARE(tabbar.currentIndex(), 0);
365 sizeHint = tabbar.sizeHint();
366 QVERIFY(sizeHint.width() < prevSizeHint.width());
367}
368
369void tst_QTabBar::setElideMode_data()
370{
371 QTest::addColumn<int>(name: "tabElideMode");
372 QTest::addColumn<int>(name: "expectedMode");
373
374 QTest::newRow(dataTag: "default") << -128 << qApp->style()->styleHint(stylehint: QStyle::SH_TabBar_ElideMode);
375 QTest::newRow(dataTag: "explicit default") << qApp->style()->styleHint(stylehint: QStyle::SH_TabBar_ElideMode)
376 << qApp->style()->styleHint(stylehint: QStyle::SH_TabBar_ElideMode);
377 QTest::newRow(dataTag: "None") << int(Qt::ElideNone) << int(Qt::ElideNone);
378 QTest::newRow(dataTag: "Left") << int(Qt::ElideLeft) << int(Qt::ElideLeft);
379 QTest::newRow(dataTag: "Center") << int(Qt::ElideMiddle) << int(Qt::ElideMiddle);
380 QTest::newRow(dataTag: "Right") << int(Qt::ElideRight) << int(Qt::ElideRight);
381}
382
383void tst_QTabBar::setElideMode()
384{
385 QFETCH(int, tabElideMode);
386 QTabBar tabBar;
387 if (tabElideMode != -128)
388 tabBar.setElideMode(Qt::TextElideMode(tabElideMode));
389 QTEST(int(tabBar.elideMode()), "expectedMode");
390 // Make sure style sheet does not override user set mode
391 tabBar.setStyleSheet("QWidget { background-color: #ABA8A6;}");
392 QTEST(int(tabBar.elideMode()), "expectedMode");
393}
394
395void tst_QTabBar::sizeHints()
396{
397 QTabBar tabBar;
398 tabBar.setFont(QFont("Arial", 10));
399
400 // No eliding and no scrolling -> tabbar becomes very wide
401 tabBar.setUsesScrollButtons(false);
402 tabBar.setElideMode(Qt::ElideNone);
403 tabBar.addTab(text: "tab 01");
404
405 // Fetch the minimum size hint width and make sure that we create enough
406 // tabs.
407 int minimumSizeHintWidth = tabBar.minimumSizeHint().width();
408 for (int i = 0; i <= 700 / minimumSizeHintWidth; ++i)
409 tabBar.addTab(text: QString("tab 0%1").arg(a: i+2));
410
411 //qDebug() << tabBar.minimumSizeHint() << tabBar.sizeHint();
412 QVERIFY(tabBar.minimumSizeHint().width() > 700);
413 QVERIFY(tabBar.sizeHint().width() > 700);
414
415 // Scrolling enabled -> no reason to become very wide
416 tabBar.setUsesScrollButtons(true);
417 QVERIFY(tabBar.minimumSizeHint().width() < 200);
418 QVERIFY(tabBar.sizeHint().width() > 700); // unchanged
419
420 // Eliding enabled -> no reason to become very wide
421 tabBar.setUsesScrollButtons(false);
422 tabBar.setElideMode(Qt::ElideRight);
423
424 // The sizeHint is very much dependent on the screen DPI value
425 // so we can not really predict it.
426 int tabBarMinSizeHintWidth = tabBar.minimumSizeHint().width();
427 int tabBarSizeHintWidth = tabBar.sizeHint().width();
428 QVERIFY(tabBarMinSizeHintWidth < tabBarSizeHintWidth);
429 QVERIFY(tabBarSizeHintWidth > 700); // unchanged
430
431 tabBar.addTab(text: "This is tab with a very long title");
432 QVERIFY(tabBar.minimumSizeHint().width() > tabBarMinSizeHintWidth);
433 QVERIFY(tabBar.sizeHint().width() > tabBarSizeHintWidth);
434}
435
436void tst_QTabBar::setUsesScrollButtons_data()
437{
438 QTest::addColumn<int>(name: "usesArrows");
439 QTest::addColumn<bool>(name: "expectedArrows");
440
441 QTest::newRow(dataTag: "default") << -128 << !qApp->style()->styleHint(stylehint: QStyle::SH_TabBar_PreferNoArrows);
442 QTest::newRow(dataTag: "explicit default")
443 << int(!qApp->style()->styleHint(stylehint: QStyle::SH_TabBar_PreferNoArrows))
444 << !qApp->style()->styleHint(stylehint: QStyle::SH_TabBar_PreferNoArrows);
445 QTest::newRow(dataTag: "No") << int(false) << false;
446 QTest::newRow(dataTag: "Yes") << int(true) << true;
447}
448
449void tst_QTabBar::setUsesScrollButtons()
450{
451 QFETCH(int, usesArrows);
452 QTabBar tabBar;
453 if (usesArrows != -128)
454 tabBar.setUsesScrollButtons(usesArrows);
455 QTEST(tabBar.usesScrollButtons(), "expectedArrows");
456
457 // Make sure style sheet does not override user set mode
458 tabBar.setStyleSheet("QWidget { background-color: #ABA8A6;}");
459 QTEST(tabBar.usesScrollButtons(), "expectedArrows");
460}
461
462void tst_QTabBar::removeLastTab()
463{
464 QTabBar tabbar;
465 QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int)));
466 int index = tabbar.addTab(text: "foo");
467 QCOMPARE(spy.count(), 1);
468 QCOMPARE(spy.at(0).at(0).toInt(), index);
469 spy.clear();
470
471 tabbar.removeTab(index);
472 QCOMPARE(spy.count(), 1);
473 QCOMPARE(spy.at(0).at(0).toInt(), -1);
474 spy.clear();
475}
476
477void tst_QTabBar::closeButton()
478{
479 QTabBar tabbar;
480 QCOMPARE(tabbar.tabsClosable(), false);
481 tabbar.setTabsClosable(true);
482 QCOMPARE(tabbar.tabsClosable(), true);
483 tabbar.addTab(text: "foo");
484
485 QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)tabbar.style()->styleHint(stylehint: QStyle::SH_TabBar_CloseButtonPosition, opt: 0, widget: &tabbar);
486 QTabBar::ButtonPosition otherSide = (closeSide == QTabBar::LeftSide ? QTabBar::RightSide : QTabBar::LeftSide);
487 QVERIFY(tabbar.tabButton(0, otherSide) == 0);
488 QVERIFY(tabbar.tabButton(0, closeSide) != 0);
489
490 QAbstractButton *button = static_cast<QAbstractButton*>(tabbar.tabButton(index: 0, position: closeSide));
491 QVERIFY(button);
492 QSignalSpy spy(&tabbar, SIGNAL(tabCloseRequested(int)));
493 button->click();
494 QCOMPARE(tabbar.count(), 1);
495 QCOMPARE(spy.count(), 1);
496}
497
498Q_DECLARE_METATYPE(QTabBar::ButtonPosition)
499void tst_QTabBar::tabButton_data()
500{
501 QTest::addColumn<QTabBar::ButtonPosition>(name: "position");
502
503 QTest::newRow(dataTag: "left") << QTabBar::LeftSide;
504 QTest::newRow(dataTag: "right") << QTabBar::RightSide;
505}
506
507// QTabBar::setTabButton(index, closeSide, closeButton);
508void tst_QTabBar::tabButton()
509{
510 QFETCH(QTabBar::ButtonPosition, position);
511 QTabBar::ButtonPosition otherSide = (position == QTabBar::LeftSide ? QTabBar::RightSide : QTabBar::LeftSide);
512
513 QTabBar tabbar;
514 tabbar.resize(w: 500, h: 200);
515 tabbar.show();
516 QTRY_VERIFY(tabbar.isVisible());
517
518 tabbar.setTabButton(index: -1, position, widget: 0);
519 QVERIFY(tabbar.tabButton(-1, position) == 0);
520 QVERIFY(tabbar.tabButton(0, position) == 0);
521
522 tabbar.addTab(text: "foo");
523 QCOMPARE(tabbar.count(), 1);
524 tabbar.setTabButton(index: 0, position, widget: 0);
525 QVERIFY(tabbar.tabButton(0, position) == 0);
526
527 QPushButton *button = new QPushButton;
528 button->show();
529 button->setText("hi");
530 button->resize(w: 10, h: 10);
531 QTRY_VERIFY(button->isVisible());
532 QTRY_VERIFY(button->isVisible());
533
534 tabbar.setTabButton(index: 0, position, widget: button);
535
536 QCOMPARE(tabbar.tabButton(0, position), static_cast<QWidget *>(button));
537 QTRY_VERIFY(!button->isHidden());
538 QVERIFY(tabbar.tabButton(0, otherSide) == 0);
539 QCOMPARE(button->parent(), static_cast<QObject *>(&tabbar));
540 QVERIFY(button->pos() != QPoint(0, 0));
541
542 QPushButton *button2 = new QPushButton;
543 tabbar.setTabButton(index: 0, position, widget: button2);
544 QVERIFY(button->isHidden());
545}
546
547typedef QList<int> IntList;
548Q_DECLARE_METATYPE(QTabBar::SelectionBehavior)
549#define ONE(x) (IntList() << x)
550void tst_QTabBar::selectionBehaviorOnRemove_data()
551{
552 QTest::addColumn<QTabBar::SelectionBehavior>(name: "selectionBehavior");
553 QTest::addColumn<int>(name: "tabs");
554 QTest::addColumn<IntList>(name: "select");
555 QTest::addColumn<IntList>(name: "remove");
556 QTest::addColumn<int>(name: "expected");
557
558 // Count select remove current
559 QTest::newRow(dataTag: "left-1") << QTabBar::SelectLeftTab << 3 << (IntList() << 0) << ONE(0) << 0;
560
561 QTest::newRow(dataTag: "left-2") << QTabBar::SelectLeftTab << 3 << (IntList() << 0) << ONE(1) << 0; // not removing current
562 QTest::newRow(dataTag: "left-3") << QTabBar::SelectLeftTab << 3 << (IntList() << 0) << ONE(2) << 0; // not removing current
563 QTest::newRow(dataTag: "left-4") << QTabBar::SelectLeftTab << 3 << (IntList() << 1) << ONE(0) << 0; // not removing current
564 QTest::newRow(dataTag: "left-5") << QTabBar::SelectLeftTab << 3 << (IntList() << 1) << ONE(1) << 0;
565 QTest::newRow(dataTag: "left-6") << QTabBar::SelectLeftTab << 3 << (IntList() << 1) << ONE(2) << 1;
566 QTest::newRow(dataTag: "left-7") << QTabBar::SelectLeftTab << 3 << (IntList() << 2) << ONE(0) << 1; // not removing current
567 QTest::newRow(dataTag: "left-8") << QTabBar::SelectLeftTab << 3 << (IntList() << 2) << ONE(1) << 1; // not removing current
568 QTest::newRow(dataTag: "left-9") << QTabBar::SelectLeftTab << 3 << (IntList() << 2) << ONE(2) << 1;
569
570 QTest::newRow(dataTag: "right-1") << QTabBar::SelectRightTab << 3 << (IntList() << 0) << ONE(0) << 0;
571 QTest::newRow(dataTag: "right-2") << QTabBar::SelectRightTab << 3 << (IntList() << 0) << ONE(1) << 0; // not removing current
572 QTest::newRow(dataTag: "right-3") << QTabBar::SelectRightTab << 3 << (IntList() << 0) << ONE(2) << 0; // not removing current
573 QTest::newRow(dataTag: "right-4") << QTabBar::SelectRightTab << 3 << (IntList() << 1) << ONE(0) << 0; // not removing current
574 QTest::newRow(dataTag: "right-5") << QTabBar::SelectRightTab << 3 << (IntList() << 1) << ONE(1) << 1;
575 QTest::newRow(dataTag: "right-6") << QTabBar::SelectRightTab << 3 << (IntList() << 1) << ONE(2) << 1; // not removing current
576 QTest::newRow(dataTag: "right-7") << QTabBar::SelectRightTab << 3 << (IntList() << 2) << ONE(0) << 1; // not removing current
577 QTest::newRow(dataTag: "right-8") << QTabBar::SelectRightTab << 3 << (IntList() << 2) << ONE(1) << 1; // not removing current
578 QTest::newRow(dataTag: "right-9") << QTabBar::SelectRightTab << 3 << (IntList() << 2) << ONE(2) << 1;
579
580 QTest::newRow(dataTag: "previous-0") << QTabBar::SelectPreviousTab << 3 << (IntList()) << ONE(0) << 0;
581 QTest::newRow(dataTag: "previous-1") << QTabBar::SelectPreviousTab << 3 << (IntList()) << ONE(1) << 0; // not removing current
582 QTest::newRow(dataTag: "previous-2") << QTabBar::SelectPreviousTab << 3 << (IntList()) << ONE(2) << 0; // not removing current
583
584 QTest::newRow(dataTag: "previous-3") << QTabBar::SelectPreviousTab << 3 << (IntList() << 2) << ONE(0) << 1; // not removing current
585 QTest::newRow(dataTag: "previous-4") << QTabBar::SelectPreviousTab << 3 << (IntList() << 2) << ONE(1) << 1; // not removing current
586 QTest::newRow(dataTag: "previous-5") << QTabBar::SelectPreviousTab << 3 << (IntList() << 2) << ONE(2) << 0;
587
588 // go back one
589 QTest::newRow(dataTag: "previous-6") << QTabBar::SelectPreviousTab << 4 << (IntList() << 0 << 2 << 3 << 1) << (IntList() << 1) << 2;
590 // go back two
591 QTest::newRow(dataTag: "previous-7") << QTabBar::SelectPreviousTab << 4 << (IntList() << 0 << 2 << 3 << 1) << (IntList() << 1 << 2) << 1;
592 // go back three
593 QTest::newRow(dataTag: "previous-8") << QTabBar::SelectPreviousTab << 4 << (IntList() << 0 << 2 << 3 << 1) << (IntList() << 1 << 2 << 1) << 0;
594
595 // pick from the middle
596 QTest::newRow(dataTag: "previous-9") << QTabBar::SelectPreviousTab << 4 << (IntList() << 0 << 2 << 3 << 1) << (IntList() << 2 << 1) << 1;
597
598 // every other one
599 QTest::newRow(dataTag: "previous-10") << QTabBar::SelectPreviousTab << 7 << (IntList() << 0 << 2 << 4 << 6) << (IntList() << 6 << 4) << 2;
600
601
602}
603
604void tst_QTabBar::selectionBehaviorOnRemove()
605{
606 QFETCH(QTabBar::SelectionBehavior, selectionBehavior);
607 QFETCH(int, tabs);
608 QFETCH(IntList, select);
609 QFETCH(IntList, remove);
610 QFETCH(int, expected);
611
612 QTabBar tabbar;
613 tabbar.setSelectionBehaviorOnRemove(selectionBehavior);
614 while(--tabs >= 0)
615 tabbar.addTab(text: QString::number(tabs));
616 QCOMPARE(tabbar.currentIndex(), 0);
617 while(!select.isEmpty())
618 tabbar.setCurrentIndex(select.takeFirst());
619 while(!remove.isEmpty())
620 tabbar.removeTab(index: remove.takeFirst());
621 QVERIFY(tabbar.count() > 0);
622 QCOMPARE(tabbar.currentIndex(), expected);
623}
624
625Q_DECLARE_METATYPE(QTabBar::Shape)
626void tst_QTabBar::moveTab_data()
627{
628 QTest::addColumn<QTabBar::Shape>(name: "shape");
629 QTest::addColumn<int>(name: "tabs");
630 QTest::addColumn<int>(name: "from");
631 QTest::addColumn<int>(name: "to");
632
633 QTest::newRow(dataTag: "null-0") << QTabBar::RoundedNorth << 0 << -1 << -1;
634 QTest::newRow(dataTag: "null-1") << QTabBar::RoundedEast << 0 << -1 << -1;
635 QTest::newRow(dataTag: "null-2") << QTabBar::RoundedEast << 1 << 0 << 0;
636
637 QTest::newRow(dataTag: "two-0") << QTabBar::RoundedNorth << 2 << 0 << 1;
638 QTest::newRow(dataTag: "two-1") << QTabBar::RoundedNorth << 2 << 1 << 0;
639
640 QTest::newRow(dataTag: "five-0") << QTabBar::RoundedNorth << 5 << 1 << 3; // forward
641 QTest::newRow(dataTag: "five-1") << QTabBar::RoundedNorth << 5 << 3 << 1; // reverse
642
643 QTest::newRow(dataTag: "five-2") << QTabBar::RoundedNorth << 5 << 0 << 4; // forward
644 QTest::newRow(dataTag: "five-3") << QTabBar::RoundedNorth << 5 << 1 << 4; // forward
645 QTest::newRow(dataTag: "five-4") << QTabBar::RoundedNorth << 5 << 3 << 4; // forward
646}
647
648void tst_QTabBar::moveTab()
649{
650 QFETCH(QTabBar::Shape, shape);
651 QFETCH(int, tabs);
652 QFETCH(int, from);
653 QFETCH(int, to);
654
655 TabBar bar;
656 bar.setShape(shape);
657 while(--tabs >= 0)
658 bar.addTab(text: QString::number(tabs));
659 bar.moveTab(from, to);
660}
661
662
663class MyTabBar : public QTabBar
664{
665 Q_OBJECT
666public slots:
667 void onCurrentChanged()
668 {
669 //we just want this to be done once
670 disconnect(sender: this, SIGNAL(currentChanged(int)), receiver: this, SLOT(onCurrentChanged()));
671 removeTab(index: 0);
672 }
673};
674
675void tst_QTabBar::task251184_removeTab()
676{
677 MyTabBar bar;
678 bar.addTab(text: "bar1");
679 bar.addTab(text: "bar2");
680 QCOMPARE(bar.count(), 2);
681 QCOMPARE(bar.currentIndex(), 0);
682
683 bar.connect(asender: &bar, SIGNAL(currentChanged(int)), SLOT(onCurrentChanged()));
684 bar.setCurrentIndex(1);
685
686 QCOMPARE(bar.count(), 1);
687 QCOMPARE(bar.currentIndex(), 0);
688 QCOMPARE(bar.tabText(bar.currentIndex()), QString("bar2"));
689}
690
691
692class TitleChangeTabBar : public QTabBar
693{
694 Q_OBJECT
695
696 QTimer timer;
697 int count;
698
699public:
700 TitleChangeTabBar(QWidget * parent = 0) : QTabBar(parent), count(0)
701 {
702 setMovable(true);
703 addTab(text: "0");
704 connect(sender: &timer, SIGNAL(timeout()), receiver: this, SLOT(updateTabText()));
705 timer.start(msec: 1);
706 }
707
708public slots:
709 void updateTabText()
710 {
711 count++;
712 setTabText(index: 0, text: QString::number(count));
713 }
714};
715
716void tst_QTabBar::changeTitleWhileDoubleClickingTab()
717{
718 TitleChangeTabBar bar;
719 QPoint tabPos = bar.tabRect(index: 0).center();
720
721 for(int i=0; i < 10; i++)
722 QTest::mouseDClick(widget: &bar, button: Qt::LeftButton, stateKey: {}, pos: tabPos);
723}
724
725class Widget10052 : public QWidget
726{
727public:
728 Widget10052(QWidget *parent) : QWidget(parent), moved(false)
729 { }
730
731 void moveEvent(QMoveEvent *e)
732 {
733 moved = e->oldPos() != e->pos();
734 QWidget::moveEvent(event: e);
735 }
736
737 bool moved;
738};
739
740void tst_QTabBar::taskQTBUG_10052_widgetLayoutWhenMoving()
741{
742 QTabBar tabBar;
743 tabBar.insertTab(index: 0, text: "My first tab");
744 Widget10052 w1(&tabBar);
745 tabBar.setTabButton(index: 0, position: QTabBar::RightSide, widget: &w1);
746 tabBar.insertTab(index: 1, text: "My other tab");
747 Widget10052 w2(&tabBar);
748 tabBar.setTabButton(index: 1, position: QTabBar::RightSide, widget: &w2);
749
750 tabBar.show();
751 QVERIFY(QTest::qWaitForWindowExposed(&tabBar));
752 w1.moved = w2.moved = false;
753 tabBar.moveTab(from: 0, to: 1);
754 QTRY_VERIFY(w1.moved);
755 QVERIFY(w2.moved);
756}
757
758void tst_QTabBar::tabBarClicked()
759{
760 QTabBar tabBar;
761 tabBar.addTab(text: "0");
762 QSignalSpy clickSpy(&tabBar, SIGNAL(tabBarClicked(int)));
763 QSignalSpy doubleClickSpy(&tabBar, SIGNAL(tabBarDoubleClicked(int)));
764
765 QCOMPARE(clickSpy.count(), 0);
766 QCOMPARE(doubleClickSpy.count(), 0);
767
768 Qt::MouseButton button = Qt::LeftButton;
769 while (button <= Qt::MaxMouseButton) {
770 const QPoint tabPos = tabBar.tabRect(index: 0).center();
771
772 QTest::mouseClick(widget: &tabBar, button, stateKey: {}, pos: tabPos);
773 QCOMPARE(clickSpy.count(), 1);
774 QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0);
775 QCOMPARE(doubleClickSpy.count(), 0);
776
777 QTest::mouseDClick(widget: &tabBar, button, stateKey: {}, pos: tabPos);
778 QCOMPARE(clickSpy.count(), 1);
779 QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0);
780 QCOMPARE(doubleClickSpy.count(), 1);
781 QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), 0);
782
783 const QPoint barPos(tabBar.tabRect(index: 0).right() + 5, tabBar.tabRect(index: 0).center().y());
784
785 QTest::mouseClick(widget: &tabBar, button, stateKey: {}, pos: barPos);
786 QCOMPARE(clickSpy.count(), 1);
787 QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1);
788 QCOMPARE(doubleClickSpy.count(), 0);
789
790 QTest::mouseDClick(widget: &tabBar, button, stateKey: {}, pos: barPos);
791 QCOMPARE(clickSpy.count(), 1);
792 QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1);
793 QCOMPARE(doubleClickSpy.count(), 1);
794 QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), -1);
795
796 button = Qt::MouseButton(button << 1);
797 }
798}
799
800void tst_QTabBar::autoHide()
801{
802 QTabBar tabBar;
803 QVERIFY(!tabBar.autoHide());
804 QVERIFY(!tabBar.isVisible());
805 tabBar.show();
806 QVERIFY(tabBar.isVisible());
807 tabBar.addTab(text: "0");
808 QVERIFY(tabBar.isVisible());
809 tabBar.removeTab(index: 0);
810 QVERIFY(tabBar.isVisible());
811
812 tabBar.setAutoHide(true);
813 QVERIFY(!tabBar.isVisible());
814 tabBar.addTab(text: "0");
815 QVERIFY(!tabBar.isVisible());
816 tabBar.addTab(text: "1");
817 QVERIFY(tabBar.isVisible());
818 tabBar.removeTab(index: 0);
819 QVERIFY(!tabBar.isVisible());
820 tabBar.removeTab(index: 0);
821 QVERIFY(!tabBar.isVisible());
822
823 tabBar.setAutoHide(false);
824 QVERIFY(tabBar.isVisible());
825}
826
827void tst_QTabBar::mouseReleaseOutsideTabBar()
828{
829 class RepaintChecker : public QObject
830 {
831 public:
832 bool repainted = false;
833 QRect rectToBeRepainted;
834 bool eventFilter(QObject *, QEvent *event) override
835 {
836 if (event->type() == QEvent::Paint
837 && rectToBeRepainted.contains(r: static_cast<QPaintEvent *>(event)->rect()))
838 repainted = true;
839 return false;
840 }
841 } repaintChecker;
842
843 QTabBar tabBar;
844 tabBar.installEventFilter(filterObj: &repaintChecker);
845 tabBar.addTab(text: " ");
846 tabBar.addTab(text: " ");
847 tabBar.show();
848 if (!QTest::qWaitForWindowExposed(widget: &tabBar))
849 QSKIP("Window failed to show, skipping test");
850
851 QRect tabRect = tabBar.tabRect(index: 1);
852 QPoint tabCenter = tabRect.center();
853 QTest::mousePress(widget: &tabBar, button: Qt::LeftButton, stateKey: {}, pos: tabCenter);
854 QTest::mouseEvent(action: QTest::MouseMove, widget: &tabBar, button: Qt::LeftButton, stateKey: {}, pos: tabCenter + QPoint(tabCenter.x(), tabCenter.y() + tabRect.height()));
855
856 // make sure the holding tab is repainted after releasing the mouse
857 repaintChecker.repainted = false;
858 repaintChecker.rectToBeRepainted = tabRect;
859 QTest::mouseRelease(widget: &tabBar, button: Qt::LeftButton, stateKey: {}, pos: tabCenter + QPoint(tabCenter.x(), tabCenter.y() + tabRect.height()));
860 QTRY_VERIFY(repaintChecker.repainted);
861}
862
863void tst_QTabBar::checkPositions(const TabBar &tabbar, const QList<int> &positions)
864{
865 QStyleOptionTab option;
866 int iPos = 0;
867 for (int i = 0; i < tabbar.count(); ++i) {
868 if (!tabbar.isTabVisible(index: i))
869 continue;
870 tabbar.initStyleOption(option: &option, tabIndex: i);
871 QCOMPARE(option.position, positions.at(iPos++));
872 }
873}
874
875void tst_QTabBar::scrollButtons_data()
876{
877 QTest::addColumn<QTabWidget::TabPosition>(name: "tabPosition");
878 QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection");
879
880 for (auto ld : {Qt::LeftToRight, Qt::RightToLeft}) {
881 const char *ldStr = ld == Qt::LeftToRight ? "LTR" : "RTL";
882 QTest::addRow(format: "North, %s", ldStr) << QTabWidget::North << ld;
883 QTest::addRow(format: "South, %s", ldStr) << QTabWidget::South << ld;
884 QTest::addRow(format: "West, %s", ldStr) << QTabWidget::West << ld;
885 QTest::addRow(format: "East, %s", ldStr) << QTabWidget::East << ld;
886 }
887}
888
889void tst_QTabBar::scrollButtons()
890{
891 QFETCH(QTabWidget::TabPosition, tabPosition);
892 QFETCH(Qt::LayoutDirection, layoutDirection);
893
894 QWidget window;
895 QTabWidget tabWidget(&window);
896 tabWidget.setLayoutDirection(layoutDirection);
897 tabWidget.setTabPosition(tabPosition);
898 tabWidget.setElideMode(Qt::ElideNone);
899 tabWidget.setUsesScrollButtons(true);
900
901 const int tabCount = 5;
902 for (int i = 0; i < tabCount; ++i)
903 {
904 const QString num = QString::number(i);
905 tabWidget.addTab(widget: new QPushButton(num), num + " - Really long tab name to force arrows");
906 }
907 tabWidget.move(ax: 0, ay: 0);
908 tabWidget.resize(tabWidget.minimumSizeHint());
909 window.show();
910 QVERIFY(QTest::qWaitForWindowActive(&window));
911
912 auto *leftB = tabWidget.tabBar()->findChild<QAbstractButton*>(QStringLiteral("ScrollLeftButton"));
913 auto *rightB = tabWidget.tabBar()->findChild<QAbstractButton*>(QStringLiteral("ScrollRightButton"));
914
915 QVERIFY(leftB->isVisible());
916 QVERIFY(!leftB->isEnabled());
917 QVERIFY(rightB->isVisible());
918 QVERIFY(rightB->isEnabled());
919 QVERIFY(!tabWidget.tabBar()->tabRect(1).intersects(tabWidget.tabBar()->rect()));
920
921 int index = 0;
922 for (; index < tabWidget.count(); ++index) {
923 QCOMPARE(leftB->isEnabled(), index > 0);
924 QCOMPARE(rightB->isEnabled(), index < tabWidget.count() - 1);
925 QVERIFY(tabWidget.tabBar()->tabRect(index).intersects(tabWidget.tabBar()->rect()));
926 QCOMPARE(tabWidget.tabBar()->tabAt(tabWidget.tabBar()->rect().center()), index);
927 if (rightB->isEnabled())
928 rightB->click();
929 }
930 for (--index; index >= 0; --index) {
931 QCOMPARE(leftB->isEnabled(), index >= 0);
932 QCOMPARE(rightB->isEnabled(), index < tabWidget.count() - 1);
933
934 QVERIFY(tabWidget.tabBar()->tabRect(index).intersects(tabWidget.tabBar()->rect()));
935 if (leftB->isEnabled())
936 leftB->click();
937 }
938 QVERIFY(!leftB->isEnabled());
939}
940
941QTEST_MAIN(tst_QTabBar)
942#include "tst_qtabbar.moc"
943

source code of qtbase/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp