From 34df4cd06047fdad1f65e2ae7238c875576a73c5 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 31 Dec 2024 21:43:47 -0800 Subject: [PATCH] support multi-select in deck editor (#5397) * support multiselect in deck editor * fix crash * don't reset selection after each action * maintain old reselecting behavior when changing cards from left side * fix crash for real (probably) * maintain reselection behavior when deleting single selection --- .../src/client/tabs/tab_deck_editor.cpp | 83 +++++++++++++++---- cockatrice/src/client/tabs/tab_deck_editor.h | 2 + 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp index 7a0cfff14..a10dc91f3 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp @@ -64,6 +64,7 @@ void TabDeckEditor::createDeckDock() deckView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); deckView->installEventFilter(&deckViewKeySignals); deckView->setContextMenuPolicy(Qt::CustomContextMenu); + deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &))); connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, @@ -1241,6 +1242,23 @@ CardInfoPtr TabDeckEditor::currentCardInfo() const return CardDatabaseManager::getInstance()->getCard(cardName); } +/** + * Gets the index of all the currently selected card nodes in the decklist table. + * The list is in reverse order of the visual selection, so that rows can be deleted while iterating over them. + * + * @return A model index list containing all selected card nodes + */ +QModelIndexList TabDeckEditor::getSelectedCardNodes() const +{ + auto selectedRows = deckView->selectionModel()->selectedRows(); + + const auto notLeafNode = [this](const auto &index) { return deckModel->hasChildren(index); }; + selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end()); + + std::reverse(selectedRows.begin(), selectedRows.end()); + return selectedRows; +} + void TabDeckEditor::addCardHelper(QString zoneName) { const CardInfoPtr info = currentCardInfo(); @@ -1251,6 +1269,7 @@ void TabDeckEditor::addCardHelper(QString zoneName) QModelIndex newCardIndex = deckModel->addPreferredPrintingCard(info->getName(), zoneName, false); recursiveExpand(newCardIndex); + deckView->clearSelection(); deckView->setCurrentIndex(newCardIndex); setModified(true); searchEdit->setSelection(0, searchEdit->text().length()); @@ -1299,25 +1318,41 @@ void TabDeckEditor::actAddCardToSideboard() void TabDeckEditor::actRemoveCard() { - const QModelIndex ¤tIndex = deckView->selectionModel()->currentIndex(); - if (!currentIndex.isValid() || deckModel->hasChildren(currentIndex)) - return; - deckModel->removeRow(currentIndex.row(), currentIndex.parent()); + auto selectedRows = getSelectedCardNodes(); - DeckLoader *const deck = deckModel->getDeckList(); - setSaveStatus(!deck->isEmpty()); - setModified(true); + // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted + // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted + if (selectedRows.length() == 1) { + deckView->setSelectionMode(QAbstractItemView::SingleSelection); + } + + bool modified = false; + for (const auto &index : selectedRows) { + if (!index.isValid() || deckModel->hasChildren(index)) { + continue; + } + deckModel->removeRow(index.row(), index.parent()); + modified = true; + } + + deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); + + if (modified) { + DeckLoader *const deck = deckModel->getDeckList(); + setSaveStatus(!deck->isEmpty()); + setModified(true); + } } void TabDeckEditor::offsetCountAtIndex(const QModelIndex &idx, int offset) { - if (!idx.isValid() || offset == 0) + if (!idx.isValid() || deckModel->hasChildren(idx)) { return; + } const QModelIndex numberIndex = idx.sibling(idx.row(), 0); const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); const int new_count = count + offset; - deckView->setCurrentIndex(numberIndex); if (new_count <= 0) deckModel->removeRow(idx.row(), idx.parent()); else @@ -1328,14 +1363,18 @@ void TabDeckEditor::offsetCountAtIndex(const QModelIndex &idx, int offset) void TabDeckEditor::decrementCardHelper(QString zoneName) { const CardInfoPtr info = currentCardInfo(); - QModelIndex idx; if (!info) return; if (info->getIsToken()) zoneName = DECK_ZONE_TOKENS; - idx = deckModel->findCard(info->getName(), zoneName); + QModelIndex idx = deckModel->findCard(info->getName(), zoneName); + if (!idx.isValid()) { + return; + } + deckView->clearSelection(); + deckView->setCurrentIndex(idx); offsetCountAtIndex(idx, -1); } @@ -1357,14 +1396,28 @@ void TabDeckEditor::copyDatabaseCellContents() void TabDeckEditor::actIncrement() { - const QModelIndex ¤tIndex = deckView->selectionModel()->currentIndex(); - offsetCountAtIndex(currentIndex, 1); + auto selectedRows = getSelectedCardNodes(); + + for (const auto &index : selectedRows) { + offsetCountAtIndex(index, 1); + } } void TabDeckEditor::actDecrement() { - const QModelIndex ¤tIndex = deckView->selectionModel()->currentIndex(); - offsetCountAtIndex(currentIndex, -1); + auto selectedRows = getSelectedCardNodes(); + + // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted + // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted + if (selectedRows.length() == 1) { + deckView->setSelectionMode(QAbstractItemView::SingleSelection); + } + + for (const auto &index : selectedRows) { + offsetCountAtIndex(index, -1); + } + + deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); } void TabDeckEditor::setDeck(DeckLoader *_deck) diff --git a/cockatrice/src/client/tabs/tab_deck_editor.h b/cockatrice/src/client/tabs/tab_deck_editor.h index 1501e5c4b..14f96a73c 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.h +++ b/cockatrice/src/client/tabs/tab_deck_editor.h @@ -111,6 +111,8 @@ private: void recursiveExpand(const QModelIndex &index); void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation); + QModelIndexList getSelectedCardNodes() const; + CardDatabaseModel *databaseModel; CardDatabaseDisplayModel *databaseDisplayModel; DeckListModel *deckModel;