diff options
Diffstat (limited to 'tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp')
-rw-r--r-- | tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp | 385 |
1 files changed, 328 insertions, 57 deletions
diff --git a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp index f51f0a23d5..03131cebe4 100644 --- a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp +++ b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -36,6 +11,12 @@ #include <QStyleOptionTab> #include <QProxyStyle> #include <QTimer> +#include <QScreen> +#include <QWindow> + +#include <QtWidgets/private/qtabbar_p.h> + +using namespace Qt::StringLiterals; class TabBar; @@ -69,6 +50,7 @@ private slots: void hideTab_data(); void hideTab(); void hideAllTabs(); + void checkHiddenTab(); void setElideMode_data(); void setElideMode(); @@ -78,6 +60,7 @@ private slots: void setUsesScrollButtons(); void removeLastTab(); + void removeLastVisibleTab(); void closeButton(); @@ -114,6 +97,13 @@ private slots: void hoverTab_data(); void hoverTab(); + void resizeKeepsScroll_data(); + void resizeKeepsScroll(); + void changeTabTextKeepsScroll(); + void settingCurrentTabBeforeShowDoesntScroll(); + void checkPositionsAfterShapeChange(); + void checkScrollOffsetAfterTabRemoval(); + private: void checkPositions(const TabBar &tabbar, const QList<int> &positions); }; @@ -229,7 +219,7 @@ void tst_QTabBar::testCurrentChanged() QCOMPARE(tabBar.currentIndex(), 0); tabBar.setCurrentIndex(tabToSet); QCOMPARE(tabBar.currentIndex(), tabToSet); - QCOMPARE(spy.count(), expectedCount); + QCOMPARE(spy.size(), expectedCount); } class TabBar : public QTabBar @@ -303,7 +293,7 @@ void tst_QTabBar::removeTab() tabbar.setCurrentIndex(currentIndex); QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); tabbar.removeTab(deleteIndex); - QTEST(int(spy.count()), "spyCount"); + QTEST(int(spy.size()), "spyCount"); QTEST(tabbar.currentIndex(), "finalIndex"); } @@ -334,7 +324,7 @@ void tst_QTabBar::hideTab() tabbar.setCurrentIndex(currentIndex); QSignalSpy spy(&tabbar, &QTabBar::currentChanged); tabbar.setTabVisible(hideIndex, false); - QTEST(int(spy.count()), "spyCount"); + QTEST(int(spy.size()), "spyCount"); QTEST(tabbar.currentIndex(), "finalIndex"); } @@ -380,6 +370,25 @@ void tst_QTabBar::hideAllTabs() QVERIFY(sizeHint.width() < prevSizeHint.width()); } +void tst_QTabBar::checkHiddenTab() +{ + QTabBar tabbar; + + tabbar.addTab("foo"); + tabbar.addTab("bar"); + tabbar.addTab("baz"); + tabbar.setCurrentIndex(0); + tabbar.setTabVisible(1, false); + + QKeyEvent keyRight(QKeyEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(&tabbar, &keyRight)); + QCOMPARE(tabbar.currentIndex(), 2); + + QKeyEvent keyLeft(QKeyEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(&tabbar, &keyLeft)); + QCOMPARE(tabbar.currentIndex(), 0); +} + void tst_QTabBar::setElideMode_data() { QTest::addColumn<int>("tabElideMode"); @@ -478,16 +487,49 @@ void tst_QTabBar::removeLastTab() QTabBar tabbar; QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); int index = tabbar.addTab("foo"); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), index); spy.clear(); tabbar.removeTab(index); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), -1); spy.clear(); } +void tst_QTabBar::removeLastVisibleTab() +{ + QTabBar tabbar; + tabbar.setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior::SelectRightTab); + + int invisible = tabbar.addTab("invisible"); + int visible = tabbar.addTab("visible"); + tabbar.setCurrentIndex(visible); + tabbar.adjustSize(); + + tabbar.setTabVisible(invisible, false); + { + QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); + tabbar.removeTab(visible); + QCOMPARE(spy.size(), 1); + QCOMPARE(spy.at(0).at(0).toInt(), -1); + QCOMPARE(tabbar.currentIndex(), -1); + } + + tabbar.setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior::SelectLeftTab); + visible = tabbar.insertTab(0, "visible"); + ++invisible; + QVERIFY(!tabbar.isTabVisible(invisible)); + tabbar.setCurrentIndex(visible); + { + QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); + tabbar.removeTab(visible); + QCOMPARE(spy.size(), 1); + QCOMPARE(spy.at(0).at(0).toInt(), -1); + QCOMPARE(tabbar.currentIndex(), -1); + } +} + void tst_QTabBar::closeButton() { QTabBar tabbar; @@ -506,7 +548,7 @@ void tst_QTabBar::closeButton() QSignalSpy spy(&tabbar, SIGNAL(tabCloseRequested(int))); button->click(); QCOMPARE(tabbar.count(), 1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } Q_DECLARE_METATYPE(QTabBar::ButtonPosition) @@ -778,36 +820,36 @@ void tst_QTabBar::tabBarClicked() QSignalSpy clickSpy(&tabBar, SIGNAL(tabBarClicked(int))); QSignalSpy doubleClickSpy(&tabBar, SIGNAL(tabBarDoubleClicked(int))); - QCOMPARE(clickSpy.count(), 0); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(clickSpy.size(), 0); + QCOMPARE(doubleClickSpy.size(), 0); Qt::MouseButton button = Qt::LeftButton; while (button <= Qt::MaxMouseButton) { const QPoint tabPos = tabBar.tabRect(0).center(); QTest::mouseClick(&tabBar, button, {}, tabPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(doubleClickSpy.size(), 0); QTest::mouseDClick(&tabBar, button, {}, tabPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0); - QCOMPARE(doubleClickSpy.count(), 1); + QCOMPARE(doubleClickSpy.size(), 1); QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), 0); QTest::mouseRelease(&tabBar, button, {}, tabPos); const QPoint barPos(tabBar.tabRect(0).right() + 5, tabBar.tabRect(0).center().y()); QTest::mouseClick(&tabBar, button, {}, barPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(doubleClickSpy.size(), 0); QTest::mouseDClick(&tabBar, button, {}, barPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1); - QCOMPARE(doubleClickSpy.count(), 1); + QCOMPARE(doubleClickSpy.size(), 1); QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), -1); QTest::mouseRelease(&tabBar, button, {}, barPos); @@ -1011,8 +1053,8 @@ void tst_QTabBar::kineticWheel() window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); - const auto *leftButton = tabbar.findChild<QAbstractButton*>(u"ScrollLeftButton"_qs); - const auto *rightButton = tabbar.findChild<QAbstractButton*>(u"ScrollRightButton"_qs); + const auto *leftButton = tabbar.findChild<QAbstractButton*>(u"ScrollLeftButton"_s); + const auto *rightButton = tabbar.findChild<QAbstractButton*>(u"ScrollRightButton"_s); QVERIFY(leftButton && rightButton); QVERIFY(leftButton->isEnabled() && rightButton->isEnabled()); @@ -1190,8 +1232,8 @@ void tst_QTabBar::scrollButtons() window.show(); QVERIFY(QTest::qWaitForWindowActive(&window)); - auto *leftB = tabWidget.tabBar()->findChild<QAbstractButton*>(u"ScrollLeftButton"_qs); - auto *rightB = tabWidget.tabBar()->findChild<QAbstractButton*>(u"ScrollRightButton"_qs); + auto *leftB = tabWidget.tabBar()->findChild<QAbstractButton*>(u"ScrollLeftButton"_s); + auto *rightB = tabWidget.tabBar()->findChild<QAbstractButton*>(u"ScrollRightButton"_s); QVERIFY(leftB->isVisible()); QVERIFY(!leftB->isEnabled()); @@ -1246,9 +1288,10 @@ void tst_QTabBar::currentTabLargeFont() void tst_QTabBar::hoverTab_data() { - // Since we still rely on moving the mouse via QCursor::setPos in QTest::mouseMove, + // Move the cursor away from the widget as not to interfere. // skip this test if we can't - const QPoint cursorPos = QCursor::pos() + QPoint(10, 10); + const QPoint topLeft = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); + const QPoint cursorPos = topLeft + QPoint(10, 10); QCursor::setPos(cursorPos); if (!QTest::qWaitFor([cursorPos]{ return QCursor::pos() == cursorPos; }, 500)) QSKIP("Can't move mouse"); @@ -1261,7 +1304,7 @@ void tst_QTabBar::hoverTab_data() void tst_QTabBar::hoverTab() { QFETCH(bool, documentMode); - QWidget window; + QWidget topLevel; class TabBar : public QTabBar { public: @@ -1272,7 +1315,7 @@ void tst_QTabBar::hoverTab() styleOptions[tabIndex] = *option; } mutable QHash<int, QStyleOptionTab> styleOptions; - } tabbar(&window); + } tabbar(&topLevel); tabbar.setDocumentMode(documentMode); tabbar.addTab("A"); @@ -1281,7 +1324,10 @@ void tst_QTabBar::hoverTab() tabbar.addTab("D"); tabbar.move(0,0); - window.setMinimumSize(tabbar.sizeHint()); + const QSize size = tabbar.sizeHint(); + const auto center = topLevel.screen()->availableGeometry().center(); + topLevel.move(center - QPoint{size.width(), size.height()} / 2); + topLevel.setMinimumSize(size); tabbar.ensurePolished(); // some styles set those flags, some don't. If not, use a style sheet @@ -1293,21 +1339,25 @@ void tst_QTabBar::hoverTab() )"); } - window.show(); - QVERIFY(QTest::qWaitForWindowExposed(&window)); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + auto *window = topLevel.windowHandle(); - QTest::mouseMove(&tabbar, tabbar.tabRect(0).center()); + auto pos = tabbar.mapToParent(tabbar.tabRect(0).center()); + QTest::mouseMove(window, pos); QTRY_VERIFY(tabbar.styleOptions[0].state & QStyle::State_Selected); QTRY_COMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); QTRY_COMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_None); QTRY_COMPARE(tabbar.styleOptions[3].state & QStyle::State_MouseOver, QStyle::State_None); - QTest::mouseMove(&tabbar, tabbar.tabRect(1).center()); + pos = tabbar.mapToParent(tabbar.tabRect(1).center()); + QTest::mouseMove(window, pos); QTRY_COMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_MouseOver); QCOMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_None); QCOMPARE(tabbar.styleOptions[3].state & QStyle::State_MouseOver, QStyle::State_None); - QTest::mouseMove(&tabbar, tabbar.tabRect(2).center()); + pos = tabbar.mapToParent(tabbar.tabRect(2).center()); + QTest::mouseMove(window, pos); QTRY_COMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_MouseOver); QCOMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); QCOMPARE(tabbar.styleOptions[3].state & QStyle::State_MouseOver, QStyle::State_None); @@ -1325,5 +1375,226 @@ void tst_QTabBar::hoverTab() QCOMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); } + +void tst_QTabBar::resizeKeepsScroll_data() +{ + QTest::addColumn<QTabBar::Shape>("tabShape"); + QTest::addColumn<bool>("expanding"); + + QTest::addRow("North, expanding") << QTabBar::RoundedNorth << true; + QTest::addRow("East, expanding") << QTabBar::RoundedEast << true; + QTest::addRow("South, expanding") << QTabBar::RoundedSouth << true; + QTest::addRow("West, expanding") << QTabBar::RoundedWest << true; + + QTest::addRow("North, not expanding") << QTabBar::RoundedNorth << false; + QTest::addRow("South, not expanding") << QTabBar::RoundedSouth << false; +} + +void tst_QTabBar::resizeKeepsScroll() +{ + QFETCH(QTabBar::Shape, tabShape); + QFETCH(const bool, expanding); + + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 10; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + tabBar.setShape(tabShape); + tabBar.setUsesScrollButtons(true); + tabBar.setExpanding(expanding); + + // resize to half + const QSize fullSize = tabBar.sizeHint(); + const bool horizontal = fullSize.width() > fullSize.height(); + if (horizontal) + tabBar.resize(fullSize.width() / 2, fullSize.height()); + else + tabBar.resize(fullSize.width(), fullSize.height() / 2); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + // select a tab outside, this will scroll + tabBar.setCurrentIndex(6); + // the first tab is now scrolled out + const int scrollOffset = getScrollOffset(); + QCOMPARE_GT(scrollOffset, 0); + // the current index is now fully visible, with margin on both sides + tabBar.setCurrentIndex(5); + + // make the tab bar a bit larger, by the width of a tab + if (horizontal) + tabBar.resize(tabBar.width() + tabBar.tabRect(5).width(), tabBar.height()); + else + tabBar.resize(tabBar.width(), tabBar.height() + tabBar.tabRect(5).height()); + + // this should not change the scroll + QCOMPARE(getScrollOffset(), scrollOffset); + + // make the tab bar large enough to fit everything with extra space + tabBar.resize(fullSize + QSize(50, 50)); + + // there should be no scroll + QCOMPARE(getScrollOffset(), 0); + + for (int i = 0; i < tabBar.count(); ++i) { + tabBar.setCurrentIndex(i); + QCOMPARE(getScrollOffset(), 0); + } +} + +void tst_QTabBar::changeTabTextKeepsScroll() +{ + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 6; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + const QSize fullSize = tabBar.sizeHint(); + tabBar.resize(fullSize.width() / 2, fullSize.height()); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + tabBar.setCurrentIndex(3); + const int scrollOffset = getScrollOffset(); + tabBar.setTabText(3, "New title"); + QCOMPARE(getScrollOffset(), scrollOffset); +} + +void tst_QTabBar::settingCurrentTabBeforeShowDoesntScroll() +{ + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 6; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + tabBar.setCurrentIndex(5); + + // changing the current index while the tab bar isn't visible shouldn't scroll yet + QCOMPARE(getScrollOffset(), 0); + + // now show the tab bar with a size that's too small to fit the current index + const QSize fullSize = tabBar.sizeHint(); + tabBar.resize(fullSize.width() / 2, fullSize.height()); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + // this should scroll + QCOMPARE_GT(getScrollOffset(), 0); +} + +void tst_QTabBar::checkPositionsAfterShapeChange() +{ + class TabWidget : public QTabWidget + { + public: + using QTabWidget::QTabWidget; + using QTabWidget::setTabBar; + }; + + class TabBar : public QTabBar + { + public: + using QTabBar::initStyleOption; + void resizeEvent(QResizeEvent *e) override + { + QTabBar::resizeEvent(e); + resized = true; + } + bool resized = false; + }; + + TabWidget tabWidget; + auto *tabBar = new TabBar; + tabWidget.setTabBar(tabBar); + for (int i = 0; i < 3; ++i) + tabWidget.addTab(new QWidget, u"Tab %1"_s.arg(i)); + tabWidget.setTabPosition(QTabWidget::North); + tabWidget.setCurrentIndex(2); + tabWidget.resize(300, 300); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + tabBar->resized = false; + tabWidget.setTabPosition(QTabWidget::East); + QVERIFY(QTest::qWaitFor([&]() { return tabBar->resized; })); + QStyleOptionTab opt; + tabBar->initStyleOption(&opt, 2); + QVERIFY(opt.rect.top() > 0); +} + +void tst_QTabBar::checkScrollOffsetAfterTabRemoval() +{ + QTabWidget tabWidget; + QTabBar *tabBar = tabWidget.tabBar(); + for (int i = 0; i < 10; ++i) + tabWidget.addTab(new QWidget, u"Tab %1"_s.arg(i)); + tabWidget.setTabPosition(QTabWidget::North); + tabWidget.resize(300, 300); + tabWidget.setCurrentIndex(0); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + auto *rightButton = tabBar->findChild<QAbstractButton *>(u"ScrollRightButton"_s); + auto *leftButton = tabBar->findChild<QAbstractButton *>(u"ScrollLeftButton"_s); + QVERIFY(leftButton); + QVERIFY(rightButton); + QVERIFY(rightButton->isEnabled()); + QVERIFY(!leftButton->isEnabled()); + // scroll to the right + tabBar->setCurrentIndex(9); + QVERIFY(!rightButton->isEnabled()); + QVERIFY(leftButton->isEnabled()); + // scroll to the center + tabBar->setCurrentIndex(2); + QVERIFY(rightButton->isEnabled()); + QVERIFY(leftButton->isEnabled()); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar))->scrollOffset; + }; + // the scroll offset should not change when a tab right outside + // the scroll rect is removed + auto oldOffset = getScrollOffset(); + tabWidget.removeTab(9); + QCOMPARE(getScrollOffset(), oldOffset); + // the scroll offset must change when a tab left outside + // the scroll rect is removed + oldOffset = getScrollOffset(); + tabWidget.removeTab(0); + QVERIFY(getScrollOffset() < oldOffset); + + // the scroll offset must change when there is empty + // place in the right after tab removal + oldOffset = getScrollOffset(); + QVERIFY(oldOffset > 0); + for (int i : { 7, 6, 5, 4, 3 }) + tabWidget.removeTab(i); + QCOMPARE(getScrollOffset(), 0); + QVERIFY(!rightButton->isVisible()); + QVERIFY(!leftButton->isVisible()); +} + QTEST_MAIN(tst_QTabBar) #include "tst_qtabbar.moc" |