diff --git a/src/KDDockWidgets.h b/src/KDDockWidgets.h index 852b1fd4..3143143e 100644 --- a/src/KDDockWidgets.h +++ b/src/KDDockWidgets.h @@ -66,6 +66,15 @@ namespace KDDockWidgets }; Q_DECLARE_FLAGS(RestoreOptions, RestoreOption) + ///@brief When a widget is added we need to figure out what's a decent size for it + ///This enum specifies the different ways to calculate it + enum class DefaultSizeMode { + ItemSize, ///< Simply uses the Item::size() of the item being added. Actual used size might be smaller if our window isn't big enough. + Fair, ///< Gives an equal relative size as the items that are already in the layout + FairButFloor, ///< Equal to fair, but if the item is smaller than the fair suggestion, then that small size is used. + SizePolicy, ///< Uses the item's sizeHint() and sizePolicy() + }; + ///@internal inline QString locationStr(Location loc) { diff --git a/src/private/DropArea.cpp b/src/private/DropArea.cpp index fd454825..3b06017d 100644 --- a/src/private/DropArea.cpp +++ b/src/private/DropArea.cpp @@ -120,9 +120,9 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa } if (option & AddingOption_StartHidden) { - m_layout->addWidget(dw, location, relativeToFrame, option); + m_layout->addWidget(dw, location, relativeToFrame, DefaultSizeMode::Fair, option); } else { - m_layout->addWidget(frame, location, relativeToFrame, option); + m_layout->addWidget(frame, location, relativeToFrame, DefaultSizeMode::Fair, option); } } @@ -240,7 +240,7 @@ bool DropArea::drop(QWidgetOrQuick *droppedWindow, KDDockWidgets::Location locat auto frame = Config::self().frameworkWidgetFactory()->createFrame(); frame->addWidget(dock); - m_layout->addWidget(frame, location, relativeTo); + m_layout->addWidget(frame, location, relativeTo, DefaultSizeMode::FairButFloor); } else if (auto floatingWindow = qobject_cast(droppedWindow)) { if (!validateAffinity(floatingWindow)) return false; diff --git a/src/private/multisplitter/Item.cpp b/src/private/multisplitter/Item.cpp index 835530a2..20b72fa6 100644 --- a/src/private/multisplitter/Item.cpp +++ b/src/private/multisplitter/Item.cpp @@ -356,7 +356,7 @@ int Item::pos(Qt::Orientation o) const return o == Qt::Vertical ? y() : x(); } -void Item::insertItem(Item *item, Location loc, AddingOption option) +void Item::insertItem(Item *item, Location loc, DefaultSizeMode defaultSizeMode, AddingOption option) { Q_ASSERT(item != this); @@ -377,10 +377,10 @@ void Item::insertItem(Item *item, Location loc, AddingOption option) m_parent->setOrientation(orientation); } - m_parent->insertItem(item, indexInParent); + m_parent->insertItem(item, indexInParent, defaultSizeMode); } else { ItemContainer *container = m_parent->convertChildToContainer(this); - container->insertItem(item, loc, option); + container->insertItem(item, loc, defaultSizeMode, option); } (void) root()->checkSanity(); @@ -715,8 +715,21 @@ int Item::visibleCount_recursive() const return isVisible() ? 1 : 0; } +struct ItemContainer::Private +{ + Private(ItemContainer *q) + : q(q) + { + } + + int defaultLengthFor(Item *item, DefaultSizeMode) const; + + ItemContainer *const q; +}; + ItemContainer::ItemContainer(QWidget *hostWidget, ItemContainer *parent) : Item(true, hostWidget, parent) + , d(new Private(this)) { Q_ASSERT(parent); connectParent(parent); @@ -736,11 +749,17 @@ ItemContainer::ItemContainer(QWidget *hostWidget, ItemContainer *parent) ItemContainer::ItemContainer(QWidget *hostWidget) : Item(true, hostWidget, /*parentContainer=*/ nullptr) + , d(new Private(this)) { // CTOR for root item Q_ASSERT(hostWidget); } +ItemContainer::~ItemContainer() +{ + delete d; +} + bool ItemContainer::checkSanity() { m_checkSanityScheduled = false; @@ -1027,17 +1046,18 @@ ItemContainer *ItemContainer::convertChildToContainer(Item *leaf) container->setParentContainer(nullptr); container->setParentContainer(this); - insertItem(container, index); + insertItem(container, index, DefaultSizeMode::None); m_children.removeOne(leaf); container->setGeometry(leaf->geometry()); - container->insertItem(leaf, Location_OnTop); + container->insertItem(leaf, Location_OnTop, DefaultSizeMode::None); Q_EMIT itemsChanged(); updateSeparators_recursive(); return container; } -void ItemContainer::insertItem(Item *item, Location loc, AddingOption option) +void ItemContainer::insertItem(Item *item, Location loc, DefaultSizeMode defaultSizeMode, + AddingOption addingOption) { Q_ASSERT(item != this); if (contains(item)) { @@ -1045,8 +1065,8 @@ void ItemContainer::insertItem(Item *item, Location loc, AddingOption option) return; } - item->setIsVisible(!(option & AddingOption_StartHidden)); - Q_ASSERT(!((option & AddingOption_StartHidden) && item->isContainer())); + item->setIsVisible(!(addingOption & AddingOption_StartHidden)); + Q_ASSERT(!((addingOption & AddingOption_StartHidden) && item->isContainer())); const Qt::Orientation locOrientation = orientationForLocation(loc); @@ -1057,7 +1077,7 @@ void ItemContainer::insertItem(Item *item, Location loc, AddingOption option) } const int index = locationIsSide1(loc) ? 0 : m_children.size(); - insertItem(item, index); + insertItem(item, index, defaultSizeMode); } else { // Inserting directly in a container ? Only if it's root. Q_ASSERT(isRoot()); @@ -1066,10 +1086,10 @@ void ItemContainer::insertItem(Item *item, Location loc, AddingOption option) container->setChildren(m_children, m_orientation); m_children.clear(); setOrientation(oppositeOrientation(m_orientation)); - insertItem(container, 0); + insertItem(container, 0, DefaultSizeMode::None); // Now we have the correct orientation, we can insert - insertItem(item, loc, option); + insertItem(item, loc, defaultSizeMode, addingOption); if (!container->hasVisibleChildren()) container->setGeometry(QRect()); @@ -1450,10 +1470,17 @@ void ItemContainer::setLength_recursive(int length, Qt::Orientation o) setSize_recursive(sz); } -void ItemContainer::insertItem(Item *item, int index) +void ItemContainer::insertItem(Item *item, int index, DefaultSizeMode defaultSizeMode) { + if (defaultSizeMode != DefaultSizeMode::None) { + /// Choose a nice size for the item we're adding + const int suggestedLength = d->defaultLengthFor(item, defaultSizeMode); + item->setLength_recursive(suggestedLength, m_orientation); + } + m_children.insert(index, item); item->setParentContainer(this); + Q_EMIT itemsChanged(); if (!m_convertingItemToContainer && item->isVisible()) @@ -1869,10 +1896,9 @@ void ItemContainer::restoreChild(Item *item) const int available = availableOnSide(item, Side1) + availableOnSide(item, Side2) - Item::separatorThickness; - const QSize proposedSize = item->size(); const int max = available; const int min = item->minLength(m_orientation); - const int proposed = Layouting::length(proposedSize, m_orientation); + const int proposed = Layouting::length(item->size(), m_orientation); const int newLength = qBound(min, proposed, max); Q_ASSERT(item->isVisible()); @@ -2684,3 +2710,32 @@ void SizingInfo::fromVariantMap(const QVariantMap &map) minSize = mapToSize(map[QStringLiteral("minSize")].toMap()); maxSize = mapToSize(map[QStringLiteral("maxSize")].toMap()); } + +int ItemContainer::Private::defaultLengthFor(Item *item, DefaultSizeMode mode) const +{ + int result = 0; + switch (mode) { + case DefaultSizeMode::None: + break; + case DefaultSizeMode::Fair: { + const int numVisibleChildren = q->numVisibleChildren() + 1; // +1 so it counts with @p item too, which we're adding + const int usableLength = q->length() - (Item::separatorThickness*(numVisibleChildren - 1)); + result = usableLength / numVisibleChildren; + break; + } + case DefaultSizeMode::FairButFloor: { + int length = defaultLengthFor(item, DefaultSizeMode::Fair); + result = qMin(length, item->length(q->m_orientation)); + break; + } + case DefaultSizeMode::ItemSize: + result = item->length(q->m_orientation); + break; + case DefaultSizeMode::SizePolicy: + qWarning() << Q_FUNC_INFO << "Now implemented yet"; + break; + } + + result = qMax(item->minLength(q->m_orientation), result); // bound with max-size too + return result; +} diff --git a/src/private/multisplitter/Item_p.h b/src/private/multisplitter/Item_p.h index 54a4f801..0957f52e 100644 --- a/src/private/multisplitter/Item_p.h +++ b/src/private/multisplitter/Item_p.h @@ -86,6 +86,16 @@ enum class NeighbourSqueezeStrategy { Side2NeighboursFirst ///< Same as Side1NeighboursFirst but does reverse order }; +///@brief When an item is added we need to figure out what's a decent size for it +///This enum specifies the different ways to calculate it +enum class DefaultSizeMode { + ItemSize, ///< Simply uses the Item::size() of the item being added. Actual used size might be smaller if our window isn't big enough. + Fair, ///< Gives an equal relative size as the items that are already in the layout + FairButFloor, ///< Equal to fair, but if the item is smaller than the fair suggestion, then that small size is used. + SizePolicy, ///< Uses the item's sizeHint() and sizePolicy() + None, ///< Don't do any sizing +}; + inline Qt::Orientation oppositeOrientation(Qt::Orientation o) { return o == Qt::Vertical ? Qt::Horizontal : Qt::Vertical; @@ -291,7 +301,9 @@ public: bool isRoot() const; virtual int visibleCount_recursive() const; - virtual void insertItem(Item *item, Location, AddingOption = AddingOption_None); + virtual void insertItem(Item *item, Location, + DefaultSizeMode defaultSizeMode = DefaultSizeMode::Fair, + AddingOption = AddingOption_None); /** * @brief No widget can have a minimum size smaller than this, regardless of their minimum size. @@ -420,7 +432,8 @@ public: explicit ItemContainer(QWidget *hostWidget, ItemContainer *parent); explicit ItemContainer(QWidget *parent); - void insertItem(Item *item, int index); + ~ItemContainer(); + void insertItem(Item *item, int index, DefaultSizeMode); [[nodiscard]] bool checkSanity() override; bool hasOrientation() const; int numChildren() const; @@ -433,7 +446,8 @@ public: void setGeometry_recursive(QRect rect) override; ItemContainer *convertChildToContainer(Item *leaf); - void insertItem(Item *item, Location, AddingOption = AddingOption_None) override; + void insertItem(Item *item, Location, DefaultSizeMode defaultSizeMode = DefaultSizeMode::Fair, + AddingOption = AddingOption_None) override; bool hasOrientationFor(Location) const; Item::List visibleChildren(bool includeBeingInserted = false) const; int usableLength() const; @@ -551,6 +565,9 @@ private: mutable bool m_checkSanityScheduled = false; QVector m_separators; bool m_convertingItemToContainer = false; + + struct Private; + Private *const d; }; /** diff --git a/src/private/multisplitter/MultiSplitterLayout.cpp b/src/private/multisplitter/MultiSplitterLayout.cpp index 292442bd..731ae978 100644 --- a/src/private/multisplitter/MultiSplitterLayout.cpp +++ b/src/private/multisplitter/MultiSplitterLayout.cpp @@ -115,7 +115,9 @@ bool MultiSplitterLayout::validateInputs(QWidgetOrQuick *widget, return true; } -void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame *relativeToWidget, AddingOption option) +void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, + Frame *relativeToWidget, DefaultSizeMode defaultSizeMode, + AddingOption option) { auto frame = qobject_cast(w); qCDebug(addwidget) << Q_FUNC_INFO << w @@ -164,7 +166,8 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame } Q_ASSERT(!newItem->geometry().isEmpty()); - relativeTo->insertItem(newItem, Layouting::Location(location), Layouting::AddingOption(option)); + relativeTo->insertItem(newItem, Layouting::Location(location), + Layouting::DefaultSizeMode(defaultSizeMode), Layouting::AddingOption(option)); if (dw && option && AddingOption_StartHidden) delete frame; diff --git a/src/private/multisplitter/MultiSplitterLayout_p.h b/src/private/multisplitter/MultiSplitterLayout_p.h index 0fc5f2e8..0c2f4177 100644 --- a/src/private/multisplitter/MultiSplitterLayout_p.h +++ b/src/private/multisplitter/MultiSplitterLayout_p.h @@ -85,7 +85,7 @@ public: * @brief Adds a widget to this MultiSplitter. */ void addWidget(QWidgetOrQuick *widget, KDDockWidgets::Location location, - Frame *relativeTo = nullptr, AddingOption option = {}); + Frame *relativeTo = nullptr, DefaultSizeMode = DefaultSizeMode::Fair, AddingOption option = {}); /** * Adds an entire MultiSplitter into this layout. The donor MultiSplitter will be deleted diff --git a/src/private/multisplitter/tests/tst_multisplitter.cpp b/src/private/multisplitter/tests/tst_multisplitter.cpp index 9f0b2176..41852463 100644 --- a/src/private/multisplitter/tests/tst_multisplitter.cpp +++ b/src/private/multisplitter/tests/tst_multisplitter.cpp @@ -514,6 +514,7 @@ void TestMultiSplitter::tst_minSize() item1->m_sizingInfo.minSize = {101, 150}; item2->m_sizingInfo.minSize = {200, 300}; + item2->setSize(item2->m_sizingInfo.minSize); item22->m_sizingInfo.minSize = {100, 100}; root->insertItem(item1, Location_OnLeft); @@ -1023,7 +1024,7 @@ void TestMultiSplitter::tst_numSeparators() root->insertItem(item5, Location_OnLeft); QCOMPARE(root->separators_recursive().size(), 0); - root->insertItem(item6, Location_OnLeft, AddingOption_StartHidden); + root->insertItem(item6, Location_OnLeft, DefaultSizeMode::Fair, AddingOption_StartHidden); QCOMPARE(root->separators_recursive().size(), 0); QVERIFY(serializeDeserializeTest(root)); } @@ -1095,7 +1096,7 @@ void TestMultiSplitter::tst_insertHiddenContainer() auto root1 = createRoot(); auto root2 = createRoot(); Item *item2 = createItem(); - root2->insertItem(item2, Location_OnLeft, AddingOption_StartHidden); + root2->insertItem(item2, Location_OnLeft, DefaultSizeMode::Fair, AddingOption_StartHidden); QVERIFY(root1->checkSanity()); QVERIFY(root2->checkSanity()); @@ -1131,7 +1132,8 @@ void TestMultiSplitter::tst_availableOnSide() Item *item3 = createItem(/*min=*/QSize(200, 200)); root->insertItem(item3, Location_OnRight); - QCOMPARE(root->availableOnSide(item3, Side1), (item1->width() - item1->minSize().width()) - (item2->width() - item2->minSize().width())); + QVERIFY(root->checkSanity()); + QCOMPARE(root->availableOnSide(item3, Side1), (item1->width() - item1->minSize().width()) + (item2->width() - item2->minSize().width())); QCOMPARE(root->availableOnSide(item3, Side2), 0); auto separator2 = root->separators_recursive()[1]; @@ -1142,9 +1144,9 @@ void TestMultiSplitter::tst_availableOnSide() item3->insertItem(item4, Location_OnBottom); auto c = item3->parentContainer(); - QCOMPARE(c->availableOnSide_recursive(item3, Side1, Qt::Horizontal), (item1->width() - item1->minSize().width()) - (item2->width() - item2->minSize().width())); + QCOMPARE(c->availableOnSide_recursive(item3, Side1, Qt::Horizontal), (item1->width() - item1->minSize().width()) + (item2->width() - item2->minSize().width())); QCOMPARE(c->availableOnSide_recursive(item3, Side2, Qt::Horizontal), 0); - QCOMPARE(c->availableOnSide_recursive(item4, Side1, Qt::Horizontal), (item1->width() - item1->minSize().width()) - (item2->width() - item2->minSize().width())); + QCOMPARE(c->availableOnSide_recursive(item4, Side1, Qt::Horizontal), (item1->width() - item1->minSize().width()) + (item2->width() - item2->minSize().width())); QCOMPARE(c->availableOnSide_recursive(item4, Side2, Qt::Horizontal), 0); QCOMPARE(c->availableOnSide_recursive(item4, Side1, Qt::Vertical), (item3->height() - item3->minSize().height())); @@ -1153,9 +1155,9 @@ void TestMultiSplitter::tst_availableOnSide() Item *item31 = createItem(/*min=*/QSize(100, 100)); item3->insertItem(item31, Location_OnRight); auto container31 = item31->parentContainer(); - auto separato31 = container31->separators_recursive()[0]; - QCOMPARE(container31->minPosForSeparator_global(separato31), item1->minSize().width() + item2->minSize().width() + item3->minSize().width() + 2*Item::separatorThickness); - QCOMPARE(container31->maxPosForSeparator_global(separato31), root->width() - item31->width() - Item::separatorThickness); + auto separator31 = container31->separators()[0]; + QCOMPARE(container31->minPosForSeparator_global(separator31), item1->minSize().width() + item2->minSize().width() + item3->minSize().width() + 2*Item::separatorThickness); + QCOMPARE(container31->maxPosForSeparator_global(separator31), root->width() -item31->minSize().width() - Item::separatorThickness); } void TestMultiSplitter::tst_resizeViaSeparator()