diff --git a/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp b/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp index 8042fb9fc..af07590a8 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp +++ b/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp @@ -46,6 +46,22 @@ template class QSharedPointer; 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; @@ -125,7 +141,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}; } // --------------------------------------------------------------------------- @@ -176,7 +192,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(); @@ -205,11 +226,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(); @@ -219,11 +238,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; } } @@ -290,6 +309,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); @@ -338,7 +365,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()); @@ -380,22 +407,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); @@ -416,7 +448,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 ad81d86e4..cef25b783 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_delegate.h +++ b/cockatrice/src/interface/widgets/utility/card_completer_delegate.h @@ -36,6 +36,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; @@ -48,7 +51,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 1ddcc4200..8aa32556f 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_styler.cpp +++ b/cockatrice/src/interface/widgets/utility/card_completer_styler.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,7 @@ bool CardCompleterStyler::eventFilter(QObject *obj, QEvent *ev) case QEvent::Show: case QEvent::Move: case QEvent::Resize: + applyPopupWidth(); updateOrientation(); reposition(); break; @@ -159,6 +161,42 @@ 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); + + // setMinimumWidth() already widened the popup if it was narrower, but + // QCompleter positioned it for the line-edit width, so the widened popup + // can now extend past the right screen edge. The popup is a top-level + // Qt::Popup window, so geometry()/move() are already in screen + // coordinates: clamp the x from geometry() and move directly. + if (!available.isEmpty()) { + const QRect geom = popup->geometry(); + const int clampedX = + qBound(available.left(), geom.x(), qMax(available.left(), available.right() - geom.width() + 1)); + if (clampedX != geom.x()) { + popup->move(QPoint(clampedX, popup->y())); + } + } +} + +// --------------------------------------------------------------------------- + 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 9245c9095..2912f7312 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_styler.h +++ b/cockatrice/src/interface/widgets/utility/card_completer_styler.h @@ -52,6 +52,7 @@ private: void stopPreviewFade(); void reposition(); + void applyPopupWidth(); void updateOrientation(); void ensureClosestSelected();