[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()
{
// 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;
proxy.setSourceModel(deckListModel);
proxy.setSortCriteria(activeSortCriteria);
@ -174,16 +186,18 @@ void CardGroupDisplayWidget::updateCardDisplays()
QModelIndex sourceIndex = proxy.mapToSource(proxyIndex);
// 4. persist the source index
QPersistentModelIndex persistent(sourceIndex);
addCardWidgets(QPersistentModelIndex(sourceIndex));
}
}
// Get the card amount
int cardAmount =
sourceIndex.sibling(sourceIndex.row(), DeckListModelColumns::CARD_AMOUNT).data(Qt::EditRole).toInt();
void CardGroupDisplayWidget::addCardWidgets(const QPersistentModelIndex &persistent)
{
// Get the card amount
int cardAmount = persistent.sibling(persistent.row(), DeckListModelColumns::CARD_AMOUNT).data(Qt::EditRole).toInt();
// Create multiple widgets for the card count
for (int copy = 0; copy < cardAmount; ++copy) {
addToLayout(constructWidgetForIndex(persistent));
}
// Create multiple widgets for the card count
for (int copy = 0; copy < cardAmount; ++copy) {
addToLayout(constructWidgetForIndex(persistent));
}
}

View file

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

View file

@ -5,6 +5,7 @@
#include "libcockatrice/card/database/card_database_manager.h"
#include <QResizeEvent>
#include <algorithm>
#include <libcockatrice/models/deck_list/deck_list_model.h>
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
@ -51,11 +52,6 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
// User Interaction
// =====================================================================================================================
void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, const ExactCard &card)
{
emit cardClicked(event, card, zoneName);
}
void DeckCardZoneDisplayWidget::onHover(const ExactCard &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();
// 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) {
auto *displayWidget = new OverlappedCardGroupDisplayWidget(
cardGroupContainer, deckListModel, selectionModel, index, zoneName, categoryName, activeGroupCriteria,
activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this,
&DeckCardZoneDisplayWidget::onClick);
cardGroupContainer, deckListModel, selectionModel, index, effectiveZoneName, categoryName,
activeGroupCriteria, activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this, routeCardClick);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardHovered, this,
&DeckCardZoneDisplayWidget::onHover);
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
@ -111,9 +113,9 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
indexToWidgetMap.insert(index, displayWidget);
} else if (displayType == DisplayType::Flat) {
auto *displayWidget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, selectionModel, index,
zoneName, categoryName, activeGroupCriteria,
effectiveZoneName, categoryName, activeGroupCriteria,
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, &CardGroupDisplayWidget::cleanupRequested, this,
&DeckCardZoneDisplayWidget::cleanupInvalidCardGroup);
@ -126,24 +128,22 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
void DeckCardZoneDisplayWidget::displayCards()
{
QSortFilterProxyModel proxy;
proxy.setSourceModel(deckListModel);
proxy.setSortRole(Qt::EditRole);
proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
if (!trackedIndex.isValid()) {
return;
}
// 1. trackedIndex is a source index → map it to proxy space
QModelIndex proxyParent = proxy.mapFromSource(trackedIndex);
// Iterate the direct children of the tracked zone, keeping the tree view's row
// 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
for (int i = 0; i < proxy.rowCount(proxyParent); ++i) {
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);
std::stable_partition(rows.begin(), rows.end(), [](const QPersistentModelIndex &row) {
return !row.data(DeckRoles::IsCustomZoneRole).toBool();
});
for (const QPersistentModelIndex &persistent : rows) {
constructAppropriateWidget(persistent);
}
}

View file

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