diff --git a/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp b/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp index 29bf2263e..c1c49ccb7 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp +++ b/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp @@ -22,6 +22,22 @@ namespace { +// Below this row width the delegate switches to a compact layout so the card +// name stays readable when the popup cannot be as wide as PopupRowWidth. +constexpr int CompactRowWidth = 380; +constexpr int CompactSymbolRadius = 6; +constexpr int CompactSymbolSpacing = 1; +constexpr int CompactRightPad = 8; +constexpr int ManaZoneWidth = 110; +constexpr int CompactManaZoneWidth = 70; +constexpr int RightPad = 14; +constexpr int TextLeftPad = 12; +constexpr int TextRightGap = 8; + +// Below this text width the type-line band is dropped so the name can use the +// whole row height instead of being elided to a single readable character. +constexpr int MinNameWidth = 120; + struct ManaColor { QColor fill; @@ -101,7 +117,7 @@ QSize CardCompleterDelegate::sizeHint(const QStyleOptionViewItem &option, const } // Fixed wide rows so the popup has room for name, type line, set and mana - return {480, CardRowHeight}; + return {PopupRowWidth, CardRowHeight}; } // --------------------------------------------------------------------------- @@ -152,7 +168,12 @@ void CardCompleterDelegate::drawManaSymbol(QPainter *p, QPoint centre, const QSt // --------------------------------------------------------------------------- -int CardCompleterDelegate::drawManaCost(QPainter *p, const QRect &row, const QString &manaCost, int radius) const +int CardCompleterDelegate::drawManaCost(QPainter *p, + const QRect &row, + const QString &manaCost, + int radius, + int spacing, + int rightPad) const { if (manaCost.isEmpty()) { return row.right(); @@ -181,11 +202,9 @@ int CardCompleterDelegate::drawManaCost(QPainter *p, const QRect &row, const QSt totalW += PartGap; } - totalW += parts.at(i).size() * diam + qMax(0, parts.at(i).size() - 1) * SymbolSpacing; + totalW += parts.at(i).size() * diam + qMax(0, parts.at(i).size() - 1) * spacing; } - const int rightPad = 14; - int x = row.right() - rightPad - totalW + radius; const int cy = row.center().y(); @@ -195,11 +214,11 @@ int CardCompleterDelegate::drawManaCost(QPainter *p, const QRect &row, const QSt for (const QString &sym : symbols) { drawManaSymbol(p, {x, cy}, sym, radius); - x += diam + SymbolSpacing; + x += diam + spacing; } if (i < parts.size() - 1) { - x += PartGap - SymbolSpacing; + x += PartGap - spacing; } } @@ -266,6 +285,14 @@ void CardCompleterDelegate::paint(QPainter *painter, const QStyleOptionViewItem const QString typeLine = card->getCardType(); const QString setCode = setCodeForCard(card); + // When the row is too narrow for the full layout, use smaller mana pips and + // a slimmer mana zone so the card name keeps as much room as possible. + const bool compact = rect.width() < CompactRowWidth; + const int symbolRadius = compact ? CompactSymbolRadius : SymbolRadius; + const int symbolSpacing = compact ? CompactSymbolSpacing : SymbolSpacing; + const int rightPad = compact ? CompactRightPad : RightPad; + const int manaZoneWidth = compact ? CompactManaZoneWidth : ManaZoneWidth; + const QColor accent = accentForColors(card->getColors()); const QColor base = pal.color(QPalette::Base); @@ -314,7 +341,7 @@ void CardCompleterDelegate::paint(QPainter *painter, const QStyleOptionViewItem // Right mana zone // ----------------------------------------------------------------------- - const QRectF manaZone(cardRect.right() - 110, cardRect.top(), 110, cardRect.height()); + const QRectF manaZone(cardRect.right() - manaZoneWidth, cardRect.top(), manaZoneWidth, cardRect.height()); QLinearGradient manaGrad(manaZone.topLeft(), manaZone.bottomLeft()); @@ -356,22 +383,27 @@ void CardCompleterDelegate::paint(QPainter *painter, const QStyleOptionViewItem // Mana cost // ----------------------------------------------------------------------- - const int costLeft = drawManaCost(painter, cardRect.toRect(), manaCost, SymbolRadius); + const int costLeft = drawManaCost(painter, cardRect.toRect(), manaCost, symbolRadius, symbolSpacing, rightPad); // ----------------------------------------------------------------------- // Card name + type line + set code // ----------------------------------------------------------------------- - const int textLeft = cardRect.left() + AccentBarWidth + 12; - const int textRight = costLeft - 8; + const int textLeft = cardRect.left() + AccentBarWidth + TextLeftPad; + const int textRight = costLeft - TextRightGap; const int textWidth = qMax(0, textRight - textLeft); + // If even the compact layout leaves too little room for a readable name, + // drop the type-line band and let the name use the full row height. + const bool showInfoBand = textWidth >= MinNameWidth; + // ----------------------------------------------------------------------- // Card name (top band) // ----------------------------------------------------------------------- { - const QRect nameRect(textLeft, rect.top() + 2, textWidth, 20); + const QRect nameRect = showInfoBand ? QRect(textLeft, rect.top() + 2, textWidth, 20) + : QRect(textLeft, rect.top() + 2, textWidth, rect.height() - 4); QFont f = option.font; f.setPixelSize(13); @@ -392,7 +424,7 @@ void CardCompleterDelegate::paint(QPainter *painter, const QStyleOptionViewItem // Type line + set code (bottom band) // ----------------------------------------------------------------------- - { + if (showInfoBand) { const QRect infoRect(textLeft, rect.top() + 22, textWidth, rect.height() - 24); QFont f = option.font; diff --git a/cockatrice/src/interface/widgets/utility/card_completer_delegate.h b/cockatrice/src/interface/widgets/utility/card_completer_delegate.h index 78667f56b..266296f49 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_delegate.h +++ b/cockatrice/src/interface/widgets/utility/card_completer_delegate.h @@ -30,6 +30,9 @@ public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + // Content width of one popup row; the completer popup is sized to fit it. + static constexpr int PopupRowWidth = 480; + private: // Set short codes, resolved once per card name and cached mutable QCache setCodeCache; @@ -42,7 +45,8 @@ private: // Draw all mana pips for a cost string like "2RG" or "{2}{R}{G}"; split and // adventure costs ("1W // W") are drawn as separate groups. Returns the left-most x used - int drawManaCost(QPainter *p, const QRect &row, const QString &manaCost, int radius) const; + int + drawManaCost(QPainter *p, const QRect &row, const QString &manaCost, int radius, int spacing, int rightPad) const; // Resolve the preferred printing's set short code for a card QString setCodeForCard(const QSharedPointer &card) const; diff --git a/cockatrice/src/interface/widgets/utility/card_completer_styler.cpp b/cockatrice/src/interface/widgets/utility/card_completer_styler.cpp index 6794ccbb4..c2dbf299c 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_styler.cpp +++ b/cockatrice/src/interface/widgets/utility/card_completer_styler.cpp @@ -10,8 +10,11 @@ #include #include #include +#include #include +#include #include +#include #include #include #include @@ -86,6 +89,7 @@ bool CardCompleterStyler::eventFilter(QObject *obj, QEvent *ev) case QEvent::Show: case QEvent::Move: case QEvent::Resize: + applyPopupWidth(); updateOrientation(); reposition(); break; @@ -140,6 +144,43 @@ bool CardCompleterStyler::handlePopupKeyPress(QKeyEvent *event) // --------------------------------------------------------------------------- +void CardCompleterStyler::applyPopupWidth() +{ + auto *popup = completer->popup(); + + // QCompleter sizes its popup to the line edit, so a narrow search field + // would crush the rows. Ask for the width the rows are designed for (plus + // the frame and scrollbar) and let QWidget's geometry clamping keep the + // popup that wide, clamped to the screen it is shown on. + int contentWidth = CardCompleterDelegate::PopupRowWidth + popup->frameWidth() * 2; + if (auto *vbar = popup->verticalScrollBar(); vbar != nullptr) { + contentWidth += vbar->sizeHint().width(); + } + + const QScreen *screen = popup->screen(); + const QRect available = screen ? screen->availableGeometry() : QRect(); + + const int minWidth = available.isEmpty() ? contentWidth : qMin(contentWidth, available.width()); + popup->setMinimumWidth(minWidth); + + // Widen the popup right away on the first show; QCompleter has already + // positioned it for the narrow size, so keep it on screen afterwards. + if (popup->width() < minWidth) { + popup->resize(minWidth, popup->height()); + + if (!available.isEmpty()) { + // Clamp in screen coordinates, then map back to parent coordinates. + QPoint globalPos = popup->mapToGlobal(QPoint(0, 0)); + int clampedGlobalX = + qBound(available.left(), globalPos.x(), qMax(available.left(), available.right() - popup->width() + 1)); + QPoint parentPos = popup->mapFromGlobal(QPoint(clampedGlobalX, globalPos.y())); + popup->move(parentPos); + } + } +} + +// --------------------------------------------------------------------------- + void CardCompleterStyler::updateOrientation() { above = isPopupAboveWidget(); diff --git a/cockatrice/src/interface/widgets/utility/card_completer_styler.h b/cockatrice/src/interface/widgets/utility/card_completer_styler.h index 616e3fe61..e2b360d1e 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_styler.h +++ b/cockatrice/src/interface/widgets/utility/card_completer_styler.h @@ -51,6 +51,7 @@ private: void stopPreviewFade(); void reposition(); + void applyPopupWidth(); void updateOrientation(); void ensureClosestSelected();