Visual Deck Editor.

This commit is contained in:
Lukas Brübach 2024-11-21 14:24:14 +01:00
parent 13d18986b2
commit 0c73768c79
32 changed files with 3474 additions and 0 deletions

View file

@ -25,6 +25,8 @@
#include "tab_replays.h"
#include "tab_room.h"
#include "tab_server.h"
#include "visual_deck_editor/tab_deck_editor_visual.h"
#include "visual_deck_editor/tab_deck_editor_visual_tab_widget.h"
#include "visual_deck_storage/tab_deck_storage_visual.h"
#include <QApplication>
@ -804,6 +806,14 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(const DeckLoader *deckToOpen)
return tab;
}
TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab()
{
TabDeckEditorVisual *tab = new TabDeckEditorVisual(this);
myAddTab(tab);
setCurrentWidget(tab);
return tab;
}
TabEdhRec *TabSupervisor::addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander)
{
auto *tab = new TabEdhRec(this);

View file

@ -2,6 +2,8 @@
#define TAB_SUPERVISOR_H
#include "../../deck/deck_loader.h"
#include "visual_deck_editor/tab_deck_editor_visual.h"
#include "visual_deck_editor/tab_deck_editor_visual_tab_widget.h"
#include "../../server/user/user_list_proxy.h"
#include "abstract_tab_deck_editor.h"
#include "api/edhrec/tab_edhrec.h"
@ -150,6 +152,8 @@ signals:
public slots:
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false);
TabDeckStorageVisual *addVisualDeckStorageTab();
TabDeckEditorVisual *addVisualDeckEditorTab();
void openReplay(GameReplay *replay);
void maximizeMainWindow();
private slots:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
#ifndef WINDOW_DECKEDITORVISUAL_H
#define WINDOW_DECKEDITORVISUAL_H
#include "../../../deck/custom_line_edit.h"
#include "../../game_logic/key_signals.h"
#include "../tab.h"
#include "../tab_deck_editor.h"
#include "tab_deck_editor_visual_tab_widget.h"
#include <QAbstractItemModel>
#include <QDir>
class TabDeckEditorVisual : public Tab
{
Q_OBJECT
private slots:
void updateName(const QString &name);
void updateComments();
void updateHash();
void updateCardInfoLeft(const QModelIndex &current, const QModelIndex &previous);
void updateCardInfoRight(const QModelIndex &current, const QModelIndex &previous);
void updateSearch(const QString &search);
void databaseCustomMenu(QPoint point);
void actNewDeck();
void actLoadDeck();
bool actSaveDeck();
bool actSaveDeckAs();
void actLoadDeckFromClipboard();
void actSaveDeckToClipboard();
void actSaveDeckToClipboardRaw();
void actPrintDeck();
void actExportDeckDecklist();
void actAnalyzeDeckDeckstats();
void actAnalyzeDeckTappedout();
void actClearFilterAll();
void actClearFilterOne();
void actSwapCard();
void actAddCard();
void actAddCardToSideboard();
void actRemoveCard();
void actIncrement();
void actDecrement();
void actDecrementCard();
void actDecrementCardFromSideboard();
void copyDatabaseCellContents();
void saveDeckRemoteFinished(const Response &r);
void filterViewCustomContextMenu(const QPoint &point);
void filterRemove(QAction *action);
void loadLayout();
void restartLayout();
void freeDocksSize();
void refreshShortcuts();
bool eventFilter(QObject *o, QEvent *e) override;
void dockVisibleTriggered();
void dockFloatingTriggered();
void dockTopLevelChanged(bool topLevel);
void saveDbHeaderState();
void setSaveStatus(bool newStatus);
void showSearchSyntaxHelp();
private:
CardInfoPtr currentCardInfo() const;
void changeModelIndexToCard(CardInfoPtr activeCard);
void addCardHelper(QString zoneName);
void addCardInfo(CardInfoPtr info, QString zoneName);
void offsetCountAtIndex(const QModelIndex &idx, int offset);
void decrementCardHelper(QString zoneName);
void recursiveExpand(const QModelIndex &index);
QWidget *parent;
CardDatabaseModel *databaseModel;
CardDatabaseDisplayModel *databaseDisplayModel;
DeckListModel *deckModel;
QTreeView *databaseView;
TabDeckEditorVisualTabWidget *tabContainer;
QTreeView *deckView;
KeySignals deckViewKeySignals;
CardInfoFrameWidget *cardInfo;
SearchLineEdit *searchEdit;
KeySignals searchKeySignals;
QLabel *nameLabel;
LineEditUnfocusable *nameEdit;
QLabel *commentsLabel;
QTextEdit *commentsEdit;
QLabel *hashLabel1;
LineEditUnfocusable *hashLabel;
FilterTreeModel *filterModel;
QTreeView *filterView;
KeySignals filterViewKeySignals;
QWidget *filterBox;
QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *deckAnalyticsMenu, *filterDockMenu, *analyzeDeckMenu,
*saveDeckToClipboardMenu;
QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard,
*aSaveDeckToClipboardRaw, *aPrintDeck, *aExportDeckDecklist, *aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout,
*aClose;
QAction *aClearFilterAll, *aClearFilterOne;
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;
QAction *aResetLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating,
*aDeckAnalyticsDockFloating, *aDeckAnalyticsDockVisible, *aFilterDockVisible, *aFilterDockFloating;
bool modified;
QVBoxLayout *centralFrame;
QVBoxLayout *searchAndDatabaseFrame;
QHBoxLayout *searchLayout;
QDockWidget *searchAndDatabaseDock;
QDockWidget *cardInfoDock;
QDockWidget *deckAnalyticsDock;
QDockWidget *deckDock;
QDockWidget *filterDock;
QWidget *centralWidget;
public:
explicit TabDeckEditorVisual(TabSupervisor *_tabSupervisor, QWidget *parent = nullptr);
~TabDeckEditorVisual() override;
void retranslateUi() override;
QString getTabText() const override;
void setDeck(DeckLoader *_deckLoader);
void setModified(bool _windowModified);
bool confirmClose();
void createDeckDock();
void createCardInfoDock();
void createDeckAnalyticsDock();
void createFiltersDock();
void createMenus();
void createSearchAndDatabaseFrame();
void createCentralFrame();
public slots:
void setCurrentCardInfo(CardInfoPtr activeCard);
void changeModelIndexAndCardInfo(CardInfoPtr activeCard);
void processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void processSideboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
signals:
void deckEditorClosing(TabDeckEditorVisual *tab);
};
#endif

View file

@ -0,0 +1,138 @@
#include "tab_deck_editor_visual_database_search_tab.h"
#include "../../../game/cards/card_database_manager.h"
#include "../../../main.h"
TabDeckEditorVisualDatabaseSearchTab::TabDeckEditorVisualDatabaseSearchTab(TabSupervisor *_tabSupervisor,
QWidget *_parent)
: Tab(_tabSupervisor), parent(_parent)
{
// Set up the layout and add tab widget
layout = new QVBoxLayout(this);
this->setLayout(layout);
visualDeckView = new VisualDeckEditorWidget(this, this->deckModel);
visualDeckView->setObjectName("visualDeckView");
visualDeckView->updateDisplay();
layout->addWidget(visualDeckView);
}
TabDeckEditorVisualDatabaseSearchTab::~TabDeckEditorVisualDatabaseSearchTab()
{
}
void TabDeckEditorVisualDatabaseSearchTab::updateCardInfoLeft(const QModelIndex &current, const QModelIndex &previous)
{
(void)current;
(void)previous;
}
void TabDeckEditorVisualDatabaseSearchTab::updateCardInfoRight(const QModelIndex &current, const QModelIndex &previous)
{
(void)current;
(void)previous;
}
void TabDeckEditorVisualDatabaseSearchTab::updateSearch(const QString &search)
{
(void)search;
}
void TabDeckEditorVisualDatabaseSearchTab::databaseCustomMenu(QPoint point)
{
(void)point;
}
void TabDeckEditorVisualDatabaseSearchTab::actClearFilterAll()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actClearFilterOne()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actSwapCard()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actAddCard()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actAddCardToSideboard()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actRemoveCard()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actIncrement()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actDecrement()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actDecrementCard()
{
}
void TabDeckEditorVisualDatabaseSearchTab::actDecrementCardFromSideboard()
{
}
void TabDeckEditorVisualDatabaseSearchTab::copyDatabaseCellContents()
{
}
void TabDeckEditorVisualDatabaseSearchTab::filterViewCustomContextMenu(const QPoint &point)
{
(void)point;
}
void TabDeckEditorVisualDatabaseSearchTab::filterRemove(QAction *action)
{
(void)action;
}
void TabDeckEditorVisualDatabaseSearchTab::saveDbHeaderState()
{
}
void TabDeckEditorVisualDatabaseSearchTab::setSaveStatus(bool newStatus)
{
(void)newStatus;
}
void TabDeckEditorVisualDatabaseSearchTab::showSearchSyntaxHelp()
{
}
CardInfoPtr TabDeckEditorVisualDatabaseSearchTab::currentCardInfo() const
{
const QModelIndex currentIndex = databaseView->selectionModel()->currentIndex();
if (!currentIndex.isValid()) {
return {};
}
const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString();
return CardDatabaseManager::getInstance()->getCard(cardName);
}
void TabDeckEditorVisualDatabaseSearchTab::addCardHelper(QString zoneName)
{
(void)zoneName;
}
void TabDeckEditorVisualDatabaseSearchTab::offsetCountAtIndex(const QModelIndex &idx, int offset)
{
(void)idx;
(void)offset;
}
void TabDeckEditorVisualDatabaseSearchTab::decrementCardHelper(QString zoneName)
{
(void)zoneName;
}
void TabDeckEditorVisualDatabaseSearchTab::recursiveExpand(const QModelIndex &index)
{
(void)index;
}
void TabDeckEditorVisualDatabaseSearchTab::retranslateUi()
{
}
void TabDeckEditorVisualDatabaseSearchTab::setDeck(DeckLoader *_deckLoader)
{
(void)_deckLoader;
}
void TabDeckEditorVisualDatabaseSearchTab::setModified(bool _windowModified)
{
(void)_windowModified;
}
bool TabDeckEditorVisualDatabaseSearchTab::confirmClose()
{
return true;
}

View file

@ -0,0 +1,97 @@
#ifndef TAB_DECK_EDITOR_VISUAL_DATABASE_SEARCH_TAB_H
#define TAB_DECK_EDITOR_VISUAL_DATABASE_SEARCH_TAB_H
#include "../../../client/game_logic/key_signals.h"
#include "../../../deck/deck_list_model.h"
#include "../../../game/cards/card_database.h"
#include "../../../game/cards/card_database_model.h"
#include "../../ui/widgets/cards/card_info_frame_widget.h"
#include "../../ui/widgets/deck_analytics/deck_analytics_widget.h"
#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_widget.h"
#include "../tab.h"
#include "../tab_deck_editor.h"
#include <QTreeView>
class TabDeckEditorVisualDatabaseSearchTab : public Tab
{
Q_OBJECT
private slots:
void updateCardInfoLeft(const QModelIndex &current, const QModelIndex &previous);
void updateCardInfoRight(const QModelIndex &current, const QModelIndex &previous);
void updateSearch(const QString &search);
void databaseCustomMenu(QPoint point);
void actClearFilterAll();
void actClearFilterOne();
void actSwapCard();
void actAddCard();
void actAddCardToSideboard();
void actRemoveCard();
void actIncrement();
void actDecrement();
void actDecrementCard();
void actDecrementCardFromSideboard();
void copyDatabaseCellContents();
void filterViewCustomContextMenu(const QPoint &point);
void filterRemove(QAction *action);
void saveDbHeaderState();
void setSaveStatus(bool newStatus);
void showSearchSyntaxHelp();
private:
QWidget *parent;
CardInfoPtr currentCardInfo() const;
void addCardHelper(QString zoneName);
void offsetCountAtIndex(const QModelIndex &idx, int offset);
void decrementCardHelper(QString zoneName);
void recursiveExpand(const QModelIndex &index);
CardDatabaseModel *databaseModel;
CardDatabaseDisplayModel *databaseDisplayModel;
DeckListModel *deckModel;
QTreeView *databaseView;
VisualDeckEditorWidget *visualDeckView;
CardInfoFrameWidget *cardInfo;
SearchLineEdit *searchEdit;
KeySignals searchKeySignals;
FilterTreeModel *filterModel;
QTreeView *filterView;
KeySignals filterViewKeySignals;
QWidget *filterBox;
QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *filterDockMenu;
QAction *aClose;
QAction *aClearFilterAll, *aClearFilterOne;
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;
QAction *aResetLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating,
*aDeckAnalyticsDockFloating, *aDeckAnalyticsDockVisible, *aFilterDockVisible, *aFilterDockFloating;
bool modified;
QVBoxLayout *layout;
QVBoxLayout *searchAndDatabaseFrame;
QHBoxLayout *searchLayout;
QDockWidget *searchAndDatabaseDock;
QDockWidget *cardInfoDock;
QDockWidget *deckDock;
QDockWidget *filterDock;
QWidget *centralWidget;
public:
explicit TabDeckEditorVisualDatabaseSearchTab(TabSupervisor *_tabSupervisor, QWidget *_parent);
~TabDeckEditorVisualDatabaseSearchTab() override;
void retranslateUi() override;
void setDeck(DeckLoader *_deckLoader);
void setModified(bool _windowModified);
bool confirmClose();
signals:
void deckEditorClosing(TabDeckEditorVisualDatabaseSearchTab *tab);
};
#endif // TAB_DECK_EDITOR_VISUAL_DATABASE_SEARCH_TAB_H

View file

@ -0,0 +1,8 @@
//
// Created by ascor on 10/12/24.
//
#ifndef TAB_DECK_EDITOR_VISUAL_DECK_CARDS_TAB_H
#define TAB_DECK_EDITOR_VISUAL_DECK_CARDS_TAB_H
#endif // TAB_DECK_EDITOR_VISUAL_DECK_CARDS_TAB_H

View file

@ -0,0 +1,8 @@
//
// Created by ascor on 10/12/24.
//
#ifndef TAB_DECK_EDITOR_VISUAL_DECK_STATS_TAB_H
#define TAB_DECK_EDITOR_VISUAL_DECK_STATS_TAB_H
#endif // TAB_DECK_EDITOR_VISUAL_DECK_STATS_TAB_H

View file

@ -0,0 +1,116 @@
#include "tab_deck_editor_visual_tab_widget.h"
#include "../../ui/widgets/visual_database_display/visual_database_display_widget.h"
TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent,
DeckListModel *model,
CardDatabaseModel *card_database_model,
CardDatabaseDisplayModel *card_database_display_model)
: QTabWidget(parent), deckModel(model), cardDatabaseModel(card_database_model),
cardDatabaseDisplayModel(card_database_display_model)
{
this->setTabsClosable(true); // Enable tab closing
connect(this, &QTabWidget::tabCloseRequested, this, &TabDeckEditorVisualTabWidget::handleTabClose);
// Set up the layout and add tab widget
layout = new QVBoxLayout(this);
setLayout(layout);
visualDeckView = new VisualDeckEditorWidget(this, this->deckModel);
visualDeckView->setObjectName("visualDeckView");
visualDeckView->updateDisplay();
connect(visualDeckView, SIGNAL(activeCardChanged(CardInfoPtr)), this, SLOT(onCardChanged(CardInfoPtr)));
connect(visualDeckView, SIGNAL(mainboardCardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onMainboardCardClickedDeckEditor(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(visualDeckView, SIGNAL(sideboardCardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onSideboardCardClickedDeckEditor(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
visualDatabaseDisplay =
new VisualDatabaseDisplayWidget(this, this->cardDatabaseModel, this->cardDatabaseDisplayModel);
visualDatabaseDisplay->setObjectName("visualDatabaseView");
visualDatabaseDisplay->updateDisplay();
connect(visualDatabaseDisplay, SIGNAL(cardHoveredDatabaseDisplay(CardInfoPtr)), this,
SLOT(onCardChangedDatabaseDisplay(CardInfoPtr)));
connect(visualDatabaseDisplay,
SIGNAL(cardClickedDatabaseDisplay(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onCardClickedDatabaseDisplay(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
deckAnalytics = new DeckAnalyticsWidget(this, this->deckModel);
deckAnalytics->setObjectName("deckAnalytics");
// deckAnalytics->analyzeManaCurve();
this->addNewTab(visualDeckView, "Visual Deck View");
this->addNewTab(visualDatabaseDisplay, "Visual Database Display");
this->addNewTab(deckAnalytics, "Deck Analytics");
}
void TabDeckEditorVisualTabWidget::onCardChanged(CardInfoPtr activeCard)
{
emit cardChanged(activeCard);
}
void TabDeckEditorVisualTabWidget::onCardChangedDatabaseDisplay(CardInfoPtr activeCard)
{
emit cardChangedDatabaseDisplay(activeCard);
}
void TabDeckEditorVisualTabWidget::onMainboardCardClickedDeckEditor(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance)
{
emit mainboardCardClicked(event, instance);
}
void TabDeckEditorVisualTabWidget::onSideboardCardClickedDeckEditor(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance)
{
emit sideboardCardClicked(event, instance);
}
void TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance)
{
emit cardClickedDatabaseDisplay(event, instance);
}
void TabDeckEditorVisualTabWidget::addNewTab(QWidget *widget, const QString &title)
{
// Add new tab to the tab widget
this->addTab(widget, title);
}
void TabDeckEditorVisualTabWidget::removeCurrentTab()
{
// Remove the currently selected tab
int currentIndex = this->currentIndex();
if (currentIndex != -1) {
this->removeTab(currentIndex);
}
}
void TabDeckEditorVisualTabWidget::setTabTitle(int index, const QString &title)
{
// Set the title of the tab at the given index
if (index >= 0 && index < this->count()) {
this->setTabText(index, title);
}
}
QWidget *TabDeckEditorVisualTabWidget::getCurrentTab() const
{
// Return the currently selected tab widget
return this->currentWidget();
}
int TabDeckEditorVisualTabWidget::getTabCount() const
{
// Return the number of tabs
return this->count();
}
void TabDeckEditorVisualTabWidget::handleTabClose(int index)
{
// Handle closing of the tab at the given index
QWidget *tab = this->widget(index);
this->removeTab(index);
delete tab; // Delete the tab's widget to free memory
}

View file

@ -0,0 +1,57 @@
#ifndef TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
#define TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
#include "../../ui/widgets/deck_analytics/deck_analytics_widget.h"
#include "../../ui/widgets/visual_database_display/visual_database_display_widget.h"
#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_widget.h"
#include <QTabWidget>
#include <QVBoxLayout>
#include <QWidget>
class TabDeckEditorVisualTabWidget : public QTabWidget
{
Q_OBJECT
public:
explicit TabDeckEditorVisualTabWidget(QWidget *parent = nullptr,
DeckListModel *deckModel = nullptr,
CardDatabaseModel *card_database_model = nullptr,
CardDatabaseDisplayModel *card_database_display_model = nullptr);
// Utility functions
void addNewTab(QWidget *widget, const QString &title);
void removeCurrentTab();
void setTabTitle(int index, const QString &title);
QWidget *getCurrentTab() const;
int getTabCount() const;
VisualDeckEditorWidget *visualDeckView;
DeckAnalyticsWidget *deckAnalytics;
VisualDatabaseDisplayWidget *visualDatabaseDisplay;
public slots:
void onCardChanged(CardInfoPtr activeCard);
void onCardChangedDatabaseDisplay(CardInfoPtr activeCard);
void onMainboardCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void onSideboardCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
signals:
void cardChanged(CardInfoPtr activeCard);
void cardChangedDatabaseDisplay(CardInfoPtr activeCard);
void mainboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void sideboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
private:
QVBoxLayout *layout; // Layout for the tab widget and other controls
DeckListModel *deckModel;
CardDatabaseModel *cardDatabaseModel;
CardDatabaseDisplayModel *cardDatabaseDisplayModel;
private slots:
void handleTabClose(int index); // Slot for closing a tab
};
#endif // TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H

View file

@ -61,6 +61,7 @@ CardInfoFrameWidget::CardInfoFrameWidget(const QString &cardName, QWidget *paren
setViewMode(SettingsCache::instance().getCardInfoViewMode());
// TODO: Change this to be by UUID
setCard(CardDatabaseManager::getInstance()->getCard(cardName));
}

View file

@ -0,0 +1,54 @@
#include "deck_card_zone_display_widget.h"
#include "overlapped_card_group_display_widget.h"
#include <QResizeEvent>
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
QList<QPair<QString, QList<CardInfoPtr>>> cardLists,
QString bannerText,
int bannerOpacity,
int subBannerOpacity)
: QWidget(parent), cardLists(cardLists), bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity)
{
layout = new QVBoxLayout(this);
setLayout(layout);
banner = new BannerWidget(bannerText, Qt::Orientation::Vertical, bannerOpacity, this);
layout->addWidget(banner);
displayCards();
}
void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
for (QObject *child : layout->children()) {
QWidget *widget = qobject_cast<QWidget *>(child);
if (widget) {
widget->resize(event->size());
}
}
}
void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
{
emit cardClicked(event, card);
}
void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card)
{
emit cardHovered(card);
}
void DeckCardZoneDisplayWidget::displayCards()
{
for (QPair<QString, QList<CardInfoPtr>> &pair : cardLists) {
QString criteriaValue = pair.first;
QList<CardInfoPtr> cardList = pair.second;
OverlappedCardGroupDisplayWidget *display_widget =
new OverlappedCardGroupDisplayWidget(this, &cardList, criteriaValue, subBannerOpacity);
connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
layout->addWidget(display_widget);
}
}

View file

@ -0,0 +1,43 @@
#ifndef DECK_CARD_ZONE_DISPLAY_WIDGET_H
#define DECK_CARD_ZONE_DISPLAY_WIDGET_H
#include "../../../../game/cards/card_database.h"
#include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h"
#include "card_info_picture_with_text_overlay_widget.h"
#include <QVBoxLayout>
#include <QWidget>
class DeckCardZoneDisplayWidget : public QWidget
{
Q_OBJECT
public:
DeckCardZoneDisplayWidget(QWidget *parent,
QList<QPair<QString, QList<CardInfoPtr>>> cardLists,
QString bannerText,
int bannerOpacity,
int subBannerOpacity);
void addCardsToOverlapWidget();
void resizeEvent(QResizeEvent *event) override;
public slots:
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void onHover(CardInfoPtr card);
void displayCards();
signals:
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void cardHovered(CardInfoPtr card);
private:
QList<QPair<QString, QList<CardInfoPtr>>> cardLists;
QVBoxLayout *layout;
BannerWidget *banner;
int bannerOpacity = 20;
int subBannerOpacity = 10;
OverlapWidget *overlapWidget;
};
#endif // DECK_CARD_ZONE_DISPLAY_WIDGET_H

View file

@ -0,0 +1,59 @@
#include "overlapped_card_group_display_widget.h"
#include "card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent>
OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent,
QList<CardInfoPtr> *cards,
QString bannerText,
int bannerOpacity)
: QWidget(parent), cardsToDisplay(cards)
{
layout = new QVBoxLayout(this);
setLayout(layout);
setMinimumSize(QSize(0, 0));
banner = new BannerWidget(bannerText, Qt::Orientation::Vertical, bannerOpacity, this);
overlapWidget = new OverlapWidget(this, 80, 1, 1, Qt::Vertical, true);
layout->addWidget(banner);
layout->addWidget(overlapWidget);
addCardsToOverlapWidget();
}
void OverlappedCardGroupDisplayWidget::addCardsToOverlapWidget()
{
for (int i = 0; i < cardsToDisplay->size(); i++) {
CardInfoPtr card = cardsToDisplay->at(i);
if (card) {
CardInfoPictureWithTextOverlayWidget *display =
new CardInfoPictureWithTextOverlayWidget(overlapWidget, true);
display->setCard(card);
connect(display, SIGNAL(imageClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(display, SIGNAL(hoveredOnCard(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
overlapWidget->addWidget(display);
}
}
overlapWidget->adjustMaxColumnsAndRows();
}
void OverlappedCardGroupDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
{
emit cardClicked(event, card);
}
void OverlappedCardGroupDisplayWidget::onHover(CardInfoPtr card)
{
emit cardHovered(card);
}
void OverlappedCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
overlapWidget->resize(event->size());
overlapWidget->adjustMaxColumnsAndRows();
}

View file

@ -0,0 +1,41 @@
//
// Created by ascor on 11/12/24.
//
#ifndef OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H
#define OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H
#include "../../../../game/cards/card_database.h"
#include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h"
#include "card_info_picture_with_text_overlay_widget.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
class OverlappedCardGroupDisplayWidget : public QWidget
{
Q_OBJECT
public:
OverlappedCardGroupDisplayWidget(QWidget *parent, QList<CardInfoPtr> *cards, QString bannerText, int bannerOpacity);
void addCardsToOverlapWidget();
void resizeEvent(QResizeEvent *event) override;
public slots:
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void onHover(CardInfoPtr card);
signals:
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void cardHovered(CardInfoPtr card);
private:
QList<CardInfoPtr> *cardsToDisplay;
QVBoxLayout *layout;
BannerWidget *banner;
OverlapWidget *overlapWidget;
};
#endif // OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H

View file

@ -0,0 +1,26 @@
#include "deck_analytics_widget.h"
DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListModel *deck_list_model)
: QWidget(parent), deck_list_model(deck_list_model)
{
this->setMinimumSize(0, 0);
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->setStyleSheet("border: 2px solid red;");
main_layout = new QHBoxLayout();
this->setLayout(main_layout);
flow_widget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
this->main_layout->addWidget(flow_widget);
mana_curve_widget = new ManaCurveWidget(flow_widget);
mana_curve_widget->setStyleSheet("border: 2px solid yellow;");
flow_widget->addWidget(mana_curve_widget);
mana_devotion_widget = new ManaDevotionWidget(flow_widget, this->deck_list_model);
flow_widget->addWidget(mana_devotion_widget);
mana_base_widget = new ManaBaseWidget(flow_widget, this->deck_list_model);
flow_widget->addWidget(mana_base_widget);
}

View file

@ -0,0 +1,34 @@
#ifndef DECK_ANALYTICS_WIDGET_H
#define DECK_ANALYTICS_WIDGET_H
#include "../../../../deck/deck_list_model.h"
#include "../../../ui/widgets/general/layout_containers/flow_widget.h"
#include "mana_base_widget.h"
#include "mana_curve_widget.h"
#include "mana_devotion_widget.h"
#include <QHBoxLayout>
#include <QWidget>
#include <decklist.h>
#include <qscrollarea.h>
class DeckAnalyticsWidget : public QWidget
{
Q_OBJECT
public:
explicit DeckAnalyticsWidget(QWidget *parent = nullptr, DeckListModel *deck_list_model = nullptr);
void setDeckList(const DeckList &new_deck_list_model);
std::map<int, int> analyzeManaCurve();
private:
DeckListModel *deck_list_model;
QHBoxLayout *main_layout;
FlowWidget *flow_widget;
ManaCurveWidget *mana_curve_widget;
ManaDevotionWidget *mana_devotion_widget;
ManaBaseWidget *mana_base_widget;
};
#endif // DECK_ANALYTICS_WIDGET_H

View file

@ -0,0 +1,134 @@
#include "mana_base_widget.h"
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../main.h"
#include "../general/display/bar_widget.h"
#include <QRegularExpression>
#include <decklist.h>
#include <regex>
#include <unordered_map>
ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListModel *deck_list_model)
: QWidget(parent), deck_list_model(deck_list_model)
{
layout = new QHBoxLayout(this);
this->setLayout(layout);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMinimumSize(0, 0);
}
std::unordered_map<char, int> ManaBaseWidget::analyzeManaBase()
{
manaBaseMap = std::unordered_map<char, int>();
InnerDecklistNode *listRoot = deck_list_model->getDeckList()->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
continue;
for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
if (info) {
auto devotion = determineManaProduction(info->getText());
mergeManaCounts(manaBaseMap, devotion);
}
}
}
}
update();
// this->setCurve(ManaBaseMap);
// mana_curve_widget->updateDisplay();
this->updateDisplay();
update();
return manaBaseMap;
}
void ManaBaseWidget::updateDisplay()
{
// Clear the layout first
if (layout != nullptr) {
QLayoutItem *item;
while ((item = layout->takeAt(0)) != nullptr) {
delete item->widget(); // Delete the widget
delete item; // Delete the layout item
}
}
// If layout is null, create a new layout, otherwise reuse the existing one
if (layout == nullptr) {
layout = new QHBoxLayout(this);
this->setLayout(layout);
}
int totalSum = 0;
for (auto entry : manaBaseMap) {
totalSum += entry.second;
}
// Add new widgets to the layout
for (auto entry : manaBaseMap) {
BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, totalSum, this);
layout->addWidget(barWidget);
}
update(); // Update the widget display
}
std::unordered_map<char, int> ManaBaseWidget::determineManaProduction(const QString &rulesText)
{
// Initialize mana counts
std::unordered_map<char, int> manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}, {'C', 0}};
// Define regex patterns for different mana production rules
QRegularExpression tapAddColorless(R"(\{T\}:\s*Add\s*\{C\}|Add\s*one\s*colorless\s*mana)");
QRegularExpression tapAddAnyColor(
R"(\{T\}:\s*Add\s*one\s*mana\s*of\s*any\s*color|Add\s*one\s*mana\s*of\s*any\s*color)");
QRegularExpression tapAddSpecificColor(
R"(\{T\}:\s*Add\s*\{(W|U|B|R|G)\}|Add\s*one\s*(white|blue|black|red|green)\s*mana)");
// Check for mana production rules in the text
if (tapAddColorless.match(rulesText).hasMatch()) {
manaCounts['C'] += 1; // Adds colorless mana
}
if (tapAddAnyColor.match(rulesText).hasMatch()) {
manaCounts['W'] += 1; // Assumes at least 1 of any color can be produced
manaCounts['U'] += 1;
manaCounts['B'] += 1;
manaCounts['R'] += 1;
manaCounts['G'] += 1;
}
// Check for specific color production
QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{(W|U|B|R|G)\})");
auto match = specificColorRegex.match(rulesText);
if (match.hasMatch()) {
QString color = match.captured(1);
if (color == "W")
manaCounts['W'] += 1;
else if (color == "U")
manaCounts['U'] += 1;
else if (color == "B")
manaCounts['B'] += 1;
else if (color == "R")
manaCounts['R'] += 1;
else if (color == "G")
manaCounts['G'] += 1;
}
return manaCounts;
}
void ManaBaseWidget::mergeManaCounts(std::unordered_map<char, int> &manaCounts1,
const std::unordered_map<char, int> &manaCounts2)
{
for (const auto &pair : manaCounts2) {
manaCounts1[pair.first] += pair.second; // Add values for matching keys
}
}

View file

@ -0,0 +1,29 @@
#include "../../../../deck/deck_list_model.h"
#include <QHBoxLayout>
#include <QWidget>
#include <decklist.h>
#include <utility>
#ifndef MANA_BASE_WIDGET_H
#define MANA_BASE_WIDGET_H
class ManaBaseWidget : public QWidget
{
Q_OBJECT
public:
explicit ManaBaseWidget(QWidget *parent = nullptr, DeckListModel *deck_list_model = nullptr);
std::unordered_map<char, int> analyzeManaBase();
void updateDisplay();
std::unordered_map<char, int> determineManaProduction(const QString &manaString);
void mergeManaCounts(std::unordered_map<char, int> &manaCounts1, const std::unordered_map<char, int> &manaCounts2);
private:
DeckListModel *deck_list_model;
std::unordered_map<char, int> manaBaseMap;
QHBoxLayout *layout;
};
#endif // MANA_BASE_WIDGET_H

View file

@ -0,0 +1,44 @@
#include "mana_curve_widget.h"
#include "../general/display/bar_widget.h"
#include <QLabel>
ManaCurveWidget::ManaCurveWidget(QWidget *parent) : QWidget(parent)
{
layout = new QHBoxLayout(this);
this->setLayout(layout);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMinimumSize(0, 0);
}
void ManaCurveWidget::updateDisplay()
{
// Clear the layout first
if (layout != nullptr) {
QLayoutItem *item;
while ((item = layout->takeAt(0)) != nullptr) {
delete item->widget(); // Delete the widget
delete item; // Delete the layout item
}
}
// If layout is null, create a new layout, otherwise reuse the existing one
if (layout == nullptr) {
layout = new QHBoxLayout(this);
this->setLayout(layout);
}
int totalSum = 0;
for (auto entry : curve) {
totalSum += entry.second;
}
// Add a bar for each CMC to the layout
for (auto entry : curve) {
BarWidget *barWidget = new BarWidget(QString::number(entry.first), entry.second, totalSum, this);
layout->addWidget(barWidget);
}
update(); // Update the widget display
}

View file

@ -0,0 +1,25 @@
#ifndef MANA_CURVE_WIDGET_H
#define MANA_CURVE_WIDGET_H
#include <QHBoxLayout>
#include <QWidget>
#include <utility>
class ManaCurveWidget : public QWidget
{
Q_OBJECT
public:
explicit ManaCurveWidget(QWidget *parent = nullptr);
void updateDisplay();
void setCurve(std::map<int, int> new_curve)
{
this->curve = std::move(new_curve);
}
private:
std::map<int, int> curve;
QHBoxLayout *layout;
};
#endif // MANA_CURVE_WIDGET_H

View file

@ -0,0 +1,132 @@
#include "mana_devotion_widget.h"
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../main.h"
#include "../general/display/bar_widget.h"
#include <decklist.h>
#include <iostream>
#include <regex>
#include <string>
#include <unordered_map>
ManaDevotionWidget::ManaDevotionWidget(QWidget *parent, DeckListModel *deck_list_model)
: QWidget(parent), deck_list_model(deck_list_model)
{
layout = new QHBoxLayout(this);
this->setLayout(layout);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMinimumSize(0, 0);
}
std::unordered_map<char, int> ManaDevotionWidget::analyzeManaDevotion()
{
manaDevotionMap = std::unordered_map<char, int>();
InnerDecklistNode *listRoot = deck_list_model->getDeckList()->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
continue;
for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
if (info) {
auto devotion = countManaSymbols(info->getManaCost());
mergeManaCounts(manaDevotionMap, devotion);
}
}
}
}
update();
// this->setCurve(manaDevotionMap);
// mana_curve_widget->updateDisplay();
this->updateDisplay();
update();
return manaDevotionMap;
}
void ManaDevotionWidget::updateDisplay()
{
// Clear the layout first
if (layout != nullptr) {
QLayoutItem *item;
while ((item = layout->takeAt(0)) != nullptr) {
delete item->widget(); // Delete the widget
delete item; // Delete the layout item
}
}
// If layout is null, create a new layout, otherwise reuse the existing one
if (layout == nullptr) {
layout = new QHBoxLayout(this);
this->setLayout(layout);
}
int totalSum = 0;
for (auto entry : manaDevotionMap) {
totalSum += entry.second;
}
// Add new widgets to the layout
for (auto entry : manaDevotionMap) {
BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, totalSum, this);
layout->addWidget(barWidget);
}
update(); // Update the widget display
}
// Function to count mana symbols W, U, B, R, G in the input string
std::unordered_map<char, int> ManaDevotionWidget::countManaSymbols(const QString &manaString)
{
// Only track counts for W, U, B, R, G
std::unordered_map<char, int> manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}};
// Convert QString to std::string for regex processing
std::string stdManaString = manaString.toStdString();
std::regex regexPattern(R"(\{([WUBRG])\/([WUBRG])\})");
std::string::const_iterator searchStart(stdManaString.cbegin());
std::smatch match;
// Go through the string and find pairs within {}/ for W, U, B, R, G
while (std::regex_search(searchStart, stdManaString.cend(), match, regexPattern)) {
char mana1 = match[1].str()[0];
char mana2 = match[2].str()[0];
// Increment the count for both mana symbols
manaCounts[mana1]++;
manaCounts[mana2]++;
// Move past the current match
searchStart = match.suffix().first;
}
// Now go through the string again but ignore the {}/ parts and non-WUBRG symbols
for (int i = 0; i < manaString.size(); ++i) {
if (manaString[i] == '{') {
// Skip over the {X/Y} pattern
while (manaString[i] != '}' && i < manaString.size()) {
++i;
}
} else if (manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
// If it's one of W, U, B, R, G, count it
manaCounts[manaString[i].toLatin1()]++;
}
}
return manaCounts;
}
void ManaDevotionWidget::mergeManaCounts(std::unordered_map<char, int> &manaCounts1,
const std::unordered_map<char, int> &manaCounts2)
{
for (const auto &pair : manaCounts2) {
manaCounts1[pair.first] += pair.second; // Add values for matching keys
}
}

View file

@ -0,0 +1,28 @@
#ifndef MANA_DEVOTION_WIDGET_H
#define MANA_DEVOTION_WIDGET_H
#include "../../../../deck/deck_list_model.h"
#include <QHBoxLayout>
#include <QWidget>
#include <decklist.h>
#include <utility>
class ManaDevotionWidget : public QWidget
{
Q_OBJECT
public:
explicit ManaDevotionWidget(QWidget *parent = nullptr, DeckListModel *deck_list_model = nullptr);
std::unordered_map<char, int> analyzeManaDevotion();
void updateDisplay();
std::unordered_map<char, int> countManaSymbols(const QString &manaString);
void mergeManaCounts(std::unordered_map<char, int> &manaCounts1, const std::unordered_map<char, int> &manaCounts2);
private:
DeckListModel *deck_list_model;
std::unordered_map<char, int> manaDevotionMap;
QHBoxLayout *layout;
};
#endif // MANA_DEVOTION_WIDGET_H

View file

@ -0,0 +1,50 @@
#include "bar_widget.h"
#include <utility>
BarWidget::BarWidget(QString label, int value, int total, QWidget *parent)
: QWidget(parent), label(std::move(label)), value(value), total(total)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
QSize BarWidget::sizeHint() const
{
QFontMetrics metrics(font());
int labelHeight = metrics.height();
int valueHeight = metrics.height();
// Calculate the height based on total and value
int barHeight = total > 0 ? total * 2 : 20; // Adjust factor as necessary for visual size
int totalHeight = barHeight + labelHeight + valueHeight + 30; // Extra space for padding
return QSize(40, totalHeight); // Width is fixed, height is calculated
}
void BarWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// Calculate the full bar width and value-specific bar width
int fullBarWidth = total; // Full bar (sum of all entries)
int valueBarWidth = (total > 0) ? (value * 100 / total) : 0;
// Draw the full bar background (gray)
painter.setBrush(QColor(200, 200, 200)); // Gray background for full bar
painter.drawRect(0, 10, 20, fullBarWidth);
// Draw the value-specific bar (blue)
painter.setBrush(QColor(100, 150, 255)); // Blue for the value bar
painter.drawRect(0, fullBarWidth + 10, 20, -valueBarWidth);
// Draw the CMC label
painter.setPen(Qt::white);
QRect textRect(0, fullBarWidth + 10, 20, 30); // Define a rectangle for text
painter.drawText(textRect, Qt::AlignCenter, label);
painter.setPen(Qt::black);
QRect valueRect(0, 10, 20, fullBarWidth); // Define a rectangle for text
painter.drawText(valueRect, Qt::AlignCenter, QString::number(value));
(void)event; // Suppress unused parameter warning
}

View file

@ -0,0 +1,25 @@
#ifndef BAR_WIDGET_H
#define BAR_WIDGET_H
#include <QPainter>
#include <QWidget>
class BarWidget : public QWidget
{
Q_OBJECT
public:
BarWidget(QString label, int value, int total, QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *event) override;
QSize sizeHint() const override;
private:
QString label; // The CMC value (label for the bar)
int value; // The specific value for this CMC (the inner bar length)
int total; // Total value for calculating proportional bar length
static int totalSum; // The total sum of all entry.second values (shared across instances)
};
#endif // BAR_WIDGET_H

View file

@ -0,0 +1,273 @@
#include "visual_database_display_widget.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../main.h"
#include "../../../../utility/card_info_comparator.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include <QPushButton>
#include <qpropertyanimation.h>
VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
CardDatabaseModel *database_model,
CardDatabaseDisplayModel *database_display_model)
: QWidget(parent), databaseModel(database_model), databaseDisplayModel(database_display_model)
{
cards = new QList<CardInfoPtr>;
connect(databaseDisplayModel, &CardDatabaseDisplayModel::modelDirty, this,
&VisualDatabaseDisplayWidget::modelDirty);
// Set up main layout and widgets
setMinimumSize(0, 0);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
main_layout = new QVBoxLayout();
setLayout(main_layout);
flow_widget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarPolicy::ScrollBarAsNeeded);
main_layout->addWidget(flow_widget);
overlap_control_widget = new OverlapControlWidget(80, 10, 10, Qt::Vertical, flow_widget);
main_layout->addWidget(overlap_control_widget);
debounce_timer = new QTimer(this);
debounce_timer->setSingleShot(true); // Ensure it only fires once after the timeout
connect(debounce_timer, &QTimer::timeout, this, &VisualDatabaseDisplayWidget::searchModelChanged);
setupPaginationControls();
loadCurrentPage(); // Load the first page of cards
}
void VisualDatabaseDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
loadCurrentPage();
}
void VisualDatabaseDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance)
{
emit cardClickedDatabaseDisplay(event, instance);
}
void VisualDatabaseDisplayWidget::onHover(CardInfoPtr hoveredCard)
{
emit cardHoveredDatabaseDisplay(hoveredCard);
}
void VisualDatabaseDisplayWidget::populateCards()
{
this->cards->clear();
int rowCount = databaseDisplayModel->rowCount();
// Calculate the start and end indices for the current page
int start = currentPage * cardsPerPage;
int end = qMin(start + cardsPerPage, rowCount);
// Load more cards if we are at the end of the current list and can fetch more
if (end >= rowCount && databaseDisplayModel->canFetchMore(QModelIndex())) {
databaseDisplayModel->fetchMore(QModelIndex());
}
for (int row = start; row < end; ++row) {
QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn);
QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole);
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(name.toString());
cards->append(info);
}
}
void VisualDatabaseDisplayWidget::searchModelChanged()
{
currentPage = 0;
loadCurrentPage();
}
void VisualDatabaseDisplayWidget::loadCurrentPage()
{
// If an animation is already in progress, don't start a new one
if (isAnimating) {
return;
}
// Set the flag indicating animation is in progress
isAnimating = true;
// Store the original position of the widget
QPoint originalPos = flow_widget->pos();
// Create the first animation to slide the flow_widget
QPropertyAnimation *slideOutAnimation = new QPropertyAnimation(flow_widget, "pos");
slideOutAnimation->setDuration(300); // Adjust for smoothness
slideOutAnimation->setStartValue(originalPos); // Start from the current position
// Adjust the position (e.g., slide it up or down)
QPoint slideOutPos = originalPos;
slideOutPos.setY(originalPos.y() - flow_widget->height()); // Slide up (adjust direction as needed)
slideOutAnimation->setEndValue(slideOutPos); // End at the new position
// Create the second animation to bring it back to its original position
QPropertyAnimation *slideBackAnimation = new QPropertyAnimation(flow_widget, "pos");
slideBackAnimation->setDuration(300); // Duration for smooth transition
slideBackAnimation->setStartValue(slideOutPos); // Start from where the first animation ended
slideBackAnimation->setEndValue(originalPos); // End at the original position
// Connect the end of the first animation to the start of the second
connect(slideOutAnimation, &QPropertyAnimation::finished, [this, slideBackAnimation]() {
// After sliding out, adjust the number of cards, load the new page, and start the slide back animation
adjustCardsPerPage(); // Adjust the cards per page before loading
populateCards(); // Load the new page of cards
updateDisplay(); // Update the visual display
// Start sliding back to original position
slideBackAnimation->start(QAbstractAnimation::DeleteWhenStopped);
});
// Once the second animation finishes, allow new animations to start
connect(slideBackAnimation, &QPropertyAnimation::finished, [this]() {
isAnimating = false; // Reset the flag after animation finishes
});
// Start the slide-out animation
slideOutAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}
void VisualDatabaseDisplayWidget::updateDisplay()
{
// Clear the layout first
flow_widget->clearLayout();
OverlapWidget *printings_group_widget = new OverlapWidget(flow_widget, 0, cardsPerRow, rowsPerColumn, Qt::Vertical);
// Create card widgets and store their sizes
QList<CardInfoPictureWithTextOverlayWidget *> cardDisplays;
for (const auto &info : *cards) {
if (info) {
CardInfoPictureWithTextOverlayWidget *display =
new CardInfoPictureWithTextOverlayWidget(printings_group_widget, true);
display->setCard(info);
cardDisplays.append(display);
printings_group_widget->addWidget(display);
connect(display, SIGNAL(imageClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(display, SIGNAL(hoveredOnCard(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
} else {
qDebug() << "Card not found in database!";
}
}
// Calculate the maximum size of the card widgets after they've been added to the layout
// adjustCardsPerPage();
overlap_control_widget->connectOverlapWidget(printings_group_widget);
flow_widget->addWidget(printings_group_widget);
flow_widget->update();
update(); // Update the widget display
}
void VisualDatabaseDisplayWidget::adjustCardsPerPage()
{
// Calculate available width and height in the OverlapWidget
int availableWidth = flow_widget->width();
int availableHeight = flow_widget->height();
QList<CardInfoPictureWithTextOverlayWidget *> cardDisplays;
for (OverlapWidget *child : flow_widget->findChildren<OverlapWidget *>()) {
for (CardInfoPictureWithTextOverlayWidget *overlapChild :
child->findChildren<CardInfoPictureWithTextOverlayWidget *>()) {
if (CardInfoPictureWithTextOverlayWidget *cardDisplay =
qobject_cast<CardInfoPictureWithTextOverlayWidget *>(overlapChild)) {
cardDisplays.append(cardDisplay);
}
}
}
// Measure the size of the first card display to determine the size of cards
if (!cardDisplays.isEmpty()) {
int cardWidth = cardDisplays.first()->sizeHint().width();
int cardHeight = cardDisplays.first()->sizeHint().height();
const int overlapMargin = 10; // Margin between cards
// Calculate how many cards can fit horizontally and vertically
cardsPerRow = availableWidth / (cardWidth + overlapMargin);
rowsPerColumn = availableHeight / (cardHeight + overlapMargin);
// qDebug() << "available width " << availableWidth << "available height: " << availableHeight;
// qDebug() << "width: " << cardWidth << "height: " << cardHeight << "cardsPerRow: " << cardsPerRow <<
// "rowsPerColumn: " << rowsPerColumn;
// Update cardsPerPage based on rows and columns
cardsPerPage = cardsPerRow * rowsPerColumn;
// qDebug() << "Adjusted cards per page to:" << cardsPerPage;
}
}
void VisualDatabaseDisplayWidget::modelDirty()
{
debounce_timer->start(debounce_time);
}
void VisualDatabaseDisplayWidget::sortCardList(const QStringList properties, Qt::SortOrder order = Qt::AscendingOrder)
{
CardInfoComparator comparator(properties, order);
std::sort(cards->begin(), cards->end(), comparator);
}
void VisualDatabaseDisplayWidget::databaseDataChanged(QModelIndex topLeft, QModelIndex bottomRight)
{
(void)topLeft;
(void)bottomRight;
updateDisplay();
}
void VisualDatabaseDisplayWidget::wheelEvent(QWheelEvent *event)
{
if (isAnimating) {
return;
}
// Check scroll direction
if (event->angleDelta().y() > 0) {
// Scrolling up
if (currentPage > 0) {
currentPage--;
loadCurrentPage(); // Load the previous page
}
} else if (event->angleDelta().y() < 0) {
// Scrolling down
if ((currentPage + 1) * cardsPerPage < databaseDisplayModel->rowCount()) {
currentPage++;
loadCurrentPage(); // Load the next page
}
}
// Prevent overscrolling by stopping the event if needed
if ((currentPage == 0 && event->angleDelta().y() > 0) ||
((currentPage + 1) * cardsPerPage >= databaseDisplayModel->rowCount() && event->angleDelta().y() < 0)) {
event->ignore(); // Ignore the event to prevent overscrolling
} else {
event->accept(); // Accept the event if scrolling is valid
}
}
void VisualDatabaseDisplayWidget::setupPaginationControls()
{
QPushButton *prevButton = new QPushButton("Previous", this);
QPushButton *nextButton = new QPushButton("Next", this);
connect(prevButton, &QPushButton::clicked, this, [this]() {
if (currentPage > 0) {
currentPage--;
loadCurrentPage();
}
});
connect(nextButton, &QPushButton::clicked, this, [this]() {
if ((currentPage + 1) * cardsPerPage < databaseDisplayModel->rowCount()) {
currentPage++;
loadCurrentPage();
}
});
// Add buttons to layout (for example, at the bottom of the main layout)
main_layout->addWidget(prevButton);
main_layout->addWidget(nextButton);
}

View file

@ -0,0 +1,69 @@
#ifndef VISUAL_DATABASE_DISPLAY_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_WIDGET_H
#include "../../../../deck/deck_list_model.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_model.h"
#include "../../layouts/flow_layout.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include "../general/layout_containers/overlap_control_widget.h"
#include <QVBoxLayout>
#include <QWheelEvent>
#include <QWidget>
#include <qscrollarea.h>
class VisualDatabaseDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit VisualDatabaseDisplayWidget(QWidget *parent,
CardDatabaseModel *database_model,
CardDatabaseDisplayModel *database_display_model);
void updateDisplay();
void adjustCardsPerPage();
void populateCards();
void searchModelChanged();
void loadCurrentPage();
void sortCardList(QStringList properties, Qt::SortOrder order);
void setDeckList(const DeckList &new_deck_list_model);
signals:
void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void cardHoveredDatabaseDisplay(CardInfoPtr hoveredCard);
protected slots:
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void onHover(CardInfoPtr hoveredCard);
void databaseDataChanged(QModelIndex topLeft, QModelIndex bottomRight);
void wheelEvent(QWheelEvent *event) override;
void setupPaginationControls();
void modelDirty();
private:
CardDatabaseModel *databaseModel;
CardDatabaseDisplayModel *databaseDisplayModel;
QList<CardInfoPtr> *cards;
QVBoxLayout *main_layout;
QScrollArea *scrollArea;
FlowWidget *flow_widget;
QWidget *overlap_categories;
QVBoxLayout *overlap_categories_layout;
OverlapControlWidget *overlap_control_widget;
QWidget *container;
QTimer *debounce_timer;
bool isAnimating = false;
int debounce_time = 300;
int currentPage = 0; // Current page index
int cardsPerPage = 9; // Number of cards per page
int cardsPerRow = 0;
int rowsPerColumn = 0;
protected:
void resizeEvent(QResizeEvent *event) override;
};
#endif // VISUAL_DATABASE_DISPLAY_WIDGET_H

View file

@ -0,0 +1,247 @@
#include "visual_deck_editor_widget.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../main.h"
#include "../../../../utility/card_info_comparator.h"
#include "../../layouts/flow_layout.h"
#include "../../layouts/overlap_layout.h"
#include "../../layouts/vertical_flow_layout.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../cards/deck_card_zone_display_widget.h"
#include "../cards/overlapped_card_group_display_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include "../general/layout_containers/overlap_control_widget.h"
#include "../general/layout_containers/overlap_widget.h"
#include <QHBoxLayout>
#include <QResizeEvent>
#include <qscrollarea.h>
VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *deck_list_model)
: QWidget(parent), deck_list_model(deck_list_model)
{
this->mainDeckCards = new QList<CardInfoPtr>;
this->sideboardCards = new QList<CardInfoPtr>;
connect(deck_list_model, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged);
// The Main Widget and Main Layout, which contain a single Widget: The Scroll Area
this->setMinimumSize(0, 0);
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout = new QVBoxLayout();
this->setLayout(mainLayout);
sortByComboBox = new QComboBox();
QStringList sortProperties;
sortProperties << "maintype"
<< "colors"
<< "cmc"
<< "name";
sortByComboBox->addItems(sortProperties);
connect(sortByComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(activeSortCriteriaChanged()));
scrollArea = new QScrollArea();
scrollArea->setWidgetResizable(true);
scrollArea->setMinimumSize(0, 0);
// Set scrollbar policies
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
zoneContainer = new QWidget(scrollArea);
zoneContainerLayout = new QVBoxLayout(zoneContainer);
zoneContainer->setLayout(zoneContainerLayout);
scrollArea->addScrollBarWidget(zoneContainer, Qt::AlignHCenter);
scrollArea->setWidget(zoneContainer);
flowWidget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
overlap_control_widget = new OverlapControlWidget(80, 1, 1, Qt::Vertical, this);
mainLayout->addWidget(sortByComboBox);
mainLayout->addWidget(scrollArea);
mainLayout->addWidget(overlap_control_widget);
}
void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
zoneContainer->setMaximumWidth(scrollArea->viewport()->width());
}
void VisualDeckEditorWidget::activeSortCriteriaChanged()
{
activeSortCriteria = sortByComboBox->currentText();
qDebug() << "activeSortCriteria changed: " << activeSortCriteria;
updateDisplay();
}
void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight)
{
// Might use these at some point.
(void)topLeft;
(void)bottomRight;
// Necessary to delay this in this manner else the updateDisplay will nuke widgets while their onClick event
// hasn't returned yet. Interval of 0 means QT will schedule this after the current event loop has finished.
QTimer::singleShot(0, this, [this] { updateDisplay(); });
}
void VisualDeckEditorWidget::updateDisplay()
{
// Clear the layout first
populateCards();
zoneContainer = new QWidget(scrollArea);
zoneContainerLayout = new QVBoxLayout(zoneContainer);
DeckCardZoneDisplayWidget *mainBoardWidget =
new DeckCardZoneDisplayWidget(zoneContainer, sortCards(mainDeckCards), "Mainboard", 20, 10);
connect(mainBoardWidget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(mainBoardWidget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onMainboardClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
DeckCardZoneDisplayWidget *sideBoardWidget =
new DeckCardZoneDisplayWidget(zoneContainer, sortCards(sideboardCards), "Sideboard", 40, 30);
connect(sideBoardWidget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(sideBoardWidget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onSideboardClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
zoneContainerLayout->addWidget(mainBoardWidget);
zoneContainerLayout->addWidget(sideBoardWidget);
scrollArea->setWidget(zoneContainer);
update();
}
QList<QPair<QString, QList<CardInfoPtr>>> VisualDeckEditorWidget::sortCards(QList<CardInfoPtr> *cardsToSort)
{
QList<QPair<QString, QList<CardInfoPtr>>> sortedCardGroups;
QList<CardInfoPtr> activeList;
QStringList sortCriteria;
sortCriteria.append(activeSortCriteria);
sortCardList(sortCriteria, Qt::SortOrder::AscendingOrder);
QString lastSortCriteriaValue = cardsToSort->isEmpty() ? "" : cardsToSort->at(0)->getProperty(activeSortCriteria);
for (int i = 0; i < cardsToSort->size(); ++i) {
CardInfoPtr info = cardsToSort->at(i);
if (info) {
QString currentCriteriaValue = info->getProperty(activeSortCriteria);
if (currentCriteriaValue != lastSortCriteriaValue) {
// Save the current tuple (criteriaValue, list) and start a new list
sortedCardGroups.append(qMakePair(lastSortCriteriaValue, activeList));
activeList.clear();
lastSortCriteriaValue = currentCriteriaValue;
}
activeList.append(info);
} else {
qDebug() << "Card not found in database!";
}
}
// Add the last tuple to sortedCardGroups
if (!activeList.isEmpty()) {
sortedCardGroups.append(qMakePair(lastSortCriteriaValue, activeList));
}
return sortedCardGroups;
}
/*void VisualDeckEditorWidget::sortCards()
{
QStringList sortCriteria;
sortCriteria.append(activeSortCriteria);
this->sortCardList(sortCriteria, Qt::SortOrder::AscendingOrder);
QString lastSortCriteriaValue = "";
overlapCategories = new QWidget(scrollArea);
overlapCategoriesLayout = new QVBoxLayout(overlapCategories);
OverlapWidget *printings_group_widget = new OverlapWidget(80, 1, 1, Qt::Vertical, overlapCategories);
overlap_control_widget->connectOverlapWidget(printings_group_widget);
for (int i = 0; i < cards->size(); i++) {
CardInfoPtr info = cards->at(i);
if (info) {
if (info->getProperty(activeSortCriteria) != lastSortCriteriaValue) {
printings_group_widget->adjustMaxColumnsAndRows();
lastSortCriteriaValue = info->getProperty(activeSortCriteria);
printings_group_widget = new OverlapWidget(80, 1, 1, Qt::Vertical, overlapCategories);
overlapCategoriesLayout->addWidget(printings_group_widget);
overlap_control_widget->connectOverlapWidget(printings_group_widget);
}
CardInfoPictureWithTextOverlayWidget* display = new
CardInfoPictureWithTextOverlayWidget(printings_group_widget, true);
display->setCard(info);
printings_group_widget->addWidget(display);
update();
} else {
qDebug() << "Card not found in database!";
}
}
printings_group_widget->adjustMaxColumnsAndRows();
scrollArea->setWidget(overlapCategories);
update();
}*/
void VisualDeckEditorWidget::onHover(CardInfoPtr hoveredCard)
{
emit activeCardChanged(hoveredCard);
}
void VisualDeckEditorWidget::onMainboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance)
{
emit mainboardCardClicked(event, instance);
}
void VisualDeckEditorWidget::onSideboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance)
{
emit sideboardCardClicked(event, instance);
}
void VisualDeckEditorWidget::populateCards()
{
mainDeckCards->clear();
sideboardCards->clear();
if (!deck_list_model)
return;
DeckList *decklist = deck_list_model->getDeckList();
if (!decklist)
return;
InnerDecklistNode *listRoot = decklist->getRoot();
if (!listRoot)
return;
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
if (!currentZone)
continue;
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
continue;
for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCardByNameAndProviderId(
currentCard->getName(), currentCard->getCardProviderId());
if (info) {
if (currentZone->getName() == DECK_ZONE_MAIN) {
mainDeckCards->append(info);
} else if (currentZone->getName() == DECK_ZONE_SIDE) {
sideboardCards->append(info);
}
} else {
qDebug() << "Card not found in database!";
}
}
}
}
}
void VisualDeckEditorWidget::sortCardList(const QStringList properties, Qt::SortOrder order = Qt::AscendingOrder)
{
CardInfoComparator comparator(properties, order);
std::sort(mainDeckCards->begin(), mainDeckCards->end(), comparator);
std::sort(sideboardCards->begin(), sideboardCards->end(), comparator);
}

View file

@ -0,0 +1,58 @@
#ifndef VISUAL_DECK_EDITOR_H
#define VISUAL_DECK_EDITOR_H
#include "../../../../deck/deck_list_model.h"
#include "../../../../game/cards/card_database.h"
#include "../../layouts/flow_layout.h"
#include "../../layouts/vertical_flow_layout.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include "../general/layout_containers/overlap_control_widget.h"
#include <QWidget>
#include <qscrollarea.h>
class VisualDeckEditorWidget : public QWidget
{
Q_OBJECT
public:
explicit VisualDeckEditorWidget(QWidget *parent = nullptr, DeckListModel *deck_list_model = nullptr);
void resizeEvent(QResizeEvent *event) override;
void updateDisplay();
QWidget *displayCards(QList<QPair<QString, QList<CardInfoPtr>>> cardLists, int bannerOpacity);
QList<QPair<QString, QList<CardInfoPtr>>> sortCards(QList<CardInfoPtr> *cardsToSort);
OverlapWidget *getSortedCards();
void populateCards();
void sortCardList(QStringList properties, Qt::SortOrder order);
void setDeckList(const DeckList &new_deck_list_model);
signals:
void activeCardChanged(CardInfoPtr activeCard);
void mainboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void sideboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
protected slots:
void onHover(CardInfoPtr hoveredCard);
void onMainboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void onSideboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight);
void activeSortCriteriaChanged();
private:
DeckListModel *deck_list_model;
QList<CardInfoPtr> *mainDeckCards;
QList<CardInfoPtr> *sideboardCards;
QVBoxLayout *mainLayout;
QComboBox *sortByComboBox;
QString activeSortCriteria = "maintype";
FlowWidget *flowWidget;
QScrollArea *scrollArea;
QWidget *zoneContainer;
QVBoxLayout *zoneContainerLayout;
OverlapControlWidget *overlap_control_widget;
QWidget *container;
};
#endif // VISUAL_DECK_EDITOR_H

View file

@ -878,6 +878,8 @@ MainWindow::MainWindow(QWidget *parent)
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
&MainWindow::refreshShortcuts);
refreshShortcuts();
connect(CardDatabaseManager::getInstance(), SIGNAL(cardDatabaseLoadingFinished()), tabSupervisor,
SLOT(addVisualDeckEditorTab()));
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFailed, this,
&MainWindow::cardDatabaseLoadingFailed);

View file

@ -0,0 +1,75 @@
#include "card_info_comparator.h"
CardInfoComparator::CardInfoComparator(const QStringList &properties, Qt::SortOrder order)
: m_properties(properties), m_order(order)
{
}
bool CardInfoComparator::operator()(const CardInfoPtr &a, const CardInfoPtr &b) const
{
// Iterate over each property in the list
for (const QString &property : m_properties) {
QVariant valueA = getProperty(a, property);
QVariant valueB = getProperty(b, property);
// Compare the current property
if (valueA != valueB) {
// If values differ, perform comparison
return compareVariants(valueA, valueB) ? (m_order == Qt::AscendingOrder) : (m_order == Qt::DescendingOrder);
}
}
// If all properties are equal, return false (indicating they are considered equal for sorting purposes)
return false;
}
bool CardInfoComparator::compareVariants(const QVariant &a, const QVariant &b) const
{
// Determine the type of QVariant based on Qt version
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (a.typeId() != b.typeId()) {
#else
if (a.type() != b.type()) {
#endif
// If they are not the same type, compare as strings
return a.toString() < b.toString();
}
// Perform type-specific comparison
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
switch (a.typeId()) {
#else
switch (a.type()) {
#endif
case QMetaType::Int:
return a.toInt() < b.toInt();
case QMetaType::Double:
return a.toDouble() < b.toDouble();
case QMetaType::QString:
return a.toString() < b.toString();
case QMetaType::Bool:
return a.toBool() < b.toBool();
default:
// Default to comparing as strings
return a.toString() < b.toString();
}
}
QVariant CardInfoComparator::getProperty(const CardInfoPtr &card, const QString &property) const
{
// Check if the property exists in the main fields of the class
if (property == "name") {
return card->getName();
} else if (property == "text") {
return card->getText();
} else if (property == "isToken") {
return card->getIsToken();
}
// Otherwise, check if it's a custom property in the QVariantHash
if (card->hasProperty(property)) {
return card->getProperty(property);
}
return QVariant(); // Return an invalid variant if the property does not exist
}

View file

@ -0,0 +1,24 @@
#ifndef CARD_INFO_COMPARATOR_H
#define CARD_INFO_COMPARATOR_H
#include "../game/cards/card_database.h"
#include <QStringList>
#include <QVariant>
#include <Qt>
class CardInfoComparator
{
public:
explicit CardInfoComparator(const QStringList &properties, Qt::SortOrder order = Qt::AscendingOrder);
bool operator()(const CardInfoPtr &a, const CardInfoPtr &b) const;
private:
QStringList m_properties; // List of properties to sort by
Qt::SortOrder m_order;
QVariant getProperty(const CardInfoPtr &card, const QString &property) const;
bool compareVariants(const QVariant &a, const QVariant &b) const;
};
#endif // CARD_INFO_COMPARATOR_H