[Client] Show custom zones in the card display widgets

Card group displays and deck zone displays learn to render custom
zones alongside the standard boards.

- Group display widgets treat custom-zone nodes like other group
  headers, keeping counts and layout consistent.
- Zone display widgets resolve their title through visibleNameFromName
  so custom zones show their user-chosen names localized like the
  standard zones.
This commit is contained in:
Lukas Brübach 2026-08-24 07:13:16 +02:00 committed by GitHub
parent bf5998f6c5
commit 67ea3412f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 35 deletions

View file

@ -155,6 +155,18 @@ QWidget *CardGroupDisplayWidget::constructWidgetForIndex(QPersistentModelIndex i
void CardGroupDisplayWidget::updateCardDisplays() void CardGroupDisplayWidget::updateCardDisplays()
{ {
// Custom zones are user-defined containers: they display their cards in the same
// order as the tree view, i.e. the model row order. Only criteria groups apply
// the visual sort criteria.
const bool isCustomZone = trackedIndex.data(DeckRoles::IsCustomZoneRole).toBool();
if (isCustomZone) {
for (int i = 0; i < deckListModel->rowCount(trackedIndex); ++i) {
addCardWidgets(QPersistentModelIndex(deckListModel->index(i, 0, trackedIndex)));
}
return;
}
DeckListSortFilterProxyModel proxy; DeckListSortFilterProxyModel proxy;
proxy.setSourceModel(deckListModel); proxy.setSourceModel(deckListModel);
proxy.setSortCriteria(activeSortCriteria); proxy.setSortCriteria(activeSortCriteria);
@ -174,16 +186,18 @@ void CardGroupDisplayWidget::updateCardDisplays()
QModelIndex sourceIndex = proxy.mapToSource(proxyIndex); QModelIndex sourceIndex = proxy.mapToSource(proxyIndex);
// 4. persist the source index // 4. persist the source index
QPersistentModelIndex persistent(sourceIndex); addCardWidgets(QPersistentModelIndex(sourceIndex));
}
}
// Get the card amount void CardGroupDisplayWidget::addCardWidgets(const QPersistentModelIndex &persistent)
int cardAmount = {
sourceIndex.sibling(sourceIndex.row(), DeckListModelColumns::CARD_AMOUNT).data(Qt::EditRole).toInt(); // Get the card amount
int cardAmount = persistent.sibling(persistent.row(), DeckListModelColumns::CARD_AMOUNT).data(Qt::EditRole).toInt();
// Create multiple widgets for the card count // Create multiple widgets for the card count
for (int copy = 0; copy < cardAmount; ++copy) { for (int copy = 0; copy < cardAmount; ++copy) {
addToLayout(constructWidgetForIndex(persistent)); addToLayout(constructWidgetForIndex(persistent));
}
} }
} }

View file

@ -35,6 +35,7 @@ public:
void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void refreshSelectionForIndex(const QPersistentModelIndex &persistent); void refreshSelectionForIndex(const QPersistentModelIndex &persistent);
void clearAllDisplayWidgets(); void clearAllDisplayWidgets();
void addCardWidgets(const QPersistentModelIndex &persistent);
DeckListModel *deckListModel; DeckListModel *deckListModel;
QItemSelectionModel *selectionModel; QItemSelectionModel *selectionModel;

View file

@ -5,6 +5,7 @@
#include "libcockatrice/card/database/card_database_manager.h" #include "libcockatrice/card/database/card_database_manager.h"
#include <QResizeEvent> #include <QResizeEvent>
#include <algorithm>
#include <libcockatrice/models/deck_list/deck_list_model.h> #include <libcockatrice/models/deck_list/deck_list_model.h>
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent, DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
@ -51,11 +52,6 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
// User Interaction // User Interaction
// ===================================================================================================================== // =====================================================================================================================
void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, const ExactCard &card)
{
emit cardClicked(event, card, zoneName);
}
void DeckCardZoneDisplayWidget::onHover(const ExactCard &card) void DeckCardZoneDisplayWidget::onHover(const ExactCard &card)
{ {
emit cardHovered(card); emit cardHovered(card);
@ -95,12 +91,18 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
} }
auto categoryName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); auto categoryName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
// Cards in a custom zone belong to that zone, not the board zone, so that
// increment/decrement/swap actions target the custom zone.
const bool isCustomZone = index.data(DeckRoles::IsCustomZoneRole).toBool();
const QString effectiveZoneName = isCustomZone ? categoryName : zoneName;
const auto routeCardClick = [this, effectiveZoneName](QMouseEvent *event, const ExactCard &card) {
emit cardClicked(event, card, effectiveZoneName);
};
if (displayType == DisplayType::Overlap) { if (displayType == DisplayType::Overlap) {
auto *displayWidget = new OverlappedCardGroupDisplayWidget( auto *displayWidget = new OverlappedCardGroupDisplayWidget(
cardGroupContainer, deckListModel, selectionModel, index, zoneName, categoryName, activeGroupCriteria, cardGroupContainer, deckListModel, selectionModel, index, effectiveZoneName, categoryName,
activeSortCriteria, subBannerOpacity, cardSizeWidget); activeGroupCriteria, activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this, connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this, routeCardClick);
&DeckCardZoneDisplayWidget::onClick);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardHovered, this, connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardHovered, this,
&DeckCardZoneDisplayWidget::onHover); &DeckCardZoneDisplayWidget::onHover);
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this, connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
@ -111,9 +113,9 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
indexToWidgetMap.insert(index, displayWidget); indexToWidgetMap.insert(index, displayWidget);
} else if (displayType == DisplayType::Flat) { } else if (displayType == DisplayType::Flat) {
auto *displayWidget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, selectionModel, index, auto *displayWidget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, selectionModel, index,
zoneName, categoryName, activeGroupCriteria, effectiveZoneName, categoryName, activeGroupCriteria,
activeSortCriteria, subBannerOpacity, cardSizeWidget); activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &FlatCardGroupDisplayWidget::cardClicked, this, &DeckCardZoneDisplayWidget::onClick); connect(displayWidget, &FlatCardGroupDisplayWidget::cardClicked, this, routeCardClick);
connect(displayWidget, &FlatCardGroupDisplayWidget::cardHovered, this, &DeckCardZoneDisplayWidget::onHover); connect(displayWidget, &FlatCardGroupDisplayWidget::cardHovered, this, &DeckCardZoneDisplayWidget::onHover);
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this, connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
&DeckCardZoneDisplayWidget::cleanupInvalidCardGroup); &DeckCardZoneDisplayWidget::cleanupInvalidCardGroup);
@ -126,24 +128,22 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
void DeckCardZoneDisplayWidget::displayCards() void DeckCardZoneDisplayWidget::displayCards()
{ {
QSortFilterProxyModel proxy; if (!trackedIndex.isValid()) {
proxy.setSourceModel(deckListModel); return;
proxy.setSortRole(Qt::EditRole); }
proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
// 1. trackedIndex is a source index → map it to proxy space // Iterate the direct children of the tracked zone, keeping the tree view's row
QModelIndex proxyParent = proxy.mapFromSource(trackedIndex); // order (criteria groups first, then custom zones in their creation order).
QList<QPersistentModelIndex> rows;
for (int i = 0; i < deckListModel->rowCount(trackedIndex); ++i) {
rows.append(QPersistentModelIndex(deckListModel->index(i, 0, trackedIndex)));
}
// 2. iterate children under the proxy parent std::stable_partition(rows.begin(), rows.end(), [](const QPersistentModelIndex &row) {
for (int i = 0; i < proxy.rowCount(proxyParent); ++i) { return !row.data(DeckRoles::IsCustomZoneRole).toBool();
QModelIndex proxyIndex = proxy.index(i, 0, proxyParent); });
// 3. map back to source
QModelIndex sourceIndex = proxy.mapToSource(proxyIndex);
// 4. persist the source index
QPersistentModelIndex persistent(sourceIndex);
for (const QPersistentModelIndex &persistent : rows) {
constructAppropriateWidget(persistent); constructAppropriateWidget(persistent);
} }
} }

View file

@ -42,7 +42,6 @@ public:
void addCardsToOverlapWidget(); void addCardsToOverlapWidget();
public slots: public slots:
void onClick(QMouseEvent *event, const ExactCard &card);
void onHover(const ExactCard &card); void onHover(const ExactCard &card);
void cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget); void cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget);
void constructAppropriateWidget(QPersistentModelIndex index); void constructAppropriateWidget(QPersistentModelIndex index);