More granular decklist signals (#5981)

* Performance stuffs.

* Actually make widgets track their indices.

* Functional stuff.

* More display stuff.

* Determine where we will insert the card before actually inserting it in the model.

* Allow overlap layouts to insert widgets at specific positions.

* Modified signals.

* Raise trailing widgets on overlap layout widget insertion.

* Nix the logging config changes.

* Lint.

* Address comments.

* Address comments.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2025-06-13 19:21:34 +02:00 committed by GitHub
parent 18d9c1d609
commit d5dc70ccee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 546 additions and 322 deletions

View file

@ -81,7 +81,7 @@ void TabDeckEditorVisual::createCentralFrame()
void TabDeckEditorVisual::onDeckChanged()
{
AbstractTabDeckEditor::onDeckModified();
tabContainer->visualDeckView->decklistDataChanged(QModelIndex(), QModelIndex());
tabContainer->visualDeckView->constructZoneWidgetsFromDeckListModel();
tabContainer->deckAnalytics->refreshDisplays(deckDockWidget->deckModel);
tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel);
}

View file

@ -101,9 +101,9 @@ void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &ma
DecklistCardNode *addedCard;
if (node->getName() == DECK_ZONE_SIDE)
addedCard = sideboard.addCard(card->getName(), node->getName());
addedCard = sideboard.addCard(card->getName(), node->getName(), -1);
else
addedCard = mainboard.addCard(card->getName(), node->getName());
addedCard = mainboard.addCard(card->getName(), node->getName(), -1);
addedCard->setNumber(card->getNumber());
};

View file

@ -548,6 +548,17 @@ void FlowLayout::addItem(QLayoutItem *item)
}
}
void FlowLayout::insertWidgetAtIndex(QWidget *toInsert, int index)
{
addChildWidget(toInsert);
// We don't want to fail on an index that violates the bounds, so we just clamp it.
int boundedIndex = qBound(0, index, qMax(0, static_cast<int>(items.size())));
items.insert(boundedIndex, new QWidgetItem(toInsert));
invalidate();
}
/**
* @brief Retrieves the count of items in the layout.
* @return The number of layout items.

View file

@ -15,6 +15,7 @@ public:
explicit FlowLayout(QWidget *parent = nullptr);
FlowLayout(QWidget *parent, Qt::Orientation _flowDirection, int margin = 0, int hSpacing = 0, int vSpacing = 0);
~FlowLayout() override;
void insertWidgetAtIndex(QWidget *toInsert, int index);
QSize calculateMinimumSizeHorizontal() const;
QSize calculateSizeHintVertical() const;

View file

@ -54,6 +54,19 @@ OverlapLayout::~OverlapLayout()
}
}
void OverlapLayout::insertWidgetAtIndex(QWidget *toInsert, int index)
{
addChildWidget(toInsert);
int clampedIndex = qBound(0, index, qMax(0, static_cast<int>(itemList.size())));
itemList.insert(clampedIndex, new QWidgetItem(toInsert));
for (int i = clampedIndex; i < itemList.size(); ++i) {
dynamic_cast<QWidgetItem *>(itemList.at(i))->widget()->raise();
}
invalidate();
}
/**
* @brief Adds a new item to the layout.
*

View file

@ -18,6 +18,7 @@ public:
Qt::Orientation overlapDirection = Qt::Vertical,
Qt::Orientation flowDirection = Qt::Horizontal);
~OverlapLayout();
void insertWidgetAtIndex(QWidget *toInsert, int index);
void addItem(QLayoutItem *item) override;
int count() const override;

View file

@ -9,15 +9,16 @@
CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QPersistentModelIndex _trackedIndex,
QString _zoneName,
QString _cardGroupCategory,
QString _activeGroupCriteria,
QStringList _activeSortCriteria,
int bannerOpacity,
CardSizeWidget *_cardSizeWidget)
: QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), cardGroupCategory(_cardGroupCategory),
activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria),
cardSizeWidget(_cardSizeWidget)
: QWidget(parent), deckListModel(_deckListModel), trackedIndex(_trackedIndex), zoneName(_zoneName),
cardGroupCategory(_cardGroupCategory), activeGroupCriteria(_activeGroupCriteria),
activeSortCriteria(_activeSortCriteria), cardSizeWidget(_cardSizeWidget)
{
layout = new QVBoxLayout(this);
setLayout(layout);
@ -26,42 +27,70 @@ CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
banner = new BannerWidget(this, cardGroupCategory, Qt::Orientation::Vertical, bannerOpacity);
layout->addWidget(banner);
CardGroupDisplayWidget::updateCardDisplays();
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::onCardAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::onCardRemoval);
}
QWidget *CardGroupDisplayWidget::constructWidgetForIndex(int rowIndex)
{
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(rowIndex, 0, trackedIndex));
if (indexToWidgetMap.contains(index)) {
return indexToWidgetMap[index];
}
auto cardName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString();
auto cardProviderId = deckListModel->data(index.sibling(index.row(), 4), Qt::EditRole).toString();
auto widget = new CardInfoPictureWithTextOverlayWidget(getLayoutParent(), true);
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
widget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderId));
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this, &CardGroupDisplayWidget::onClick);
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this, &CardGroupDisplayWidget::onHover);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget, &CardInfoPictureWidget::setScaleFactor);
indexToWidgetMap.insert(index, widget);
return widget;
}
void CardGroupDisplayWidget::updateCardDisplays()
{
for (int i = 0; i < deckListModel->rowCount(trackedIndex); ++i) {
addToLayout(constructWidgetForIndex(i));
}
}
QList<CardInfoPtr> CardGroupDisplayWidget::getCardsMatchingGroup(QList<CardInfoPtr> cardsToSort)
void CardGroupDisplayWidget::onCardAddition(const QModelIndex &parent, int first, int last)
{
cardsToSort = sortCardList(cardsToSort, activeSortCriteria, Qt::SortOrder::AscendingOrder);
QList<CardInfoPtr> activeList;
for (const CardInfoPtr &info : cardsToSort) {
if (info && info->getProperty(activeGroupCriteria) == cardGroupCategory) {
activeList.append(info);
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
return;
}
if (parent == trackedIndex) {
for (int i = first; i <= last; i++) {
insertIntoLayout(constructWidgetForIndex(i), i);
}
}
return activeList;
}
QList<CardInfoPtr> CardGroupDisplayWidget::sortCardList(QList<CardInfoPtr> cardsToSort,
const QStringList properties,
Qt::SortOrder order = Qt::AscendingOrder)
void CardGroupDisplayWidget::onCardRemoval(const QModelIndex &parent, int first, int last)
{
CardInfoComparator comparator(properties, order);
std::sort(cardsToSort.begin(), cardsToSort.end(), comparator);
return cardsToSort;
}
void CardGroupDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSortCriteria)
{
if (activeSortCriteria != _activeSortCriteria) {
activeSortCriteria = _activeSortCriteria;
updateCardDisplays(); // Refresh display with new sorting
Q_UNUSED(first);
Q_UNUSED(last);
if (parent == trackedIndex) {
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
}
}
}

View file

@ -18,6 +18,7 @@ class CardGroupDisplayWidget : public QWidget
public:
CardGroupDisplayWidget(QWidget *parent,
DeckListModel *deckListModel,
QPersistentModelIndex trackedIndex,
QString zoneName,
QString cardGroupCategory,
QString activeGroupCriteria,
@ -25,10 +26,9 @@ public:
int bannerOpacity,
CardSizeWidget *cardSizeWidget);
QList<CardInfoPtr> getCardsMatchingGroup(QList<CardInfoPtr> cardsToSort);
void resizeEvent(QResizeEvent *event) override;
DeckListModel *deckListModel;
QPersistentModelIndex trackedIndex;
QHash<QPersistentModelIndex, QWidget *> indexToWidgetMap;
QString zoneName;
QString cardGroupCategory;
QString activeGroupCriteria;
@ -36,18 +36,41 @@ public:
CardSizeWidget *cardSizeWidget;
public slots:
QList<CardInfoPtr> sortCardList(QList<CardInfoPtr> cardsToSort, QStringList properties, Qt::SortOrder order);
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void onHover(CardInfoPtr card);
virtual QWidget *constructWidgetForIndex(int rowIndex);
virtual void updateCardDisplays();
void onActiveSortCriteriaChanged(QStringList activeSortCriteria);
virtual void onCardAddition(const QModelIndex &parent, int first, int last);
virtual void onCardRemoval(const QModelIndex &parent, int first, int last);
void resizeEvent(QResizeEvent *event) override;
signals:
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void cardHovered(CardInfoPtr card);
void cleanupRequested(CardGroupDisplayWidget *cardGroupDisplayWidget);
protected:
QVBoxLayout *layout;
BannerWidget *banner;
virtual QWidget *getLayoutParent()
{
return this;
}
virtual void addToLayout(QWidget *toAdd)
{
layout->addWidget(toAdd);
}
virtual void insertIntoLayout(QWidget *toInsert, int insertAt)
{
layout->insertWidget(insertAt, toInsert);
}
virtual void removeFromLayout(QWidget *toRemove)
{
layout->removeWidget(toRemove);
}
};
#endif // CARD_GROUP_DISPLAY_WIDGET_H

View file

@ -6,9 +6,11 @@
#include "../card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent>
#include <utility>
FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QPersistentModelIndex _trackedIndex,
QString _zoneName,
QString _cardGroupCategory,
QString _activeGroupCriteria,
@ -17,6 +19,7 @@ FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,
CardSizeWidget *_cardSizeWidget)
: CardGroupDisplayWidget(parent,
_deckListModel,
std::move(_trackedIndex),
_zoneName,
_cardGroupCategory,
_activeGroupCriteria,
@ -28,77 +31,72 @@ FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,
banner->setBuddy(flowWidget);
layout->addWidget(flowWidget);
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
FlatCardGroupDisplayWidget::removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
FlatCardGroupDisplayWidget::updateCardDisplays();
connect(deckListModel, &DeckListModel::dataChanged, this, &FlatCardGroupDisplayWidget::updateCardDisplays);
disconnect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::onCardAddition);
disconnect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::onCardRemoval);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &FlatCardGroupDisplayWidget::onCardAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &FlatCardGroupDisplayWidget::onCardRemoval);
}
void FlatCardGroupDisplayWidget::updateCardDisplays()
void FlatCardGroupDisplayWidget::onCardAddition(const QModelIndex &parent, int first, int last)
{
// Retrieve and sort cards
QList<CardInfoPtr> cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName));
// Show or hide widget
bool shouldBeVisible = !cardsInZone.isEmpty();
if (shouldBeVisible != isVisible()) {
setVisible(shouldBeVisible);
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
return;
}
// Retrieve existing widgets
QList<CardInfoPictureWithTextOverlayWidget *> existingWidgets =
flowWidget->findChildren<CardInfoPictureWithTextOverlayWidget *>();
QHash<QString, QList<CardInfoPictureWithTextOverlayWidget *>> widgetMap;
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
widgetMap[widget->getInfo()->getName()].append(widget);
}
QList<CardInfoPictureWithTextOverlayWidget *> sortedWidgets;
QSet<CardInfoPictureWithTextOverlayWidget *> usedWidgets;
// Ensure widgets are ordered to match the sorted cards
for (const CardInfoPtr &card : cardsInZone) {
QString name = card->getName();
CardInfoPictureWithTextOverlayWidget *widget = nullptr;
if (!widgetMap[name].isEmpty()) {
// Reuse an existing widget
widget = widgetMap[name].takeFirst();
} else {
// Create a new widget if needed
widget = new CardInfoPictureWithTextOverlayWidget(flowWidget, true);
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
widget->setCard(card);
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
&FlatCardGroupDisplayWidget::onClick);
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this,
&FlatCardGroupDisplayWidget::onHover);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget,
&CardInfoPictureWidget::setScaleFactor);
flowWidget->addWidget(widget);
}
// Store in sorted order
sortedWidgets.append(widget);
usedWidgets.insert(widget);
}
// Remove extra widgets
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
if (!usedWidgets.contains(widget)) {
flowWidget->layout()->removeWidget(widget);
widget->deleteLater();
if (parent == trackedIndex) {
for (int i = first; i <= last; i++) {
insertIntoLayout(constructWidgetForIndex(i), i);
}
}
}
// **Reorder widgets in place**
for (int i = 0; i < sortedWidgets.size(); ++i) {
sortedWidgets[i]->setParent(nullptr); // Temporarily detach
void FlatCardGroupDisplayWidget::onCardRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(first);
Q_UNUSED(last);
if (parent == trackedIndex) {
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
}
}
for (int i = 0; i < sortedWidgets.size(); ++i) {
flowWidget->addWidget(sortedWidgets[i]); // Reattach in correct order
}
QWidget *FlatCardGroupDisplayWidget::constructWidgetForIndex(int row)
{
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(row, 0, trackedIndex));
if (indexToWidgetMap.contains(index)) {
return indexToWidgetMap[index];
}
auto cardName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString();
auto cardProviderId = deckListModel->data(index.sibling(index.row(), 4), Qt::EditRole).toString();
auto widget = new CardInfoPictureWithTextOverlayWidget(flowWidget, true);
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
widget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderId));
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this, &FlatCardGroupDisplayWidget::onClick);
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this, &FlatCardGroupDisplayWidget::onHover);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget, &CardInfoPictureWidget::setScaleFactor);
indexToWidgetMap.insert(index, widget);
return widget;
}
void FlatCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)

View file

@ -11,6 +11,7 @@ class FlatCardGroupDisplayWidget : public CardGroupDisplayWidget
public:
FlatCardGroupDisplayWidget(QWidget *parent,
DeckListModel *deckListModel,
QPersistentModelIndex trackedIndex,
QString zoneName,
QString cardGroupCategory,
QString activeGroupCriteria,
@ -18,13 +19,34 @@ public:
int bannerOpacity,
CardSizeWidget *cardSizeWidget);
void resizeEvent(QResizeEvent *event) override;
public slots:
void updateCardDisplays() override;
QWidget *constructWidgetForIndex(int row) override;
void resizeEvent(QResizeEvent *event) override;
void onCardAddition(const QModelIndex &parent, int first, int last) override;
void onCardRemoval(const QModelIndex &parent, int first, int last) override;
private:
FlowWidget *flowWidget;
QWidget *getLayoutParent() override
{
return flowWidget;
}
void addToLayout(QWidget *toAdd) override
{
flowWidget->addWidget(toAdd);
}
void insertIntoLayout(QWidget *toInsert, int insertAt) override
{
flowWidget->insertWidgetAtIndex(toInsert, insertAt);
}
void removeFromLayout(QWidget *toRemove) override
{
flowWidget->removeWidget(toRemove);
}
};
#endif // FLAT_CARD_GROUP_DISPLAY_WIDGET_H

View file

@ -9,6 +9,7 @@
OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QPersistentModelIndex _trackedIndex,
QString _zoneName,
QString _cardGroupCategory,
QString _activeGroupCriteria,
@ -17,6 +18,7 @@ OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *pare
CardSizeWidget *_cardSizeWidget)
: CardGroupDisplayWidget(parent,
_deckListModel,
_trackedIndex,
_zoneName,
_cardGroupCategory,
_activeGroupCriteria,
@ -28,89 +30,54 @@ OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *pare
banner->setBuddy(overlapWidget);
layout->addWidget(overlapWidget);
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
OverlappedCardGroupDisplayWidget::removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
OverlappedCardGroupDisplayWidget::updateCardDisplays();
connect(deckListModel, &DeckListModel::dataChanged, this, &OverlappedCardGroupDisplayWidget::updateCardDisplays);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, this,
[this]() { overlapWidget->adjustMaxColumnsAndRows(); });
disconnect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::onCardAddition);
disconnect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::onCardRemoval);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &OverlappedCardGroupDisplayWidget::onCardAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &OverlappedCardGroupDisplayWidget::onCardRemoval);
}
void OverlappedCardGroupDisplayWidget::updateCardDisplays()
void OverlappedCardGroupDisplayWidget::onCardAddition(const QModelIndex &parent, int first, int last)
{
overlapWidget->setUpdatesEnabled(false);
// Retrieve and sort cards
QList<CardInfoPtr> cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName));
// Show or hide widget
bool shouldBeVisible = !cardsInZone.isEmpty();
if (shouldBeVisible != isVisible()) {
setVisible(shouldBeVisible);
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
return;
}
// Retrieve existing widgets
QList<CardInfoPictureWithTextOverlayWidget *> existingWidgets =
overlapWidget->findChildren<CardInfoPictureWithTextOverlayWidget *>();
QHash<QString, QList<CardInfoPictureWithTextOverlayWidget *>> widgetMap;
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
widgetMap[widget->getInfo()->getName()].append(widget);
}
QList<CardInfoPictureWithTextOverlayWidget *> sortedWidgets;
QSet<CardInfoPictureWithTextOverlayWidget *> usedWidgets;
// Ensure widgets are ordered to match the sorted cards
for (const CardInfoPtr &card : cardsInZone) {
QString name = card->getName();
CardInfoPictureWithTextOverlayWidget *widget = nullptr;
if (!widgetMap[name].isEmpty()) {
// Reuse an existing widget
widget = widgetMap[name].takeFirst();
} else {
// Create a new widget if needed
widget = new CardInfoPictureWithTextOverlayWidget(overlapWidget, true);
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
widget->setCard(card);
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
&OverlappedCardGroupDisplayWidget::onClick);
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this,
&OverlappedCardGroupDisplayWidget::onHover);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget,
&CardInfoPictureWidget::setScaleFactor);
overlapWidget->addWidget(widget);
}
// Store in sorted order
sortedWidgets.append(widget);
usedWidgets.insert(widget);
}
// Remove extra widgets
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
if (!usedWidgets.contains(widget)) {
overlapWidget->layout()->removeWidget(widget);
widget->deleteLater();
if (parent == trackedIndex) {
for (int i = first; i <= last; i++) {
insertIntoLayout(constructWidgetForIndex(i), i);
}
}
}
// **Reorder widgets in place**
for (int i = 0; i < sortedWidgets.size(); ++i) {
sortedWidgets[i]->setParent(nullptr); // Temporarily detach
void OverlappedCardGroupDisplayWidget::onCardRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(first);
Q_UNUSED(last);
if (parent == trackedIndex) {
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
}
}
for (int i = 0; i < sortedWidgets.size(); ++i) {
overlapWidget->addWidget(sortedWidgets[i]); // Reattach in correct order
}
// Ensure proper layering
for (CardInfoPictureWithTextOverlayWidget *widget : sortedWidgets) {
widget->raise();
}
overlapWidget->adjustMaxColumnsAndRows();
overlapWidget->setUpdatesEnabled(true);
overlapWidget->update();
}
void OverlappedCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)

View file

@ -11,6 +11,7 @@ class OverlappedCardGroupDisplayWidget : public CardGroupDisplayWidget
public:
OverlappedCardGroupDisplayWidget(QWidget *parent,
DeckListModel *deckListModel,
QPersistentModelIndex trackedIndex,
QString zoneName,
QString cardGroupCategory,
QString activeGroupCriteria,
@ -18,13 +19,33 @@ public:
int bannerOpacity,
CardSizeWidget *cardSizeWidget);
void resizeEvent(QResizeEvent *event) override;
public slots:
void updateCardDisplays() override;
void onCardAddition(const QModelIndex &parent, int first, int last) override;
void onCardRemoval(const QModelIndex &parent, int first, int last) override;
void resizeEvent(QResizeEvent *event) override;
private:
OverlapWidget *overlapWidget;
QWidget *getLayoutParent() override
{
return overlapWidget;
}
void addToLayout(QWidget *toAdd) override
{
overlapWidget->addWidget(toAdd);
}
void insertIntoLayout(QWidget *toInsert, int insertAt) override
{
overlapWidget->insertWidgetAtIndex(toInsert, insertAt);
}
void removeFromLayout(QWidget *toRemove) override
{
overlapWidget->removeWidget(toRemove);
}
};
#endif // OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H

View file

@ -9,6 +9,7 @@
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QPersistentModelIndex _trackedIndex,
QString _zoneName,
QString _activeGroupCriteria,
QStringList _activeSortCriteria,
@ -16,9 +17,9 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
int bannerOpacity,
int subBannerOpacity,
CardSizeWidget *_cardSizeWidget)
: QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), activeGroupCriteria(_activeGroupCriteria),
activeSortCriteria(_activeSortCriteria), displayType(_displayType), bannerOpacity(bannerOpacity),
subBannerOpacity(subBannerOpacity), cardSizeWidget(_cardSizeWidget)
: QWidget(parent), deckListModel(_deckListModel), trackedIndex(_trackedIndex), zoneName(_zoneName),
activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria), displayType(_displayType),
bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity), cardSizeWidget(_cardSizeWidget)
{
layout = new QVBoxLayout(this);
setLayout(layout);
@ -34,8 +35,94 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
banner->setBuddy(cardGroupContainer);
displayCards();
connect(deckListModel, &DeckListModel::dataChanged, this, &DeckCardZoneDisplayWidget::displayCards);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &DeckCardZoneDisplayWidget::onCategoryAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &DeckCardZoneDisplayWidget::onCategoryRemoval);
}
void DeckCardZoneDisplayWidget::cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget)
{
cardGroupLayout->removeWidget(displayWidget);
displayWidget->setParent(nullptr);
for (auto idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
indexToWidgetMap.remove(idx);
}
}
delete displayWidget;
}
void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex index)
{
auto categoryName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString();
if (indexToWidgetMap.contains(index)) {
return;
}
if (displayType == DisplayType::Overlap) {
auto *displayWidget = new OverlappedCardGroupDisplayWidget(
cardGroupContainer, deckListModel, index, zoneName, categoryName, activeGroupCriteria, activeSortCriteria,
subBannerOpacity, cardSizeWidget);
connect(displayWidget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(displayWidget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
&DeckCardZoneDisplayWidget::cleanupInvalidCardGroup);
cardGroupLayout->addWidget(displayWidget);
indexToWidgetMap.insert(index, displayWidget);
} else if (displayType == DisplayType::Flat) {
auto *displayWidget =
new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, index, zoneName, categoryName,
activeGroupCriteria, activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(displayWidget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
&DeckCardZoneDisplayWidget::cleanupInvalidCardGroup);
cardGroupLayout->addWidget(displayWidget);
indexToWidgetMap.insert(index, displayWidget);
}
}
void DeckCardZoneDisplayWidget::displayCards()
{
for (int i = 0; i < deckListModel->rowCount(trackedIndex); ++i) {
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(i, 0, trackedIndex));
constructAppropriateWidget(index);
}
}
void DeckCardZoneDisplayWidget::onCategoryAddition(const QModelIndex &parent, int first, int last)
{
if (!trackedIndex.isValid()) {
emit requestCleanup(this);
return;
}
if (parent == trackedIndex) {
for (int i = first; i <= last; i++) {
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(i, 0, trackedIndex));
constructAppropriateWidget(index);
}
}
}
void DeckCardZoneDisplayWidget::onCategoryRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(parent);
Q_UNUSED(first);
Q_UNUSED(last);
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
cardGroupLayout->removeWidget(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
if (!trackedIndex.isValid()) {
emit requestCleanup(this);
}
}
void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
@ -55,12 +142,6 @@ void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card)
emit cardHovered(card);
}
void DeckCardZoneDisplayWidget::displayCards()
{
addCardGroupIfItDoesNotExist();
deleteCardGroupIfItDoesNotExist();
}
void DeckCardZoneDisplayWidget::refreshDisplayType(const DisplayType &_displayType)
{
displayType = _displayType;
@ -74,6 +155,8 @@ void DeckCardZoneDisplayWidget::refreshDisplayType(const DisplayType &_displayTy
delete item;
}
indexToWidgetMap.clear();
// We gotta wait for all the deleteLater's to finish so we fire after the next event cycle
auto timer = new QTimer(this);
@ -82,66 +165,6 @@ void DeckCardZoneDisplayWidget::refreshDisplayType(const DisplayType &_displayTy
timer->start();
}
void DeckCardZoneDisplayWidget::addCardGroupIfItDoesNotExist()
{
QList<CardGroupDisplayWidget *> cardGroupsDisplayWidgets =
cardGroupContainer->findChildren<CardGroupDisplayWidget *>();
QList<QString> cardGroups = getGroupCriteriaValueList();
for (QString cardGroup : cardGroups) {
bool found = false;
for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
if (cardGroupDisplayWidget->cardGroupCategory == cardGroup) {
found = true;
cardGroupDisplayWidget->updateCardDisplays();
break;
}
}
if (found) {
continue;
}
if (displayType == DisplayType::Overlap) {
auto *display_widget = new OverlappedCardGroupDisplayWidget(
cardGroupContainer, deckListModel, zoneName, cardGroup, activeGroupCriteria, activeSortCriteria,
subBannerOpacity, cardSizeWidget);
connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, display_widget,
&CardGroupDisplayWidget::onActiveSortCriteriaChanged);
cardGroupLayout->addWidget(display_widget);
} else if (displayType == DisplayType::Flat) {
auto *display_widget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, zoneName,
cardGroup, activeGroupCriteria, activeSortCriteria,
subBannerOpacity, cardSizeWidget);
connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, display_widget,
&CardGroupDisplayWidget::onActiveSortCriteriaChanged);
cardGroupLayout->addWidget(display_widget);
}
}
}
void DeckCardZoneDisplayWidget::deleteCardGroupIfItDoesNotExist()
{
QList<CardGroupDisplayWidget *> cardGroupsDisplayWidgets =
cardGroupContainer->findChildren<CardGroupDisplayWidget *>();
QList<QString> validGroups = getGroupCriteriaValueList();
for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
if (!validGroups.contains(cardGroupDisplayWidget->cardGroupCategory)) {
cardGroupLayout->removeWidget(cardGroupDisplayWidget);
cardGroupDisplayWidget->deleteLater(); // Properly delete the widget after the event loop cycles
}
}
}
void DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged(QString _activeGroupCriteria)
{
activeGroupCriteria = _activeGroupCriteria;

View file

@ -6,6 +6,7 @@
#include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h"
#include "../visual_deck_editor/visual_deck_editor_widget.h"
#include "card_group_display_widgets/card_group_display_widget.h"
#include "card_info_picture_with_text_overlay_widget.h"
#include "card_size_widget.h"
@ -19,6 +20,7 @@ class DeckCardZoneDisplayWidget : public QWidget
public:
DeckCardZoneDisplayWidget(QWidget *parent,
DeckListModel *deckListModel,
QPersistentModelIndex trackedIndex,
QString zoneName,
QString activeGroupCriteria,
QStringList activeSortCriteria,
@ -27,6 +29,7 @@ public:
int subBannerOpacity,
CardSizeWidget *_cardSizeWidget);
DeckListModel *deckListModel;
QPersistentModelIndex trackedIndex;
QString zoneName;
void addCardsToOverlapWidget();
void resizeEvent(QResizeEvent *event) override;
@ -34,18 +37,21 @@ public:
public slots:
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void onHover(CardInfoPtr card);
void cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget);
void constructAppropriateWidget(QPersistentModelIndex index);
void displayCards();
void refreshDisplayType(const DisplayType &displayType);
void addCardGroupIfItDoesNotExist();
void deleteCardGroupIfItDoesNotExist();
void onActiveGroupCriteriaChanged(QString activeGroupCriteria);
void onActiveSortCriteriaChanged(QStringList activeSortCriteria);
QList<QString> getGroupCriteriaValueList();
void onCategoryAddition(const QModelIndex &parent, int first, int last);
void onCategoryRemoval(const QModelIndex &parent, int first, int last);
signals:
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card, QString zoneName);
void cardHovered(CardInfoPtr card);
void activeSortCriteriaChanged(QStringList activeSortCriteria);
void requestCleanup(DeckCardZoneDisplayWidget *displayWidget);
private:
QString activeGroupCriteria;
@ -59,6 +65,7 @@ private:
QWidget *cardGroupContainer;
QVBoxLayout *cardGroupLayout;
OverlapWidget *overlapWidget;
QHash<QPersistentModelIndex, QWidget *> indexToWidgetMap;
};
#endif // DECK_CARD_ZONE_DISPLAY_WIDGET_H

View file

@ -245,6 +245,7 @@ void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*&current*/, const
void DeckEditorDeckDockWidget::updateName(const QString &name)
{
deckModel->getDeckList()->setName(name);
deckEditor->setModified(name.isEmpty());
emit nameChanged();
emit deckModified();
}
@ -252,6 +253,7 @@ void DeckEditorDeckDockWidget::updateName(const QString &name)
void DeckEditorDeckDockWidget::updateComments()
{
deckModel->getDeckList()->setComments(commentsEdit->toPlainText());
deckEditor->setModified(commentsEdit->toPlainText().isEmpty());
emit commentsChanged();
emit deckModified();
}
@ -333,6 +335,7 @@ void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */)
{
auto cardAndId = bannerCardComboBox->currentData().value<QPair<QString, QString>>();
deckModel->getDeckList()->setBannerCard(cardAndId);
deckEditor->setModified(true);
emit deckModified();
}

View file

@ -88,6 +88,12 @@ void FlowWidget::addWidget(QWidget *widget_to_add) const
flowLayout->addWidget(widget_to_add);
}
void FlowWidget::insertWidgetAtIndex(QWidget *toInsert, int index)
{
flowLayout->insertWidgetAtIndex(toInsert, index);
update();
}
void FlowWidget::removeWidget(QWidget *widgetToRemove) const
{
flowLayout->removeWidget(widgetToRemove);

View file

@ -20,6 +20,7 @@ public:
Qt::ScrollBarPolicy horizontalPolicy,
Qt::ScrollBarPolicy verticalPolicy);
void addWidget(QWidget *widget_to_add) const;
void insertWidgetAtIndex(QWidget *toInsert, int index);
void removeWidget(QWidget *widgetToRemove) const;
void clearLayout();
[[nodiscard]] int count() const;

View file

@ -59,6 +59,12 @@ void OverlapWidget::addWidget(QWidget *widgetToAdd) const
overlapLayout->addWidget(widgetToAdd);
}
void OverlapWidget::insertWidgetAtIndex(QWidget *toInsert, int index)
{
overlapLayout->insertWidgetAtIndex(toInsert, index);
update();
}
void OverlapWidget::removeWidget(QWidget *widgetToRemove) const
{
overlapLayout->removeWidget(widgetToRemove);

View file

@ -17,6 +17,7 @@ public:
Qt::Orientation direction,
bool adjustOnResize = false);
void addWidget(QWidget *widgetToAdd) const;
void insertWidgetAtIndex(QWidget *toInsert, int index);
void removeWidget(QWidget *widgetToRemove) const;
void clearLayout();
void adjustMaxColumnsAndRows();

View file

@ -26,8 +26,6 @@
VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel)
: QWidget(parent), deckListModel(_deckListModel)
{
connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged);
// The Main Widget and Main Layout, which contain a single Widget: The Scroll Area
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout = new QVBoxLayout(this);
@ -175,14 +173,17 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_
scrollArea->addScrollBarWidget(zoneContainer, Qt::AlignHCenter);
scrollArea->setWidget(zoneContainer);
updateZoneWidgets();
cardSizeWidget = new CardSizeWidget(this);
mainLayout->addWidget(groupAndSortContainer);
mainLayout->addWidget(scrollArea);
mainLayout->addWidget(cardSizeWidget);
connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &VisualDeckEditorWidget::onCardAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &VisualDeckEditorWidget::onCardRemoval);
constructZoneWidgetsFromDeckListModel();
retranslateUi();
}
@ -198,10 +199,95 @@ void VisualDeckEditorWidget::retranslateUi()
tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)"));
}
void VisualDeckEditorWidget::cleanupInvalidZones(DeckCardZoneDisplayWidget *displayWidget)
{
zoneContainerLayout->removeWidget(displayWidget);
for (auto idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
indexToWidgetMap.remove(idx);
}
}
delete displayWidget;
}
void VisualDeckEditorWidget::onCardAddition(const QModelIndex &parent, int first, int last)
{
if (parent == deckListModel->getRoot()) {
for (int i = first; i <= last; i++) {
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(i, 0, deckListModel->getRoot()));
if (indexToWidgetMap.contains(index)) {
continue;
}
DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget(
zoneContainer, deckListModel, index,
deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString(), activeGroupCriteria,
activeSortCriteria, currentDisplayType, 20, 10, cardSizeWidget);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this,
&VisualDeckEditorWidget::onCardClick);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::requestCleanup, this,
&VisualDeckEditorWidget::cleanupInvalidZones);
connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged);
connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::refreshDisplayType);
zoneDisplayWidget->refreshDisplayType(currentDisplayType);
zoneContainerLayout->addWidget(zoneDisplayWidget);
indexToWidgetMap.insert(index, zoneDisplayWidget);
}
}
}
void VisualDeckEditorWidget::onCardRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(parent);
Q_UNUSED(first);
Q_UNUSED(last);
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
zoneContainerLayout->removeWidget(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
}
void VisualDeckEditorWidget::constructZoneWidgetsFromDeckListModel()
{
for (int i = 0; i < deckListModel->rowCount(deckListModel->parent(QModelIndex())); i++) {
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(i, 0, deckListModel->getRoot()));
if (indexToWidgetMap.contains(index)) {
continue;
}
DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget(
zoneContainer, deckListModel, index,
deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString(), activeGroupCriteria,
activeSortCriteria, currentDisplayType, 20, 10, cardSizeWidget);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this, &VisualDeckEditorWidget::onCardClick);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::requestCleanup, this,
&VisualDeckEditorWidget::cleanupInvalidZones);
connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged);
connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::refreshDisplayType);
zoneContainerLayout->addWidget(zoneDisplayWidget);
indexToWidgetMap.insert(index, zoneDisplayWidget);
}
}
void VisualDeckEditorWidget::updateZoneWidgets()
{
addZoneIfDoesNotExist();
deleteZoneIfDoesNotExist();
}
void VisualDeckEditorWidget::updateDisplayType()
@ -221,57 +307,6 @@ void VisualDeckEditorWidget::updateDisplayType()
emit displayTypeChanged(currentDisplayType);
}
void VisualDeckEditorWidget::addZoneIfDoesNotExist()
{
QList<DeckCardZoneDisplayWidget *> cardZoneDisplayWidgets =
zoneContainer->findChildren<DeckCardZoneDisplayWidget *>();
for (const QString &zone : *deckListModel->getZones()) {
bool found = false;
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
if (displayWidget->zoneName == zone) {
found = true;
displayWidget->displayCards();
break;
}
}
if (found) {
continue;
}
DeckCardZoneDisplayWidget *zoneDisplayWidget =
new DeckCardZoneDisplayWidget(zoneContainer, deckListModel, zone, activeGroupCriteria, activeSortCriteria,
currentDisplayType, 20, 10, cardSizeWidget);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this, &VisualDeckEditorWidget::onCardClick);
connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged);
connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::refreshDisplayType);
zoneContainerLayout->addWidget(zoneDisplayWidget);
}
}
void VisualDeckEditorWidget::deleteZoneIfDoesNotExist()
{
QList<DeckCardZoneDisplayWidget *> cardZoneDisplayWidgets =
zoneContainer->findChildren<DeckCardZoneDisplayWidget *>();
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
bool found = false;
for (const QString &zone : *deckListModel->getZones()) {
if (displayWidget->zoneName == zone) {
found = true;
break;
}
}
if (!found) {
zoneContainerLayout->removeWidget(displayWidget);
}
}
}
void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);

View file

@ -17,6 +17,7 @@
#include <QWidget>
#include <qscrollarea.h>
class DeckCardZoneDisplayWidget;
enum class DisplayType
{
Flat,
@ -41,8 +42,10 @@ public slots:
void decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight);
void updateZoneWidgets();
void updateDisplayType();
void addZoneIfDoesNotExist();
void deleteZoneIfDoesNotExist();
void cleanupInvalidZones(DeckCardZoneDisplayWidget *displayWidget);
void onCardAddition(const QModelIndex &parent, int first, int last);
void onCardRemoval(const QModelIndex &parent, int first, int last);
void constructZoneWidgetsFromDeckListModel();
signals:
void activeCardChanged(CardInfoPtr activeCard);
@ -83,6 +86,7 @@ private:
QVBoxLayout *zoneContainerLayout;
// OverlapControlWidget *overlapControlWidget;
QWidget *container;
QHash<QPersistentModelIndex, QWidget *> indexToWidgetMap;
};
#endif // VISUAL_DECK_EDITOR_H

View file

@ -312,7 +312,7 @@ bool DeckListModel::removeRows(int row, int count, const QModelIndex &parent)
} else {
emitRecursiveUpdates(parent);
}
emit dataChanged(parent, parent);
return true;
}
@ -408,10 +408,14 @@ QModelIndex DeckListModel::addCard(const QString &cardName,
const auto cardSetName = cardInfoSet.getPtr().isNull() ? "" : cardInfoSet.getPtr()->getCorrectedShortName();
if (!cardNode) {
auto *decklistCard = deckList->addCard(cardInfo->getName(), zoneName, cardSetName,
// Determine the correct index
int insertRow = findSortedInsertRow(cardTypeNode, cardInfo);
auto *decklistCard = deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName,
cardInfoSet.getProperty("num"), cardInfoSet.getProperty("uuid"));
beginInsertRows(parentIndex, static_cast<int>(cardTypeNode->size()), static_cast<int>(cardTypeNode->size()));
cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode);
beginInsertRows(parentIndex, insertRow, insertRow);
cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode, insertRow);
endInsertRows();
} else {
cardNode->setNumber(cardNode->getNumber() + 1);
@ -420,11 +424,44 @@ QModelIndex DeckListModel::addCard(const QString &cardName,
cardNode->setCardProviderId(cardInfoSet.getProperty("uuid"));
deckList->refreshDeckHash();
}
sort(lastKnownColumn, lastKnownOrder);
// sort(lastKnownColumn, lastKnownOrder);
emitRecursiveUpdates(parentIndex);
return nodeToIndex(cardNode);
}
int DeckListModel::findSortedInsertRow(InnerDecklistNode *parent, CardInfoPtr cardInfo) const
{
if (!cardInfo) {
return parent->size(); // fallback: append at end
}
for (int i = 0; i < parent->size(); ++i) {
auto *existingCard = dynamic_cast<DecklistModelCardNode *>(parent->at(i));
if (!existingCard)
continue;
bool lessThan = false;
switch (lastKnownColumn) {
case 0: // ByNumber
lessThan = lastKnownOrder == Qt::AscendingOrder
? cardInfo->getProperty("collectorNumber") < existingCard->getCardCollectorNumber()
: cardInfo->getProperty("collectorNumber") > existingCard->getCardCollectorNumber();
break;
case 1: // ByName
default:
lessThan = lastKnownOrder == Qt::AscendingOrder
? cardInfo->getName().localeAwareCompare(existingCard->getName()) < 0
: cardInfo->getName().localeAwareCompare(existingCard->getName()) > 0;
break;
}
if (lessThan)
return i;
}
return parent->size(); // insert at end if no earlier match
}
QModelIndex DeckListModel::nodeToIndex(AbstractDecklistNode *node) const
{
if (node == nullptr || node == root) {

View file

@ -25,8 +25,8 @@ private:
DecklistCardNode *dataNode;
public:
DecklistModelCardNode(DecklistCardNode *_dataNode, InnerDecklistNode *_parent)
: AbstractDecklistCardNode(_parent), dataNode(_dataNode)
DecklistModelCardNode(DecklistCardNode *_dataNode, InnerDecklistNode *_parent, int position = -1)
: AbstractDecklistCardNode(_parent, position), dataNode(_dataNode)
{
}
int getNumber() const override
@ -92,6 +92,10 @@ signals:
public:
explicit DeckListModel(QObject *parent = nullptr);
~DeckListModel() override;
QModelIndex getRoot() const
{
return nodeToIndex(root);
};
QString getSortCriteriaForCard(CardInfoPtr info);
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
@ -111,6 +115,7 @@ public:
const CardInfoPerSet &cardInfoSet,
const QString &zoneName,
bool abAddAnyway = false);
int findSortedInsertRow(InnerDecklistNode *parent, CardInfoPtr cardInfo) const;
void sort(int column, Qt::SortOrder order) override;
void cleanList();
DeckLoader *getDeckList() const

View file

@ -72,7 +72,7 @@ void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &desti
auto copyIfNotAToken = [this, &destination](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
if (dbCard && !dbCard->getIsToken()) {
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName());
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName(), -1);
addedCard->setNumber(card->getNumber());
}
};

View file

@ -71,10 +71,15 @@ void SideboardPlan::write(QXmlStreamWriter *xml)
xml->writeEndElement();
}
AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent) : parent(_parent), sortMethod(Default)
AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent, int position)
: parent(_parent), sortMethod(Default)
{
if (parent) {
parent->append(this);
if (position == -1) {
parent->append(this);
} else {
parent->insert(position, this);
}
}
}
@ -274,7 +279,7 @@ bool InnerDecklistNode::readElement(QXmlStreamReader *xml)
} else if (childName == "card") {
DecklistCardNode *newCard = new DecklistCardNode(
xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(),
this, xml->attributes().value("setShortName").toString(),
this, -1, xml->attributes().value("setShortName").toString(),
xml->attributes().value("collectorNumber").toString(), xml->attributes().value("uuid").toString());
newCard->readElement(xml);
}
@ -725,7 +730,7 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
QString zoneName = getCardZoneFromName(cardName, sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN);
// make new entry in decklist
new DecklistCardNode(cardName, amount, getZoneObjFromName(zoneName), setCode, collectorNumber);
new DecklistCardNode(cardName, amount, getZoneObjFromName(zoneName), -1, setCode, collectorNumber);
}
refreshDeckHash();
@ -856,6 +861,7 @@ int DeckList::getSideboardSize() const
DecklistCardNode *DeckList::addCard(const QString &cardName,
const QString &zoneName,
const int position,
const QString &cardSetName,
const QString &cardSetCollectorNumber,
const QString &cardProviderId)
@ -865,7 +871,8 @@ DecklistCardNode *DeckList::addCard(const QString &cardName,
zoneNode = new InnerDecklistNode(zoneName, root);
}
auto *node = new DecklistCardNode(cardName, 1, zoneNode, cardSetName, cardSetCollectorNumber, cardProviderId);
auto *node =
new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber, cardProviderId);
refreshDeckHash();
return node;

View file

@ -56,7 +56,7 @@ protected:
DeckSortMethod sortMethod;
public:
explicit AbstractDecklistNode(InnerDecklistNode *_parent = nullptr);
explicit AbstractDecklistNode(InnerDecklistNode *_parent = nullptr, int position = -1);
virtual ~AbstractDecklistNode() = default;
virtual void setSortMethod(DeckSortMethod method)
{
@ -88,8 +88,8 @@ class InnerDecklistNode : public AbstractDecklistNode, public QList<AbstractDeck
class compareFunctor;
public:
explicit InnerDecklistNode(QString _name = QString(), InnerDecklistNode *_parent = nullptr)
: AbstractDecklistNode(_parent), name(std::move(_name))
explicit InnerDecklistNode(QString _name = QString(), InnerDecklistNode *_parent = nullptr, int position = -1)
: AbstractDecklistNode(_parent, position), name(std::move(_name))
{
}
explicit InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = nullptr);
@ -153,7 +153,8 @@ public:
class AbstractDecklistCardNode : public AbstractDecklistNode
{
public:
explicit AbstractDecklistCardNode(InnerDecklistNode *_parent = nullptr) : AbstractDecklistNode(_parent)
explicit AbstractDecklistCardNode(InnerDecklistNode *_parent = nullptr, int position = -1)
: AbstractDecklistNode(_parent, position)
{
}
virtual int getNumber() const = 0;
@ -190,10 +191,11 @@ public:
explicit DecklistCardNode(QString _name = QString(),
int _number = 1,
InnerDecklistNode *_parent = nullptr,
int position = -1,
QString _cardSetShortName = QString(),
QString _cardSetNumber = QString(),
QString _cardProviderId = QString())
: AbstractDecklistCardNode(_parent), name(std::move(_name)), number(_number),
: AbstractDecklistCardNode(_parent, position), name(std::move(_name)), number(_number),
cardSetShortName(std::move(_cardSetShortName)), cardSetNumber(std::move(_cardSetNumber)),
cardProviderId(std::move(_cardProviderId))
{
@ -373,6 +375,7 @@ public:
}
DecklistCardNode *addCard(const QString &cardName,
const QString &zoneName,
int position,
const QString &cardSetName = QString(),
const QString &cardSetCollectorNumber = QString(),
const QString &cardProviderId = QString());