diff --git a/cockatrice/src/deckview.cpp b/cockatrice/src/deckview.cpp index 3d08dbc9..e3ffe863 100644 --- a/cockatrice/src/deckview.cpp +++ b/cockatrice/src/deckview.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "deckview.h" #include "decklist.h" #include "carddatabase.h" @@ -124,7 +125,7 @@ void DeckViewCard::hoverEnterEvent(QGraphicsSceneHoverEvent *event) } DeckViewCardContainer::DeckViewCardContainer(const QString &_name) - : QGraphicsItem(), name(_name), width(0), height(0), maxWidth(0) + : QGraphicsItem(), name(_name), width(0), height(0) { QString bgPath = settingsCache->getTableBgPath(); if (!bgPath.isEmpty()) @@ -140,6 +141,8 @@ QRectF DeckViewCardContainer::boundingRect() const void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { + qreal totalTextWidth = getCardTypeTextWidth(); + if (bgPixmap.isNull()) painter->fillRect(boundingRect(), QColor(0, 0, 100)); else @@ -154,43 +157,108 @@ void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsI f.setWeight(QFont::Bold); painter->setFont(f); painter->drawText(10, 0, width - 20, separatorY, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, InnerDecklistNode::visibleNameFromName(name) + QString(": %1").arg(cards.size())); + + f.setPixelSize(16); + painter->setFont(f); + QList cardTypeList = cardsByType.uniqueKeys(); + qreal yUntilNow = separatorY + paddingY; + for (int i = 0; i < cardTypeList.size(); ++i) { + if (i != 0) { + painter->setPen(QColor(255, 255, 255, 100)); + painter->drawLine(QPointF(0, yUntilNow - paddingY / 2), QPointF(width, yUntilNow - paddingY / 2)); + } + + qreal thisRowHeight = CARD_HEIGHT * currentRowsAndCols[i].first; + QRectF textRect(0, yUntilNow, totalTextWidth, thisRowHeight); + yUntilNow += thisRowHeight + paddingY; + + painter->setPen(Qt::white); + painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, cardTypeList[i]); + } } void DeckViewCardContainer::addCard(DeckViewCard *card) { cards.append(card); + cardsByType.insert(card->getInfo()->getMainCardType(), card); } void DeckViewCardContainer::removeCard(DeckViewCard *card) { cards.removeAt(cards.indexOf(card)); + cardsByType.remove(card->getInfo()->getMainCardType(), card); } -void DeckViewCardContainer::rearrangeItems() +QList > DeckViewCardContainer::getRowsAndCols() const { - separatorY = 30; - - QMap cardsByType; - for (int i = 0; i < cards.size(); ++i) - cardsByType.insertMulti(cards[i]->getInfo()->getMainCardType(), cards[i]); - + QList > result; QList cardTypeList = cardsByType.uniqueKeys(); - int rows = cardTypeList.size(); - int cols = 0; - for (int i = 0; i < rows; ++i) { + for (int i = 0; i < cardTypeList.size(); ++i) + result.append(QPair(1, cardsByType.count(cardTypeList[i]))); + return result; +} + +int DeckViewCardContainer::getCardTypeTextWidth() const +{ + QFont f("Serif"); + f.setStyleHint(QFont::Serif); + f.setPixelSize(16); + f.setWeight(QFont::Bold); + QFontMetrics fm(f); + + int maxCardTypeWidth = 0; + QMapIterator i(cardsByType); + while (i.hasNext()) { + int cardTypeWidth = fm.size(Qt::TextSingleLine, i.next().key()).width(); + if (cardTypeWidth > maxCardTypeWidth) + maxCardTypeWidth = cardTypeWidth; + } + + return maxCardTypeWidth + 15; +} + +QSizeF DeckViewCardContainer::calculateBoundingRect(const QList > &rowsAndCols) const +{ + qreal totalHeight = 0; + qreal totalWidth = 0; + + // Calculate space needed for cards + for (int i = 0; i < rowsAndCols.size(); ++i) { + totalHeight += CARD_HEIGHT * rowsAndCols[i].first + paddingY; + if (CARD_WIDTH * rowsAndCols[i].second > totalWidth) + totalWidth = CARD_WIDTH * rowsAndCols[i].second; + } + + return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY); +} + +void DeckViewCardContainer::rearrangeItems(const QList > &rowsAndCols) +{ + currentRowsAndCols = rowsAndCols; + + int totalCols = 0, totalRows = 0; + qreal yUntilNow = separatorY + paddingY; + qreal x = (qreal) getCardTypeTextWidth(); + for (int i = 0; i < rowsAndCols.size(); ++i) { + const int tempRows = rowsAndCols[i].first; + const int tempCols = rowsAndCols[i].second; + totalRows += tempRows; + if (tempCols > totalCols) + totalCols = tempCols; + + QList cardTypeList = cardsByType.uniqueKeys(); QList row = cardsByType.values(cardTypeList[i]); - if (row.size() > cols) - cols = row.size(); for (int j = 0; j < row.size(); ++j) { DeckViewCard *card = row[j]; - card->setPos(j * CARD_WIDTH, separatorY + 10 + i * (CARD_HEIGHT + rowSpacing)); + card->setPos(x + (j % tempCols) * CARD_WIDTH, yUntilNow + (j / tempCols) * CARD_HEIGHT); } + yUntilNow += tempRows * CARD_HEIGHT + paddingY; } prepareGeometryChange(); - if (cols * CARD_WIDTH > maxWidth) - width = maxWidth = cols * CARD_WIDTH; - height = separatorY + 10 + rows * CARD_HEIGHT + rowSpacing * (rows - 1); + QSizeF bRect = calculateBoundingRect(rowsAndCols); + width = bRect.width(); + height = bRect.height(); } void DeckViewCardContainer::setWidth(qreal _width) @@ -201,7 +269,7 @@ void DeckViewCardContainer::setWidth(qreal _width) } DeckViewScene::DeckViewScene(QObject *parent) - : QGraphicsScene(parent), locked(false), deck(0) + : QGraphicsScene(parent), locked(false), deck(0), optimalAspectRatio(1.0) { } @@ -281,16 +349,60 @@ void DeckViewScene::rearrangeItems() { const int spacing = CARD_HEIGHT / 3; QList contList = cardContainers.values(); - qreal totalHeight = -spacing; - qreal totalWidth = 0; + + // Initialize space requirements + QList > > rowsAndColsList; + QList > cardCountList; + for (int i = 0; i < contList.size(); ++i) { + QList > rowsAndCols = contList[i]->getRowsAndCols(); + rowsAndColsList.append(rowsAndCols); + + cardCountList.append(QList()); + for (int j = 0; j < rowsAndCols.size(); ++j) + cardCountList[i].append(rowsAndCols[j].second); + } + + qreal totalHeight, totalWidth; + for (;;) { + // Calculate total size before this iteration + totalHeight = -spacing; + totalWidth = 0; + for (int i = 0; i < contList.size(); ++i) { + QSizeF contSize = contList[i]->calculateBoundingRect(rowsAndColsList[i]); + totalHeight += contSize.height() + spacing; + if (contSize.width() > totalWidth) + totalWidth = contSize.width(); + } + + // We're done when the aspect ratio shifts from too high to too low. + if (totalWidth / totalHeight <= optimalAspectRatio) + break; + + // Find category with highest column count + int maxIndex1 = -1, maxIndex2 = -1, maxCols = 0; + for (int i = 0; i < rowsAndColsList.size(); ++i) + for (int j = 0; j < rowsAndColsList[i].size(); ++j) + if (rowsAndColsList[i][j].second > maxCols) { + maxIndex1 = i; + maxIndex2 = j; + maxCols = rowsAndColsList[i][j].second; + } + + // Add row to category + const int maxRows = rowsAndColsList[maxIndex1][maxIndex2].first; + const int maxCardCount = cardCountList[maxIndex1][maxIndex2]; + rowsAndColsList[maxIndex1][maxIndex2] = QPair(maxRows + 1, (int) ceil((qreal) maxCardCount / (qreal) (maxRows + 1))); + } + + totalHeight = -spacing; for (int i = 0; i < contList.size(); ++i) { DeckViewCardContainer *c = contList[i]; - c->rearrangeItems(); + c->rearrangeItems(rowsAndColsList[i]); c->setPos(0, totalHeight + spacing); totalHeight += c->boundingRect().height() + spacing; - if (c->boundingRect().width() > totalWidth) - totalWidth = c->boundingRect().width(); } + + totalWidth = totalHeight * optimalAspectRatio; for (int i = 0; i < contList.size(); ++i) contList[i]->setWidth(totalWidth); @@ -335,7 +447,8 @@ DeckView::DeckView(QWidget *parent) void DeckView::resizeEvent(QResizeEvent *event) { QGraphicsView::resizeEvent(event); - updateSceneRect(scene()->sceneRect()); + deckViewScene->setOptimalAspectRatio((qreal) width() / (qreal) height()); + deckViewScene->rearrangeItems(); } void DeckView::updateSceneRect(const QRectF &rect) diff --git a/cockatrice/src/deckview.h b/cockatrice/src/deckview.h index bb8b6415..34c73c98 100644 --- a/cockatrice/src/deckview.h +++ b/cockatrice/src/deckview.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "abstractcarditem.h" #include "abstractcarddragitem.h" @@ -42,13 +43,16 @@ protected: class DeckViewCardContainer : public QGraphicsItem { private: + static const int separatorY = 20; + static const int paddingY = 10; + QString name; QList cards; + QMultiMap cardsByType; + QList > currentRowsAndCols; qreal width, height; - qreal maxWidth; - qreal separatorY; QPixmap bgPixmap; - static const int rowSpacing = 5; + int getCardTypeTextWidth() const; public: enum { Type = typeDeckViewCardContainer }; int type() const { return Type; } @@ -59,8 +63,11 @@ public: void removeCard(DeckViewCard *card); const QList &getCards() const { return cards; } const QString &getName() const { return name; } - void rearrangeItems(); void setWidth(qreal _width); + + QList > getRowsAndCols() const; + QSizeF calculateBoundingRect(const QList > &rowsAndCols) const; + void rearrangeItems(const QList > &rowsAndCols); }; class DeckViewScene : public QGraphicsScene { @@ -72,15 +79,17 @@ private: bool locked; DeckList *deck; QMap cardContainers; + qreal optimalAspectRatio; void rebuildTree(); void applySideboardPlan(const QList &plan); - void rearrangeItems(); public: DeckViewScene(QObject *parent = 0); ~DeckViewScene(); void setLocked(bool _locked) { locked = _locked; } bool getLocked() const { return locked; } void setDeck(DeckList *_deck); + void setOptimalAspectRatio(qreal _optimalAspectRatio) { optimalAspectRatio = _optimalAspectRatio; } + void rearrangeItems(); void updateContents(); QList getSideboardPlan() const; }; diff --git a/cockatrice/src/gameview.cpp b/cockatrice/src/gameview.cpp index 7b13ae11..8c32f755 100644 --- a/cockatrice/src/gameview.cpp +++ b/cockatrice/src/gameview.cpp @@ -27,6 +27,7 @@ GameView::GameView(QGraphicsScene *scene, QWidget *parent) void GameView::resizeEvent(QResizeEvent *event) { QGraphicsView::resizeEvent(event); + GameScene *s = dynamic_cast(scene()); if (s) { s->processViewSizeChange(event->size());