Refactor Card Info Widgets (#5176)

* Refactor CardInfo Widgets to reside in their appropriate folder and to have a clearer naming structure.

* Add optional HoverToZoom functionality to CardInfoPictureWidget (default: disabled) and utility class to display text over a CardInfoPictureWidget.

* Patch CardInfoWidgets to use the new CardDatabaseManager.

* Add HoverToZoom to CardInfoPictureWithTextOverlayWidget

* Refactors and new signals for CardInfoPictureWidgets.

* Address pull request comments (nullptr checks and additional comments, mostly.)

* Reformat code so the linter will stop yelling at me.

* Linting.

* Fix the build.

* Fix warnings.

* Formatting, const qualifiers.

* Sensibly call the base class's (QWidget) paint event.

* Address PR comments (card picture).

* QT Version check because enterEvent signature changed.

* Linting.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL
2024-11-17 16:49:22 +01:00
committed by GitHub
parent c2fe3cda35
commit c8336df49d
24 changed files with 785 additions and 177 deletions

View File

@@ -20,10 +20,10 @@ set(cockatrice_SOURCES
src/game/cards/card_database_parser/cockatrice_xml_4.cpp
src/game/cards/card_drag_item.cpp
src/game/filters/filter_card.cpp
src/game/cards/card_frame.cpp
src/game/cards/card_info_picture.cpp
src/game/cards/card_info_text.cpp
src/game/cards/card_info_widget.cpp
src/client/ui/widgets/cards/card_info_frame_widget.cpp
src/client/ui/widgets/cards/card_info_picture_widget.cpp
src/client/ui/widgets/cards/card_info_text_widget.cpp
src/client/ui/widgets/cards/card_info_display_widget.cpp
src/game/cards/card_item.cpp
src/game/cards/card_list.cpp
src/game/zones/card_zone.cpp
@@ -72,6 +72,8 @@ set(cockatrice_SOURCES
src/server/local_server.cpp
src/server/local_server_interface.cpp
src/utility/logger.cpp
src/client/ui/widgets/cards/card_info_picture_enlarged_widget.cpp
src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
src/main.cpp
src/server/message_log_widget.cpp
src/server/pending_command.cpp

View File

@@ -1,6 +1,6 @@
#include "tab.h"
#include "../../game/cards/card_info_widget.h"
#include "../ui/widgets/cards/card_info_display_widget.h"
#include <QApplication>
#include <QDebug>
@@ -18,7 +18,7 @@ void Tab::showCardInfoPopup(const QPoint &pos, const QString &cardName)
infoPopup->deleteLater();
}
currentCardName = cardName;
infoPopup = new CardInfoWidget(
infoPopup = new CardInfoDisplayWidget(
cardName, 0, Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint);
infoPopup->setAttribute(Qt::WA_TransparentForMouseEvents);

View File

@@ -5,7 +5,7 @@
class QMenu;
class TabSupervisor;
class CardInfoWidget;
class CardInfoDisplayWidget;
class Tab : public QMainWindow
{
@@ -27,7 +27,7 @@ protected slots:
private:
QString currentCardName;
bool contentsChanged;
CardInfoWidget *infoPopup;
CardInfoDisplayWidget *infoPopup;
QList<QMenu *> tabMenus;
public:

View File

@@ -2,12 +2,12 @@
#include "../../client/game_logic/abstract_client.h"
#include "../../client/tapped_out_interface.h"
#include "../../client/ui/widgets/cards/card_info_frame_widget.h"
#include "../../deck/deck_list_model.h"
#include "../../deck/deck_stats_interface.h"
#include "../../dialogs/dlg_load_deck_from_clipboard.h"
#include "../../game/cards/card_database_manager.h"
#include "../../game/cards/card_database_model.h"
#include "../../game/cards/card_frame.h"
#include "../../game/filters/filter_builder.h"
#include "../../game/filters/filter_tree_model.h"
#include "../../main.h"
@@ -187,7 +187,7 @@ void TabDeckEditor::createDeckDock()
void TabDeckEditor::createCardInfoDock()
{
cardInfo = new CardFrame();
cardInfo = new CardInfoFrameWidget();
cardInfo->setObjectName("cardInfo");
auto *cardInfoFrame = new QVBoxLayout;
cardInfoFrame->setObjectName("cardInfoFrame");

View File

@@ -14,7 +14,7 @@ class CardDatabaseDisplayModel;
class DeckListModel;
class QTreeView;
class CardFrame;
class CardInfoFrameWidget;
class QTextEdit;
class QLabel;
class DeckLoader;
@@ -113,7 +113,7 @@ private:
QTreeView *deckView;
KeySignals deckViewKeySignals;
CardFrame *cardInfo;
CardInfoFrameWidget *cardInfo;
SearchLineEdit *searchEdit;
KeySignals searchKeySignals;

View File

@@ -1,5 +1,6 @@
#include "tab_game.h"
#include "../../client/ui/widgets/cards/card_info_frame_widget.h"
#include "../../deck/deck_loader.h"
#include "../../deck/deck_view.h"
#include "../../dialogs/dlg_create_game.h"
@@ -8,7 +9,6 @@
#include "../../game/board/arrow_item.h"
#include "../../game/cards/card_database.h"
#include "../../game/cards/card_database_manager.h"
#include "../../game/cards/card_frame.h"
#include "../../game/cards/card_item.h"
#include "../../game/game_scene.h"
#include "../../game/game_view.h"
@@ -597,7 +597,7 @@ void TabGame::retranslateUi()
aResetLayout->setText(tr("Reset layout"));
cardInfo->retranslateUi();
cardInfoFrameWidget->retranslateUi();
QMapIterator<int, Player *> i(players);
while (i.hasNext())
@@ -1380,7 +1380,7 @@ void TabGame::eventSetActivePhase(const Event_SetActivePhase &event,
void TabGame::newCardAdded(AbstractCardItem *card)
{
connect(card, SIGNAL(hovered(AbstractCardItem *)), cardInfo, SLOT(setCard(AbstractCardItem *)));
connect(card, SIGNAL(hovered(AbstractCardItem *)), cardInfoFrameWidget, SLOT(setCard(AbstractCardItem *)));
connect(card, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(card, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
connect(card, SIGNAL(cardShiftClicked(QString)), this, SLOT(linkCardToChat(QString)));
@@ -1809,18 +1809,18 @@ void TabGame::createDeckViewContainerWidget(bool bReplay)
void TabGame::viewCardInfo(const QString &cardName)
{
cardInfo->setCard(cardName);
cardInfoFrameWidget->setCard(cardName);
}
void TabGame::createCardInfoDock(bool bReplay)
{
Q_UNUSED(bReplay);
cardInfo = new CardFrame();
cardInfoFrameWidget = new CardInfoFrameWidget();
cardHInfoLayout = new QHBoxLayout;
cardVInfoLayout = new QVBoxLayout;
cardVInfoLayout->setContentsMargins(0, 0, 0, 0);
cardVInfoLayout->addWidget(cardInfo);
cardVInfoLayout->addWidget(cardInfoFrameWidget);
cardVInfoLayout->addLayout(cardHInfoLayout);
cardBoxLayoutWidget = new QWidget;
@@ -1862,7 +1862,7 @@ void TabGame::createPlayerListDock(bool bReplay)
void TabGame::createMessageDock(bool bReplay)
{
messageLog = new MessageLogWidget(tabSupervisor, tabSupervisor, this);
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString)));
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfoFrameWidget, SLOT(setCard(QString)));
connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));

View File

@@ -16,7 +16,7 @@ class CardDatabase;
class GameView;
class DeckView;
class GameScene;
class CardFrame;
class CardInfoFrameWidget;
class MessageLogWidget;
class QTimer;
class QSplitter;
@@ -149,7 +149,7 @@ private:
QToolButton *replayPlayButton, *replayFastForwardButton;
QAction *aReplaySkipForward, *aReplaySkipBackward, *aReplaySkipForwardBig, *aReplaySkipBackwardBig;
CardFrame *cardInfo;
CardInfoFrameWidget *cardInfoFrameWidget;
PlayerListWidget *playerListWidget;
QLabel *timeElapsedLabel;
MessageLogWidget *messageLog;

View File

@@ -1,23 +1,23 @@
#include "card_info_widget.h"
#include "card_info_display_widget.h"
#include "../../main.h"
#include "card_database_manager.h"
#include "card_info_picture.h"
#include "card_info_text.h"
#include "card_item.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/cards/card_item.h"
#include "../../../../main.h"
#include "card_info_picture_widget.h"
#include "card_info_text_widget.h"
#include <QApplication>
#include <QScreen>
#include <QVBoxLayout>
#include <utility>
CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::WindowFlags flags)
CardInfoDisplayWidget::CardInfoDisplayWidget(const QString &cardName, QWidget *parent, Qt::WindowFlags flags)
: QFrame(parent, flags), aspectRatio((qreal)CARD_HEIGHT / (qreal)CARD_WIDTH), info(nullptr)
{
setContentsMargins(3, 3, 3, 3);
pic = new CardInfoPicture();
pic = new CardInfoPictureWidget();
pic->setObjectName("pic");
text = new CardInfoText();
text = new CardInfoTextWidget();
text->setObjectName("text");
connect(text, SIGNAL(linkActivated(const QString &)), this, SLOT(setCard(const QString &)));
@@ -43,7 +43,7 @@ CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::Win
resize(width(), sizeHint().height());
}
void CardInfoWidget::setCard(CardInfoPtr card)
void CardInfoDisplayWidget::setCard(CardInfoPtr card)
{
if (info)
disconnect(info.data(), nullptr, this, nullptr);
@@ -55,7 +55,7 @@ void CardInfoWidget::setCard(CardInfoPtr card)
pic->setCard(info);
}
void CardInfoWidget::setCard(const QString &cardName)
void CardInfoDisplayWidget::setCard(const QString &cardName)
{
setCard(CardDatabaseManager::getInstance()->guessCard(cardName));
if (info == nullptr) {
@@ -63,12 +63,12 @@ void CardInfoWidget::setCard(const QString &cardName)
}
}
void CardInfoWidget::setCard(AbstractCardItem *card)
void CardInfoDisplayWidget::setCard(AbstractCardItem *card)
{
setCard(card->getInfo());
}
void CardInfoWidget::clear()
void CardInfoDisplayWidget::clear()
{
setCard((CardInfoPtr) nullptr);
}

View File

@@ -1,28 +1,28 @@
#ifndef CARDINFOWIDGET_H
#define CARDINFOWIDGET_H
#include "card_database.h"
#include "../../../../game/cards/card_database.h"
#include <QComboBox>
#include <QFrame>
#include <QStringList>
class CardInfoPicture;
class CardInfoText;
class CardInfoPictureWidget;
class CardInfoTextWidget;
class AbstractCardItem;
class CardInfoWidget : public QFrame
class CardInfoDisplayWidget : public QFrame
{
Q_OBJECT
private:
qreal aspectRatio;
CardInfoPtr info;
CardInfoPicture *pic;
CardInfoText *text;
CardInfoPictureWidget *pic;
CardInfoTextWidget *text;
public:
explicit CardInfoWidget(const QString &cardName, QWidget *parent = nullptr, Qt::WindowFlags f = {});
explicit CardInfoDisplayWidget(const QString &cardName, QWidget *parent = nullptr, Qt::WindowFlags f = {});
public slots:
void setCard(CardInfoPtr card);

View File

@@ -1,22 +1,22 @@
#include "card_frame.h"
#include "card_info_frame_widget.h"
#include "../../main.h"
#include "../../settings/cache_settings.h"
#include "card_database_manager.h"
#include "card_info_picture.h"
#include "card_info_text.h"
#include "card_item.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/cards/card_item.h"
#include "../../../../settings/cache_settings.h"
#include "card_info_picture_widget.h"
#include "card_info_text_widget.h"
#include <QSplitter>
#include <QVBoxLayout>
#include <utility>
CardFrame::CardFrame(const QString &cardName, QWidget *parent) : QTabWidget(parent), info(nullptr), cardTextOnly(false)
CardInfoFrameWidget::CardInfoFrameWidget(const QString &cardName, QWidget *parent)
: QTabWidget(parent), info(nullptr), cardTextOnly(false)
{
setContentsMargins(3, 3, 3, 3);
pic = new CardInfoPicture();
pic = new CardInfoPictureWidget();
pic->setObjectName("pic");
text = new CardInfoText();
text = new CardInfoTextWidget();
text->setObjectName("text");
connect(text, SIGNAL(linkActivated(const QString &)), this, SLOT(setCard(const QString &)));
@@ -61,14 +61,14 @@ CardFrame::CardFrame(const QString &cardName, QWidget *parent) : QTabWidget(pare
setCard(CardDatabaseManager::getInstance()->getCard(cardName));
}
void CardFrame::retranslateUi()
void CardInfoFrameWidget::retranslateUi()
{
setTabText(ImageOnlyView, tr("Image"));
setTabText(TextOnlyView, tr("Description"));
setTabText(ImageAndTextView, tr("Both"));
}
void CardFrame::setViewMode(int mode)
void CardInfoFrameWidget::setViewMode(int mode)
{
if (currentIndex() != mode)
setCurrentIndex(mode);
@@ -90,7 +90,7 @@ void CardFrame::setViewMode(int mode)
SettingsCache::instance().setCardInfoViewMode(mode);
}
void CardFrame::setCard(CardInfoPtr card)
void CardInfoFrameWidget::setCard(CardInfoPtr card)
{
if (info) {
disconnect(info.data(), nullptr, this, nullptr);
@@ -106,19 +106,19 @@ void CardFrame::setCard(CardInfoPtr card)
pic->setCard(info);
}
void CardFrame::setCard(const QString &cardName)
void CardInfoFrameWidget::setCard(const QString &cardName)
{
setCard(CardDatabaseManager::getInstance()->guessCard(cardName));
}
void CardFrame::setCard(AbstractCardItem *card)
void CardInfoFrameWidget::setCard(AbstractCardItem *card)
{
if (card) {
setCard(card->getInfo());
}
}
void CardFrame::clearCard()
void CardInfoFrameWidget::clearCard()
{
setCard((CardInfoPtr) nullptr);
}

View File

@@ -1,23 +1,23 @@
#ifndef CARDFRAME_H
#define CARDFRAME_H
#include "card_database.h"
#include "../../../../game/cards/card_database.h"
#include <QTabWidget>
class AbstractCardItem;
class CardInfoPicture;
class CardInfoText;
class CardInfoPictureWidget;
class CardInfoTextWidget;
class QVBoxLayout;
class QSplitter;
class CardFrame : public QTabWidget
class CardInfoFrameWidget : public QTabWidget
{
Q_OBJECT
private:
CardInfoPtr info;
CardInfoPicture *pic;
CardInfoText *text;
CardInfoPictureWidget *pic;
CardInfoTextWidget *text;
bool cardTextOnly;
QWidget *tab1, *tab2, *tab3;
QVBoxLayout *tab1Layout, *tab2Layout, *tab3Layout;
@@ -30,7 +30,7 @@ public:
TextOnlyView,
ImageAndTextView
};
explicit CardFrame(const QString &cardName = QString(), QWidget *parent = nullptr);
explicit CardInfoFrameWidget(const QString &cardName = QString(), QWidget *parent = nullptr);
void retranslateUi();
public slots:

View File

@@ -0,0 +1,95 @@
#include "card_info_picture_enlarged_widget.h"
#include "../../picture_loader.h"
#include <QPainterPath>
#include <QStylePainter>
#include <utility>
/**
* @brief Constructs a CardPictureEnlargedWidget.
* @param parent The parent widget.
*
* Sets the widget's window flags to keep it displayed as a tooltip overlay.
*/
CardInfoPictureEnlargedWidget::CardInfoPictureEnlargedWidget(QWidget *parent)
: QWidget(parent), pixmapDirty(true), info(nullptr)
{
setWindowFlags(Qt::ToolTip); // Keeps this widget on top of everything
setAttribute(Qt::WA_TranslucentBackground);
}
/**
* @brief Loads the pixmap based on the given size and card information.
* @param size The desired size for the loaded pixmap.
*
* If card information is available, it loads the card's specific pixmap. Otherwise, it loads a default card back
* pixmap.
*/
void CardInfoPictureEnlargedWidget::loadPixmap(const QSize &size)
{
if (info) {
PictureLoader::getPixmap(enlargedPixmap, info, size);
} else {
PictureLoader::getCardBackPixmap(enlargedPixmap, size);
}
pixmapDirty = false;
}
/**
* @brief Sets the pixmap for the widget based on a provided card.
* @param card The card information to load.
* @param size The desired size for the pixmap.
*
* Sets the widget's pixmap to the card image and resizes the widget to match the specified size. Triggers a repaint.
*/
void CardInfoPictureEnlargedWidget::setCardPixmap(CardInfoPtr card, const QSize size)
{
info = std::move(card);
loadPixmap(size);
setFixedSize(size); // Set the widget size to the enlarged size
update(); // Trigger a repaint
}
/**
* @brief Custom paint event that draws the enlarged card image with rounded corners.
* @param event The paint event (unused).
*
* Checks if the pixmap is valid. Then, calculates the size and position for centering the
* scaled pixmap within the widget, applies rounded corners, and draws the pixmap.
*/
void CardInfoPictureEnlargedWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
if (width() == 0 || height() == 0 || enlargedPixmap.isNull()) {
return;
}
if (pixmapDirty) {
loadPixmap(size());
}
// Scale the size of the pixmap to fit the widget while maintaining the aspect ratio
QSize scaledSize = enlargedPixmap.size().scaled(size().width(), size().height(), Qt::KeepAspectRatio);
// Calculate the position to center the scaled pixmap
QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2};
// Define the radius for rounded corners
qreal radius = 0.05 * scaledSize.width(); // Adjust the radius as needed for rounded corners
QStylePainter painter(this);
// Fill the background with transparent color to ensure rounded corners are rendered properly
painter.fillRect(rect(), Qt::transparent); // Use the transparent background
QPainterPath shape;
shape.addRoundedRect(QRect(topLeft, scaledSize), radius, radius);
painter.setClipPath(shape); // Set the clipping path
// Draw the pixmap scaled to the calculated size
painter.drawItemPixmap(QRect(topLeft, scaledSize), Qt::AlignCenter,
enlargedPixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
}

View File

@@ -0,0 +1,38 @@
#ifndef CARD_PICTURE_ENLARGED_WIDGET_H
#define CARD_PICTURE_ENLARGED_WIDGET_H
#include "../../../../game/cards/card_database.h"
#include <QPixmap>
#include <QWidget>
class CardInfoPictureEnlargedWidget final : public QWidget
{
Q_OBJECT
public:
// Constructor
explicit CardInfoPictureEnlargedWidget(QWidget *parent = nullptr);
// Sets the card pixmap to display
void setCardPixmap(CardInfoPtr card, QSize size);
protected:
// Handles the painting event for the enlarged card
void paintEvent(QPaintEvent *event) override;
private:
// Cached pixmap for the enlarged card
QPixmap enlargedPixmap;
// Tracks if the pixmap needs to be refreshed/redrawn
bool pixmapDirty;
// Card information (card data pointer)
CardInfoPtr info;
// Loads the enlarged card pixmap
void loadPixmap(const QSize &size);
};
#endif // CARD_PICTURE_ENLARGED_WIDGET_H

View File

@@ -0,0 +1,238 @@
#include "card_info_picture_widget.h"
#include "../../../../game/cards/card_item.h"
#include "../../picture_loader.h"
#include <QMouseEvent>
#include <QStylePainter>
#include <QWidget>
#include <utility>
/**
* @class CardInfoPictureWidget
* @brief Widget that displays an enlarged image of a card, loading the image based on the card's info or showing a
* default image.
*
* This widget can optionally display a larger version of the card's image when hovered over,
* depending on the `hoverToZoomEnabled` parameter.
*/
/**
* @brief Constructs a CardInfoPictureWidget.
* @param parent The parent widget, if any.
* @param hoverToZoomEnabled If this widget will spawn a larger widget when hovered over.
*
* Initializes the widget with a minimum height and sets the pixmap to a dirty state for initial loading.
*/
CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool hoverToZoomEnabled)
: QWidget(parent), info(nullptr), pixmapDirty(true), hoverToZoomEnabled(hoverToZoomEnabled)
{
setMinimumHeight(baseHeight);
if (hoverToZoomEnabled) {
setMouseTracking(true);
}
enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(this);
enlargedPixmapWidget->hide();
hoverTimer = new QTimer(this);
hoverTimer->setSingleShot(true);
connect(hoverTimer, &QTimer::timeout, this, &CardInfoPictureWidget::showEnlargedPixmap);
}
/**
* @brief Sets the card to be displayed and updates the pixmap.
* @param card A shared pointer to the card information (CardInfoPtr).
*
* Disconnects any existing signal connections from the previous card info and connects to the `pixmapUpdated`
* signal of the new card to automatically update the pixmap when the card image changes.
*/
void CardInfoPictureWidget::setCard(CardInfoPtr card)
{
if (info) {
disconnect(info.data(), nullptr, this, nullptr);
}
info = std::move(card);
if (info) {
connect(info.data(), SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap()));
}
updatePixmap();
}
/**
* @brief Sets the hover to zoom feature.
* @param enabled If true, enables the hover-to-zoom functionality; otherwise, disables it.
*/
void CardInfoPictureWidget::setHoverToZoomEnabled(const bool enabled)
{
hoverToZoomEnabled = enabled;
setMouseTracking(enabled);
}
/**
* @brief Handles widget resizing by updating the pixmap size.
* @param event The resize event (unused).
*
* Calls `updatePixmap()` to ensure the image scales appropriately when the widget is resized.
*/
void CardInfoPictureWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
updatePixmap();
}
/**
* @brief Sets the scale factor for the widget.
* @param scale The scale factor to apply.
*
* Adjusts the widget's size according to the scale factor and updates the pixmap.
*/
void CardInfoPictureWidget::setScaleFactor(const int scale)
{
const int newWidth = baseWidth + scale * 20;
const int newHeight = static_cast<int>(newWidth * aspectRatio);
scaleFactor = scale;
setFixedSize(newWidth, newHeight);
updatePixmap();
}
/**
* @brief Marks the pixmap as dirty and triggers a widget repaint.
*
* Sets `pixmapDirty` to true, indicating that the pixmap needs to be reloaded before the next display.
*/
void CardInfoPictureWidget::updatePixmap()
{
pixmapDirty = true;
update();
}
/**
* @brief Loads the appropriate pixmap based on the current card info.
*
* If `info` is valid, loads the card's image. Otherwise, loads a default card back image.
*/
void CardInfoPictureWidget::loadPixmap()
{
if (info) {
PictureLoader::getPixmap(resizedPixmap, info, size());
} else {
PictureLoader::getCardBackPixmap(resizedPixmap, size());
}
pixmapDirty = false;
}
/**
* @brief Custom paint event that draws the card image with rounded corners.
* @param event The paint event (unused).
*
* Checks if the pixmap needs to be reloaded. Then, calculates the size and position for centering the
* scaled pixmap within the widget, applies rounded corners, and draws the pixmap.
*/
void CardInfoPictureWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (width() == 0 || height() == 0) {
return;
}
if (pixmapDirty) {
loadPixmap();
}
const QSize scaledSize = resizedPixmap.size().scaled(size(), Qt::KeepAspectRatio);
const QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2};
const qreal radius = 0.05 * scaledSize.width();
QStylePainter painter(this);
QPainterPath shape;
shape.addRoundedRect(QRect(topLeft, scaledSize), radius, radius);
painter.setClipPath(shape);
painter.drawItemPixmap(QRect(topLeft, scaledSize), Qt::AlignCenter, resizedPixmap);
}
/**
* @brief Provides the recommended size for the widget based on the scale factor.
* @return The recommended widget size.
*/
QSize CardInfoPictureWidget::sizeHint() const
{
return {static_cast<int>(baseWidth * scaleFactor), static_cast<int>(baseHeight * scaleFactor)};
}
/**
* @brief Starts the hover timer to show the enlarged pixmap on hover.
* @param event The enter event.
*/
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void CardInfoPictureWidget::enterEvent(QEnterEvent *event)
#else
void CardInfoPictureWidget::enterEvent(QEvent *event)
#endif
{
QWidget::enterEvent(event); // Call the base class implementation
// If hover-to-zoom is enabled, start the hover timer
if (hoverToZoomEnabled) {
hoverTimer->start(hoverActivateThresholdInMs);
}
// Emit signal indicating a card is being hovered on
emit hoveredOnCard(info);
}
/**
* @brief Stops the hover timer and hides the enlarged pixmap when the mouse leaves.
* @param event The leave event.
*/
void CardInfoPictureWidget::leaveEvent(QEvent *event)
{
QWidget::leaveEvent(event);
if (hoverToZoomEnabled) {
hoverTimer->stop();
enlargedPixmapWidget->hide();
}
}
/**
* @brief Moves the enlarged pixmap widget to follow the mouse cursor.
* @param event The mouse move event.
*/
void CardInfoPictureWidget::mouseMoveEvent(QMouseEvent *event)
{
QWidget::mouseMoveEvent(event);
if (hoverToZoomEnabled && enlargedPixmapWidget->isVisible()) {
const QPointF cursorPos = QCursor::pos();
enlargedPixmapWidget->move(QPoint(static_cast<int>(cursorPos.x()) + enlargedPixmapOffset,
static_cast<int>(cursorPos.y()) + enlargedPixmapOffset));
}
}
/**
* @brief Displays the enlarged version of the card's pixmap near the cursor.
*
* If card information is available, the enlarged pixmap is loaded, positioned near the cursor,
* and displayed.
*/
void CardInfoPictureWidget::showEnlargedPixmap() const
{
if (!info) {
return;
}
const QSize enlargedSize(static_cast<int>(size().width() * scaleFactor),
static_cast<int>(size().width() * aspectRatio * scaleFactor));
enlargedPixmapWidget->setCardPixmap(info, enlargedSize);
const QPointF cursorPos = QCursor::pos();
enlargedPixmapWidget->move(static_cast<int>(cursorPos.x()) + enlargedPixmapOffset,
static_cast<int>(cursorPos.y()) + enlargedPixmapOffset);
enlargedPixmapWidget->show();
}

View File

@@ -0,0 +1,67 @@
#ifndef CARD_INFO_PICTURE_H
#define CARD_INFO_PICTURE_H
#include "../../../../game/cards/card_database.h"
#include "card_info_picture_enlarged_widget.h"
#include <QTimer>
#include <QWidget>
class AbstractCardItem;
class CardInfoPictureWidget : public QWidget
{
Q_OBJECT
public:
explicit CardInfoPictureWidget(QWidget *parent = nullptr, bool hoverToZoomEnabled = false);
CardInfoPtr getInfo()
{
return info;
}
[[nodiscard]] QSize sizeHint() const override;
void setHoverToZoomEnabled(bool enabled);
public slots:
void setCard(CardInfoPtr card);
void setScaleFactor(int scale); // New slot for scaling
void updatePixmap();
signals:
void hoveredOnCard(CardInfoPtr hoveredCard);
protected:
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *) override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent *event) override; // Qt6 signature
#else
void enterEvent(QEvent *event) override; // Qt5 signature
#endif
void leaveEvent(QEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void loadPixmap();
[[nodiscard]] const QPixmap &getResizedPixmap() const
{
return resizedPixmap;
}
void showEnlargedPixmap() const;
private:
CardInfoPtr info;
qreal magicTheGatheringCardAspectRatio = 1.396;
qreal yuGiOhCardAspectRatio = 1.457;
qreal aspectRatio = magicTheGatheringCardAspectRatio;
int baseWidth = 200;
int baseHeight = 200;
double scaleFactor = 1.5;
QPixmap resizedPixmap;
bool pixmapDirty;
bool hoverToZoomEnabled;
int hoverActivateThresholdInMs = 500;
CardInfoPictureEnlargedWidget *enlargedPixmapWidget;
int enlargedPixmapOffset = 10;
QTimer *hoverTimer;
};
#endif

View File

@@ -0,0 +1,216 @@
#include "card_info_picture_with_text_overlay_widget.h"
#include <QFontMetrics>
#include <QPainterPath>
#include <QStylePainter>
#include <QTextOption>
/**
* @brief Constructs a CardPictureWithTextOverlay widget.
* @param parent The parent widget.
* @param hoverToZoomEnabled If this widget will spawn a larger widget when hovered over.
* @param textColor The color of the overlay text.
* @param outlineColor The color of the outline around the text.
* @param fontSize The font size of the overlay text.
* @param alignment The alignment of the text within the overlay.
*
* Sets the widget's size policy and default border style.
*/
CardInfoPictureWithTextOverlayWidget::CardInfoPictureWithTextOverlayWidget(QWidget *parent,
const bool hoverToZoomEnabled,
const QColor &textColor,
const QColor &outlineColor,
const int fontSize,
const Qt::Alignment alignment)
: CardInfoPictureWidget(parent, hoverToZoomEnabled), textColor(textColor), outlineColor(outlineColor),
fontSize(fontSize), textAlignment(alignment)
{
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
/**
* @brief Sets the overlay text to be displayed on the card.
* @param text The text to overlay.
*
* Updates the widget to display the new overlay text.
*/
void CardInfoPictureWithTextOverlayWidget::setOverlayText(const QString &text)
{
overlayText = text;
update(); // Trigger a redraw to display the updated text
}
/**
* @brief Sets the color of the overlay text.
* @param color The new text color.
*/
void CardInfoPictureWithTextOverlayWidget::setTextColor(const QColor &color)
{
textColor = color;
update();
}
/**
* @brief Sets the outline color around the overlay text.
* @param color The new outline color.
*/
void CardInfoPictureWithTextOverlayWidget::setOutlineColor(const QColor &color)
{
outlineColor = color;
update();
}
/**
* @brief Sets the font size for the overlay text.
* @param size The new font size.
*/
void CardInfoPictureWithTextOverlayWidget::setFontSize(const int size)
{
fontSize = size;
update();
}
/**
* @brief Sets the alignment of the overlay text within the widget.
* @param alignment The new text alignment.
*/
void CardInfoPictureWithTextOverlayWidget::setTextAlignment(const Qt::Alignment alignment)
{
textAlignment = alignment;
update();
}
void CardInfoPictureWithTextOverlayWidget::mousePressEvent(QMouseEvent *event)
{
emit imageClicked(event, this);
}
/**
* @brief Paints the widget, including both the card image and the text overlay.
* @param event The paint event.
*
* Draws the card image first, then overlays text on top. The text is wrapped and centered within the image.
*/
void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
{
// Call the base class's paintEvent to draw the card image
CardInfoPictureWidget::paintEvent(event);
// Now add the custom text overlay on top of the image
if (overlayText.isEmpty()) {
return;
}
QStylePainter painter(this);
// Set text properties
QFont font = painter.font();
font.setPointSize(fontSize);
painter.setFont(font);
// Get the pixmap from the base class using the getter
const QPixmap &pixmap = getResizedPixmap();
if (!pixmap.isNull()) {
// Calculate size and position for drawing
const QSize scaledSize = pixmap.size().scaled(size(), Qt::KeepAspectRatio);
const QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2};
const QRect pixmapRect(topLeft, scaledSize);
// Prepare text wrapping
const QFontMetrics fontMetrics(font);
const int lineHeight = fontMetrics.height();
const int textWidth = pixmapRect.width();
QString wrappedText;
// Break the text into multiple lines to fit within the pixmap width
QString currentLine;
QStringList words = overlayText.split(' ');
for (const QString &word : words) {
if (fontMetrics.horizontalAdvance(currentLine + " " + word) > textWidth) {
wrappedText += currentLine + '\n';
currentLine = word;
} else {
if (!currentLine.isEmpty()) {
currentLine += " ";
}
currentLine += word;
}
}
wrappedText += currentLine;
// Calculate total text block height
const int totalTextHeight = static_cast<int>(wrappedText.count('\n')) * lineHeight + lineHeight;
// Set up the text layout options
QTextOption textOption;
textOption.setAlignment(textAlignment);
// Create a text rectangle centered within the pixmap rect
auto textRect = QRect(pixmapRect.left(), pixmapRect.top(), pixmapRect.width(), totalTextHeight);
textRect.moveTop((pixmapRect.height() - totalTextHeight) / 2 + pixmapRect.top());
// Draw the outlined text
drawOutlinedText(painter, textRect, wrappedText, textOption);
}
}
/**
* @brief Draws text with an outline for visibility.
* @param painter The painter to draw the text.
* @param textRect The rectangle area to draw the text in.
* @param text The text to display.
* @param textOption The text layout options, such as alignment.
*
* Draws an outline around the text to enhance readability before drawing the main text.
*/
void CardInfoPictureWithTextOverlayWidget::drawOutlinedText(QPainter &painter,
const QRect &textRect,
const QString &text,
const QTextOption &textOption) const
{
// Draw the black outline (outlineColor)
painter.setPen(outlineColor);
for (int dx = -1; dx <= 1; ++dx) {
for (int dy = -1; dy <= 1; ++dy) {
if (dx != 0 || dy != 0) {
QRect shiftedTextRect = textRect.translated(dx, dy);
painter.drawText(shiftedTextRect, text, textOption);
}
}
}
// Draw the main text (textColor)
painter.setPen(textColor);
painter.drawText(textRect, text, textOption);
}
/**
* @brief Provides the recommended size for this widget.
* @return The suggested widget size.
*/
QSize CardInfoPictureWithTextOverlayWidget::sizeHint() const
{
return CardInfoPictureWidget::sizeHint();
}
/**
* @brief Provides the minimum recommended size for this widget.
* @return The minimum widget size.
*/
QSize CardInfoPictureWithTextOverlayWidget::minimumSizeHint() const
{
// Same as sizeHint, but ensure that there is at least some space for the pixmap
const QPixmap &pixmap = getResizedPixmap();
const QSize pixmapSize = pixmap.isNull() ? QSize(0, 0) : pixmap.size();
// Get the font metrics for the overlay text
QFont font;
font.setPointSize(fontSize);
const QFontMetrics fontMetrics(font);
// Calculate the height required for the text
const QStringList lines = overlayText.split('\n');
const int totalTextHeight = static_cast<int>(lines.size()) * fontMetrics.height();
// Return the maximum width and combined height
return {pixmapSize.width(), pixmapSize.height() + totalTextHeight};
}

View File

@@ -0,0 +1,50 @@
#ifndef CARD_PICTURE_WITH_TEXT_OVERLAY_H
#define CARD_PICTURE_WITH_TEXT_OVERLAY_H
#include "card_info_picture_widget.h"
#include <QColor>
#include <QSize>
#include <QTextOption>
class CardInfoPictureWithTextOverlayWidget final : public CardInfoPictureWidget
{
Q_OBJECT
public:
explicit CardInfoPictureWithTextOverlayWidget(QWidget *parent = nullptr,
bool hoverToZoomEnabled = false,
const QColor &textColor = Qt::white,
const QColor &outlineColor = Qt::black,
int fontSize = 12,
Qt::Alignment alignment = Qt::AlignCenter);
void setOverlayText(const QString &text);
void setTextColor(const QColor &color);
void setOutlineColor(const QColor &color);
void setFontSize(int size);
void setTextAlignment(Qt::Alignment alignment);
[[nodiscard]] QSize sizeHint() const override;
signals:
void imageClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
[[nodiscard]] QSize minimumSizeHint() const override;
private:
void drawOutlinedText(QPainter &painter,
const QRect &textRect,
const QString &text,
const QTextOption &textOption) const;
QString overlayText;
QColor textColor;
QColor outlineColor;
int fontSize;
Qt::Alignment textAlignment;
};
#endif // CARD_PICTURE_WITH_TEXT_OVERLAY_H

View File

@@ -1,14 +1,13 @@
#include "card_info_text.h"
#include "card_info_text_widget.h"
#include "../../game/game_specific_terms.h"
#include "../../main.h"
#include "card_item.h"
#include "../../../../game/cards/card_item.h"
#include "../../../../game/game_specific_terms.h"
#include <QGridLayout>
#include <QLabel>
#include <QTextEdit>
CardInfoText::CardInfoText(QWidget *parent) : QFrame(parent), info(nullptr)
CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent), info(nullptr)
{
nameLabel = new QLabel;
nameLabel->setOpenExternalLinks(false);
@@ -27,7 +26,7 @@ CardInfoText::CardInfoText(QWidget *parent) : QFrame(parent), info(nullptr)
retranslateUi();
}
void CardInfoText::setCard(CardInfoPtr card)
void CardInfoTextWidget::setCard(CardInfoPtr card)
{
if (card == nullptr) {
nameLabel->setText("");
@@ -65,13 +64,13 @@ void CardInfoText::setCard(CardInfoPtr card)
textLabel->setText(card->getText());
}
void CardInfoText::setInvalidCardName(const QString &cardName)
void CardInfoTextWidget::setInvalidCardName(const QString &cardName)
{
nameLabel->setText(tr("Unknown card:") + " " + cardName);
textLabel->setText("");
}
void CardInfoText::retranslateUi()
void CardInfoTextWidget::retranslateUi()
{
/*
* There's no way we can really translate the text currently being rendered.

View File

@@ -1,13 +1,13 @@
#ifndef CARDINFOTEXT_H
#define CARDINFOTEXT_H
#include "card_database.h"
#include "../../../../game/cards/card_database.h"
#include <QFrame>
class QLabel;
class QTextEdit;
class CardInfoText : public QFrame
class CardInfoTextWidget : public QFrame
{
Q_OBJECT
@@ -17,7 +17,7 @@ private:
CardInfoPtr info;
public:
explicit CardInfoText(QWidget *parent = nullptr);
explicit CardInfoTextWidget(QWidget *parent = nullptr);
void retranslateUi();
void setInvalidCardName(const QString &cardName);

View File

@@ -1,8 +1,8 @@
#include "dlg_create_token.h"
#include "../client/ui/widgets/cards/card_info_picture_widget.h"
#include "../game/cards/card_database_manager.h"
#include "../game/cards/card_database_model.h"
#include "../game/cards/card_info_picture.h"
#include "../main.h"
#include "../settings/cache_settings.h"
#include "decklist.h"
@@ -25,7 +25,7 @@
DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *parent)
: QDialog(parent), predefinedTokens(_predefinedTokens)
{
pic = new CardInfoPicture();
pic = new CardInfoPictureWidget();
pic->setObjectName("pic");
nameLabel = new QLabel(tr("&Name:"));

View File

@@ -15,7 +15,7 @@ class QTreeView;
class DeckList;
class CardDatabaseModel;
class TokenDisplayModel;
class CardInfoPicture;
class CardInfoPictureWidget;
class DlgCreateToken : public QDialog
{
@@ -47,7 +47,7 @@ private:
QLineEdit *nameEdit, *ptEdit, *annotationEdit;
QCheckBox *destroyCheckBox;
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
CardInfoPicture *pic;
CardInfoPictureWidget *pic;
QTreeView *chooseTokenView;
void updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const;

View File

@@ -1,66 +0,0 @@
#include "card_info_picture.h"
#include "../../client/ui/picture_loader.h"
#include "../../main.h"
#include "card_item.h"
#include <QStylePainter>
#include <QWidget>
CardInfoPicture::CardInfoPicture(QWidget *parent) : QWidget(parent), info(nullptr), pixmapDirty(true)
{
setMinimumHeight(100);
}
void CardInfoPicture::setCard(CardInfoPtr card)
{
if (info) {
disconnect(info.data(), nullptr, this, nullptr);
}
info = card;
if (info) {
connect(info.data(), SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap()));
}
updatePixmap();
}
void CardInfoPicture::resizeEvent(QResizeEvent *)
{
updatePixmap();
}
void CardInfoPicture::updatePixmap()
{
pixmapDirty = true;
update();
}
void CardInfoPicture::loadPixmap()
{
if (info)
PictureLoader::getPixmap(resizedPixmap, info, size());
else
PictureLoader::getCardBackPixmap(resizedPixmap, size());
}
void CardInfoPicture::paintEvent(QPaintEvent *)
{
if (width() == 0 || height() == 0)
return;
if (pixmapDirty)
loadPixmap();
QSize scaledSize = resizedPixmap.size().scaled(size(), Qt::KeepAspectRatio);
QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2};
qreal radius = 0.05 * scaledSize.width();
QStylePainter painter(this);
QPainterPath shape;
shape.addRoundedRect(QRect(topLeft, scaledSize), radius, radius);
painter.setClipPath(shape);
painter.drawItemPixmap(QRect(topLeft, scaledSize), Qt::AlignCenter, resizedPixmap);
}

View File

@@ -1,31 +0,0 @@
#ifndef CARDINFOPICTURE_H
#define CARDINFOPICTURE_H
#include "card_database.h"
#include <QWidget>
class AbstractCardItem;
class CardInfoPicture : public QWidget
{
Q_OBJECT
private:
CardInfoPtr info;
QPixmap resizedPixmap;
bool pixmapDirty;
public:
CardInfoPicture(QWidget *parent = nullptr);
protected:
void resizeEvent(QResizeEvent *event);
void paintEvent(QPaintEvent *);
void loadPixmap();
public slots:
void setCard(CardInfoPtr card);
void updatePixmap();
};
#endif

2
vcpkg

Submodule vcpkg updated: 8ec31e98fb...bb1ca2757b