diff --git a/src/MainWindowBase.h b/src/MainWindowBase.h index ab776244..f16f115d 100644 --- a/src/MainWindowBase.h +++ b/src/MainWindowBase.h @@ -28,6 +28,8 @@ #include #include +class TestCommon; + namespace KDDockWidgets { class DockWidgetBase; @@ -180,6 +182,7 @@ private: class Private; Private *const d; + friend class ::TestCommon; friend class LayoutSaver; bool deserialize(const LayoutSaver::MainWindow &); LayoutSaver::MainWindow serialize() const; diff --git a/tests/tst_common.cpp b/tests/tst_common.cpp index cf82d78f..3f8351bc 100644 --- a/tests/tst_common.cpp +++ b/tests/tst_common.cpp @@ -47,6 +47,15 @@ using namespace KDDockWidgets; using namespace Layouting; using namespace KDDockWidgets::Tests; +static int osWindowMinWidth() +{ +#ifdef Q_OS_WIN + return GetSystemMetrics(SM_CXMIN); +#else + return 140; // Some random value for our windows. It's only important on Windows +#endif +} + class TestCommon : public QObject { Q_OBJECT @@ -145,6 +154,24 @@ private Q_SLOTS: void tst_honourGeometryOfHiddenWindow(); void tst_0_data(); void tst_0(); + void tst_dockWindowWithTwoSideBySideFramesIntoRight(); + void tst_dockWindowWithTwoSideBySideFramesIntoLeft(); + void tst_posAfterLeftDetach(); + void tst_preventClose(); + void tst_propagateMinSize(); + void tst_createFloatingWindow(); + void tst_addAndReadd(); + void tst_addToSmallMainWindow1(); + void tst_addToSmallMainWindow2(); + void tst_addToSmallMainWindow3(); + void tst_addToSmallMainWindow4(); + void tst_addToSmallMainWindow5(); + void tst_fairResizeAfterRemoveWidget(); + void tst_invalidJSON_data(); + void tst_invalidJSON(); + + void tst_invalidPlaceholderPosition_data(); + void tst_invalidPlaceholderPosition(); }; void TestCommon::tst_simple1() @@ -2529,4 +2556,463 @@ void TestCommon::tst_registry() delete dw; } +void TestCommon::tst_dockWindowWithTwoSideBySideFramesIntoRight() +{ + EnsureTopLevelsDeleted e; + + auto fw = createFloatingWindow(); + auto dock2 = createDockWidget("doc2", Qt::red); + nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnTop); // No we stack on top, unlike in previous test + QCOMPARE(fw->frames().size(), 2); + + auto fw2 = createFloatingWindow(); + fw2->move(fw->x() + fw->width() + 100, fw->y()); + + dragFloatingWindowTo(fw, fw2->dropArea(), DropIndicatorOverlayInterface::DropLocation_Right); // Outter right instead of Left + QCOMPARE(fw2->frames().size(), 3); + QVERIFY(fw2->dropArea()->checkSanity()); + + fw2->deleteLater(); + Testing::waitForDeleted(fw2); +} + +void TestCommon::tst_dockWindowWithTwoSideBySideFramesIntoLeft() +{ + EnsureTopLevelsDeleted e; + + auto fw = createFloatingWindow(); + fw->setObjectName("fw1"); + + auto dock2 = createDockWidget("doc2", Qt::red); + nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnLeft); + QCOMPARE(fw->frames().size(), 2); + + auto fw2 = createFloatingWindow(); + fw2->setObjectName("fw2"); + fw2->move(fw->x() + fw->width() + 100, fw->y()); + + QVERIFY(fw2->dropArea()->checkSanity()); + dragFloatingWindowTo(fw, fw2->dropArea(), DropIndicatorOverlayInterface::DropLocation_Left); + QCOMPARE(fw2->frames().size(), 3); + + QVERIFY(fw2->dropArea()->checkSanity()); + + ///Cleanup + fw2->deleteLater(); + Testing::waitForDeleted(fw2); +} + +void TestCommon::tst_posAfterLeftDetach() +{ + { + EnsureTopLevelsDeleted e; + auto fw = createFloatingWindow(); + auto dock2 = createDockWidget("doc2", Qt::red); + nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnRight); + QVERIFY(fw->dropArea()->checkSanity()); + // When dragging the right one there was a bug where it jumped + const QPoint globalSrc = dock2->mapToGlobal(QPoint(0, 0)); + const int offset = 10; + const QPoint globalDest = globalSrc + QPoint(offset, 0); + drag(dock2, globalDest); + QVERIFY(fw->dropArea()->checkSanity()); + const QPoint actualEndPos = dock2->mapToGlobal(QPoint(0, 0)); + QVERIFY(actualEndPos.x() - globalSrc.x() < offset + 5); // 5px so we have margin for window system fluctuations. The actual bug was a very big jump like 50px, so a 5 margin is fine to test that the bug doesn't happen + + delete dock2; + fw->deleteLater(); + Testing::waitForDeleted(fw); + } + + { + EnsureTopLevelsDeleted e; + auto fw = createFloatingWindow(); + auto dock2 = createDockWidget("doc2", Qt::red); + nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnRight); + QVERIFY(fw->dropArea()->checkSanity()); + + const int originalX = dock2->mapToGlobal(QPoint(0, 0)).x(); + dock2->frame()->titleBar()->makeWindow(); + const int finalX = dock2->mapToGlobal(QPoint(0, 0)).x(); + + QVERIFY(finalX - originalX < 10); // 10 or some other small number that is less than say 200 + + delete dock2; + fw->deleteLater(); + Testing::waitForDeleted(fw); + } +} + +void TestCommon::tst_preventClose() +{ + EnsureTopLevelsDeleted e; + + auto nonClosableWidget = new NonClosableWidget(); + auto dock1 = new DockWidgetType("1"); + dock1->setWidget(nonClosableWidget); + + // 1. Test a floating dock widget + dock1->resize(100, 100); + dock1->show(); + QVERIFY(dock1->isVisible()); + dock1->close(); + QVERIFY(dock1->isVisible()); + + // 2. Morph it into a FlatingWindow + dock1->morphIntoFloatingWindow(); + dock1->close(); + QVERIFY(dock1->isVisible()); + dock1->frame()->titleBar()->onCloseClicked(); + QVERIFY(dock1->isVisible()); + auto fw = dock1->floatingWindow(); + fw->close(); + QVERIFY(dock1->isVisible()); + + dock1->deleteLater(); + QVERIFY(Testing::waitForDeleted(dock1)); +} + +void TestCommon::tst_propagateMinSize() +{ + EnsureTopLevelsDeleted e; + auto m = createMainWindow(); + auto dropArea = m->dropArea(); + + auto dock1 = createDockWidget("dock1", new QPushButton("one")); + auto dock2 = createDockWidget("dock2", new QPushButton("two")); + auto dock3 = createDockWidget("dock3", new QPushButton("three")); + + nestDockWidget(dock1, dropArea, nullptr, KDDockWidgets::Location_OnRight); + nestDockWidget(dock2, dropArea, nullptr, KDDockWidgets::Location_OnRight); + nestDockWidget(dock3, dropArea, nullptr, KDDockWidgets::Location_OnRight); + + // TODO finish this when the 3 dock widgets have proper sizes + //QTest::qWait(50000); + +} + +void TestCommon::tst_createFloatingWindow() +{ + EnsureTopLevelsDeleted e; + + auto dock = createDockWidget("doc1", Qt::green); + QVERIFY(dock); + QVERIFY(dock->isFloating()); + + QCOMPARE(dock->uniqueName(), QLatin1String("doc1")); // 1.0 objectName() is inherited + + QPointer window = dock->floatingWindow(); + QVERIFY(window); // 1.1 DockWidget creates a FloatingWindow and is reparented + QVERIFY(window->dropArea()->checkSanity()); + dock->deleteLater(); + QVERIFY(Testing::waitForDeleted(dock)); + QVERIFY(Testing::waitForDeleted(window)); // 1.2 Floating Window is destroyed when DockWidget is destroyed + QVERIFY(!window); +} + +void TestCommon::tst_addAndReadd() +{ + EnsureTopLevelsDeleted e; + + // 1. This just tests some crash I got. + // Make a dock widget float and immediately reattach it + auto m = createMainWindow(); + QTest::qWait(10); // the DND state machine needs the event loop to start, otherwise activeState() is nullptr. (for offscreen QPA) + + auto dock1 = createDockWidget("dock1", new QPushButton("1")); + m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft); + dock1->setFloating(true); + m->multiSplitter()->checkSanity(); + m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft); + dock1->frame()->titleBar()->makeWindow(); + m->multiSplitter()->checkSanity(); + m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft); + dock1->frame()->titleBar()->makeWindow(); + m->multiSplitter()->checkSanity(); + + auto fw = dock1->floatingWindow(); + QVERIFY(fw); + auto dropArea = m->dropArea(); + dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_OutterRight); + QVERIFY(!dock1->frame()->titleBar()->isVisible()); + fw->titleBar()->makeWindow(); + m->multiSplitter()->checkSanity(); + + //Cleanup + delete dock1; + Testing::waitForDeleted(fw); +} + + +void TestCommon::tst_addToSmallMainWindow1() +{ + EnsureTopLevelsDeleted e; + auto m = createMainWindow(); + auto dock1 = createDockWidget("dock1", new MyWidget2()); + auto dock2 = createDockWidget("dock2", new MyWidget2()); + auto dock3 = createDockWidget("dock3", new MyWidget2()); + auto dock4 = createDockWidget("dock4", new MyWidget2()); + + const int mainWindowLength = 400; + + m->resize(mainWindowLength, mainWindowLength); + dock1->resize(800, 800); + dock2->resize(800, 800); + dock3->resize(800, 800); + + // Add as tabbed: + m->addDockWidgetAsTab(dock1); + + QCOMPARE(m->height(), mainWindowLength); + QVERIFY(dock1->height() < mainWindowLength); + QVERIFY(dock1->width() < mainWindowLength); + + //Add in area: + m->addDockWidget(dock2, Location_OnLeft); + m->addDockWidget(dock3, Location_OnTop, dock2); + m->addDockWidget(dock4, Location_OnBottom); + + auto dropArea = m->dropArea(); + + QVERIFY(dropArea->checkSanity()); + QVERIFY(dock2->width() < mainWindowLength); + QVERIFY(dock3->height() < m->height()); + QVERIFY(dock4->height() < m->height()); +} + +void TestCommon::tst_addToSmallMainWindow2() +{ + EnsureTopLevelsDeleted e; + auto m = createMainWindow(); + auto dropArea = m->dropArea(); + auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(100, 100))); + auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(100, 100))); + m->addDockWidgetAsTab(dock1); + m->resize(osWindowMinWidth(), 200); + + Testing::waitForResize(m.get()); + + QVERIFY(qAbs(m->width() - osWindowMinWidth()) < 15); // Not very important verification. Anyway, using 15 to account for margins and what not. + m->addDockWidget(dock2, KDDockWidgets::Location_OnRight); + QVERIFY(Testing::waitForResize(m.get())); + + QVERIFY(dropArea->width() > osWindowMinWidth()); + QMargins margins = m->centerWidgetMargins(); + QCOMPARE(dropArea->width(), m->width() - margins.left() - margins.right()); + QVERIFY(m->dropArea()->checkSanity()); +} + +void TestCommon::tst_addToSmallMainWindow3() +{ + EnsureTopLevelsDeleted e; + auto m = createMainWindow(); + auto dropArea = m->dropArea(); + auto dock1 = createDockWidget("dock1", new MyWidget2()); + auto dock2 = createDockWidget("dock2", new MyWidget2()); + m->addDockWidgetAsTab(dock1); + m->resize(osWindowMinWidth(), 200); + QTest::qWait(200); + QVERIFY(qAbs(m->width() - osWindowMinWidth()) < 15); // Not very important verification. Anyway, using 15 to account for margins and what not. + + auto fw = dock2->morphIntoFloatingWindow(); + QVERIFY(fw->isVisible()); + QVERIFY(dropArea->checkSanity()); + dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_Right); + QVERIFY(m->dropArea()->checkSanity()); + delete fw; +} + +void TestCommon::tst_addToSmallMainWindow4() +{ + EnsureTopLevelsDeleted e; + auto m = createMainWindow(QSize(100, 100), MainWindowOption_None); + auto dropArea = m->dropArea(); + auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(50, 50))); + auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(50, 50))); + MultiSplitter *layout = dropArea; + m->addDockWidget(dock1, KDDockWidgets::Location_OnBottom); + Testing::waitForResize(m.get()); + + m->addDockWidget(dock2, KDDockWidgets::Location_OnBottom); + Testing::waitForResize(m.get()); + QVERIFY(m->dropArea()->checkSanity()); + + const int item2MinHeight = layout->itemForFrame(dock2->frame())->minLength(Qt::Vertical); + QCOMPARE(dropArea->height(), dock1->frame()->height() + item2MinHeight + Item::separatorThickness); +} + +void TestCommon::tst_addToSmallMainWindow5() +{ + EnsureTopLevelsDeleted e; + // Test test shouldn't spit any warnings + + auto m = createMainWindow(QSize(100, 100), MainWindowOption_None); + auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(50, 240))); + auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(50, 240))); + m->addDockWidget(dock1, KDDockWidgets::Location_OnBottom); + m->addDockWidget(dock2, KDDockWidgets::Location_OnBottom); + QVERIFY(m->dropArea()->checkSanity()); +} + +void TestCommon::tst_fairResizeAfterRemoveWidget() +{ + // 1. Add 3 dock widgets horizontally, remove the middle one, make sure + // both left and right widgets get a share of the new available space + + EnsureTopLevelsDeleted e; + + DockWidgetBase *dock1 = createDockWidget("dock1", new QPushButton("one")); + DockWidgetBase *dock2 = createDockWidget("dock2", new QPushButton("two")); + DockWidgetBase *dock3 = createDockWidget("dock3", new QPushButton("three")); + + dock1->addDockWidgetToContainingWindow(dock2, Location_OnRight); + dock1->addDockWidgetToContainingWindow(dock3, Location_OnRight, dock2); + + auto fw = dock1->floatingWindow(); + + QPointer frame2= dock2->frame(); + + const int oldWidth1 = dock1->frame()->width(); + const int oldWidth2 = dock2->frame()->width(); + const int oldWidth3 = dock3->frame()->width(); + MultiSplitter *layout = fw->dropArea(); + QCOMPARE(layout->count(), 3); + QCOMPARE(layout->visibleCount(), 3); + QCOMPARE(layout->placeholderCount(), 0); + + delete dock2; + QVERIFY(Testing::waitForResize(dock1)); + QVERIFY(!frame2); + + QCOMPARE(layout->count(), 2); + QCOMPARE(layout->visibleCount(), 2); + QCOMPARE(layout->placeholderCount(), 0); + + const int delta1 = (dock1->frame()->width() - oldWidth1); + const int delta3 = (dock3->frame()->width() - oldWidth3); + + qDebug() << "old1=" << oldWidth1 + << "; old3=" << oldWidth3 + << "; to spread=" << oldWidth2 + << "; Delta1=" << delta1 + << "; Delta3=" << delta3; + + QVERIFY(delta1 > 0); + QVERIFY(delta3 > 0); + QVERIFY(qAbs(delta3 - delta1) <= 1); // Both dock1 and dock3 should have increased by the same amount + + delete dock1->window(); +} + +void TestCommon::tst_invalidJSON_data() +{ + // Be sure that the main windows in the json are called "MyMainWindow1" and the dock widgets + // dock-x where x starts at 0 + QTest::addColumn("layoutFileName"); + QTest::addColumn("numDockWidgets"); + QTest::addColumn("expectedWarning"); + QTest::addColumn("expectedResult"); + QTest::newRow("unsupported-serialization-version") << "unsupported-serialization-version.json" + << 10 + << "Serialization format is too old" + << false; + QTest::newRow("invalid") << "invalid.json" << 29 << "" << false; + QTest::newRow("overlapping-item") << "overlapping-item.json" << 2 << "Unexpected pos" << true; +} + +void TestCommon::tst_invalidJSON() +{ + QFETCH(QString, layoutFileName); + QFETCH(int, numDockWidgets); + QFETCH(QString, expectedWarning); + QFETCH(bool, expectedResult); + + const QString absoluteLayoutFileName = QStringLiteral(":/layouts/%1").arg(layoutFileName); + + EnsureTopLevelsDeleted e; + auto m1 = createMainWindow(QSize(800, 500), MainWindowOption_None, "MyMainWindow1"); + for (int i = 0; i < numDockWidgets; ++i) { + createDockWidget(QStringLiteral("dock-%1").arg(i), new QPushButton("one")); + } + + SetExpectedWarning sew(expectedWarning); + + LayoutSaver restorer; + QCOMPARE(restorer.restoreFromFile(absoluteLayoutFileName), expectedResult); +} + +void TestCommon::tst_invalidPlaceholderPosition_data() +{ + QTest::addColumn("restore1First"); + QTest::newRow("restore1First") << true; + QTest::newRow("restore2First") << false; +} + +void TestCommon::tst_invalidPlaceholderPosition() +{ + QFETCH(bool, restore1First); + + // Tests a bug I saw: 3 widgets stacked, close the top one, then the second top one + // result: the bottom most one didn't have it's top separator at y=0 + + EnsureTopLevelsDeleted e; + auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); + auto dock1 = createDockWidget("1", new QPushButton("1")); + auto dock2 = createDockWidget("2", new QPushButton("2")); + auto dock3 = createDockWidget("3", new QPushButton("3")); + + MultiSplitter *layout = m->multiSplitter(); + + // Stack: 1, 2, 3 vertically + m->addDockWidget(dock3, Location_OnTop); + m->addDockWidget(dock2, Location_OnTop); + m->addDockWidget(dock1, Location_OnTop); + + auto frame1 = dock1->frame(); + auto frame2 = dock2->frame(); + auto frame3 = dock3->frame(); + QCOMPARE(frame1->QWidgetAdapter::y(), 0); + + // Close 1 + dock1->close(); + Testing::waitForResize(frame2); + + // Check that frame2 moved up to y=1 + QCOMPARE(frame2->QWidgetAdapter::y(), 0); + + // Close 2 + dock2->close(); + Testing::waitForResize(dock3); + + QVERIFY(layout->checkSanity()); + QCOMPARE(layout->count(), 3); + QCOMPARE(layout->placeholderCount(), 2); + + // Check that frame3 moved up to y=1 + QCOMPARE(frame3->QWidgetAdapter::y(), 0); + + // Now restore: + auto toRestore1 = restore1First ? dock1 : dock2; + auto toRestore2 = restore1First ? dock2 : dock1; + + + qDebug() << "About to show dock1"; + toRestore1->show(); + QCOMPARE(layout->placeholderCount(), 1); + QVERIFY(dock3->isVisible()); + QVERIFY(!dock3->size().isNull()); + + toRestore2->show(); + + Testing::waitForResize(frame3); + QVERIFY(layout->checkSanity()); + QCOMPARE(layout->count(), 3); + QCOMPARE(layout->placeholderCount(), 0); + layout->checkSanity(); + + dock1->deleteLater(); + dock2->deleteLater(); + QVERIFY(Testing::waitForDeleted(dock2)); +} + #include "tst_common.moc" diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index 422427ad..b4f09456 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -196,36 +196,21 @@ public Q_SLOTS: private Q_SLOTS: void tst_mainWindowAlwaysHasCentralWidget(); - void tst_createFloatingWindow(); void tst_dock2FloatingWidgetsTabbed(); void tst_close(); - void tst_preventClose(); - void tst_dockWindowWithTwoSideBySideFramesIntoLeft(); - void tst_dockWindowWithTwoSideBySideFramesIntoRight(); - void tst_posAfterLeftDetach(); - void tst_propagateMinSize(); void tst_propagateSizeHonoursMinSize(); void tst_addDockWidgetAsTabToDockWidget(); void tst_addDockWidgetToMainWindow(); // Tests MainWindow::addDockWidget(); void tst_addDockWidgetToContainingWindow(); - void tst_addToSmallMainWindow1(); - void tst_addToSmallMainWindow2(); - void tst_addToSmallMainWindow3(); - void tst_addToSmallMainWindow4(); - void tst_addToSmallMainWindow5(); void tst_addToSmallMainWindow6(); - void tst_fairResizeAfterRemoveWidget(); void tst_notClosable(); void tst_constraintsAfterPlaceholder(); void tst_setFloatingAfterDraggedFromTabToSideBySide(); void tst_setFloatingAFrameWithTabs(); void tst_setVisibleFalseWhenSideBySide(); - void tst_addAndReadd(); void tst_embeddedMainWindow(); - void tst_invalidPlaceholderPosition_data(); - void tst_invalidPlaceholderPosition(); void tst_resizeViaAnchorsAfterPlaceholderCreation(); void tst_negativeAnchorPositionWhenEmbedded_data(); void tst_negativeAnchorPositionWhenEmbedded(); @@ -249,8 +234,6 @@ private Q_SLOTS: void tst_restoreWithDockFactory(); void tst_restoreResizesLayout(); void tst_invalidLayoutAfterRestore(); - void tst_invalidJSON_data(); - void tst_invalidJSON(); void tst_rectForDropCrash(); @@ -317,25 +300,6 @@ Frame* createFrameWithWidget(const QString &name, MultiSplitter *parent, int min return frame; } -void TestDocks::tst_createFloatingWindow() -{ - EnsureTopLevelsDeleted e; - - auto dock = createDockWidget("doc1", Qt::green); - QVERIFY(dock); - QVERIFY(dock->isFloating()); - - QCOMPARE(dock->uniqueName(), QLatin1String("doc1")); // 1.0 objectName() is inherited - - QPointer window = dock->floatingWindow(); - QVERIFY(window); // 1.1 DockWidget creates a FloatingWindow and is reparented - QVERIFY(window->dropArea()->checkSanity()); - dock->deleteLater(); - QVERIFY(Testing::waitForDeleted(dock)); - QVERIFY(Testing::waitForDeleted(window)); // 1.2 Floating Window is destroyed when DockWidget is destroyed - QVERIFY(!window); -} - DockWidgetBase *createAndNestDockWidget(DropArea *dropArea, Frame *relativeTo, KDDockWidgets::Location location) { static int count = 0; @@ -613,122 +577,6 @@ void TestDocks::tst_close() } } -void TestDocks::tst_preventClose() -{ - EnsureTopLevelsDeleted e; - - auto nonClosableWidget = new NonClosableWidget(); - auto dock1 = new DockWidgetType("1"); - dock1->setWidget(nonClosableWidget); - - // 1. Test a floating dock widget - dock1->resize(100, 100); - dock1->show(); - QVERIFY(dock1->isVisible()); - dock1->close(); - QVERIFY(dock1->isVisible()); - - // 2. Morph it into a FlatingWindow - dock1->morphIntoFloatingWindow(); - dock1->close(); - QVERIFY(dock1->isVisible()); - dock1->frame()->titleBar()->onCloseClicked(); - QVERIFY(dock1->isVisible()); - auto fw = dock1->floatingWindow(); - fw->close(); - QVERIFY(dock1->isVisible()); - - dock1->deleteLater(); - QVERIFY(Testing::waitForDeleted(dock1)); -} - -void TestDocks::tst_dockWindowWithTwoSideBySideFramesIntoLeft() -{ - EnsureTopLevelsDeleted e; - - auto fw = createFloatingWindow(); - fw->setObjectName("fw1"); - - auto dock2 = createDockWidget("doc2", Qt::red); - nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnLeft); - QCOMPARE(fw->frames().size(), 2); - - auto fw2 = createFloatingWindow(); - fw2->setObjectName("fw2"); - fw2->move(fw->x() + fw->width() + 100, fw->y()); - - QVERIFY(fw2->dropArea()->checkSanity()); - dragFloatingWindowTo(fw, fw2->dropArea(), DropIndicatorOverlayInterface::DropLocation_Left); - QCOMPARE(fw2->frames().size(), 3); - - QVERIFY(fw2->dropArea()->checkSanity()); - - ///Cleanup - fw2->deleteLater(); - Testing::waitForDeleted(fw2); -} - -void TestDocks::tst_dockWindowWithTwoSideBySideFramesIntoRight() -{ - EnsureTopLevelsDeleted e; - - auto fw = createFloatingWindow(); - auto dock2 = createDockWidget("doc2", Qt::red); - nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnTop); // No we stack on top, unlike in previous test - QCOMPARE(fw->frames().size(), 2); - - auto fw2 = createFloatingWindow(); - fw2->move(fw->x() + fw->width() + 100, fw->y()); - - dragFloatingWindowTo(fw, fw2->dropArea(), DropIndicatorOverlayInterface::DropLocation_Right); // Outter right instead of Left - QCOMPARE(fw2->frames().size(), 3); - QVERIFY(fw2->dropArea()->checkSanity()); - - fw2->deleteLater(); - Testing::waitForDeleted(fw2); -} - -void TestDocks::tst_posAfterLeftDetach() -{ - { - EnsureTopLevelsDeleted e; - auto fw = createFloatingWindow(); - auto dock2 = createDockWidget("doc2", Qt::red); - nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnRight); - QVERIFY(fw->dropArea()->checkSanity()); - // When dragging the right one there was a bug where it jumped - const QPoint globalSrc = dock2->mapToGlobal(QPoint(0, 0)); - const int offset = 10; - const QPoint globalDest = globalSrc + QPoint(offset, 0); - drag(dock2, globalDest); - QVERIFY(fw->dropArea()->checkSanity()); - const QPoint actualEndPos = dock2->mapToGlobal(QPoint(0, 0)); - QVERIFY(actualEndPos.x() - globalSrc.x() < offset + 5); // 5px so we have margin for window system fluctuations. The actual bug was a very big jump like 50px, so a 5 margin is fine to test that the bug doesn't happen - - delete dock2; - fw->deleteLater(); - Testing::waitForDeleted(fw); - } - - { - EnsureTopLevelsDeleted e; - auto fw = createFloatingWindow(); - auto dock2 = createDockWidget("doc2", Qt::red); - nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnRight); - QVERIFY(fw->dropArea()->checkSanity()); - - const int originalX = dock2->mapToGlobal(QPoint(0, 0)).x(); - dock2->frame()->titleBar()->makeWindow(); - const int finalX = dock2->mapToGlobal(QPoint(0, 0)).x(); - - QVERIFY(finalX - originalX < 10); // 10 or some other small number that is less than say 200 - - delete dock2; - fw->deleteLater(); - Testing::waitForDeleted(fw); - } -} - void TestDocks::tst_mainWindowAlwaysHasCentralWidget() { EnsureTopLevelsDeleted e; @@ -771,25 +619,6 @@ void TestDocks::tst_mainWindowAlwaysHasCentralWidget() delete dock->window(); } -void TestDocks::tst_propagateMinSize() -{ - EnsureTopLevelsDeleted e; - auto m = createMainWindow(); - auto dropArea = m->dropArea(); - - auto dock1 = createDockWidget("dock1", new QPushButton("one")); - auto dock2 = createDockWidget("dock2", new QPushButton("two")); - auto dock3 = createDockWidget("dock3", new QPushButton("three")); - - nestDockWidget(dock1, dropArea, nullptr, KDDockWidgets::Location_OnRight); - nestDockWidget(dock2, dropArea, nullptr, KDDockWidgets::Location_OnRight); - nestDockWidget(dock3, dropArea, nullptr, KDDockWidgets::Location_OnRight); - - // TODO finish this when the 3 dock widgets have proper sizes - //QTest::qWait(50000); - -} - void TestDocks::tst_propagateSizeHonoursMinSize() { // Here we dock a widget on the left size, and on the right side. @@ -1354,7 +1183,7 @@ void TestDocks::tst_addDockWidgetToMainWindow() QCOMPARE(dock1->window(), m.get()); QCOMPARE(dock2->window(), m.get()); - QVERIFY(dock1->frame()->QWidget::y() > dock2->frame()->QWidget::y()); + QVERIFY(dock1->frame()->QWidgetAdapter::y() > dock2->frame()->QWidgetAdapter::y()); QCOMPARE(dock1->frame()->QWidgetAdapter::x(), dock2->frame()->QWidgetAdapter::x()); } @@ -1372,7 +1201,7 @@ void TestDocks::tst_addDockWidgetToContainingWindow() QCOMPARE(dock1->window(), dock2->window()); QCOMPARE(dock2->window(), dock3->window()); - QVERIFY(dock3->frame()->QWidget::y() < dock2->frame()->QWidget::y()); + QVERIFY(dock3->frame()->QWidgetAdapter::y() < dock2->frame()->QWidgetAdapter::y()); QVERIFY(dock1->frame()->QWidgetAdapter::x() < dock2->frame()->QWidgetAdapter::x()); QCOMPARE(dock2->frame()->QWidgetAdapter::x(), dock3->frame()->QWidgetAdapter::x()); @@ -1383,118 +1212,6 @@ void TestDocks::tst_addDockWidgetToContainingWindow() Testing::waitForDeleted(window); } -void TestDocks::tst_addToSmallMainWindow1() -{ - EnsureTopLevelsDeleted e; - auto m = createMainWindow(); - auto dock1 = createDockWidget("dock1", new MyWidget2()); - auto dock2 = createDockWidget("dock2", new MyWidget2()); - auto dock3 = createDockWidget("dock3", new MyWidget2()); - auto dock4 = createDockWidget("dock4", new MyWidget2()); - - const int mainWindowLength = 400; - - m->resize(mainWindowLength, mainWindowLength); - dock1->resize(800, 800); - dock2->resize(800, 800); - dock3->resize(800, 800); - - // Add as tabbed: - m->addDockWidgetAsTab(dock1); - - QCOMPARE(m->height(), mainWindowLength); - QVERIFY(dock1->height() < mainWindowLength); - QVERIFY(dock1->width() < mainWindowLength); - - //Add in area: - m->addDockWidget(dock2, Location_OnLeft); - m->addDockWidget(dock3, Location_OnTop, dock2); - m->addDockWidget(dock4, Location_OnBottom); - - auto dropArea = m->dropArea(); - - QVERIFY(dropArea->checkSanity()); - QVERIFY(dock2->width() < mainWindowLength); - QVERIFY(dock3->height() < m->height()); - QVERIFY(dock4->height() < m->height()); -} - -void TestDocks::tst_addToSmallMainWindow2() -{ - EnsureTopLevelsDeleted e; - auto m = createMainWindow(); - auto dropArea = m->dropArea(); - auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(100, 100))); - auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(100, 100))); - m->addDockWidgetAsTab(dock1); - m->resize(osWindowMinWidth(), 200); - - Testing::waitForResize(m.get()); - - QVERIFY(qAbs(m->width() - osWindowMinWidth()) < 15); // Not very important verification. Anyway, using 15 to account for margins and what not. - m->addDockWidget(dock2, KDDockWidgets::Location_OnRight); - QVERIFY(Testing::waitForResize(m.get())); - - QVERIFY(dropArea->width() > osWindowMinWidth()); - QMargins margins = m->centralWidget()->layout()->contentsMargins(); - QCOMPARE(dropArea->width(), m->width() - margins.left() - margins.right()); - QVERIFY(m->dropArea()->checkSanity()); -} - -void TestDocks::tst_addToSmallMainWindow3() -{ - EnsureTopLevelsDeleted e; - auto m = createMainWindow(); - auto dropArea = m->dropArea(); - auto dock1 = createDockWidget("dock1", new MyWidget2()); - auto dock2 = createDockWidget("dock2", new MyWidget2()); - m->addDockWidgetAsTab(dock1); - m->resize(osWindowMinWidth(), 200); - QTest::qWait(200); - QVERIFY(qAbs(m->width() - osWindowMinWidth()) < 15); // Not very important verification. Anyway, using 15 to account for margins and what not. - - auto fw = dock2->morphIntoFloatingWindow(); - QVERIFY(fw->isVisible()); - QVERIFY(dropArea->checkSanity()); - dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_Right); - QVERIFY(m->dropArea()->checkSanity()); - delete fw; -} - -void TestDocks::tst_addToSmallMainWindow4() -{ - EnsureTopLevelsDeleted e; - auto m = createMainWindow(QSize(100, 100), MainWindowOption_None); - auto dropArea = m->dropArea(); - auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(50, 50))); - auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(50, 50))); - MultiSplitter *layout = dropArea; - m->addDockWidget(dock1, KDDockWidgets::Location_OnBottom); - Testing::waitForResize(m.get()); - - m->addDockWidget(dock2, KDDockWidgets::Location_OnBottom); - Testing::waitForResize(m.get()); - QVERIFY(m->dropArea()->checkSanity()); - - const int item2MinHeight = layout->itemForFrame(dock2->frame())->minLength(Qt::Vertical); - QCOMPARE(dropArea->height(), dock1->frame()->height() + item2MinHeight + Item::separatorThickness); -} - -void TestDocks::tst_addToSmallMainWindow5() -{ - EnsureTopLevelsDeleted e; - // Test test shouldn't spit any warnings - - MainWindow m("MyMainWindow_tst_addToSmallMainWindow5", MainWindowOption_None); - auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(50, 240))); - auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(50, 240))); - m.addDockWidget(dock1, KDDockWidgets::Location_OnBottom); - m.addDockWidget(dock2, KDDockWidgets::Location_OnBottom); - QVERIFY(m.dropArea()->checkSanity()); - - Testing::waitForResize(&m); -} - void TestDocks::tst_addToSmallMainWindow6() { EnsureTopLevelsDeleted e; @@ -1516,58 +1233,6 @@ void TestDocks::tst_addToSmallMainWindow6() QVERIFY(m.dropArea()->checkSanity()); } -void TestDocks::tst_fairResizeAfterRemoveWidget() -{ - // 1. Add 3 dock widgets horizontally, remove the middle one, make sure - // both left and right widgets get a share of the new available space - - EnsureTopLevelsDeleted e; - - DockWidgetBase *dock1 = createDockWidget("dock1", new QPushButton("one")); - DockWidgetBase *dock2 = createDockWidget("dock2", new QPushButton("two")); - DockWidgetBase *dock3 = createDockWidget("dock3", new QPushButton("three")); - - dock1->addDockWidgetToContainingWindow(dock2, Location_OnRight); - dock1->addDockWidgetToContainingWindow(dock3, Location_OnRight, dock2); - - auto fw = dock1->floatingWindow(); - - QPointer frame2= dock2->frame(); - - const int oldWidth1 = dock1->frame()->width(); - const int oldWidth2 = dock2->frame()->width(); - const int oldWidth3 = dock3->frame()->width(); - MultiSplitter *layout = fw->dropArea(); - QCOMPARE(layout->count(), 3); - QCOMPARE(layout->visibleCount(), 3); - QCOMPARE(layout->placeholderCount(), 0); - - delete dock2; - QVERIFY(Testing::waitForResize(dock1)); - QVERIFY(!frame2); - - QCOMPARE(layout->count(), 2); - QCOMPARE(layout->visibleCount(), 2); - QCOMPARE(layout->placeholderCount(), 0); - - const int delta1 = (dock1->frame()->width() - oldWidth1); - const int delta3 = (dock3->frame()->width() - oldWidth3); - - qDebug() << "old1=" << oldWidth1 - << "; old3=" << oldWidth3 - << "; to spread=" << oldWidth2 - << "; Delta1=" << delta1 - << "; Delta3=" << delta3; - - QVERIFY(delta1 > 0); - QVERIFY(delta3 > 0); - QVERIFY(qAbs(delta3 - delta1) <= 1); // Both dock1 and dock3 should have increased by the same amount - - QWidget *window = dock1->window(); - window->deleteLater(); - Testing::waitForDeleted(window); -} - void TestDocks::tst_notClosable() { EnsureTopLevelsDeleted e; @@ -1806,39 +1471,6 @@ void TestDocks::tst_setVisibleFalseWhenSideBySide() Testing::waitForDeleted(window); } -void TestDocks::tst_addAndReadd() -{ - EnsureTopLevelsDeleted e; - - // 1. This just tests some crash I got. - // Make a dock widget float and immediately reattach it - auto m = createMainWindow(); - QTest::qWait(10); // the DND state machine needs the event loop to start, otherwise activeState() is nullptr. (for offscreen QPA) - - auto dock1 = createDockWidget("dock1", new QPushButton("1")); - m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft); - dock1->setFloating(true); - m->multiSplitter()->checkSanity(); - m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft); - dock1->frame()->titleBar()->makeWindow(); - m->multiSplitter()->checkSanity(); - m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft); - dock1->frame()->titleBar()->makeWindow(); - m->multiSplitter()->checkSanity(); - - auto fw = dock1->floatingWindow(); - QVERIFY(fw); - auto dropArea = m->dropArea(); - dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_OutterRight); - QVERIFY(!dock1->frame()->titleBar()->isVisible()); - fw->titleBar()->makeWindow(); - m->multiSplitter()->checkSanity(); - - //Cleanup - delete dock1; - Testing::waitForDeleted(fw); -} - void TestDocks::tst_embeddedMainWindow() { EnsureTopLevelsDeleted e; @@ -1864,80 +1496,6 @@ void TestDocks::tst_embeddedMainWindow() delete window; } -void TestDocks::tst_invalidPlaceholderPosition_data() -{ - QTest::addColumn("restore1First"); - QTest::newRow("restore1First") << true; - QTest::newRow("restore2First") << false; -} - -void TestDocks::tst_invalidPlaceholderPosition() -{ - QFETCH(bool, restore1First); - - // Tests a bug I saw: 3 widgets stacked, close the top one, then the second top one - // result: the bottom most one didn't have it's top separator at y=0 - - EnsureTopLevelsDeleted e; - auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); - auto dock1 = createDockWidget("1", new QPushButton("1")); - auto dock2 = createDockWidget("2", new QPushButton("2")); - auto dock3 = createDockWidget("3", new QPushButton("3")); - - MultiSplitter *layout = m->multiSplitter(); - - // Stack: 1, 2, 3 vertically - m->addDockWidget(dock3, Location_OnTop); - m->addDockWidget(dock2, Location_OnTop); - m->addDockWidget(dock1, Location_OnTop); - - auto frame1 = dock1->frame(); - auto frame2 = dock2->frame(); - auto frame3 = dock3->frame(); - QCOMPARE(frame1->QWidget::y(), 0); - - // Close 1 - dock1->close(); - Testing::waitForResize(frame2); - - // Check that frame2 moved up to y=1 - QCOMPARE(frame2->QWidget::y(), 0); - - // Close 2 - dock2->close(); - Testing::waitForResize(dock3); - - QVERIFY(layout->checkSanity()); - QCOMPARE(layout->count(), 3); - QCOMPARE(layout->placeholderCount(), 2); - - // Check that frame3 moved up to y=1 - QCOMPARE(frame3->QWidget::y(), 0); - - // Now restore: - auto toRestore1 = restore1First ? dock1 : dock2; - auto toRestore2 = restore1First ? dock2 : dock1; - - - qDebug() << "About to show dock1"; - toRestore1->show(); - QCOMPARE(layout->placeholderCount(), 1); - QVERIFY(dock3->isVisible()); - QVERIFY(!dock3->size().isNull()); - - toRestore2->show(); - - Testing::waitForResize(frame3); - QVERIFY(layout->checkSanity()); - QCOMPARE(layout->count(), 3); - QCOMPARE(layout->placeholderCount(), 0); - layout->checkSanity(); - - dock1->deleteLater(); - dock2->deleteLater(); - QVERIFY(Testing::waitForDeleted(dock2)); -} - void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() { EnsureTopLevelsDeleted e; @@ -2768,43 +2326,6 @@ void TestDocks::tst_invalidLayoutAfterRestore() layout->checkSanity(); } -void TestDocks::tst_invalidJSON_data() -{ - // Be sure that the main windows in the json are called "MyMainWindow1" and the dock widgets - // dock-x where x starts at 0 - QTest::addColumn("layoutFileName"); - QTest::addColumn("numDockWidgets"); - QTest::addColumn("expectedWarning"); - QTest::addColumn("expectedResult"); - QTest::newRow("unsupported-serialization-version") << "unsupported-serialization-version.json" - << 10 - << "Serialization format is too old" - << false; - QTest::newRow("invalid") << "invalid.json" << 29 << "" << false; - QTest::newRow("overlapping-item") << "overlapping-item.json" << 2 << "Unexpected pos" << true; -} - -void TestDocks::tst_invalidJSON() -{ - QFETCH(QString, layoutFileName); - QFETCH(int, numDockWidgets); - QFETCH(QString, expectedWarning); - QFETCH(bool, expectedResult); - - const QString absoluteLayoutFileName = QStringLiteral(":/layouts/%1").arg(layoutFileName); - - EnsureTopLevelsDeleted e; - auto m1 = createMainWindow(QSize(800, 500), MainWindowOption_None, "MyMainWindow1"); - for (int i = 0; i < numDockWidgets; ++i) { - createDockWidget(QStringLiteral("dock-%1").arg(i), new QPushButton("one")); - } - - SetExpectedWarning sew(expectedWarning); - - LayoutSaver restorer; - QCOMPARE(restorer.restoreFromFile(absoluteLayoutFileName), expectedResult); -} - void TestDocks::tst_restoreEmbeddedMainWindow() { EnsureTopLevelsDeleted e; diff --git a/tests/utils.cpp b/tests/utils.cpp index e538eaf7..15a30a17 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -36,19 +36,6 @@ using namespace KDDockWidgets::Tests; // clazy:excludeall=ctor-missing-parent-argument,missing-qobject-macro,range-loop,missing-typeinfo,detaching-member,function-args-by-ref,non-pod-global-static,reserve-candidates -NonClosableWidget::NonClosableWidget(QWidget *parent) - : QWidget(parent) -{ -} - -NonClosableWidget::~NonClosableWidget() -{ -} - -void NonClosableWidget::closeEvent(QCloseEvent *ev) -{ - ev->ignore(); // don't allow to close -} std::unique_ptr KDDockWidgets::Tests::createMainWindow(QSize sz, KDDockWidgets::MainWindowOptions options, const QString &name) { @@ -161,6 +148,21 @@ void MyWidget::paintEvent(QPaintEvent *) QPainter p(this); p.fillRect(rect(), c); } + +NonClosableWidget::NonClosableWidget(QWidget *parent) + : QWidget(parent) +{ +} + +NonClosableWidget::~NonClosableWidget() +{ +} + +void NonClosableWidget::closeEvent(QCloseEvent *ev) +{ + ev->ignore(); // don't allow to close +} + #endif bool KDDockWidgets::Tests::shouldBlacklistWarning(const QString &msg, const QString &category) diff --git a/tests/utils.h b/tests/utils.h index 050e74f5..627a44f2 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -133,18 +133,6 @@ KDDockWidgets::DockWidgetBase *createDockWidget(const QString &name, QColor colo void nestDockWidget(DockWidgetBase *dock, DropArea *dropArea, Frame *relativeTo, KDDockWidgets::Location location); -class NonClosableWidget : public QWidget -{ -public: - Q_OBJECT -public: - explicit NonClosableWidget(QWidget *parent = nullptr); - ~NonClosableWidget() override; - -protected: - void closeEvent(QCloseEvent *event) override; -}; - class MyWidget : public QWidgetOrQuick { public: @@ -200,6 +188,17 @@ public: } }; +class NonClosableWidget : public QWidgetAdapter +{ +public: + Q_OBJECT +public: + explicit NonClosableWidget() + : QWidgetAdapter() + { + } +}; + class MyWidget2 : public QWidgetAdapter { public: @@ -225,6 +224,18 @@ public: #else +class NonClosableWidget : public QWidget +{ +public: + Q_OBJECT +public: + explicit NonClosableWidget(QWidget *parent = nullptr); + ~NonClosableWidget() override; + +protected: + void closeEvent(QCloseEvent *event) override; +}; + namespace { class MyWidget2 : public QWidget