/* This file is part of KDDockWidgets. SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Sérgio Martins SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only Contact KDAB at for commercial licensing options. */ #include "simple_test_framework.h" #include "core/layouting/Item_p.h" #include "core/layouting/LayoutingHost_p.h" #include "core/layouting/LayoutingGuest_p.h" #include "core/layouting/LayoutingSeparator_p.h" #include "core/DropArea.h" #include "core/View_p.h" #include "core/Utils_p.h" #include "core/ObjectGuard_p.h" #include "core/ScopedValueRollback_p.h" #include #include #include using namespace KDDockWidgets; using namespace KDDockWidgets::Core; static int st = Item::layoutSpacing; namespace { class Guest : public Core::LayoutingGuest { public: using Core::LayoutingGuest::LayoutingGuest; Guest(Core::View *view, LayoutingHost *host) : m_host(host) , m_view(view) { view->d->layoutInvalidated.connect([this] { layoutInvalidated.emit(); }); } ~Guest() override { beingDestroyed.emit(); } void setHost(LayoutingHost *host) override { m_host = host; if (m_view && host) m_view->setParent(dynamic_cast(Layout::fromLayoutingHost(host))->view()); } LayoutingHost *host() const override { return m_host; } Size minSize() const override { return m_view->minSize(); } Size maxSizeHint() const override { return m_view->maxSizeHint(); } void setGeometry(Rect r) override { if (r != geometry()) { m_numSetGeometry++; m_view->setGeometry(r); } } void setVisible(bool is) override { m_view->setVisible(is); } Rect geometry() const override { return m_view->geometry(); } QString id() const override { return m_view->d->id(); } LayoutingHost *m_host = nullptr; View *const m_view; int m_numSetGeometry = 0; }; } static std::vector s_views; struct DeleteViews { ~DeleteViews() { deleteAll(s_views); s_views.clear(); } }; static bool serializeDeserializeTest(const std::unique_ptr &root) { // Serializes and deserializes a layout if (!root->checkSanity()) return false; nlohmann::json serialized; root->to_json(serialized); ItemBoxContainer root2(root->host()); std::unordered_map widgets; const Item::List originalItems = root->items_recursive(); for (Item *item : originalItems) if (auto guest = item->guest()) widgets[guest->id()] = guest; root2.fillFromJson(serialized, widgets); return root2.checkSanity(); } static std::unique_ptr createRoot() { auto dropArea = new DropArea(nullptr, MainWindowOption_None); s_views.push_back(dropArea); auto root = new ItemBoxContainer(dropArea->asLayoutingHost()); root->setSize({ 1000, 1000 }); return std::unique_ptr(root); } static Item *createItem(Size minSz = {}, Size maxSz = {}) { static int count = 0; count++; auto item = new Item(nullptr); item->setGeometry(Rect(0, 0, 200, 200)); item->setObjectName(QString::number(count)); Core::CreateViewOptions opts; if (minSz.isValid()) opts.minSize = minSz; if (maxSz.isValid()) opts.maxSize = maxSz; auto guestView = Core::Platform::instance()->tests_createView(opts); guestView->setViewName(item->objectName()); auto guest = new Guest(guestView, s_views[s_views.size() - 1]->asLayoutingHost()); guestView->d->beingDestroyed.connect([guest] { delete guest; }); item->setGuest(guest); return item; } static ItemBoxContainer *createRootWithSingleItem() { auto dropArea = new DropArea(nullptr, MainWindowOption_None); s_views.push_back(dropArea); auto root = new ItemBoxContainer(dropArea->asLayoutingHost()); root->setSize({ 1000, 1000 }); Item *item1 = createItem(); root->insertItem(item1, Location_OnTop); return root; } KDDW_QCORO_TASK tst_createRoot() { DeleteViews deleteViews; auto root = createRoot(); CHECK(root->isRoot()); CHECK(root->isContainer()); CHECK(root->hasOrientation()); CHECK_EQ(root->size(), Size(1000, 1000)); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertOne() { DeleteViews deleteViews; auto root = createRoot(); auto item = createItem(); root->insertItem(item, Location_OnTop); CHECK(root->checkSanity()); CHECK_EQ(root->numChildren(), 1); CHECK(!item->isContainer()); CHECK_EQ(root->size(), Size(1000, 1000)); CHECK_EQ(item->size(), root->size()); CHECK_EQ(item->pos(), Point()); CHECK_EQ(item->pos(), root->pos()); CHECK(root->hasChildren()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertThreeSideBySide() { DeleteViews deleteViews; // Result is [1, 2, 3] auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); root->insertItem(item3, Location_OnRight); CHECK(root->checkSanity()); CHECK_EQ(root->numChildren(), 3); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertTwoHorizontal() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); root->insertItem(item1, Location_OnLeft); ItemBoxContainer::insertItemRelativeTo(item2, item1, Location_OnRight); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertTwoVertical() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); root->insertItem(item1, Location_OnTop); ItemBoxContainer::insertItemRelativeTo(item2, item1, Location_OnBottom); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertOnWidgetItem1() { DeleteViews deleteViews; // We insert into a widget item instead of in a container. It will insert in the container still // Result is still [1, 2, 3] auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnRight); CHECK(item3->x() > item2->x()); CHECK_EQ(item3->y(), item2->y()); CHECK(root->checkSanity()); CHECK_EQ(root->numChildren(), 3); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertOnWidgetItem2() { DeleteViews deleteViews; // Same, but result [1, 3, 2] auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnLeft); CHECK(item1->x() < item3->x()); CHECK(item3->x() < item2->x()); CHECK_EQ(item3->y(), item2->y()); CHECK(root->checkSanity()); CHECK_EQ(root->numChildren(), 3); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertOnWidgetItem1DifferentOrientation() { DeleteViews deleteViews; // Result [1, 2, |3 |] // |3.1| auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item31 = createItem(); root->insertItem(item1, Location_OnLeft); CHECK(root->checkSanity()); root->insertItem(item2, Location_OnRight); CHECK(root->checkSanity()); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnRight); CHECK(root->checkSanity()); ItemBoxContainer::insertItemRelativeTo(item31, item3, Location_OnBottom); CHECK(root->checkSanity()); auto container3 = item3->parentBoxContainer(); CHECK(container3->isContainer()); CHECK(container3 != root.get()); CHECK(root->isHorizontal()); CHECK(container3->isVertical()); CHECK_EQ(root->numChildren(), 3); CHECK_EQ(container3->numChildren(), 2); CHECK(item1->x() < item2->x()); CHECK(item3->parentBoxContainer()->x() > item2->x()); CHECK_EQ(item3->x(), 0); CHECK_EQ(item3->y(), item2->y()); CHECK_EQ(item1->y(), item2->y()); CHECK(item31->y() >= item3->y()); CHECK_EQ(item31->parentBoxContainer(), container3); CHECK_EQ(item3->parentBoxContainer(), container3); CHECK_EQ(container3->parentBoxContainer(), root.get()); CHECK_EQ(Point(0, 0), item3->pos()); CHECK_EQ(container3->width(), item3->width()); CHECK_EQ(container3->height(), item3->height() + st + item31->height()); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertOnWidgetItem2DifferentOrientation() { DeleteViews deleteViews; // Result [1, 2, |3 3.2|] // |3.1 | auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item5 = createItem(); auto item4 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item4, item3, Location_OnBottom); auto container3Parent = item3->parentBoxContainer(); ItemBoxContainer::insertItemRelativeTo(item5, item3, Location_OnRight); CHECK(root->checkSanity()); auto container3 = item3->parentBoxContainer(); CHECK_EQ(container3->parentBoxContainer(), container3Parent); CHECK(container3->isContainer()); CHECK(container3 != root.get()); CHECK(root->isHorizontal()); CHECK(container3->isHorizontal()); CHECK(container3Parent->isVertical()); CHECK_EQ(root->numChildren(), 3); CHECK_EQ(container3->numChildren(), 2); CHECK_EQ(container3Parent->numChildren(), 2); CHECK(item1->x() < item2->x()); CHECK_EQ(container3->pos(), Point(0, 0l)); CHECK_EQ(item3->pos(), container3->pos()); CHECK(container3Parent->x() > item2->x()); CHECK_EQ(item3->y(), item2->y()); CHECK_EQ(item1->y(), item2->y()); CHECK(item4->y() >= item3->y()); CHECK_EQ(item4->parentBoxContainer(), container3Parent); CHECK_EQ(item3->parentBoxContainer(), container3); CHECK_EQ(container3Parent->parentBoxContainer(), root.get()); CHECK_EQ(container3->pos(), item3->pos()); CHECK_EQ(container3->width(), item3->width() + item5->width() + st); CHECK_EQ(container3->height(), item3->height()); CHECK_EQ(container3Parent->height(), item3->height() + st + item4->height()); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertOnRootDifferentOrientation() { DeleteViews deleteViews; // [ 4 ] // Result [1, 2, |3 3.2|] // |3.1 | auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item31 = createItem(); auto item32 = createItem(); auto item4 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item31, item3, Location_OnBottom); ItemBoxContainer::insertItemRelativeTo(item32, item3, Location_OnRight); root->insertItem(item4, Location_OnTop); CHECK_EQ(item4->parentBoxContainer(), root.get()); CHECK_EQ(item4->pos(), root->pos()); CHECK_EQ(item4->width(), root->width()); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_removeItem1() { // [ 4 ] // Result [1, 2, |3 3.2|] // |3.1 | DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item31 = createItem(); auto item32 = createItem(); auto item4 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item31, item3, Location_OnBottom); ItemBoxContainer::insertItemRelativeTo(item32, item3, Location_OnRight); root->insertItem(item4, Location_OnTop); CHECK(root->checkSanity()); CHECK_EQ(root->numChildren(), 2); root->removeItem(item4); CHECK(root->checkSanity()); CHECK_EQ(root->numChildren(), 1); auto c1 = item1->parentBoxContainer(); CHECK_EQ(c1->pos(), Point(0, 0)); CHECK_EQ(c1->width(), root->width()); CHECK_EQ(c1->height(), item1->height()); CHECK_EQ(c1->height(), root->height()); const int item3and32Width = item3->width() + item32->width() + st; root->removeItem(item32); CHECK_EQ(item3->width(), item3and32Width); CHECK(root->checkSanity()); root->removeItem(item31); CHECK(root->checkSanity()); CHECK_EQ(item2->height(), item3->height()); ObjectGuard c3 = item3->parentBoxContainer(); root->removeItem(c3); CHECK(c3.isNull()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_removeItem2() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item31 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item31, item3, Location_OnBottom); item31->parentBoxContainer()->removeItem(item31); item3->parentBoxContainer()->removeItem(item3); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_minSize() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item22 = createItem(); 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); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item22, item2, Location_OnBottom); CHECK_EQ(item2->minSize(), Size(200, 300)); CHECK_EQ(item2->parentBoxContainer()->minSize(), Size(200, 300 + 100 + st)); CHECK_EQ(root->minSize(), Size(101 + 200 + st, 300 + 100 + st)); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_resize() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item31 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); root->insertItem(item3, Location_OnRight); const int item1Percentage = item1->width() / root->width(); const int item2Percentage = item1->width() / root->width(); const int item3Percentage = item1->width() / root->width(); // Now resize: root->setSize_recursive({ 2000, 505 }); CHECK(root->checkSanity()); CHECK(item1Percentage - (1.0 * item1->width() / root->width()) < 0.01); CHECK(item2Percentage - (1.0 * item2->width() / root->width()) < 0.01); CHECK(item3Percentage - (1.0 * item3->width() / root->width()) < 0.01); CHECK_EQ(root->width(), 2000); CHECK_EQ(root->height(), 505); CHECK_EQ(item1->height(), 505); CHECK_EQ(item2->height(), 505); CHECK_EQ(item3->height(), 505); ItemBoxContainer::insertItemRelativeTo(item31, item3, Location_OnBottom); CHECK(root->checkSanity()); root->setSize_recursive({ 2500, 505 }); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_resizeWithConstraints() { DeleteViews deleteViews; Platform::instance()->m_expectedWarning = "New size doesn't respect size constraints"; { // Test that resizing below minSize isn't permitted. auto root = createRoot(); auto item1 = createItem(); item1->setMinSize(Size(500, 500)); root->insertItem(item1, Location_OnLeft); CHECK(root->checkSanity()); root->setSize_recursive(item1->minSize()); // Still fits root->setSize_recursive(item1->minSize() - Size(1, 0)); // wouldn't fit CHECK_EQ(root->size(), item1->size()); // still has the old size CHECK(serializeDeserializeTest(root)); } { // |1|2|3| auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); root->setSize_recursive(Size(2000, 500)); item1->setMinSize(Size(500, 500)); item2->setMinSize(Size(500, 500)); item3->setMinSize(Size(500, 500)); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); root->insertItem(item3, Location_OnRight); CHECK(root->checkSanity()); } Platform::instance()->m_expectedWarning.clear(); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_availableSize() { DeleteViews deleteViews; auto root = createRoot(); CHECK_EQ(root->availableSize(), Size(1000, 1000)); CHECK_EQ(root->minSize(), Size(0, 0)); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); item1->m_sizingInfo.minSize = { 100, 100 }; item2->m_sizingInfo.minSize = { 100, 100 }; item3->m_sizingInfo.minSize = { 100, 100 }; root->insertItem(item1, Location_OnLeft); CHECK_EQ(root->availableSize(), Size(900, 900)); CHECK_EQ(root->minSize(), Size(100, 100)); CHECK_EQ(root->neighboursLengthFor(item1, Side1, Qt::Horizontal), 0); CHECK_EQ(root->neighboursLengthFor(item1, Side2, Qt::Horizontal), 0); CHECK_EQ(root->neighboursMinLengthFor(item1, Side1, Qt::Horizontal), 0); CHECK_EQ(root->neighboursMinLengthFor(item1, Side2, Qt::Horizontal), 0); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side1, Qt::Vertical), 0); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side2, Qt::Vertical), 0); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side1, Qt::Horizontal), 0); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side2, Qt::Horizontal), 0); root->insertItem(item2, Location_OnLeft); CHECK_EQ(root->availableSize(), Size(800 - st, 900)); CHECK_EQ(root->minSize(), Size(200 + st, 100)); CHECK_EQ(root->neighboursLengthFor(item1, Side1, Qt::Horizontal), item2->width()); CHECK_EQ(root->neighboursLengthFor(item1, Side2, Qt::Horizontal), 0); CHECK_EQ(root->neighboursLengthFor(item2, Side1, Qt::Horizontal), 0); CHECK_EQ(root->neighboursLengthFor(item2, Side2, Qt::Horizontal), item1->width()); CHECK_EQ(root->neighboursMinLengthFor(item1, Side1, Qt::Horizontal), item2->minSize().width()); CHECK_EQ(root->neighboursMinLengthFor(item1, Side2, Qt::Horizontal), 0); CHECK_EQ(root->neighboursMinLengthFor(item2, Side1, Qt::Horizontal), 0); CHECK_EQ(root->neighboursMinLengthFor(item2, Side2, Qt::Horizontal), item1->minSize().width()); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side1, Qt::Vertical), 0); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side2, Qt::Vertical), 0); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side1, Qt::Horizontal), item2->width()); CHECK_EQ(root->neighboursLengthFor_recursive(item1, Side2, Qt::Horizontal), 0); root->insertItem(item3, Location_OnBottom); CHECK_EQ(root->availableSize(), Size(800 - st, 800 - st)); CHECK_EQ(root->minSize(), Size(200 + st, 100 + 100 + st)); CHECK_EQ(item3->parentBoxContainer()->neighboursMinLengthFor(item3, Side1, Qt::Vertical), item1->minSize().height()); auto container2 = item2->parentBoxContainer(); CHECK_EQ(container2->neighboursLengthFor_recursive(item1, Side1, Qt::Vertical), 0); CHECK_EQ(container2->neighboursLengthFor_recursive(item1, Side2, Qt::Vertical), item3->height()); CHECK_EQ(container2->neighboursLengthFor_recursive(item1, Side1, Qt::Horizontal), item2->width()); CHECK_EQ(container2->neighboursLengthFor_recursive(item1, Side2, Qt::Horizontal), 0); // More nesting auto item4 = createItem(); auto item5 = createItem(); ItemBoxContainer::insertItemRelativeTo(item4, item3, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item5, item4, Location_OnBottom); auto container4 = item4->parentBoxContainer(); CHECK_EQ(container4->neighboursLengthFor_recursive(item4, Side1, Qt::Vertical), item1->height()); CHECK_EQ(container4->neighboursLengthFor_recursive(item4, Side2, Qt::Vertical), item5->height()); CHECK_EQ(container4->neighboursLengthFor_recursive(item4, Side1, Qt::Horizontal), item3->width()); CHECK_EQ(container4->neighboursLengthFor_recursive(item4, Side2, Qt::Horizontal), 0); CHECK_EQ(container4->neighboursLengthFor_recursive(item5, Side1, Qt::Vertical), item4->height() + item1->height()); CHECK_EQ(container4->neighboursLengthFor_recursive(item5, Side2, Qt::Vertical), 0); CHECK_EQ(container4->neighboursLengthFor_recursive(item5, Side1, Qt::Horizontal), item3->width()); CHECK_EQ(container4->neighboursLengthFor_recursive(item5, Side2, Qt::Horizontal), 0); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_missingSize() { DeleteViews deleteViews; auto root = createRoot(); CHECK_EQ(root->size(), Size(1000, 1000)); CHECK_EQ(root->availableSize(), Size(1000, 1000)); Item *item1 = createItem(); item1->setMinSize({ 100, 100 }); Item *item2 = createItem(); item2->setMinSize(root->size()); Item *item3 = createItem(); item3->setMinSize(root->size() + Size(100, 200)); // Test with an existing item root->insertItem(item1, Location_OnTop); CHECK(serializeDeserializeTest(root)); // Add just so the views are deleted at the end root->insertItem(item2, Location_OnTop); root->insertItem(item3, Location_OnTop); delete item2; delete item3; KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_ensureEnoughSize() { DeleteViews deleteViews; // Tests that the layout's size grows when the item being inserted wouldn't have enough space auto root = createRoot(); /// 1000x1000 Item *item1 = createItem(); item1->setMinSize({ 2000, 500 }); // Insert to empty layout: root->insertItem(item1, Location_OnLeft); CHECK_EQ(root->size(), Size(2000, 1000)); CHECK_EQ(item1->size(), Size(2000, 1000)); CHECK_EQ(item1->minSize(), root->minSize()); CHECK(root->checkSanity()); // Insert to non-empty layout Item *item2 = createItem(); item2->setMinSize({ 2000, 2000 }); root->insertItem(item2, Location_OnRight); CHECK(root->checkSanity()); CHECK_EQ( root->size(), Size(item1->minSize().width() + item2->minSize().width() + st, item2->minSize().height())); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_turnIntoPlaceholder() { DeleteViews deleteViews; auto root = createRoot(); int numVisibleItems = 0; root->numVisibleItemsChanged.connect( [&numVisibleItems](int count) { numVisibleItems = count; }); Item *item1 = createItem(); Item *item2 = createItem(); Item *item3 = createItem(); root->insertItem(item1, Location_OnLeft); CHECK_EQ(numVisibleItems, 1); CHECK(item1->isVisible()); item1->turnIntoPlaceholder(); CHECK_EQ(numVisibleItems, 0); CHECK(!item1->isVisible()); CHECK_EQ(root->visibleCount_recursive(), 0); CHECK_EQ(root->count_recursive(), 1); CHECK(root->checkSanity()); root->insertItem(item2, Location_OnLeft); CHECK(root->checkSanity()); CHECK_EQ(numVisibleItems, 1); root->insertItem(item3, Location_OnLeft); CHECK_EQ(numVisibleItems, 2); CHECK(root->checkSanity()); CHECK_EQ(item2->width() + item3->width() + st, root->width()); item2->turnIntoPlaceholder(); CHECK_EQ(numVisibleItems, 1); CHECK(root->checkSanity()); CHECK_EQ(item3->width(), root->width()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_suggestedRect() { DeleteViews deleteViews; auto root = createRoot(); root->setSize(Size(2000, 1000)); const Size minSize(100, 100); Item itemBeingDropped(nullptr); itemBeingDropped.setMinSize(minSize); Rect leftRect = root->suggestedDropRect(&itemBeingDropped, nullptr, Location_OnLeft); Rect topRect = root->suggestedDropRect(&itemBeingDropped, nullptr, Location_OnTop); Rect bottomRect = root->suggestedDropRect(&itemBeingDropped, nullptr, Location_OnBottom); Rect rightRect = root->suggestedDropRect(&itemBeingDropped, nullptr, Location_OnRight); // Test relative to root: CHECK(leftRect.width() >= minSize.width()); CHECK(topRect.height() >= minSize.height()); CHECK(bottomRect.height() >= minSize.height()); CHECK(rightRect.width() >= minSize.width()); CHECK_EQ(leftRect.topLeft(), Point(0, 0)); CHECK_EQ(leftRect.bottomLeft(), root->rect().bottomLeft()); CHECK_EQ(rightRect.topRight(), root->rect().topRight()); CHECK_EQ(rightRect.bottomRight(), root->rect().bottomRight()); CHECK_EQ(topRect.topLeft(), root->rect().topLeft()); CHECK_EQ(topRect.topRight(), root->rect().topRight()); CHECK_EQ(bottomRect.bottomLeft(), root->rect().bottomLeft()); CHECK_EQ(bottomRect.bottomRight(), root->rect().bottomRight()); // Test relative to an item Item *item1 = createItem(); item1->setMinSize(Size(100, 100)); root->insertItem(item1, Location_OnLeft); leftRect = root->suggestedDropRect(&itemBeingDropped, item1, Location_OnLeft); topRect = root->suggestedDropRect(&itemBeingDropped, item1, Location_OnTop); bottomRect = root->suggestedDropRect(&itemBeingDropped, item1, Location_OnBottom); rightRect = root->suggestedDropRect(&itemBeingDropped, item1, Location_OnRight); CHECK(leftRect.width() >= minSize.width()); CHECK(topRect.height() >= minSize.height()); CHECK(bottomRect.height() >= minSize.height()); CHECK(rightRect.width() >= minSize.width()); CHECK_EQ(leftRect.topLeft(), Point(0, 0)); CHECK_EQ(leftRect.bottomLeft(), root->rect().bottomLeft()); CHECK_EQ(rightRect.topRight(), root->rect().topRight()); CHECK_EQ(rightRect.bottomRight(), root->rect().bottomRight()); CHECK_EQ(topRect.topLeft(), root->rect().topLeft()); CHECK_EQ(topRect.topRight(), root->rect().topRight()); CHECK_EQ(bottomRect.bottomLeft(), root->rect().bottomLeft()); CHECK_EQ(bottomRect.bottomRight(), root->rect().bottomRight()); // Insert another item: Item *item2 = createItem(); item1->setMinSize(Size(100, 100)); root->insertItem(item2, Location_OnRight); leftRect = root->suggestedDropRect(&itemBeingDropped, item2, Location_OnLeft); topRect = root->suggestedDropRect(&itemBeingDropped, item2, Location_OnTop); bottomRect = root->suggestedDropRect(&itemBeingDropped, item2, Location_OnBottom); rightRect = root->suggestedDropRect(&itemBeingDropped, item2, Location_OnRight); CHECK_EQ(leftRect.y(), item2->geometry().y()); CHECK(leftRect.x() < item2->geometry().x()); CHECK(leftRect.x() > item1->geometry().x()); CHECK_EQ(rightRect.topRight(), root->rect().topRight()); CHECK_EQ(rightRect.bottomRight(), root->rect().bottomRight()); CHECK_EQ(topRect.topLeft(), item2->geometry().topLeft()); CHECK_EQ(topRect.topRight(), item2->geometry().topRight()); CHECK_EQ(bottomRect.bottomLeft(), item2->geometry().bottomLeft()); CHECK_EQ(bottomRect.bottomRight(), item2->geometry().bottomRight()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_suggestedRect2() { DeleteViews deleteViews; // Tests a bug where the inner drop locations didn't work when there was a nested container // Like container >> container >> Item auto root1 = createRoot(); auto root2 = createRoot(); Item itemBeingDropped(nullptr); itemBeingDropped.setMinSize(Size(100, 100)); Item *item = createItem(); root2->insertItem(item, Location_OnRight); root1->insertItem(root2.release(), Location_OnRight); CHECK(item->parentBoxContainer() ->suggestedDropRect(&itemBeingDropped, item, Location_OnRight) .isValid()); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_suggestedRect3() { DeleteViews deleteViews; auto root1 = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); Item *item3 = createItem(); Item *itemToDrop = createItem(); root1->insertItem(item1, Location_OnLeft); root1->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnBottom); CHECK(!item3->parentBoxContainer() ->suggestedDropRect(itemToDrop, item3, Location_OnLeft) .isEmpty()); /// insert just to cleanup at shutdown root1->insertItem(itemToDrop, Location_OnRight); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_suggestedRect4() { DeleteViews deleteViews; auto root = createRoot(); auto root1 = createRoot(); Item *item1 = createItem(); root1->insertItem(item1, Location_OnLeft); root->insertItem(root1.release(), Location_OnLeft); auto root2 = createRoot(); Item *item2 = createItem(); root2->insertItem(item2, Location_OnLeft); auto root3 = createRoot(); Item *item3 = createItem(); root3->insertItem(item3, Location_OnLeft); ItemBoxContainer::insertItemRelativeTo(root2.release(), item1, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(root3.release(), item2, Location_OnBottom); Item *itemToDrop = createItem(); CHECK(root->checkSanity()); CHECK(!item3->parentBoxContainer() ->suggestedDropRect(itemToDrop, item3, Location_OnLeft) .isEmpty()); /// insert just to cleanup at shutdown root->insertItem(itemToDrop, Location_OnRight); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertAnotherRoot() { DeleteViews deleteViews; { auto root1 = createRoot(); Item *item1 = createItem(); root1->insertItem(item1, Location_OnRight); auto host1 = root1->host(); auto root2 = createRoot(); Item *item2 = createItem(); root2->insertItem(item2, Location_OnRight); root1->insertItem(root2.release(), Location_OnBottom); CHECK_EQ(root1->host(), host1); CHECK_EQ(item2->host(), host1); const auto &items = root1->items_recursive(); for (Item *item : items) { CHECK_EQ(item->host(), host1); CHECK(item->isVisible()); } CHECK(root1->checkSanity()); CHECK(serializeDeserializeTest(root1)); } { auto root1 = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); root1->insertItem(item1, Location_OnLeft); root1->insertItem(item2, Location_OnRight); auto host1 = root1->host(); auto root2 = createRoot(); Item *item12 = createItem(); root2->insertItem(item12, Location_OnRight); root1->insertItem(root2.release(), Location_OnTop); CHECK_EQ(root1->host(), host1); CHECK_EQ(item2->host(), host1); for (Item *item : root1->items_recursive()) { CHECK_EQ(item->host(), host1); CHECK(item->isVisible()); } CHECK(root1->checkSanity()); CHECK(serializeDeserializeTest(root1)); } KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_misc1() { DeleteViews deleteViews; // Random test1 auto root = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); Item *item3 = createItem(); Item *item4 = createItem(); Item *item5 = createItem(); root->insertItem(item1, Location_OnTop); ItemBoxContainer::insertItemRelativeTo(item2, item1, Location_OnRight); root->insertItem(item3, Location_OnBottom); ItemBoxContainer::insertItemRelativeTo(item4, item3, Location_OnRight); root->insertItem(item5, Location_OnLeft); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_misc2() { DeleteViews deleteViews; // Random test1 // |5|1|2| // | |3|4| auto root = createRoot(); ItemBoxContainer *root1 = createRootWithSingleItem(); Item *item1 = root1->childItems().constFirst(); ItemBoxContainer *root2 = createRootWithSingleItem(); Item *item3 = createItem(); ItemBoxContainer *root4 = createRootWithSingleItem(); ItemBoxContainer *root5 = createRootWithSingleItem(); Item *item5 = root5->childItems().constFirst(); root->insertItem(root1, Location_OnTop); CHECK(root->checkSanity()); ItemBoxContainer::insertItemRelativeTo(root2, item1, Location_OnRight); CHECK(root->checkSanity()); root->insertItem(item3, Location_OnBottom); CHECK(root->checkSanity()); ItemBoxContainer::insertItemRelativeTo(root4, item3, Location_OnRight); CHECK(root->checkSanity()); root->insertItem(root5, Location_OnLeft); CHECK(root->checkSanity()); item5->parentBoxContainer()->removeItem(item5); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_misc3() { DeleteViews deleteViews; // Random test1 // |1|2|3| // | |3|4| auto root = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); Item *root2 = createRootWithSingleItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); root->insertItem(root2, Location_OnRight); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_containerGetsHidden() { DeleteViews deleteViews; auto root = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); Item *item3 = createItem(); root->insertItem(item1, Location_OnLeft); CHECK(root->checkSanity()); root->insertItem(item2, Location_OnRight); CHECK(root->checkSanity()); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnBottom); CHECK(root->checkSanity()); item2->turnIntoPlaceholder(); CHECK(root->checkSanity()); item3->turnIntoPlaceholder(); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_minSizeChanges() { DeleteViews deleteViews; auto root = createRoot(); Item *item1 = createItem(); root->insertItem(item1, Location_OnLeft); root->setSize_recursive(Size(200, 200)); CHECK(root->checkSanity()); auto w1 = item1; w1->setMinSize(Size(300, 300)); CHECK(root->checkSanity()); CHECK_EQ(root->size(), Size(300, 300)); Item *item2 = createItem(); root->insertItem(item2, Location_OnTop); CHECK(root->checkSanity()); root->setSize_recursive(Size(1000, 1000)); CHECK(root->checkSanity()); w1->setMinSize(Size(700, 700)); CHECK(root->checkSanity()); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_numSeparators() { DeleteViews deleteViews; auto root = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); Item *item3 = createItem(); Item *item4 = createItem(); CHECK_EQ(root->separators_recursive().size(), 0); root->insertItem(item1, Location_OnLeft); CHECK_EQ(root->separators_recursive().size(), 0); root->insertItem(item2, Location_OnLeft); CHECK_EQ(root->separators_recursive().size(), 1); root->insertItem(item3, Location_OnTop); CHECK_EQ(root->separators_recursive().size(), 2); ItemBoxContainer::insertItemRelativeTo(item4, item3, Location_OnRight); CHECK_EQ(root->separators_recursive().size(), 3); root->removeItem(item3); CHECK_EQ(root->separators_recursive().size(), 2); root->clear(); CHECK_EQ(root->separators_recursive().size(), 0); Item *item5 = createItem(); Item *item6 = createItem(); root->insertItem(item5, Location_OnLeft); CHECK_EQ(root->separators_recursive().size(), 0); root->insertItem(item6, Location_OnLeft, KDDockWidgets::InitialVisibilityOption::StartHidden); CHECK_EQ(root->separators_recursive().size(), 0); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_separatorMinMax() { DeleteViews deleteViews; auto root = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnLeft); item1->setMinSize(Size(200, 200)); item2->setMinSize(Size(200, 200)); auto separator = root->separators_recursive().at(0); CHECK_EQ(root->minPosForSeparator(separator), 200); CHECK_EQ(root->minPosForSeparator_global(separator), 200); // same, since there's no nesting CHECK_EQ(root->maxPosForSeparator(separator), root->width() - st - 200); CHECK_EQ(root->maxPosForSeparator(separator), root->width() - st - 200); CHECK(serializeDeserializeTest(root)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_separatorRecreatedOnParentChange() { DeleteViews deleteViews; auto root1 = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); root1->insertItem(item1, Location_OnLeft); root1->insertItem(item2, Location_OnLeft); auto root2 = createRoot(); Item *item21 = createItem(); Item *item22 = createItem(); root2->insertItem(item21, Location_OnLeft); root2->insertItem(item22, Location_OnLeft); root1->insertItem(root2.get(), Location_OnTop); CHECK(root1->checkSanity()); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_containerReducesSize() { DeleteViews deleteViews; // Tests that the container reduces size when its children get hidden auto root = createRoot(); Item *item1 = createItem(); Item *item2 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnLeft); Item *item21 = createItem(); Item *item22 = createItem(); ItemBoxContainer::insertItemRelativeTo(item21, item2, Location_OnTop); ItemBoxContainer::insertItemRelativeTo(item22, item2, Location_OnTop); CHECK(root->checkSanity()); item2->turnIntoPlaceholder(); CHECK(root->checkSanity()); item21->turnIntoPlaceholder(); CHECK(root->checkSanity()); item22->turnIntoPlaceholder(); CHECK(root->checkSanity()); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_insertHiddenContainer() { DeleteViews deleteViews; auto root1 = createRoot(); auto root2 = createRoot(); Item *item2 = createItem(); root2->insertItem(item2, Location_OnLeft, KDDockWidgets::InitialVisibilityOption::StartHidden); CHECK(root1->checkSanity()); CHECK(root2->checkSanity()); root1->insertItem(root2.release(), Location_OnTop); CHECK(root1->checkSanity()); auto anotherRoot = createRoot(); anotherRoot->insertItem(root1.release(), Location_OnTop); CHECK(anotherRoot->checkSanity()); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_availableOnSide() { DeleteViews deleteViews; // Tests that items are available to squeeze a certain amount (without violating their min-size) auto root = createRoot(); Item *item1 = createItem(/*min=*/Size(100, 100)); root->setSize(Size(1000, 1000)); root->insertItem(item1, Location_OnLeft); CHECK_EQ(root->availableToSqueezeOnSide(item1, Side1), 0); CHECK_EQ(root->availableToSqueezeOnSide(item1, Side2), 0); Item *item2 = createItem(/*min=*/Size(200, 200)); root->insertItem(item2, Location_OnRight); auto separator = root->separators_recursive()[0]; CHECK_EQ(root->minPosForSeparator_global(separator), item1->minSize().width()); CHECK_EQ(root->maxPosForSeparator_global(separator), root->width() - item2->minSize().width() - Item::layoutSpacing); CHECK_EQ(root->availableToSqueezeOnSide(item1, Side1), 0); CHECK_EQ(root->availableToSqueezeOnSide(item1, Side2), item2->width() - item2->minSize().width()); CHECK_EQ(root->availableToSqueezeOnSide(item2, Side1), item1->width() - item1->minSize().width()); CHECK_EQ(root->availableToSqueezeOnSide(item2, Side2), 0); Item *item3 = createItem(/*min=*/Size(200, 200)); root->insertItem(item3, Location_OnRight); CHECK(root->checkSanity()); CHECK_EQ(root->availableToSqueezeOnSide(item3, Side1), (item1->width() - item1->minSize().width()) + (item2->width() - item2->minSize().width())); CHECK_EQ(root->availableToSqueezeOnSide(item3, Side2), 0); auto separator2 = root->separators_recursive()[1]; CHECK_EQ(root->minPosForSeparator_global(separator2), item1->minSize().width() + item2->minSize().width() + Item::layoutSpacing); CHECK_EQ(root->maxPosForSeparator_global(separator2), root->width() - item3->minSize().width() - Item::layoutSpacing); Item *item4 = createItem(/*min=*/Size(200, 200)); ItemBoxContainer::insertItemRelativeTo(item4, item3, Location_OnBottom); auto c = item3->parentBoxContainer(); CHECK_EQ(c->availableToSqueezeOnSide_recursive(item3, Side1, Qt::Horizontal), (item1->width() - item1->minSize().width()) + (item2->width() - item2->minSize().width())); CHECK_EQ(c->availableToSqueezeOnSide_recursive(item3, Side2, Qt::Horizontal), 0); CHECK_EQ(c->availableToSqueezeOnSide_recursive(item4, Side1, Qt::Horizontal), (item1->width() - item1->minSize().width()) + (item2->width() - item2->minSize().width())); CHECK_EQ(c->availableToSqueezeOnSide_recursive(item4, Side2, Qt::Horizontal), 0); CHECK_EQ(c->availableToSqueezeOnSide_recursive(item4, Side1, Qt::Vertical), (item3->height() - item3->minSize().height())); CHECK_EQ(c->availableToSqueezeOnSide_recursive(item4, Side2, Qt::Vertical), 0); Item *item31 = createItem(/*min=*/Size(100, 100)); ItemBoxContainer::insertItemRelativeTo(item31, item3, Location_OnRight); auto container31 = item31->parentBoxContainer(); auto separator31 = container31->separators().at(0); // Since we don't have widgets with max-size, these two must be the same CHECK_EQ(container31->minPosForSeparator_global(separator31, false), container31->minPosForSeparator_global(separator31, true)); CHECK_EQ(container31->minPosForSeparator_global(separator31), item1->minSize().width() + item2->minSize().width() + item3->minSize().width() + 2 * Item::layoutSpacing); CHECK_EQ(container31->maxPosForSeparator_global(separator31), root->width() - item31->minSize().width() - Item::layoutSpacing); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_availableToGrowOnSide() { DeleteViews deleteViews; // Tests that items are available to grow a certain amount (without violating their max-size) auto root = createRoot(); Item *item1 = createItem(/*min=*/Size(100, 100), /*max=*/Size(230, 230)); root->setSize(Size(1000, 1000)); root->insertItem(item1, Location_OnLeft); CHECK_EQ(root->availableToGrowOnSide(item1, Side1), 0); CHECK_EQ(root->availableToGrowOnSide(item1, Side2), 0); Item *item2 = createItem(/*min=*/Size(200, 200)); root->insertItem(item2, Location_OnRight); // give a resize, so item1 gets smaller than its max-size. Will be unneeded soon root->setSize_recursive(Size(1001, 1001)); CHECK_EQ(root->availableToGrowOnSide(item1, Side1), 0); CHECK_EQ(root->availableToGrowOnSide(item2, Side2), 0); CHECK_EQ(root->availableToGrowOnSide(item1, Side2), root->length() - item2->width()); CHECK_EQ(root->availableToGrowOnSide(item2, Side1), item1->maxSizeHint().width() - item1->width()); auto separator = root->separators_recursive()[0]; CHECK_EQ(root->minPosForSeparator_global(separator, true), item1->minSize().width()); CHECK_EQ(root->maxPosForSeparator_global(separator, true), item1->maxSizeHint().width()); Item *item3 = createItem(/*min=*/Size(200, 200), /*max=*/Size(200, 200)); root->insertItem(item3, Location_OnRight); CHECK(root->checkSanity()); CHECK_EQ(root->availableToGrowOnSide(item3, Side2), 0); CHECK_EQ(root->availableToGrowOnSide(item3, Side1), root->length() - item2->width() - item1->width()); CHECK_EQ(root->availableToGrowOnSide(item2, Side1), item1->maxSizeHint().width() - item1->width()); CHECK_EQ(root->availableToGrowOnSide(item2, Side2), item3->maxSizeHint().width() - item3->width()); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_resizeViaSeparator() { DeleteViews deleteViews; auto root = createRoot(); Item *item1 = createItem(/*min=*/Size(100, 100)); Item *item2 = createItem(/*min=*/Size(100, 100)); root->setSize(Size(1000, 1000)); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); auto separator = root->separators_recursive().at(0); int oldPos = separator->position(); const int delta = -50; root->requestSeparatorMove(separator, delta); CHECK_EQ(separator->position(), oldPos + delta); // Now move right oldPos = separator->position(); root->requestSeparatorMove(separator, -delta); CHECK_EQ(separator->position(), oldPos - delta); Item *item3 = createItem(/*min=*/Size(100, 100)); Item *item4 = createItem(/*min=*/Size(100, 100)); root->insertItem(item4, Location_OnLeft); root->insertItem(item3, Location_OnRight); item2->turnIntoPlaceholder(); item1->turnIntoPlaceholder(); separator = root->separators_recursive().at(0); root->requestSeparatorMove(separator, delta); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_resizeViaSeparator2() { DeleteViews deleteViews; // Here we resize one of the separators and make sure only the items next to the separator move // propagation should only start when constraints have been met auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item4 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); root->insertItem(item3, Location_OnRight); root->insertItem(item4, Location_OnRight); auto resizeChildrenTo1000px = [&root] { /// Make sure each item has 1000 of width. Cheating here as we don't have API to resize /// all. const int numChildren = root->numChildren(); auto children = root->childItems(); for (auto item : std::as_const(children)) { item->m_sizingInfo.percentageWithinParent = 1.0 / numChildren; } root->setSize_recursive(Size(4000 + Item::layoutSpacing * (numChildren - 1), 1000)); }; const int delta = 100; const int originalChildWidth = 1000; resizeChildrenTo1000px(); const auto separators = root->separators_recursive(); CHECK(root->checkSanity()); CHECK_EQ(separators.size(), 3); root->requestSeparatorMove(separators[1], delta); CHECK_EQ(item1->width(), originalChildWidth); // item1 didn't change when we moved the second separator, only // item2 and 3 are supposed to move CHECK_EQ(item2->width(), originalChildWidth + delta); CHECK_EQ(item3->width(), originalChildWidth - delta); CHECK_EQ(item4->width(), originalChildWidth); // And back root->requestSeparatorMove(separators[1], -delta); CHECK_EQ(item1->width(), originalChildWidth); // item1 didn't change when we moved the second separator, only // item2 and 3 are supposed to move CHECK_EQ(item2->width(), originalChildWidth); CHECK_EQ(item3->width(), originalChildWidth); CHECK_EQ(item4->width(), originalChildWidth); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_resizeViaSeparator3() { DeleteViews deleteViews; // Like tst_resizeViaSeparator2 but we have nesting, when a container is shrunk, it too // should only shrink its children that are near the separator, instead of all of them equally // Layout: |1 | 3| // |4 | | // ------- // 2 auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item4 = createItem(); root->insertItem(item1, Location_OnTop); root->insertItem(item2, Location_OnBottom); ItemBoxContainer::insertItemRelativeTo(item3, item1, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item4, item1, Location_OnBottom); // Make some room, so each item has enough space to shrink without hitting constraints root->setSize_recursive(Size(1000, 4000)); // Our horizontal separator const auto separators = root->separators(); const auto horizontalSeparator = separators[0]; CHECK_EQ(separators.size(), 1); const int delta = 10; const int oldH1 = item1->height(); const int oldH2 = item2->height(); const int oldH3 = item3->height(); const int oldH4 = item4->height(); // If the following ever fails, then make sure item4 has space before we move the separator CHECK(item4->availableLength(Qt::Vertical) > delta); // Move separator up: root->requestSeparatorMove(horizontalSeparator, -delta); CHECK_EQ(item2->height(), oldH2 + delta); CHECK_EQ(item3->height(), oldH3 - delta); CHECK_EQ(item4->height(), oldH4 - delta); CHECK_EQ(item1->height(), oldH1); // Move down again root->requestSeparatorMove(horizontalSeparator, delta); CHECK_EQ(item2->height(), oldH2); CHECK_EQ(item3->height(), oldH3); CHECK_EQ(item4->height(), oldH4); CHECK_EQ(item1->height(), oldH1); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_mapToRoot() { DeleteViews deleteViews; // 1 // ----- // 21|22 auto root = createRoot(); Item *item1 = createItem(); root->insertItem(item1, Location_OnLeft); auto root2 = createRoot(); Item *item21 = createItem(); Item *item22 = createItem(); root2->insertItem(item21, Location_OnLeft); root2->insertItem(item22, Location_OnRight); root->insertItem(root2.release(), Location_OnBottom); CHECK(root->checkSanity()); auto c = item22->parentBoxContainer(); Point rootPt = c->mapToRoot(Point(0, 0)); CHECK_EQ(rootPt, Point(0, item1->height() + st)); CHECK_EQ(c->mapFromRoot(rootPt), Point(0, 0)); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_closeAndRestorePreservesPosition() { DeleteViews deleteViews; // Result is [1, 2, 3] auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item4 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); root->insertItem(item3, Location_OnRight); root->insertItem(item4, Location_OnRight); const int oldW1 = item1->width(); const int oldW2 = item2->width(); const int oldW3 = item3->width(); const int oldW4 = item4->width(); auto guest3 = item3->guest(); item3->turnIntoPlaceholder(); // Test that both sides reclaimed the space equally CHECK_EQ(item1->width(), oldW1); CHECK(std::abs(item2->width() - (oldW2 + (oldW2 / 2))) < Item::layoutSpacing); CHECK(std::abs(item4->width() - (oldW4 + (oldW4 / 2))) < Item::layoutSpacing); item3->restore(guest3); CHECK_EQ(item1->width(), oldW1); CHECK_EQ(item2->width(), oldW2); CHECK_EQ(item3->width(), oldW3); CHECK_EQ(item4->width(), oldW4); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_minSizeChangedBeforeRestore() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); root->insertItem(item1, Location_OnTop); root->insertItem(item2, Location_OnBottom); const Size originalSize2 = item2->size(); auto guest2 = item2->guest(); const Size newMinSize = originalSize2 + Size(10, 10); item2->turnIntoPlaceholder(); dynamic_cast(guest2)->m_view->setMinimumSize(newMinSize); item2->restore(guest2); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_separatorMoveCrash() { DeleteViews deleteViews; // Tests a crash I got when moving separator to the right auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item4 = createItem(); auto item5 = createItem(); auto item6 = createItem(); root->insertItem(item1, Location_OnTop); root->insertItem(item2, Location_OnBottom); ItemBoxContainer::insertItemRelativeTo(item3, item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item4, item3, Location_OnBottom); ItemBoxContainer::insertItemRelativeTo(item5, item4, Location_OnRight); root->insertItem(item6, Location_OnRight); ItemBoxContainer *c = item5->parentBoxContainer(); auto separator = c->separators().constFirst(); const int available5 = item5->availableLength(Qt::Horizontal); // Separator squeezes item5 and starts squeezing item6 by 10px c->requestSeparatorMove(separator, available5 + 10); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_separatorMoveHonoursMax() { DeleteViews deleteViews; const int maxWidth = 250; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem({}, Size(maxWidth, 250)); auto item3 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); root->insertItem(item3, Location_OnRight); CHECK(root->checkSanity()); auto separator1 = root->separators()[0]; auto separator2 = root->separators()[1]; const int min1 = root->minPosForSeparator_global(separator1); const int max1 = root->maxPosForSeparator_global(separator1); // const int min2 = root->minPosForSeparator_global(separator2); const int max2 = root->maxPosForSeparator_global(separator2); CHECK_EQ(min1, item1->minSize().width()); root->requestSeparatorMove(separator1, -(separator1->position() - min1)); CHECK(item2->width() <= maxWidth); CHECK(root->checkSanity()); root->requestSeparatorMove(separator2, max2 - separator2->position()); CHECK(root->checkSanity()); CHECK(item2->width() <= maxWidth); root->requestSeparatorMove(separator1, max1 - separator1->position()); CHECK(root->checkSanity()); CHECK(item2->width() <= maxWidth); root->requestSeparatorMove(separator1, -(separator1->position() - min1)); CHECK(root->checkSanity()); CHECK(item2->width() <= maxWidth); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_maxSizeHonoured1() { DeleteViews deleteViews; // Tests that the suggested rect honors max size when adding an item to a layout. auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); root->insertItem(item1, Location_OnTop); root->setSize_recursive(Size(3000, 3000)); auto guest2 = item2->guest(); const int maxHeight = 250; CHECK_EQ(dynamic_cast(guest2)->m_view->size(), item2->size()); dynamic_cast(guest2)->m_view->setMaximumSize(Size(250, maxHeight)); CHECK_EQ(dynamic_cast(guest2)->m_view->size(), item2->size()); CHECK_EQ(item2->maxSizeHint(), guest2->maxSizeHint()); root->insertItem(item2, Location_OnBottom); CHECK_EQ(item2->height(), maxHeight); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_maxSizeHonoured2() { DeleteViews deleteViews; // Tests that a container gets the max size of its children // 2 // ----- // 1|3 auto root1 = createRoot(); auto root2 = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); root1->insertItem(item1, Location_OnTop); root1->insertItem(item3, Location_OnRight); root2->insertItem(item2, Location_OnTop); root2->insertItem(item3, Location_OnLeft); root2->removeItem(item3, /*hardRemove=*/false); item2->setMaxSizeHint(Size(200, 200)); root1->insertItem(root2.release(), Location_OnBottom); CHECK_EQ(item2->parentBoxContainer()->maxSizeHint(), item2->maxSizeHint()); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_maxSizeHonoured3() { DeleteViews deleteViews; { // Tests that resizing a window will now make the item with max-size grow past its max auto root = createRoot(); const int minHeight = 100; const int maxHeight = 200; auto item1 = createItem(Size(100, minHeight), Size(200, maxHeight)); auto item2 = createItem(); root->setSize(Size(2000, 2000)); root->insertItem(item2, Location_OnBottom); root->insertItem(item1, Location_OnTop); // When adding, we respect max-size CHECK(item1->height() <= maxHeight); CHECK(item1->height() >= minHeight); // Now resize the window root->setSize_recursive(Size(200, 8000)); // and we respected max-size too CHECK(item1->height() <= maxHeight); CHECK(item1->height() >= minHeight); } { // Also do it with nested containers auto root1 = createRoot(); auto root2 = createRoot(); const int minHeight = 100; const int maxHeight = 200; auto item1 = createItem(Size(100, minHeight), Size(200, maxHeight)); auto item2 = createItem(); root1->setSize(Size(2000, 2000)); root2->setSize(Size(2000, 2000)); root2->insertItem(item2, Location_OnBottom); root1->insertItem(item1, Location_OnTop); root2->insertItem(root1.release(), Location_OnTop); // When adding, we respect max-size CHECK(item1->height() <= maxHeight); CHECK(item1->height() >= minHeight); // Now resize the window root2->setSize_recursive(Size(200, 8000)); // and we respected max-size too CHECK(item1->height() <= maxHeight); CHECK(item1->height() >= minHeight); } KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_requestEqualSize() { DeleteViews deleteViews; // Tests that double-clicking a separator will make both sides equal { auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); auto separator = root->separators().constFirst(); const int item1Squeeze = item1->m_sizingInfo.availableLength(Qt::Horizontal); root->requestSeparatorMove(separator, -item1Squeeze); CHECK_EQ(item1->width(), item1->minSize().width()); CHECK_EQ(item2->width(), root->length() - st - item1->width()); root->requestEqualSize(separator); CHECK(std::abs(item1->width() - item2->width()) < 5); } { // Similar, but now we have max-size to honour too auto root = createRoot(); const int minWidth1 = 100; const int maxWidth1 = 200; auto item1 = createItem(Size(minWidth1, 100), Size(maxWidth1, 200)); auto item2 = createItem(); root->insertItem(item2, Location_OnRight); root->insertItem(item1, Location_OnLeft); CHECK_EQ(item1->width(), item1->maxSizeHint().width()); auto separator = root->separators().constFirst(); root->requestEqualSize(separator); // Separator didn't move, doing so would make item1 bigger than its max size CHECK_EQ(item1->width(), item1->maxSizeHint().width()); { // Let's put the separator further right manually, then try again: // (Can't use ItemBoxContainer::requstSeparatorMove() as it respects max-size // constraints item1->m_sizingInfo.incrementLength(20, Qt::Horizontal); item2->m_sizingInfo.incrementLength(-20, Qt::Horizontal); root->positionItems(); } CHECK_EQ(item1->width(), maxWidth1 + 20); // Double clicking on the separator will put it back at a sane place const int minPos = root->minPosForSeparator_global(separator, true); const int maxPos = root->maxPosForSeparator_global(separator, true); CHECK_EQ(minPos, minWidth1); CHECK_EQ(maxPos, maxWidth1); root->requestEqualSize(separator); CHECK_EQ(item1->width(), maxWidth1); } KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_maxSizeHonouredWhenAnotherRemoved() { DeleteViews deleteViews; // Test that when removing item 3 that all the new available space goes to item1, so that // we don't violate the space of item 1 auto root = createRoot(); root->setSize(Size(300, 3000)); auto item1 = createItem(); const int minHeight = 100; const int maxHeight = 200; auto item2 = createItem(Size(100, minHeight), Size(200, maxHeight)); auto item3 = createItem(); root->insertItem(item1, Location_OnTop); root->insertItem(item2, Location_OnBottom); root->insertItem(item3, Location_OnBottom); CHECK(item2->height() <= maxHeight); root->removeItem(item3); CHECK(item2->height() <= maxHeight); root->dumpLayout(); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_simplify() { DeleteViews deleteViews; ScopedValueRollback inhibitSimplify(ItemBoxContainer::s_inhibitSimplify, true); auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); root->insertItem(item1, Location_OnTop); root->insertItem(item2, Location_OnBottom); auto root2 = createRoot(); auto root22 = createRoot(); auto item21 = createItem(); root22->insertItem(item21, Location_OnLeft); root2->insertItem(root22.release(), Location_OnLeft); root->insertItem(root2.release(), Location_OnBottom); CHECK(root->childItems().at(2)->isContainer()); root->simplify(); for (Item *item : root->childItems()) { CHECK(!item->isContainer()); } KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_adjacentLayoutBorders() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item4 = createItem(); auto item5 = createItem(); root->insertItem(item1, Location_OnTop); const int allBorders = int(LayoutBorderLocation_All); auto borders1 = item1->adjacentLayoutBorders(); CHECK_EQ(borders1, allBorders); root->insertItem(item2, Location_OnBottom); borders1 = item1->adjacentLayoutBorders(); CHECK_EQ(borders1, allBorders & ~LayoutBorderLocation_South); auto borders2 = item2->adjacentLayoutBorders(); CHECK_EQ(borders2, allBorders & ~LayoutBorderLocation_North); root->insertItem(item3, Location_OnRight); borders1 = item1->adjacentLayoutBorders(); CHECK_EQ(borders1, LayoutBorderLocation_North | LayoutBorderLocation_West); borders2 = item2->adjacentLayoutBorders(); CHECK_EQ(borders2, LayoutBorderLocation_South | LayoutBorderLocation_West); auto borders3 = item3->adjacentLayoutBorders(); CHECK_EQ(borders3, allBorders & ~(LayoutBorderLocation_West)); ItemBoxContainer::insertItemRelativeTo(item4, item3, Location_OnBottom); auto borders4 = item4->adjacentLayoutBorders(); CHECK_EQ(borders4, LayoutBorderLocation_East | LayoutBorderLocation_South); root->insertItem(item5, Location_OnRight); borders4 = item4->adjacentLayoutBorders(); CHECK_EQ(borders4, LayoutBorderLocation_South); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_numSideBySide_recursive() { DeleteViews deleteViews; auto root = createRoot(); CHECK(root->isVertical()); CHECK_EQ(root->numSideBySide_recursive(Qt::Vertical), 0); CHECK_EQ(root->numSideBySide_recursive(Qt::Horizontal), 0); auto item1 = createItem(); root->insertItem(item1, Location_OnRight); CHECK_EQ(root->numSideBySide_recursive(Qt::Vertical), 1); CHECK_EQ(root->numSideBySide_recursive(Qt::Horizontal), 1); auto item2 = createItem(); root->insertItem(item2, Location_OnTop); CHECK_EQ(root->numSideBySide_recursive(Qt::Vertical), 2); CHECK_EQ(root->numSideBySide_recursive(Qt::Horizontal), 1); auto item3 = createItem(); root->insertItem(item3, Location_OnTop); CHECK_EQ(root->numSideBySide_recursive(Qt::Vertical), 3); CHECK_EQ(root->numSideBySide_recursive(Qt::Horizontal), 1); auto item1Child = createItem(); ItemBoxContainer::insertItemRelativeTo(item1Child, item1, Location_OnLeft); CHECK_EQ(root->numSideBySide_recursive(Qt::Vertical), 3); CHECK_EQ(root->numSideBySide_recursive(Qt::Horizontal), 2); auto item1Child1Child = createItem(); ItemBoxContainer::insertItemRelativeTo(item1Child1Child, item1Child, Location_OnBottom); CHECK_EQ(root->numSideBySide_recursive(Qt::Vertical), 4); CHECK_EQ(root->numSideBySide_recursive(Qt::Horizontal), 2); item2->turnIntoPlaceholder(); CHECK_EQ(root->numSideBySide_recursive(Qt::Vertical), 3); CHECK_EQ(root->numSideBySide_recursive(Qt::Horizontal), 2); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_sizingInfoSerialization() { DeleteViews deleteViews; SizingInfo info; info.minSize = { 10, 10 }; info.geometry = { 10, 10, 100, 100 }; info.maxSizeHint = { 300, 300 }; info.percentageWithinParent = 5; info.isBeingInserted = false; nlohmann::json json = info; SizingInfo info2; json.get_to(info2); CHECK_EQ(info2.minSize, info.minSize); CHECK_EQ(info2.maxSizeHint, info.maxSizeHint); CHECK_EQ(info2.geometry, info.geometry); CHECK_EQ(info2.isBeingInserted, info.isBeingInserted); CHECK_EQ(info2.percentageWithinParent, info.percentageWithinParent); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_itemSerialization() { DeleteViews deleteViews; auto root = createRoot(); auto item1 = createItem(); root->insertItem(item1, Location_OnRight); auto item2 = createItem(); root->insertItem(item2, Location_OnRight); auto item3 = createItem(); ItemBoxContainer::insertItemRelativeTo(item3, item1, Location_OnBottom); Item *rootItem = root.get(); nlohmann::json json = rootItem; CHECK_EQ(json["children"].size(), 2); CHECK(json["isContainer"]); CHECK(json["children"][0]["isContainer"]); CHECK(!json["children"][1]["isContainer"]); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_outermostVisibleNeighbor() { DeleteViews deleteViews; auto root = createRoot(); root->setSize({ 1000, 1000 }); CHECK(!root->outermostNeighbor(KDDockWidgets::Location_OnRight)); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item0 = createItem(); auto itemTop = createItem(); root->insertItem(item1, Location_OnRight); CHECK(!item1->outermostNeighbor(KDDockWidgets::Location_OnLeft)); CHECK(!item1->outermostNeighbor(KDDockWidgets::Location_OnRight)); root->insertItem(item2, Location_OnRight); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight), item2); root->insertItem(item3, Location_OnRight); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight), item3); root->insertItem(item0, Location_OnLeft); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft), item0); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight), item3); root->insertItem(itemTop, Location_OnTop); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnBottom), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop), itemTop); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft), item0); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight), item3); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_outermostNeighbor() { DeleteViews deleteViews; auto root = createRoot(); root->setSize({ 1000, 1000 }); CHECK(!root->outermostNeighbor(KDDockWidgets::Location_OnRight, false)); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); auto item0 = createItem(); auto itemTop = createItem(); root->insertItem(item1, Location_OnRight, InitialVisibilityOption::StartHidden); CHECK(item1->parentBoxContainer() == root.get()); CHECK(!item1->outermostNeighbor(KDDockWidgets::Location_OnLeft, false)); CHECK(!item1->outermostNeighbor(KDDockWidgets::Location_OnRight, false)); root->insertItem(item2, Location_OnRight, InitialVisibilityOption::StartHidden); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop, false), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft, false), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight, false), item2); root->insertItem(item3, Location_OnRight, InitialVisibilityOption::StartHidden); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop, false), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft, false), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight, false), item3); root->insertItem(item0, Location_OnLeft, InitialVisibilityOption::StartHidden); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop, false), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft, false), item0); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight, false), item3); root->insertItem(itemTop, Location_OnTop, InitialVisibilityOption::StartHidden); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnBottom, false), nullptr); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnTop, false), itemTop); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnLeft, false), item0); CHECK_EQ(item1->outermostNeighbor(KDDockWidgets::Location_OnRight, false), item3); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_relativeToHidden() { // Tests that we can insert relative to an hidden item DeleteViews deleteViews; auto root = createRoot(); root->setSize({ 1000, 1000 }); auto item0 = createItem(); auto item1 = createItem(); auto item2 = createItem(); root->insertItem(item0, Location_OnRight); root->insertItem(item1, Location_OnRight, InitialVisibilityOption::StartHidden); CHECK(!item1->isVisible()); CHECK(root->checkSanity()); ItemBoxContainer::insertItemRelativeTo(item2, item1, Location_OnBottom); CHECK(root->checkSanity()); CHECK(!item1->isVisible()); CHECK(item2->isVisible()); CHECK_EQ(root->childItems().size(), 2); auto innerContainer = root->childItems().constLast()->asBoxContainer(); CHECK(innerContainer); CHECK_EQ(innerContainer->childItems().size(), 2); CHECK(innerContainer->isVertical()); CHECK_EQ(innerContainer->childItems()[0], item1); CHECK_EQ(innerContainer->childItems()[1], item2); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_spuriousResize() { DeleteViews deleteViews; auto root = createRoot(); root->setSize({ 1000, 1000 }); auto item1 = createItem(); auto item2 = createItem(); auto item3 = createItem(); root->insertItem(item1, Location_OnTop); root->insertItem(item2, Location_OnTop); // root was vertical, now it will be horizontal. // This used to generate a spurious resize auto guest1 = static_cast(item1->guest()); guest1->m_numSetGeometry = 0; root->insertItem(item3, Location_OnRight, Size(200, 200)); CHECK_EQ(guest1->m_numSetGeometry, 1); KDDW_TEST_RETURN(true); } KDDW_QCORO_TASK tst_relayoutIfNeeded() { DeleteViews deleteViews; auto root = createRoot(); root->setSize({ 1000, 1000 }); auto item1 = createItem({ 600, 0 }); item1->setSize({ 600, 100 }); auto item11 = createItem(); auto item12 = createItem(); auto item2 = createItem(); root->insertItem(item1, Location_OnLeft); root->insertItem(item2, Location_OnRight); ItemBoxContainer::insertItemRelativeTo(item11, item1, Location_OnBottom); ItemBoxContainer::insertItemRelativeTo(item12, item1, Location_OnBottom); CHECK(root->checkSanity()); CHECK(!root->isOverflowing()); // Make item1's min width bigger than it's actual width. Layout is now invalid! item1->m_sizingInfo.minSize = { item1->width() + 1, 0 }; { SetExpectedWarning w("Size constraints not honoured"); CHECK(!root->checkSanity()); } root->relayoutIfNeeded(); root->positionItems_recursive(); CHECK(!root->isOverflowing()); CHECK(root->checkSanity()); KDDW_TEST_RETURN(true); } static const std::vector s_tests = { TEST(tst_createRoot), TEST(tst_insertOne), TEST(tst_insertThreeSideBySide), TEST(tst_insertTwoHorizontal), TEST(tst_insertTwoVertical), TEST(tst_insertOnWidgetItem1), TEST(tst_insertOnWidgetItem2), TEST(tst_insertOnWidgetItem1DifferentOrientation), TEST(tst_insertOnWidgetItem2DifferentOrientation), TEST(tst_insertOnRootDifferentOrientation), TEST(tst_removeItem1), TEST(tst_removeItem2), TEST(tst_minSize), TEST(tst_resize), TEST(tst_resizeWithConstraints), TEST(tst_availableSize), TEST(tst_missingSize), TEST(tst_ensureEnoughSize), TEST(tst_turnIntoPlaceholder), TEST(tst_suggestedRect), TEST(tst_suggestedRect2), TEST(tst_suggestedRect3), TEST(tst_suggestedRect4), TEST(tst_insertAnotherRoot), TEST(tst_misc1), TEST(tst_misc2), TEST(tst_misc3), TEST(tst_containerGetsHidden), TEST(tst_minSizeChanges), TEST(tst_numSeparators), TEST(tst_separatorMinMax), TEST(tst_separatorRecreatedOnParentChange), TEST(tst_containerReducesSize), TEST(tst_insertHiddenContainer), TEST(tst_availableOnSide), TEST(tst_availableToGrowOnSide), TEST(tst_resizeViaSeparator), TEST(tst_resizeViaSeparator2), TEST(tst_resizeViaSeparator3), TEST(tst_mapToRoot), TEST(tst_closeAndRestorePreservesPosition), TEST(tst_minSizeChangedBeforeRestore), TEST(tst_separatorMoveCrash), TEST(tst_separatorMoveHonoursMax), TEST(tst_maxSizeHonoured1), TEST(tst_maxSizeHonoured2), TEST(tst_maxSizeHonoured3), TEST(tst_requestEqualSize), TEST(tst_maxSizeHonouredWhenAnotherRemoved), TEST(tst_simplify), TEST(tst_adjacentLayoutBorders), TEST(tst_numSideBySide_recursive), TEST(tst_sizingInfoSerialization), TEST(tst_itemSerialization), TEST(tst_relayoutIfNeeded), TEST(tst_outermostVisibleNeighbor), TEST(tst_outermostNeighbor), TEST(tst_relativeToHidden), TEST(tst_spuriousResize), }; #include "tests_main.h"