#include "Document/EditorSession.h" #include "UI/NativeLayerList.h" #include #include // Swift 1.4.8: a layer chosen on the canvas shows. namespace { std::unique_ptr twentyLayers() { auto session = std::make_unique(); session->createDocument(10, 10); for (int each = 0; each < 20; ++each) session->addBlankLayer(); return session; } QUuid rowID(const NativeLayerList &list, int row) { return list.cells().at(size_t(row))->layerID(); } // Row `row` lies wholly inside the list's view. bool shown(const NativeLayerList &list, int row) { const QWidget &cell = *list.cells().at(size_t(row)); return list.viewport()->rect().contains(QRect(cell.mapTo(list.viewport(), QPoint(0, 0)), cell.size())); } } class LayerRevealTests : public QObject { Q_OBJECT private slots: void selectingAnOffscreenLayerRevealsItsRow(); void refreshingTheSameSelectionDoesNotScrollAgain(); void changingPrimaryLayerWithinTheSameSelectionRevealsIt(); void selectingAChildExpandsOnlyItsAncestorFolders(); void multipleSelectionRevealsThePrimaryChild(); void aChildInAShutFolderScrollsIntoView(); }; void LayerRevealTests::selectingAnOffscreenLayerRevealsItsRow() { const auto session = twentyLayers(); NativeLayerList list(*session); list.resize(260, 160); list.show(); QVERIFY(QTest::qWaitForWindowExposed(&list)); list.verticalScrollBar()->setValue(0); QVERIFY(!shown(list, 19)); session->selectLayer(rowID(list, 19)); QCOMPARE(session->selectedLayerIDs(), QSet{rowID(list, 19)}); QVERIFY(shown(list, 19) && list.verticalScrollBar()->value() > 0); } void LayerRevealTests::refreshingTheSameSelectionDoesNotScrollAgain() { const auto session = twentyLayers(); NativeLayerList list(*session); list.resize(260, 160); list.show(); QVERIFY(QTest::qWaitForWindowExposed(&list)); session->selectLayer(rowID(list, 19)); QVERIFY(shown(list, 19)); // Scrolled away by hand, a rename leaves it there. list.verticalScrollBar()->setValue(0); session->renameLayer(rowID(list, 0), QStringLiteral("Renamed")); QCOMPARE(list.verticalScrollBar()->value(), 0); session->selectLayer(std::nullopt); QCOMPARE(list.verticalScrollBar()->value(), 0); session->selectLayer(rowID(list, 19)); QVERIFY(shown(list, 19)); } void LayerRevealTests::changingPrimaryLayerWithinTheSameSelectionRevealsIt() { const auto session = twentyLayers(); NativeLayerList list(*session); list.resize(260, 160); list.show(); QVERIFY(QTest::qWaitForWindowExposed(&list)); const QSet both{rowID(list, 0), rowID(list, 19)}; session->selectLayers(both, rowID(list, 19)); QVERIFY(shown(list, 19)); session->selectLayers(both, rowID(list, 0)); QCOMPARE(session->selectedLayerIDs(), both); QVERIFY(shown(list, 0) && list.verticalScrollBar()->value() == 0); } void LayerRevealTests::selectingAChildExpandsOnlyItsAncestorFolders() { EditorSession session; session.createDocument(10, 10); session.addGroup(); const QUuid outer = session.activeLayerID().value(); session.addGroup(); const QUuid inner = session.activeLayerID().value(); session.addBlankLayer(); const QUuid child = session.activeLayerID().value(); session.selectLayer(std::nullopt); session.addGroup(); const QUuid unrelated = session.activeLayerID().value(); for (const QUuid folder : {outer, inner, unrelated}) session.toggleGroupExpansion(folder); QCOMPARE(session.collapsedGroupIDs(), (QSet{outer, inner, unrelated})); // The last signal sees the folders open. std::vector> heard; connect(&session, &EditorSession::changed, &session, [&] { heard.push_back(session.collapsedGroupIDs()); }); session.selectLayer(child); QCOMPARE(session.collapsedGroupIDs(), QSet{unrelated}); QCOMPARE(heard.back(), QSet{unrelated}); QVERIFY(std::ranges::any_of(session.layerRows(), [&](const LayerHierarchy::Entry &entry) { return entry.layer.id == child; })); session.selectLayer(unrelated); QCOMPARE(session.collapsedGroupIDs(), QSet{unrelated}); // Only the inner folder shut: the signal sees it open. session.toggleGroupExpansion(inner); heard.clear(); session.selectLayer(child); QCOMPARE(session.collapsedGroupIDs(), QSet{unrelated}); QCOMPARE(heard.back(), QSet{unrelated}); } void LayerRevealTests::multipleSelectionRevealsThePrimaryChild() { EditorSession session; session.createDocument(10, 10); session.addGroup(); const QUuid folder = session.activeLayerID().value(); session.addBlankLayer(); const QUuid child = session.activeLayerID().value(); session.selectLayer(std::nullopt); session.addBlankLayer(); const QUuid other = session.activeLayerID().value(); session.toggleGroupExpansion(folder); QCOMPARE(session.collapsedGroupIDs(), QSet{folder}); session.selectLayers({child, other}, child); QVERIFY(session.collapsedGroupIDs().isEmpty()); QCOMPARE(session.selectedLayerIDs(), (QSet{child, other})); // The other as primary opens nothing: it is no folder's. session.toggleGroupExpansion(folder); session.selectLayers({child, other}, other); QCOMPARE(session.collapsedGroupIDs(), QSet{folder}); } void LayerRevealTests::aChildInAShutFolderScrollsIntoView() { // A folder at the bottom, shut, under twenty layers. EditorSession session; session.createDocument(10, 10); session.addGroup(); const QUuid folder = session.activeLayerID().value(); session.addBlankLayer(); const QUuid child = session.activeLayerID().value(); session.selectLayer(std::nullopt); for (int each = 0; each < 20; ++each) session.addBlankLayer(); session.toggleGroupExpansion(folder); NativeLayerList list(session); list.resize(260, 160); list.show(); QVERIFY(QTest::qWaitForWindowExposed(&list)); for (const bool several : {false, true}) { list.verticalScrollBar()->setValue(0); if (several) session.selectLayers({child, session.document().value().layers.back().id}, child); else session.selectLayer(child); const std::vector cells = list.cells(); const int row = int(std::ranges::find_if(cells, [&](const LayerCell *cell) { return cell->layerID() == child; }) - cells.begin()); QVERIFY(row < int(cells.size())); QVERIFY2(shown(list, row), several ? "several" : "one"); session.selectLayer(std::nullopt); session.toggleGroupExpansion(folder); } } QTEST_MAIN(LayerRevealTests) #include "LayerRevealTests.moc"