diff --git a/src/private/multisplitter/Item.cpp b/src/private/multisplitter/Item.cpp index ab5dcb8d..d8c4aef0 100644 --- a/src/private/multisplitter/Item.cpp +++ b/src/private/multisplitter/Item.cpp @@ -724,7 +724,7 @@ bool ItemContainer::checkSanity() root()->dumpLayout(); qWarning() << Q_FUNC_INFO << "Percentages don't add up" << totalPercentage << percentages; - const_cast(this)->updateChildPercentages(); + const_cast(this)->updateSeparators(); qWarning() << Q_FUNC_INFO << childPercentages(); } } @@ -811,7 +811,7 @@ void ItemContainer::removeItem(Item *item, bool hardRemove) growNeighbours(side1Item, side2Item); Q_EMIT itemsChanged(); updateSizeConstraints(); - updateChildPercentages(); + updateSeparators(); } } else { // Not ours, ask parent @@ -847,7 +847,7 @@ ItemContainer *ItemContainer::convertChildToContainer(Item *leaf) container->setGeometry(leaf->geometry()); container->insertItem(leaf, Location_OnTop); Q_EMIT itemsChanged(); - updateChildPercentages(); + updateSeparators(); return container; } @@ -887,7 +887,7 @@ void ItemContainer::insertItem(Item *item, Location loc, AddingOption option) insertItem(item, loc, option); } - updateChildPercentages(); + updateSeparators(); scheduleCheckSanity(); } @@ -1095,7 +1095,7 @@ void ItemContainer::positionItems() positionItems(/*by-ref=*/sizes); applyPositions(sizes); - updateChildPercentages(); + updateSeparators(); } void ItemContainer::applyPositions(const SizingInfo::List &sizes) @@ -1370,7 +1370,7 @@ void ItemContainer::setOrientation(Qt::Orientation o) { if (o != m_orientation) { m_orientation = o; - updateChildPercentages(); + updateSeparators(); } } @@ -1586,7 +1586,7 @@ void ItemContainer::restoreChild(Item *item) if (numVisibleChildren() == 1) { // The easy case. Child is alone in the layout, occupies everything. item->setGeometry_recursive(rect()); - updateChildPercentages(); + updateSeparators(); return; } @@ -1608,7 +1608,7 @@ void ItemContainer::restoreChild(Item *item) } growItem(item, newLength, GrowthStrategy::BothSidesEqually, /*accountForNewSeparator=*/ true); - updateChildPercentages(); + updateSeparators(); } void ItemContainer::updateWidgetGeometries() @@ -2085,16 +2085,37 @@ Anchor::List ItemContainer::separators() const return m_separators; } -void ItemContainer::createSeparators() +QVector ItemContainer::requiredSeparatorPositions() const +{ + const int numSeparators = qMax(0, numVisibleChildren() - 1); + QVector positions; + positions.reserve(numSeparators); + + for (Item *item : m_children) { + if (item->isVisible()) { + positions << item->m_sizingInfo.edge(m_orientation) + 1; + } + } + + return positions; +} + +void ItemContainer::updateSeparators() { const Item::List items = visibleChildren(); const int numSeparators = qMax(0, items.size() - 1); m_separators.reserve(numSeparators); - for (int i = m_separators.size(); i < numSeparators; ++i) { + for (int i = 0; i < numSeparators; ++i) { m_separators << new Anchor(m_orientation, Anchor::Option::None, hostWidget()); } + for (Item *item : items) { + if (auto c = item->asContainer()) + c->updateSeparators(); + } + + updateChildPercentages(); } bool ItemContainer::isVertical() const diff --git a/src/private/multisplitter/Item_p.h b/src/private/multisplitter/Item_p.h index 8584f1d6..00f91619 100644 --- a/src/private/multisplitter/Item_p.h +++ b/src/private/multisplitter/Item_p.h @@ -506,7 +506,9 @@ public: QVector separators() const; Qt::Orientation m_orientation = Qt::Vertical; private: - void createSeparators(); + /// Returns the positions that each separator should have (x position if Qt::Horizontal, y otherwise) + QVector requiredSeparatorPositions() const; + void updateSeparators(); QVector childPercentages() const; mutable bool m_checkSanityScheduled = false; QVector m_separators; diff --git a/src/private/multisplitter/MultiSplitterLayout.cpp b/src/private/multisplitter/MultiSplitterLayout.cpp index a5c10f9c..14355dac 100644 --- a/src/private/multisplitter/MultiSplitterLayout.cpp +++ b/src/private/multisplitter/MultiSplitterLayout.cpp @@ -286,6 +286,19 @@ void MultiSplitterLayout::emitVisibleWidgetCountChanged() Q_EMIT visibleWidgetCountChanged(visibleCount()); } +int MultiSplitterLayout::availableLengthForOrientation(Qt::Orientation orientation) const +{ + if (orientation == Qt::Vertical) + return availableSize().height(); + else + return availableSize().width(); +} + +QSize MultiSplitterLayout::availableSize() const +{ + return m_rootItem->availableSize(); +} + Item *MultiSplitterLayout::itemForFrame(const Frame *frame) const { if (!frame) diff --git a/src/private/multisplitter/MultiSplitterLayout_p.h b/src/private/multisplitter/MultiSplitterLayout_p.h index a3eff0c7..435f4ff0 100644 --- a/src/private/multisplitter/MultiSplitterLayout_p.h +++ b/src/private/multisplitter/MultiSplitterLayout_p.h @@ -320,7 +320,7 @@ private: /** * @brief Like @ref availableLengthForDrop but just returns the total available width or height (depending on @p orientation) * So no need to receive any location. - * @param orientation If Qt::Vertical then returns the available width. Height otherwise. + * @param orientation If Qt::Vertical then returns the available height. Width otherwise. */ int availableLengthForOrientation(Qt::Orientation orientation) const; diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index bb3c383f..125be392 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -293,25 +293,25 @@ private Q_SLOTS: void tst_setAstCurrentTab(); void tst_closeShowWhenNoCentralFrame(); void tst_placeholderDisappearsOnReadd(); -// void tst_placeholdersAreRemovedPropertly(); -// void tst_embeddedMainWindow(); + void tst_placeholdersAreRemovedPropertly(); + void tst_embeddedMainWindow(); void tst_toggleMiddleDockCrash(); // tests some crash I got // void tst_28NestedWidgets(); void tst_28NestedWidgets_data(); void tst_invalidPlaceholderPosition_data(); -// void tst_invalidPlaceholderPosition(); -// void tst_invalidAnchorGroup(); + void tst_invalidPlaceholderPosition(); + void tst_invalidAnchorGroup(); // void tst_resizeViaAnchorsAfterPlaceholderCreation(); -// void tst_negativeAnchorPosition(); -// void tst_negativeAnchorPosition2(); -// void tst_negativeAnchorPosition3(); -// void tst_negativeAnchorPosition4(); -// void tst_negativeAnchorPosition5(); -// void tst_negativeAnchorPosition6(); -// void tst_negativeAnchorPosition7(); + void tst_negativeAnchorPosition(); + void tst_negativeAnchorPosition2(); + void tst_negativeAnchorPosition3(); + void tst_negativeAnchorPosition4(); + void tst_negativeAnchorPosition5(); + void tst_negativeAnchorPosition6(); + void tst_negativeAnchorPosition7(); void tst_negativeAnchorPositionWhenEmbedded_data(); -// void tst_negativeAnchorPositionWhenEmbedded(); -// void tst_availableSizeWithPlaceholders(); + void tst_negativeAnchorPositionWhenEmbedded(); + void tst_availableSizeWithPlaceholders(); void tst_stealFrame(); void tst_addAsPlaceholder(); void tst_removeItem(); @@ -321,7 +321,7 @@ private Q_SLOTS: // void tst_invalidLayoutAfterRestore(); void tst_samePositionAfterHideRestore(); // void tst_anchorFollowingItselfAssert(); -// void tst_positionWhenShown(); + void tst_positionWhenShown(); // void tst_restoreEmpty(); // void tst_restoreSimple(); // void tst_restoreNestedAndTabbed(); @@ -369,7 +369,7 @@ private: }; } -/*static EmbeddedWindow *createEmbeddedMainWindow(QSize sz) +static EmbeddedWindow *createEmbeddedMainWindow(QSize sz) { static int count = 0; count++; @@ -383,7 +383,7 @@ private: window->show(); window->resize(sz); return window; -}*/ +} namespace { @@ -2279,7 +2279,6 @@ void TestDocks::tst_rectForDropMath_data() const QRect layoutRect(0, 0, 1000, 1000); const QSize contentsSize = layoutRect.size(); - const int 0 = Anchor::thickness(true); const int anchorThickness = Item::separatorThickness(); const QRect relativeToWindowRect = layoutRect.adjusted(0, 0, -0, -0); @@ -3080,10 +3079,10 @@ void TestDocks::tst_availableLengthForOrientation() auto dropArea = m->dropArea(); MultiSplitterLayout *layout = dropArea->multiSplitterLayout(); - int availableWidth = layout->availableLengthForOrientation(Qt::Vertical); - int availableHeight = layout->availableLengthForOrientation(Qt::Horizontal); - QCOMPARE(availableWidth, layout->width() - 2 * Anchor::thickness(true)); - QCOMPARE(availableHeight, layout->height() - 2 *Anchor::thickness(true)); + int availableWidth = layout->availableLengthForOrientation(Qt::Horizontal); + int availableHeight = layout->availableLengthForOrientation(Qt::Vertical); + QCOMPARE(availableWidth, layout->width()); + QCOMPARE(availableHeight, layout->height()); //2. Now do the same, but we have some widget docked @@ -3093,10 +3092,10 @@ void TestDocks::tst_availableLengthForOrientation() const int dock1MinWidth = layout->itemForFrame(dock1->frame())->minLength(Qt::Vertical); const int dock1MinHeight = layout->itemForFrame(dock1->frame())->minLength(Qt::Horizontal); - availableWidth = layout->availableLengthForOrientation(Qt::Vertical); - availableHeight = layout->availableLengthForOrientation(Qt::Horizontal); - QCOMPARE(availableWidth, layout->width() - 2 * Anchor::thickness(true) - Item::separatorThickness() - dock1MinWidth); - QCOMPARE(availableHeight, layout->height() - 2 *Anchor::thickness(true) - Item::separatorThickness() - dock1MinHeight); + availableWidth = layout->availableLengthForOrientation(Qt::Horizontal); + availableHeight = layout->availableLengthForOrientation(Qt::Vertical); + QCOMPARE(availableWidth, layout->width() - Item::separatorThickness() - dock1MinWidth); + QCOMPARE(availableHeight, layout->height() - Item::separatorThickness() - dock1MinHeight); m->multiSplitterLayout()->checkSanity(); } #endif @@ -3182,7 +3181,7 @@ void TestDocks::tst_placeholderDisappearsOnReadd() QVERIFY(Testing::waitForDeleted(fw)); } -#if 0 + void TestDocks::tst_placeholdersAreRemovedPropertly() { EnsureTopLevelsDeleted e; @@ -3226,6 +3225,8 @@ void TestDocks::tst_embeddedMainWindow() // Tests a MainWindow which isn't a top-level window, but is embedded in another window EmbeddedWindow *window = createEmbeddedMainWindow(QSize(800, 800)); + QTest::qWait(10); // the DND state machine needs the event loop to start, otherwise activeState() is nullptr. (for offscreen QPA) + auto dock1 = createDockWidget("1", new QPushButton("1")); window->mainWindow->addDockWidget(dock1, Location_OnTop); dock1->setFloating(true); @@ -3241,7 +3242,7 @@ void TestDocks::tst_embeddedMainWindow() delete window; } -#endif + void TestDocks::tst_toggleMiddleDockCrash() { EnsureTopLevelsDeleted e; @@ -3276,7 +3277,7 @@ void TestDocks::tst_invalidPlaceholderPosition_data() QTest::newRow("restore1First") << true; QTest::newRow("restore2First") << false; } -#if 0 + void TestDocks::tst_invalidPlaceholderPosition() { QFETCH(bool, restore1First); @@ -3300,7 +3301,6 @@ void TestDocks::tst_invalidPlaceholderPosition() auto frame1 = dock1->frame(); auto frame2 = dock2->frame(); auto frame3 = dock3->frame(); - const int 0 = Anchor::thickness(true); QCOMPARE(frame1->y(), 0); // Close 1 @@ -3345,7 +3345,7 @@ void TestDocks::tst_invalidPlaceholderPosition() dock2->deleteLater(); QVERIFY(Testing::waitForDeleted(dock2)); } -#endif + void TestDocks::tst_28NestedWidgets_data() { QTest::addColumn>("docksToCreate"); @@ -3628,7 +3628,7 @@ void TestDocks::tst_28NestedWidgets() QVERIFY(Testing::waitForDeleted(dock)); } } - +#endif void TestDocks::tst_invalidAnchorGroup() { // Tests a bug I got. Should not warn. @@ -3672,7 +3672,7 @@ void TestDocks::tst_invalidAnchorGroup() Testing::waitForDeleted(dock1); } } - +#if 0 void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() { EnsureTopLevelsDeleted e; @@ -3720,7 +3720,6 @@ void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() Anchor *anchor = item1->anchorGroup().right; int boundToTheRight = layout->boundPositionForAnchor(anchor, Anchor::Side2); int expectedBoundToTheRight = layout->size().width() - - Anchor::thickness(true) - 3*Item::separatorThickness() - item2->minLength(Qt::Vertical) - item3->minLength(Qt::Vertical) - @@ -3743,7 +3742,6 @@ void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() boundToTheRight = layout->boundPositionForAnchor(anchor, Anchor::Side2); expectedBoundToTheRight = layout->size().width() - - Anchor::thickness(true) - 2*Item::separatorThickness() - item2->minLength(Qt::Vertical) - item4->minLength(Qt::Vertical) ; @@ -3757,7 +3755,7 @@ void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() Testing::waitForDeleted(dock3); } } - +#endif void TestDocks::tst_negativeAnchorPosition() { // Tests that we don't hit: @@ -3784,13 +3782,6 @@ void TestDocks::tst_negativeAnchorPosition() m->addDockWidget(d2, Location_OnTop); m->addDockWidget(d3, Location_OnTop); - const int minHeight = layout->minimumSize().height(); - const int min1 = KDDockWidgets::widgetMinLength(d1->frame(), Qt::Horizontal); - const int min2 = KDDockWidgets::widgetMinLength(d2->frame(), Qt::Horizontal); - const int min3 = KDDockWidgets::widgetMinLength(d3->frame(), Qt::Horizontal); - - qDebug() << "MinSizes=" << min1 << min2 << min3 << minHeight; - d2->close(); Testing::waitForResize(d3); @@ -3912,12 +3903,6 @@ void TestDocks::tst_negativeAnchorPosition5() dock1->show(); - Item *item1 = layout->itemForFrame(dock1->frame()); - - qDebug() << "contents size" << layout->size() - << "; available=" << layout->availableLengthForOrientation(Qt::Horizontal) - << "; item1.min=" << item1->minimumSize(); - dock0->show(); layout->checkSanity(); @@ -3999,7 +3984,7 @@ void TestDocks::tst_negativeAnchorPosition7() delete m; } -#endif + void TestDocks::tst_negativeAnchorPositionWhenEmbedded_data() { QTest::addColumn("embedded"); @@ -4007,7 +3992,7 @@ void TestDocks::tst_negativeAnchorPositionWhenEmbedded_data() QTest::newRow("false") << false; QTest::newRow("true") << true; } -#if 0 + void TestDocks::tst_negativeAnchorPositionWhenEmbedded() { QFETCH(bool, embedded); @@ -4039,7 +4024,7 @@ void TestDocks::tst_negativeAnchorPositionWhenEmbedded() delete m->window(); } -#endif + void TestDocks::tst_tabBarWithHiddenTitleBar_data() { QTest::addColumn("hiddenTitleBar"); @@ -4479,7 +4464,7 @@ void TestDocks::tst_rectForDropCrash() delete m->window(); } -#if 0 + void TestDocks::tst_availableSizeWithPlaceholders() { // Tests MultiSplitterLayout::available() with and without placeholders. The result should be the same. @@ -4531,15 +4516,9 @@ void TestDocks::tst_availableSizeWithPlaceholders() Item *item10 = layout1->itemForFrame(f10); Item *item30 = layout3->itemForFrame(docks2.at(0).createdDock->frame()); - // HACK: The Frame's have different minimum size than the Items as they're geometry change later - // and the item isn't updated, so for the purpose of this test (available) we force the items - // to have the same properties. TODO: Remove the restoreSizes once the Frame propagates its min size - // to the item. - item10->restoreSizes(item30->minimumSize(), item30->geometry()); - QCOMPARE(item10->geometry(), item30->geometry()); QCOMPARE(item10->frame()->minimumSizeHint(), item10->frame()->minimumSizeHint()); - QCOMPARE(item10->minimumSize(), item30->minimumSize()); + QCOMPARE(item10->minSize(), item30->minSize()); QCOMPARE(layout1->availableSize(), layout3->availableSize()); layout1->checkSanity(); @@ -4555,7 +4534,7 @@ void TestDocks::tst_availableSizeWithPlaceholders() docks2.at(2).createdDock->deleteLater(); QVERIFY(Testing::waitForDeleted(docks2.at(2).createdDock)); } - +#if 0 void TestDocks::tst_anchorFollowingItselfAssert() { // 1. Tests that we don't assert in Anchor::setFollowee() @@ -4587,7 +4566,7 @@ void TestDocks::tst_anchorFollowingItselfAssert() docks.at(4).createdDock->deleteLater(); Testing::waitForDeleted(docks.at(4).createdDock); } - +#endif void TestDocks::tst_positionWhenShown() { // Tests that when showing a dockwidget it shows in the same position as before @@ -4608,7 +4587,7 @@ void TestDocks::tst_positionWhenShown() dock1->deleteLater(); QVERIFY(Testing::waitForDeleted(dock1)); } -#endif + void TestDocks::tst_sizeConstraintWarning() { // Tests that we don't get the warning: MultiSplitterLayout::checkSanity: Widget has height= 122 but minimum is 144 KDDockWidgets::Item @@ -4812,14 +4791,14 @@ void TestDocks::tst_invalidLayoutAfterRestore() Item *item2 = layout->itemForFrame(dock2->frame()); Item *item3 = layout->itemForFrame(dock3->frame()); Item *item4 = dropArea->centralFrame(); - const int oldAvailableWidth = layout->availableLengthForOrientation(Qt::Vertical); + const int oldAvailableWidth = layout->availableLengthForOrientation(Qt::Horizontal); - qDebug() << "; Item3 min=" << item3->minimumSize().width() - << "; Item2 min=" << item2->minimumSize().width() - << "; Item1 min=" << item1->minimumSize().width() - << "; Item4 min=" << item4->minimumSize().width() + qDebug() << "; Item3 min=" << item3->minSize().width() + << "; Item2 min=" << item2->minSize().width() + << "; Item1 min=" << item1->minSize().width() + << "; Item4 min=" << item4->minSize().width() << "; oldAvailableWidth=" << oldAvailableWidth - << "; old window.minWidth=" << layout->minimumSize().width(); + << "; old window.minWidth=" << layout->minSize().width(); qDebug() << "Item3 width=" << item3->width() << "Item2 width=" << item2->width(); @@ -4833,8 +4812,8 @@ void TestDocks::tst_invalidLayoutAfterRestore() QVERIFY(!f2.data()); QTest::qWait(200); // Not sure why. Some event we're waiting for. TODO: Investigate auto fw2 = dock2->floatingWindow(); - const int newAvailableWidth = layout->availableLengthForOrientation(Qt::Vertical); - QCOMPARE(layout->minimumSize().width(), 2*Anchor::thickness(true) + 2*Item::separatorThickness() + item1->minimumSize().width() + item3->minimumSize().width() + item4->minimumSize().width()); + const int newAvailableWidth = layout->availableLengthForOrientation(Qt::Horizontal); + QCOMPARE(layout->minimumSize().width(), 2*Item::separatorThickness() + item1->minSize().width() + item3->minSize().width() + item4->minSize().width()); MultiSplitterLayout::Length availableForDock2 = layout->availableLengthForDrop(Location_OnLeft, item3); @@ -4844,7 +4823,7 @@ void TestDocks::tst_invalidLayoutAfterRestore() << "; availableforDock2=" << availableForDock2.length() << availableForDock2.side1Length << availableForDock2.side2Length; - const int item3AvailableSqueeze = item3->width() - item3->minimumSize().width(); + const int item3AvailableSqueeze = item3->width() - item3->minSize().width(); QVERIFY(availableForDock2.side2Length <= item3AvailableSqueeze);