From 7fb09cd149e54946122fc1451da37358c28fcbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Mon, 24 Feb 2025 15:13:56 +0100 Subject: [PATCH] Better filtering and analytics. --- cockatrice/resources/config/qtlogging.ini | 10 +- .../tab_deck_editor_visual.cpp | 143 +++----------- .../tab_deck_editor_visual.h | 3 +- ...deck_editor_visual_database_search_tab.cpp | 138 ------------- ...b_deck_editor_visual_database_search_tab.h | 97 --------- .../tab_deck_editor_visual_deck_cards_tab.h | 8 - .../tab_deck_editor_visual_deck_stats_tab.h | 8 - .../tab_deck_editor_visual_tab_widget.cpp | 12 +- .../tab_deck_editor_visual_tab_widget.h | 3 + .../deck_analytics/deck_analytics_widget.cpp | 37 ++-- .../deck_analytics/deck_analytics_widget.h | 16 +- .../deck_analytics/mana_base_widget.cpp | 66 ++++--- .../widgets/deck_analytics/mana_base_widget.h | 8 +- .../deck_analytics/mana_curve_widget.cpp | 84 ++++++-- .../deck_analytics/mana_curve_widget.h | 22 ++- .../deck_analytics/mana_devotion_widget.cpp | 63 +++--- .../deck_analytics/mana_devotion_widget.h | 10 +- .../ui/widgets/general/display/bar_widget.cpp | 42 ++-- .../ui/widgets/general/display/bar_widget.h | 16 +- ...l_database_display_color_filter_widget.cpp | 186 ++++++++++++++++++ ...ual_database_display_color_filter_widget.h | 58 ++++++ ...tabase_display_main_type_filter_widget.cpp | 142 +++++++++++++ ...database_display_main_type_filter_widget.h | 41 ++++ ...ual_database_display_set_filter_widget.cpp | 138 +++++++++++++ ...isual_database_display_set_filter_widget.h | 42 ++++ .../visual_database_display_widget.cpp | 116 +++++++++-- .../visual_database_display_widget.h | 23 ++- .../visual_deck_editor_widget.cpp | 4 +- 28 files changed, 999 insertions(+), 537 deletions(-) delete mode 100644 cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.cpp delete mode 100644 cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.h delete mode 100644 cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_cards_tab.h delete mode 100644 cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_stats_tab.h create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.h create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.h create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.h diff --git a/cockatrice/resources/config/qtlogging.ini b/cockatrice/resources/config/qtlogging.ini index 43dea5f2b..40d41701c 100644 --- a/cockatrice/resources/config/qtlogging.ini +++ b/cockatrice/resources/config/qtlogging.ini @@ -54,11 +54,13 @@ cockatrice_xml.* = false # card_info = false # card_list = false -#flow_layout = false -#flow_widget = false -#flow_widget.size = false +visual_database_display = false -# card_info_picture_widget = false +# stack_zone = false + +flow_layout.debug = false +flow_widget.debug = false +flow_widget.size.debug = false overlap_layout.debug = false diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index 75286a54b..e6751e44f 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -58,14 +58,13 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : TabGen { setObjectName("TabDeckEditorVisual"); - createSearchAndDatabaseFrame(); - + databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this, this); deckDockWidget = new DeckEditorDeckDockWidget(this, this); cardInfoDockWidget = new DeckEditorCardInfoDockWidget(this); filterDockWidget = new DeckEditorFilterDockWidget(this, this); printingSelectorDockWidget = new DeckEditorPrintingSelectorDockWidget(this, this); - TabDeckEditorVisual::createCentralFrame(); + createCentralFrame(); TabDeckEditorVisual::createMenus(); @@ -86,10 +85,13 @@ void TabDeckEditorVisual::createCentralFrame() centralFrame = new QVBoxLayout; centralWidget->setLayout(centralFrame); - tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this->deckDockWidget->deckModel, this->databaseModel, - this->databaseDisplayModel); - connect(tabContainer, SIGNAL(cardChanged(CardInfoPtr)), this, SLOT(changeModelIndexAndCardInfo(CardInfoPtr))); - connect(tabContainer, SIGNAL(cardChangedDatabaseDisplay(CardInfoPtr)), this, SLOT(setCurrentCardInfo(CardInfoPtr))); + tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, this->deckDockWidget->deckModel, + this->databaseDisplayDockWidget->databaseModel, + this->databaseDisplayDockWidget->databaseDisplayModel); + connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChanged, this, + &TabDeckEditorVisual::changeModelIndexAndCardInfo); + connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChangedDatabaseDisplay, this, + &TabGenericDeckEditor::updateCardInfo); connect(tabContainer, SIGNAL(mainboardCardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this, SLOT(processMainboardCardClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *))); connect(tabContainer, SIGNAL(sideboardCardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this, @@ -103,100 +105,6 @@ void TabDeckEditorVisual::createCentralFrame() setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks); } -void TabDeckEditorVisual::createSearchAndDatabaseFrame() -{ - searchEdit = new SearchLineEdit; - searchEdit->setObjectName("searchEdit"); - searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)")); - searchEdit->setClearButtonEnabled(true); - searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); - auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); - searchEdit->installEventFilter(&searchKeySignals); - - setFocusProxy(searchEdit); - setFocusPolicy(Qt::ClickFocus); - - searchKeySignals.setObjectName("searchKeySignals"); - connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); - connect(&searchKeySignals, SIGNAL(onEnter()), this, SLOT(actAddCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actAddCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltRBracket()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlC()), this, SLOT(copyDatabaseCellContents())); - connect(help, &QAction::triggered, this, &TabDeckEditorVisual::showSearchSyntaxHelp); - - databaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), true, this); - databaseModel->setObjectName("databaseModel"); - databaseDisplayModel = new CardDatabaseDisplayModel(this); - databaseDisplayModel->setSourceModel(databaseModel); - databaseDisplayModel->setFilterKeyColumn(0); - - databaseView = new QTreeView(); - databaseView->setObjectName("databaseView"); - databaseView->setFocusProxy(searchEdit); - databaseView->setUniformRowHeights(true); - databaseView->setRootIsDecorated(false); - databaseView->setAlternatingRowColors(true); - databaseView->setSortingEnabled(true); - databaseView->sortByColumn(0, Qt::AscendingOrder); - databaseView->setModel(databaseDisplayModel); - databaseView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(databaseView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(databaseCustomMenu(QPoint))); - connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, - SLOT(updateCardInfoLeft(const QModelIndex &, const QModelIndex &))); - connect(databaseView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actAddCard())); - - QByteArray dbHeaderState = SettingsCache::instance().layouts().getDeckEditorDbHeaderState(); - if (dbHeaderState.isNull()) { - // first run - databaseView->setColumnWidth(0, 200); - } else { - databaseView->header()->restoreState(dbHeaderState); - } - connect(databaseView->header(), SIGNAL(geometriesChanged()), this, SLOT(saveDbHeaderState())); - - searchEdit->setTreeView(databaseView); - - aAddCard = new QAction(QString(), this); - aAddCard->setIcon(QPixmap("theme:icons/arrow_right_green")); - connect(aAddCard, SIGNAL(triggered()), this, SLOT(actAddCard())); - auto *tbAddCard = new QToolButton(this); - tbAddCard->setDefaultAction(aAddCard); - - aAddCardToSideboard = new QAction(QString(), this); - aAddCardToSideboard->setIcon(QPixmap("theme:icons/arrow_right_blue")); - connect(aAddCardToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); - auto *tbAddCardToSideboard = new QToolButton(this); - tbAddCardToSideboard->setDefaultAction(aAddCardToSideboard); - - searchLayout = new QHBoxLayout; - searchLayout->setObjectName("searchLayout"); - searchLayout->addWidget(searchEdit); - searchLayout->addWidget(tbAddCard); - searchLayout->addWidget(tbAddCardToSideboard); - - searchAndDatabaseFrame = new QVBoxLayout; - searchAndDatabaseFrame->setObjectName("searchAndDatabaseFrame"); - searchAndDatabaseFrame->addLayout(searchLayout); - searchAndDatabaseFrame->addWidget(databaseView); - - searchAndDatabaseDock = new QDockWidget(this); - searchAndDatabaseDock->setObjectName("searchAndDatabaseDock"); - - searchAndDatabaseDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | - QDockWidget::DockWidgetMovable); - auto *searchAndDatabaseDockContents = new QWidget(this); - searchAndDatabaseDockContents->setObjectName("filterDockContents"); - searchAndDatabaseDockContents->setLayout(searchAndDatabaseFrame); - searchAndDatabaseDock->setWidget(searchAndDatabaseDockContents); - - searchAndDatabaseDock->installEventFilter(this); - connect(searchAndDatabaseDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool))); -} - void TabDeckEditorVisual::createMenus() { deckMenu = new DeckEditorMenu(this, this); @@ -274,11 +182,10 @@ void TabDeckEditorVisual::changeModelIndexToCard(CardInfoPtr activeCard) void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance) { - (void)instance; if (event->button() == Qt::LeftButton) { - deckDockWidget->actSwapCard(); + actSwapCard(instance->getInfo(), DECK_ZONE_MAIN); } else if (event->button() == Qt::RightButton) { - deckDockWidget->actDecrement(); + actDecrementCard(instance->getInfo()); } else if (event->button() == Qt::MiddleButton) { deckDockWidget->actRemoveCard(); } @@ -286,11 +193,10 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, CardInfo void TabDeckEditorVisual::processSideboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance) { - (void)instance; if (event->button() == Qt::LeftButton) { - deckDockWidget->actSwapCard(); + actSwapCard(instance->getInfo(), DECK_ZONE_SIDE); } else if (event->button() == Qt::RightButton) { - deckDockWidget->actDecrement(); + actDecrementCardFromSideboard(instance->getInfo()); } else if (event->button() == Qt::MiddleButton) { deckDockWidget->actRemoveCard(); } @@ -299,11 +205,10 @@ void TabDeckEditorVisual::processSideboardCardClick(QMouseEvent *event, CardInfo void TabDeckEditorVisual::processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance) { - (void)instance; if (event->button() == Qt::LeftButton) { - // addCardInfo(instance->getInfo(), DECK_ZONE_MAIN); + actAddCard(instance->getInfo()); } else if (event->button() == Qt::RightButton) { - deckDockWidget->actDecrement(); + actDecrementCard(instance->getInfo()); } else if (event->button() == Qt::MiddleButton) { deckDockWidget->actRemoveCard(); } @@ -354,6 +259,9 @@ void TabDeckEditorVisual::freeDocksSize() filterDockWidget->setMinimumSize(100, 100); filterDockWidget->setMaximumSize(5000, 5000); + + databaseDisplayDockWidget->setMinimumSize(100, 100); + databaseDisplayDockWidget->setMaximumSize(1400, 5000); } void TabDeckEditorVisual::refreshShortcuts() @@ -394,14 +302,14 @@ void TabDeckEditorVisual::loadLayout() deckDockWidget->setMinimumSize(layouts.getDeckEditorDeckSize()); deckDockWidget->setMaximumSize(layouts.getDeckEditorDeckSize()); + databaseDisplayDockWidget->setMinimumSize(100, 100); + databaseDisplayDockWidget->setMaximumSize(1400, 5000); + QTimer::singleShot(100, this, SLOT(freeDocksSize())); } void TabDeckEditorVisual::retranslateUi() { - aAddCard->setText(tr("Add card to &maindeck")); - aAddCardToSideboard->setText(tr("Add card to &sideboard")); - deckMenu->setTitle(tr("&Visual Deck Editor")); cardInfoDockWidget->setWindowTitle(tr("Card Info")); @@ -476,7 +384,7 @@ void TabDeckEditorVisual::openDeckFromFile(const QString &fileName, DeckOpenLoca emit openDeckEditor(l); } else { deckMenu->setSaveStatus(false); - setDeck(l); + TabDeckEditorVisual::setDeck(l); } } else { delete l; @@ -484,6 +392,13 @@ void TabDeckEditorVisual::openDeckFromFile(const QString &fileName, DeckOpenLoca } deckMenu->setSaveStatus(true); tabContainer->visualDeckView->updateDisplay(); + tabContainer->deckAnalytics->refreshDisplays(deckDockWidget->deckModel); +} + +void TabDeckEditorVisual::setDeck(DeckLoader *_deck) +{ + TabGenericDeckEditor::setDeck(_deck); + qDebug() << "set the deck"; } void TabDeckEditorVisual::showPrintingSelector() diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h index 5d48e8d25..8ed8a085e 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h @@ -38,6 +38,7 @@ protected: public: explicit TabDeckEditorVisual(TabSupervisor *_tabSupervisor); + void setDeck(DeckLoader *_deck) override; void retranslateUi() override; QString getTabText() const override; void changeModelIndexAndCardInfo(CardInfoPtr activeCard); @@ -45,7 +46,7 @@ public: void createDeckAnalyticsDock(); void createMenus() override; void createSearchAndDatabaseFrame(); - void createCentralFrame() override; + void createCentralFrame(); public slots: void showPrintingSelector() override; diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.cpp deleted file mode 100644 index f30468daf..000000000 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#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 ¤t, const QModelIndex &previous) -{ - (void)current; - (void)previous; -} -void TabDeckEditorVisualDatabaseSearchTab::updateCardInfoRight(const QModelIndex ¤t, 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; -} diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.h deleted file mode 100644 index 77ebbafea..000000000 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_database_search_tab.h +++ /dev/null @@ -1,97 +0,0 @@ -#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 - -class TabDeckEditorVisualDatabaseSearchTab : public Tab -{ - Q_OBJECT -private slots: - void updateCardInfoLeft(const QModelIndex ¤t, const QModelIndex &previous); - void updateCardInfoRight(const QModelIndex ¤t, 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 diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_cards_tab.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_cards_tab.h deleted file mode 100644 index 7c2a6f062..000000000 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_cards_tab.h +++ /dev/null @@ -1,8 +0,0 @@ -// -// 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 diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_stats_tab.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_stats_tab.h deleted file mode 100644 index 8b01bdc98..000000000 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_deck_stats_tab.h +++ /dev/null @@ -1,8 +0,0 @@ -// -// 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 diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp index 1e87bff47..b88b3ffb5 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp @@ -1,12 +1,14 @@ #include "tab_deck_editor_visual_tab_widget.h" #include "../../ui/widgets/visual_database_display/visual_database_display_widget.h" +#include "../tab_generic_deck_editor.h" TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, + TabGenericDeckEditor *_deckEditor, DeckListModel *model, CardDatabaseModel *card_database_model, CardDatabaseDisplayModel *card_database_display_model) - : QTabWidget(parent), deckModel(model), cardDatabaseModel(card_database_model), + : QTabWidget(parent), deckEditor(_deckEditor), deckModel(model), cardDatabaseModel(card_database_model), cardDatabaseDisplayModel(card_database_display_model) { this->setTabsClosable(true); // Enable tab closing @@ -16,7 +18,7 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, layout = new QVBoxLayout(this); setLayout(layout); - visualDeckView = new VisualDeckEditorWidget(this, this->deckModel); + visualDeckView = new VisualDeckEditorWidget(this, deckModel); visualDeckView->setObjectName("visualDeckView"); visualDeckView->updateDisplay(); connect(visualDeckView, SIGNAL(activeCardChanged(CardInfoPtr)), this, SLOT(onCardChanged(CardInfoPtr))); @@ -25,8 +27,7 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, connect(visualDeckView, SIGNAL(sideboardCardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this, SLOT(onSideboardCardClickedDeckEditor(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *))); - visualDatabaseDisplay = - new VisualDatabaseDisplayWidget(this, this->cardDatabaseModel, this->cardDatabaseDisplayModel); + visualDatabaseDisplay = new VisualDatabaseDisplayWidget(this, cardDatabaseModel, cardDatabaseDisplayModel); visualDatabaseDisplay->setObjectName("visualDatabaseView"); connect(visualDatabaseDisplay, SIGNAL(cardHoveredDatabaseDisplay(CardInfoPtr)), this, SLOT(onCardChangedDatabaseDisplay(CardInfoPtr))); @@ -34,9 +35,8 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, SIGNAL(cardClickedDatabaseDisplay(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this, SLOT(onCardClickedDatabaseDisplay(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *))); - deckAnalytics = new DeckAnalyticsWidget(this, this->deckModel); + deckAnalytics = new DeckAnalyticsWidget(this, deckModel); deckAnalytics->setObjectName("deckAnalytics"); - // deckAnalytics->analyzeManaCurve(); // printingSelector = new PrintingSelector(this, this->deckModel); diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h index c4c840ee5..00da329d1 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h @@ -5,6 +5,7 @@ #include "../../ui/widgets/printing_selector/printing_selector.h" #include "../../ui/widgets/visual_database_display/visual_database_display_widget.h" #include "../../ui/widgets/visual_deck_editor/visual_deck_editor_widget.h" +#include "../tab_generic_deck_editor.h" #include #include @@ -16,6 +17,7 @@ class TabDeckEditorVisualTabWidget : public QTabWidget public: explicit TabDeckEditorVisualTabWidget(QWidget *parent = nullptr, + TabGenericDeckEditor *deckEditor = nullptr, DeckListModel *deckModel = nullptr, CardDatabaseModel *card_database_model = nullptr, CardDatabaseDisplayModel *card_database_display_model = nullptr); @@ -48,6 +50,7 @@ signals: private: QVBoxLayout *layout; // Layout for the tab widget and other controls + TabGenericDeckEditor *deckEditor; DeckListModel *deckModel; CardDatabaseModel *cardDatabaseModel; CardDatabaseDisplayModel *cardDatabaseDisplayModel; diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp index d702f1aca..62a7cc117 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp @@ -1,26 +1,25 @@ #include "deck_analytics_widget.h" -DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListModel *deck_list_model) - : QWidget(parent), deck_list_model(deck_list_model) +DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListModel *_deckListModel) + : QWidget(parent), deckListModel(_deckListModel) { + mainLayout = new QVBoxLayout(); + setLayout(mainLayout); - this->setMinimumSize(0, 0); - this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - this->setStyleSheet("border: 2px solid red;"); - main_layout = new QHBoxLayout(); - this->setLayout(main_layout); + manaCurveWidget = new ManaCurveWidget(this); + mainLayout->addWidget(manaCurveWidget); - flow_widget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff); - this->main_layout->addWidget(flow_widget); + manaDevotionWidget = new ManaDevotionWidget(this, deckListModel); + mainLayout->addWidget(manaDevotionWidget); - 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); + manaBaseWidget = new ManaBaseWidget(this, deckListModel); + mainLayout->addWidget(manaBaseWidget); +} + +void DeckAnalyticsWidget::refreshDisplays(DeckListModel *_deckModel) +{ + deckListModel = _deckModel; + manaCurveWidget->setDeckModel(_deckModel); + manaDevotionWidget->setDeckModel(_deckModel); + manaBaseWidget->setDeckModel(_deckModel); } diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.h b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.h index 198adf5ab..6e7a273cf 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.h +++ b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.h @@ -17,18 +17,18 @@ 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); + explicit DeckAnalyticsWidget(QWidget *parent, DeckListModel *deckListModel); + void setDeckList(const DeckList &_deckListModel); std::map analyzeManaCurve(); + void refreshDisplays(DeckListModel *_deckListModel); private: - DeckListModel *deck_list_model; - QHBoxLayout *main_layout; - FlowWidget *flow_widget; + DeckListModel *deckListModel; + QVBoxLayout *mainLayout; - ManaCurveWidget *mana_curve_widget; - ManaDevotionWidget *mana_devotion_widget; - ManaBaseWidget *mana_base_widget; + ManaCurveWidget *manaCurveWidget; + ManaDevotionWidget *manaDevotionWidget; + ManaBaseWidget *manaBaseWidget; }; #endif // DECK_ANALYTICS_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp index 515de6635..e0fbdd8d8 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp @@ -4,6 +4,7 @@ #include "../../../../game/cards/card_database.h" #include "../../../../game/cards/card_database_manager.h" #include "../../../../main.h" +#include "../general/display/banner_widget.h" #include "../general/display/bar_widget.h" #include @@ -11,18 +12,32 @@ #include #include -ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListModel *deck_list_model) - : QWidget(parent), deck_list_model(deck_list_model) +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); + layout = new QVBoxLayout(this); + setLayout(layout); + + bannerWidget = new BannerWidget(this, "Mana Base", Qt::Vertical, 100); + bannerWidget->setMaximumHeight(100); + layout->addWidget(bannerWidget); + + barLayout = new QHBoxLayout(); + layout->addLayout(barLayout); + + connect(deck_list_model, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase); +} + +void ManaBaseWidget::setDeckModel(DeckListModel *deckModel) +{ + deck_list_model = deckModel; + connect(deck_list_model, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase); + analyzeManaBase(); } std::unordered_map ManaBaseWidget::analyzeManaBase() { - manaBaseMap = std::unordered_map(); + manaBaseMap.clear(); InnerDecklistNode *listRoot = deck_list_model->getDeckList()->getRoot(); for (int i = 0; i < listRoot->size(); i++) { InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); @@ -41,29 +56,17 @@ std::unordered_map ManaBaseWidget::analyzeManaBase() } } - update(); - // this->setCurve(ManaBaseMap); - // mana_curve_widget->updateDisplay(); - this->updateDisplay(); - update(); + updateDisplay(); 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); + QLayoutItem *item; + while ((item = barLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; } int totalSum = 0; @@ -71,13 +74,18 @@ void ManaBaseWidget::updateDisplay() totalSum += entry.second; } - // Add new widgets to the layout + // Define color mapping for mana types + std::unordered_map manaColors = {{'W', QColor(248, 231, 185)}, {'U', QColor(14, 104, 171)}, + {'B', QColor(21, 11, 0)}, {'R', QColor(211, 32, 42)}, + {'G', QColor(0, 115, 62)}, {'C', QColor(150, 150, 150)}}; + for (auto entry : manaBaseMap) { - BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, totalSum, this); - layout->addWidget(barWidget); + QColor barColor = manaColors.count(entry.first) ? manaColors[entry.first] : Qt::gray; + BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, totalSum, barColor, this); + barLayout->addWidget(barWidget); } - update(); // Update the widget display + update(); } std::unordered_map ManaBaseWidget::determineManaProduction(const QString &rulesText) @@ -131,4 +139,4 @@ void ManaBaseWidget::mergeManaCounts(std::unordered_map &manaCounts1, for (const auto &pair : manaCounts2) { manaCounts1[pair.first] += pair.second; // Add values for matching keys } -} +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.h b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.h index 4fffcfc5d..ed2bdf9f2 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.h +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.h @@ -1,4 +1,5 @@ #include "../../../../deck/deck_list_model.h" +#include "../general/display/banner_widget.h" #include #include @@ -20,10 +21,15 @@ public: std::unordered_map determineManaProduction(const QString &manaString); void mergeManaCounts(std::unordered_map &manaCounts1, const std::unordered_map &manaCounts2); +public slots: + void setDeckModel(DeckListModel *deckModel); + private: DeckListModel *deck_list_model; + BannerWidget *bannerWidget; std::unordered_map manaBaseMap; - QHBoxLayout *layout; + QVBoxLayout *layout; + QHBoxLayout *barLayout; }; #endif // MANA_BASE_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp index fc5a2b88b..982e57234 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp @@ -1,43 +1,89 @@ #include "mana_curve_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/banner_widget.h" #include "../general/display/bar_widget.h" -#include +#include +#include -ManaCurveWidget::ManaCurveWidget(QWidget *parent) : QWidget(parent) +ManaCurveWidget::ManaCurveWidget(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); + layout = new QVBoxLayout(this); + setLayout(layout); + bannerWidget = new BannerWidget(this, "Mana Curve", Qt::Vertical, 100); + bannerWidget->setMaximumHeight(100); + layout->addWidget(bannerWidget); + connect(deck_list_model, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve); +} + +void ManaCurveWidget::setDeckModel(DeckListModel *deckModel) +{ + deck_list_model = deckModel; + connect(deck_list_model, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve); + analyzeManaCurve(); +} + +std::unordered_map ManaCurveWidget::analyzeManaCurve() +{ + manaCurveMap.clear(); + InnerDecklistNode *listRoot = deck_list_model->getDeckList()->getRoot(); + for (int i = 0; i < listRoot->size(); i++) { + InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); + for (int j = 0; j < currentZone->size(); j++) { + DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); + if (!currentCard) + continue; + + for (int k = 0; k < currentCard->getNumber(); ++k) { + CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName()); + if (info) { + int cmc = info->getCmc().toInt(); + manaCurveMap[cmc]++; + } + } + } + } + + updateDisplay(); + + return manaCurveMap; } void ManaCurveWidget::updateDisplay() { // Clear the layout first - if (layout != nullptr) { + if (barLayout != nullptr) { QLayoutItem *item; - while ((item = layout->takeAt(0)) != nullptr) { - delete item->widget(); // Delete the widget - delete item; // Delete the layout item + while ((item = barLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; } } - // If layout is null, create a new layout, otherwise reuse the existing one - if (layout == nullptr) { - layout = new QHBoxLayout(this); - this->setLayout(layout); + // Ensure layout exists + if (barLayout == nullptr) { + barLayout = new QHBoxLayout(this); + layout->addLayout(barLayout); } int totalSum = 0; - for (auto entry : curve) { + for (const auto &entry : manaCurveMap) { 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); + // Convert unordered_map to ordered map to ensure sorting by CMC + std::map sortedManaCurve(manaCurveMap.begin(), manaCurveMap.end()); + + // Add new widgets to the layout in sorted order + for (const auto &entry : sortedManaCurve) { + BarWidget *barWidget = + new BarWidget(QString::number(entry.first), entry.second, totalSum, QColor(11, 11, 11), this); + barLayout->addWidget(barWidget); } update(); // Update the widget display diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.h b/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.h index 7cd9d3f7e..9baab7fed 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.h +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.h @@ -1,25 +1,31 @@ #ifndef MANA_CURVE_WIDGET_H #define MANA_CURVE_WIDGET_H + +#include "../../../../deck/deck_list_model.h" +#include "../general/display/banner_widget.h" + #include #include -#include +#include class ManaCurveWidget : public QWidget { Q_OBJECT public: - explicit ManaCurveWidget(QWidget *parent = nullptr); + explicit ManaCurveWidget(QWidget *parent = nullptr, DeckListModel *deck_list_model = nullptr); void updateDisplay(); - void setCurve(std::map new_curve) - { - this->curve = std::move(new_curve); - } +public slots: + void setDeckModel(DeckListModel *deckModel); + std::unordered_map analyzeManaCurve(); private: - std::map curve; - QHBoxLayout *layout; + DeckListModel *deck_list_model = nullptr; + std::unordered_map manaCurveMap; + QVBoxLayout *layout; + QHBoxLayout *barLayout; + BannerWidget *bannerWidget; }; #endif // MANA_CURVE_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp index 1419fdb6f..b71150177 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp @@ -4,6 +4,7 @@ #include "../../../../game/cards/card_database.h" #include "../../../../game/cards/card_database_manager.h" #include "../../../../main.h" +#include "../general/display/banner_widget.h" #include "../general/display/bar_widget.h" #include @@ -12,18 +13,32 @@ #include #include -ManaDevotionWidget::ManaDevotionWidget(QWidget *parent, DeckListModel *deck_list_model) - : QWidget(parent), deck_list_model(deck_list_model) +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); + layout = new QVBoxLayout(this); + setLayout(layout); + + bannerWidget = new BannerWidget(this, "Mana Devotion", Qt::Vertical, 100); + bannerWidget->setMaximumHeight(100); + layout->addWidget(bannerWidget); + + barLayout = new QHBoxLayout(); + layout->addLayout(barLayout); + + connect(deck_list_model, &DeckListModel::dataChanged, this, &ManaDevotionWidget::analyzeManaDevotion); +} + +void ManaDevotionWidget::setDeckModel(DeckListModel *deckModel) +{ + deck_list_model = deckModel; + connect(deck_list_model, &DeckListModel::dataChanged, this, &ManaDevotionWidget::analyzeManaDevotion); + analyzeManaDevotion(); } std::unordered_map ManaDevotionWidget::analyzeManaDevotion() { - manaDevotionMap = std::unordered_map(); + manaDevotionMap.clear(); InnerDecklistNode *listRoot = deck_list_model->getDeckList()->getRoot(); for (int i = 0; i < listRoot->size(); i++) { InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); @@ -42,30 +57,17 @@ std::unordered_map ManaDevotionWidget::analyzeManaDevotion() } } - update(); - // this->setCurve(manaDevotionMap); - // mana_curve_widget->updateDisplay(); - this->updateDisplay(); - update(); + updateDisplay(); 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); + QLayoutItem *item; + while ((item = barLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; } int totalSum = 0; @@ -73,10 +75,15 @@ void ManaDevotionWidget::updateDisplay() totalSum += entry.second; } - // Add new widgets to the layout + // Define color mapping for devotion bars + std::unordered_map manaColors = {{'W', QColor(248, 231, 185)}, {'U', QColor(14, 104, 171)}, + {'B', QColor(21, 11, 0)}, {'R', QColor(211, 32, 42)}, + {'G', QColor(0, 115, 62)}, {'C', QColor(150, 150, 150)}}; + for (auto entry : manaDevotionMap) { - BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, totalSum, this); - layout->addWidget(barWidget); + QColor barColor = manaColors.count(entry.first) ? manaColors[entry.first] : Qt::gray; + BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, totalSum, barColor, this); + barLayout->addWidget(barWidget); } update(); // Update the widget display diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.h b/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.h index 7584ba527..fcaffbf9e 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.h +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.h @@ -1,6 +1,7 @@ #ifndef MANA_DEVOTION_WIDGET_H #define MANA_DEVOTION_WIDGET_H #include "../../../../deck/deck_list_model.h" +#include "../general/display/banner_widget.h" #include #include @@ -13,16 +14,21 @@ class ManaDevotionWidget : public QWidget public: explicit ManaDevotionWidget(QWidget *parent = nullptr, DeckListModel *deck_list_model = nullptr); - std::unordered_map analyzeManaDevotion(); void updateDisplay(); std::unordered_map countManaSymbols(const QString &manaString); void mergeManaCounts(std::unordered_map &manaCounts1, const std::unordered_map &manaCounts2); +public slots: + void setDeckModel(DeckListModel *deckModel); + std::unordered_map analyzeManaDevotion(); + private: DeckListModel *deck_list_model; + BannerWidget *bannerWidget; std::unordered_map manaDevotionMap; - QHBoxLayout *layout; + QVBoxLayout *layout; + QHBoxLayout *barLayout; }; #endif // MANA_DEVOTION_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/general/display/bar_widget.cpp b/cockatrice/src/client/ui/widgets/general/display/bar_widget.cpp index d326f6009..2194b6b03 100644 --- a/cockatrice/src/client/ui/widgets/general/display/bar_widget.cpp +++ b/cockatrice/src/client/ui/widgets/general/display/bar_widget.cpp @@ -1,9 +1,10 @@ #include "bar_widget.h" -#include +#include +#include -BarWidget::BarWidget(QString label, int value, int total, QWidget *parent) - : QWidget(parent), label(std::move(label)), value(value), total(total) +BarWidget::BarWidget(QString label, int value, int total, QColor barColor, QWidget *parent) + : QWidget(parent), label(std::move(label)), value(value), total(total), barColor(barColor) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } @@ -14,10 +15,10 @@ QSize BarWidget::sizeHint() const 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 + // Calculate the height dynamically based on the total + int barHeight = (total > 0) ? (value * 200 / total) : 20; // Scale height proportionally + int totalHeight = barHeight + labelHeight + valueHeight + 30; // Extra space for text + return QSize(60, totalHeight); // Allow width to expand } void BarWidget::paintEvent(QPaintEvent *event) @@ -25,25 +26,30 @@ 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; + int widgetWidth = width(); + int widgetHeight = height(); - // Draw the full bar background (gray) - painter.setBrush(QColor(200, 200, 200)); // Gray background for full bar - painter.drawRect(0, 10, 20, fullBarWidth); + // Calculate bar dimensions + int barWidth = widgetWidth * 0.8; // Use 80% of the available width + int fullBarHeight = widgetHeight - 40; // Leave space for labels + int valueBarHeight = (total > 0) ? (value * fullBarHeight / total) : 0; - // 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 full bar background (gray) + painter.setBrush(QColor(200, 200, 200)); + painter.drawRect((widgetWidth - barWidth) / 2, 10, barWidth, fullBarHeight); + + // Draw the value-specific bar using the assigned color + painter.setBrush(barColor); + painter.drawRect((widgetWidth - barWidth) / 2, 10 + fullBarHeight - valueBarHeight, barWidth, valueBarHeight); // Draw the CMC label painter.setPen(Qt::white); - QRect textRect(0, fullBarWidth + 10, 20, 30); // Define a rectangle for text + QRect textRect(0, widgetHeight - 30, widgetWidth, 20); painter.drawText(textRect, Qt::AlignCenter, label); + // Draw the value count painter.setPen(Qt::black); - QRect valueRect(0, 10, 20, fullBarWidth); // Define a rectangle for text + QRect valueRect(0, 10, widgetWidth, 20); painter.drawText(valueRect, Qt::AlignCenter, QString::number(value)); (void)event; // Suppress unused parameter warning diff --git a/cockatrice/src/client/ui/widgets/general/display/bar_widget.h b/cockatrice/src/client/ui/widgets/general/display/bar_widget.h index 3b31c1511..e32d773e8 100644 --- a/cockatrice/src/client/ui/widgets/general/display/bar_widget.h +++ b/cockatrice/src/client/ui/widgets/general/display/bar_widget.h @@ -1,7 +1,8 @@ #ifndef BAR_WIDGET_H #define BAR_WIDGET_H -#include +#include +#include #include class BarWidget : public QWidget @@ -9,17 +10,18 @@ class BarWidget : public QWidget Q_OBJECT public: - BarWidget(QString label, int value, int total, QWidget *parent = nullptr); + explicit BarWidget(QString label, int value, int total, QColor barColor = Qt::blue, QWidget *parent = nullptr); + + QSize sizeHint() const override; 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) + QString label; + int value; + int total; + QColor barColor; // Store the bar color }; #endif // BAR_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp new file mode 100644 index 000000000..fe5efbfa7 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp @@ -0,0 +1,186 @@ +#include "visual_database_display_color_filter_widget.h" + +#include "../../../../game/filters/filter_card.h" +#include "../../../../game/filters/filter_tree_model.h" + +#include +#include + +VisualDatabaseDisplayColorFilterCircleWidget::VisualDatabaseDisplayColorFilterCircleWidget(QChar color, QWidget *parent) + : QWidget(parent), colorChar(color), isActive(false), circleDiameter(30) +{ + setFixedSize(circleDiameter, circleDiameter); +} + +void VisualDatabaseDisplayColorFilterCircleWidget::setColorActive(bool active) +{ + if (isActive != active) { + isActive = active; + update(); + } +} + +bool VisualDatabaseDisplayColorFilterCircleWidget::isColorActive() const +{ + return isActive; +} + +QChar VisualDatabaseDisplayColorFilterCircleWidget::getColorChar() const +{ + return colorChar; +} + +void VisualDatabaseDisplayColorFilterCircleWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QColor circleColor; + switch (colorChar.unicode()) { + case 'W': + circleColor = Qt::white; + break; + case 'U': + circleColor = QColor(0, 115, 230); + break; + case 'B': + circleColor = QColor(50, 50, 50); + break; + case 'R': + circleColor = QColor(230, 30, 30); + break; + case 'G': + circleColor = QColor(30, 180, 30); + break; + default: + circleColor = Qt::transparent; + break; + } + + if (!isActive) { + circleColor.setAlpha(100); // Dim inactive circles + } + + painter.setBrush(circleColor); + painter.setPen(Qt::black); + painter.drawEllipse(rect()); + + if (isActive) { + QFont font = painter.font(); + font.setBold(true); + font.setPointSize(circleDiameter / 3); + painter.setFont(font); + painter.setPen(colorChar.unicode() == 'B' ? Qt::white : Qt::black); + painter.drawText(rect(), Qt::AlignCenter, colorChar); + } +} + +void VisualDatabaseDisplayColorFilterCircleWidget::mousePressEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + isActive = !isActive; + emit colorToggled(colorChar, isActive); + update(); +} + +VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(QWidget *parent, + FilterTreeModel *_filterModel) + : QWidget(parent), filterModel(_filterModel), layout(new QHBoxLayout(this)) +{ + setLayout(layout); + layout->setSpacing(5); + layout->setContentsMargins(0, 0, 0, 0); + + QString fullColorIdentity = "WUBRG"; + for (const QChar &color : fullColorIdentity) { + auto *circle = new VisualDatabaseDisplayColorFilterCircleWidget(color, this); + + layout->addWidget(circle); + + // Initialize the activeColors map + activeColors[color] = false; + + // Connect the color toggled signal + connect(circle, &VisualDatabaseDisplayColorFilterCircleWidget::colorToggled, this, + &VisualDatabaseDisplayColorFilterWidget::handleColorToggled); + } + + toggleButton = new QPushButton(this); + toggleButton->setCheckable(true); + layout->addWidget(toggleButton); + + // Connect the button's toggled signal + connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplayColorFilterWidget::updateFilterMode); + connect(this, &VisualDatabaseDisplayColorFilterWidget::activeColorsChanged, this, + &VisualDatabaseDisplayColorFilterWidget::updateColorFilter); + connect(this, &VisualDatabaseDisplayColorFilterWidget::filterModeChanged, this, + &VisualDatabaseDisplayColorFilterWidget::updateColorFilter); + + // Call retranslateUi to set the initial text + retranslateUi(); +} + +void VisualDatabaseDisplayColorFilterWidget::retranslateUi() +{ + // Set the toggle button text based on the current mode + toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes")); +} + +void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active) +{ + activeColors[color] = active; + emit activeColorsChanged(); +} + +void VisualDatabaseDisplayColorFilterWidget::updateColorFilter() +{ + // Clear existing filters related to color + filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor); + + if (exactMatchMode) { + // Exact match: card must have ONLY the selected colors + QSet selectedColors; + for (const auto &color : activeColors.keys()) { + if (activeColors[color]) { + selectedColors.insert(color); + } + } + + if (!selectedColors.isEmpty()) { + // Require all selected colors (TypeAnd) + for (const auto &color : selectedColors) { + QString colorString = color; + filterModel->addFilter( + new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor)); + } + + // Exclude any other colors (TypeAndNot) + QStringList allPossibleColors = {"W", "U", "B", "R", "G"}; + for (const auto &color : allPossibleColors) { + if (!selectedColors.contains(color)) { + QString colorString = color; + filterModel->addFilter( + new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); + } + } + } + } else { + // Default includes mode (TypeAnd) - match any selected colors + for (const auto &color : activeColors.keys()) { + if (activeColors[color]) { // If the color is active + QString colorString = color; + filterModel->addFilter( + new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor)); + } + } + } +} + +void VisualDatabaseDisplayColorFilterWidget::updateFilterMode(bool checked) +{ + exactMatchMode = checked; // Toggle between modes + retranslateUi(); // Update the button text + emit filterModeChanged(exactMatchMode); +} diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.h b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.h new file mode 100644 index 000000000..b75deceda --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.h @@ -0,0 +1,58 @@ +#ifndef VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H +#define VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H + +#include "../../../../game/filters/filter_tree_model.h" + +#include +#include +#include + +class VisualDatabaseDisplayColorFilterCircleWidget : public QWidget +{ + Q_OBJECT + +public: + explicit VisualDatabaseDisplayColorFilterCircleWidget(QChar color, QWidget *parent = nullptr); + void setColorActive(bool active); + bool isColorActive() const; + QChar getColorChar() const; + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + +signals: + void colorToggled(QChar color, bool active); + +private: + QChar colorChar; + bool isActive; + int circleDiameter; +}; + +class VisualDatabaseDisplayColorFilterWidget : public QWidget +{ + Q_OBJECT + +public: + explicit VisualDatabaseDisplayColorFilterWidget(QWidget *parent, FilterTreeModel *filterModel); + void retranslateUi(); + +signals: + void filterModeChanged(bool exactMatchMode); + void activeColorsChanged(); + +private slots: + void handleColorToggled(QChar color, bool active); + void updateColorFilter(); + void updateFilterMode(bool checked); + +private: + FilterTreeModel *filterModel; + QHBoxLayout *layout; + QPushButton *toggleButton; + QMap activeColors; + bool exactMatchMode = false; // Default to "includes" mode +}; + +#endif // VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp new file mode 100644 index 000000000..6caa68ab3 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp @@ -0,0 +1,142 @@ +#include "visual_database_display_main_type_filter_widget.h" + +#include "../../../../game/cards/card_database_manager.h" +#include "../../../../game/filters/filter_tree_model.h" + +#include +#include + +VisualDatabaseDisplayMainTypeFilterWidget::VisualDatabaseDisplayMainTypeFilterWidget(QWidget *parent, + FilterTreeModel *_filterModel) + : QWidget(parent), filterModel(_filterModel) +{ + allMainCardTypesWithCount = CardDatabaseManager::getInstance()->getAllMainCardTypesWithCount(); + // Get all main card types with their count + + setMinimumWidth(300); + + layout = new QVBoxLayout(this); + setLayout(layout); + + // Create the spinbox + spinBox = new QSpinBox(this); + spinBox->setMinimum(1); + spinBox->setMaximum(getMaxMainTypeCount()); // Set the max value dynamically + spinBox->setValue(150); + layout->addWidget(spinBox); + connect(spinBox, QOverload::of(&QSpinBox::valueChanged), this, + &VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility); + + flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff); + layout->addWidget(flowWidget); + + // Create the toggle button for Exact Match/Includes mode + toggleButton = new QPushButton(this); + toggleButton->setCheckable(true); + layout->addWidget(toggleButton); + connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplayMainTypeFilterWidget::updateFilterMode); + + createMainTypeButtons(); // Populate buttons initially + updateFilterMode(false); // Initialize toggle button text +} + +void VisualDatabaseDisplayMainTypeFilterWidget::createMainTypeButtons() +{ + // Iterate through main types and create buttons + for (auto it = allMainCardTypesWithCount.begin(); it != allMainCardTypesWithCount.end(); ++it) { + auto *button = new QPushButton(it.key(), flowWidget); + button->setCheckable(true); + button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }" + "QPushButton:checked { background-color: green; color: white; }"); + + flowWidget->addWidget(button); + typeButtons[it.key()] = button; + + // Connect toggle signal + connect(button, &QPushButton::toggled, this, + [this, mainType = it.key()](bool checked) { handleMainTypeToggled(mainType, checked); }); + } + updateMainTypeButtonsVisibility(); // Ensure visibility is updated initially +} + +void VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility() +{ + int threshold = spinBox->value(); // Get the current spinbox value + + // Iterate through buttons and hide/disable those below the threshold + for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) { + bool visible = allMainCardTypesWithCount[it.key()] >= threshold; + it.value()->setVisible(visible); + it.value()->setEnabled(visible); + } +} + +int VisualDatabaseDisplayMainTypeFilterWidget::getMaxMainTypeCount() const +{ + int maxCount = 1; + for (auto it = allMainCardTypesWithCount.begin(); it != allMainCardTypesWithCount.end(); ++it) { + maxCount = qMax(maxCount, it.value()); + } + return maxCount; +} + +void VisualDatabaseDisplayMainTypeFilterWidget::handleMainTypeToggled(const QString &mainType, bool active) +{ + activeMainTypes[mainType] = active; + + if (typeButtons.contains(mainType)) { + typeButtons[mainType]->setChecked(active); + } + + updateMainTypeFilter(); +} + +void VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeFilter() +{ + // Clear existing filters related to main type + filterModel->clearFiltersOfType(CardFilter::Attr::AttrType); + + if (exactMatchMode) { + // Exact Match: Only selected main types are allowed + QSet selectedTypes; + for (const auto &type : activeMainTypes.keys()) { + if (activeMainTypes[type]) { + selectedTypes.insert(type); + } + } + + if (!selectedTypes.isEmpty()) { + // Require all selected types (TypeAnd) + for (const auto &type : selectedTypes) { + QString typeString = type; + filterModel->addFilter( + new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrType)); + } + + // Exclude any other types (TypeAndNot) + for (const auto &type : typeButtons.keys()) { + if (!selectedTypes.contains(type)) { + QString typeString = type; + filterModel->addFilter( + new CardFilter(typeString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrType)); + } + } + } + } else { + // Default Includes Mode (TypeOr) - match any selected main types + for (const auto &type : activeMainTypes.keys()) { + if (activeMainTypes[type]) { + QString typeString = type; + filterModel->addFilter( + new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrType)); + } + } + } +} + +void VisualDatabaseDisplayMainTypeFilterWidget::updateFilterMode(bool checked) +{ + exactMatchMode = checked; + toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes")); + updateMainTypeFilter(); +} diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.h b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.h new file mode 100644 index 000000000..370ddde9b --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.h @@ -0,0 +1,41 @@ +#ifndef VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H +#define VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H + +#include "../../../../game/filters/filter_tree_model.h" +#include "../general/layout_containers/flow_widget.h" + +#include +#include +#include +#include +#include +#include + +class VisualDatabaseDisplayMainTypeFilterWidget : public QWidget +{ + Q_OBJECT +public: + explicit VisualDatabaseDisplayMainTypeFilterWidget(QWidget *parent, FilterTreeModel *filterModel); + void createMainTypeButtons(); + void updateMainTypeButtonsVisibility(); + int getMaxMainTypeCount() const; + + void handleMainTypeToggled(const QString &mainType, bool active); + void updateMainTypeFilter(); + void updateFilterMode(bool checked); + +private: + FilterTreeModel *filterModel; + QMap allMainCardTypesWithCount; + QSpinBox *spinBox; + QVBoxLayout *layout; + FlowWidget *flowWidget; + QPushButton *toggleButton; // Mode switch button + + QMap activeMainTypes; // Track active filters + QMap typeButtons; // Store toggle buttons + + bool exactMatchMode = false; // Toggle between "Exact Match" and "Includes" +}; + +#endif // VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp new file mode 100644 index 000000000..fb602bec9 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp @@ -0,0 +1,138 @@ +#include "visual_database_display_set_filter_widget.h" + +#include "../../../../game/cards/card_database_manager.h" +#include "../../../../game/filters/filter_tree_model.h" + +#include +#include +#include + +VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidget *parent, + FilterTreeModel *_filterModel) + : QWidget(parent), filterModel(_filterModel) +{ + setMinimumWidth(300); + setMaximumHeight(300); + + layout = new QVBoxLayout(this); + setLayout(layout); + + // Create the search box + searchBox = new QLineEdit(this); + searchBox->setPlaceholderText(tr("Search sets...")); + layout->addWidget(searchBox); + connect(searchBox, &QLineEdit::textChanged, this, + &VisualDatabaseDisplaySetFilterWidget::updateSetButtonsVisibility); + + flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); + layout->addWidget(flowWidget); + + // Create the toggle button for Exact Match/Includes mode + toggleButton = new QPushButton(this); + toggleButton->setCheckable(true); + layout->addWidget(toggleButton); + connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplaySetFilterWidget::updateFilterMode); + + createSetButtons(); // Populate buttons initially + updateFilterMode(false); // Initialize toggle button text +} + +void VisualDatabaseDisplaySetFilterWidget::createSetButtons() +{ + SetList shared_pointerses = CardDatabaseManager::getInstance()->getSetList(); + + // Sort by release date + std::sort(shared_pointerses.begin(), shared_pointerses.end(), + [](const auto &a, const auto &b) { return a->getReleaseDate() > b->getReleaseDate(); }); + + for (const auto &shared_pointer : shared_pointerses) { + QString shortName = shared_pointer->getShortName(); + QString longName = shared_pointer->getLongName(); + + auto *button = new QPushButton(longName + " (" + shortName + ")", flowWidget); + button->setCheckable(true); + button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }" + "QPushButton:checked { background-color: green; color: white; }"); + + flowWidget->addWidget(button); + setButtons[shortName] = button; + + // Connect toggle signal + connect(button, &QPushButton::toggled, this, + [this, shortName](bool checked) { handleSetToggled(shortName, checked); }); + } + updateSetButtonsVisibility(); // Ensure visibility is updated initially +} + +void VisualDatabaseDisplaySetFilterWidget::updateSetButtonsVisibility() +{ + QString filterText = searchBox->text().trimmed().toLower(); + + for (auto it = setButtons.begin(); it != setButtons.end(); ++it) { + QString shortName = it.key().toLower(); + QString longName = it.value()->text().toLower(); + bool alwaysVisible = activeSets.contains(it.key()) && activeSets[it.key()]; + bool visible = + alwaysVisible || filterText.isEmpty() || shortName.contains(filterText) || longName.contains(filterText); + it.value()->setVisible(visible); + } +} + +void VisualDatabaseDisplaySetFilterWidget::handleSetToggled(const QString &setShortName, bool active) +{ + activeSets[setShortName] = active; + + if (setButtons.contains(setShortName)) { + setButtons[setShortName]->setChecked(active); + } + + updateSetFilter(); +} + +void VisualDatabaseDisplaySetFilterWidget::updateSetFilter() +{ + // Clear existing filters related to sets + filterModel->clearFiltersOfType(CardFilter::Attr::AttrSet); + + if (exactMatchMode) { + // Exact Match: Only selected sets are allowed + QSet selectedSets; + for (const auto &set : activeSets.keys()) { + if (activeSets[set]) { + selectedSets.insert(set); + } + } + + if (!selectedSets.isEmpty()) { + // Require all selected sets (TypeAnd) + for (const auto &set : selectedSets) { + QString setString = set; + filterModel->addFilter(new CardFilter(setString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrSet)); + } + + // Exclude any other sets (TypeAndNot) + for (const auto &set : setButtons.keys()) { + if (!selectedSets.contains(set)) { + QString setString = set; + filterModel->addFilter( + new CardFilter(setString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrSet)); + } + } + } + } else { + // Default Includes Mode (TypeOr) - match any selected sets + for (const auto &set : activeSets.keys()) { + if (activeSets[set]) { + QString setString = set; + filterModel->addFilter(new CardFilter(setString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrSet)); + } + } + } +} + +void VisualDatabaseDisplaySetFilterWidget::updateFilterMode(bool checked) +{ + exactMatchMode = checked; + toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes")); + updateSetFilter(); +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.h b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.h new file mode 100644 index 000000000..3d8b5d033 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.h @@ -0,0 +1,42 @@ +#ifndef VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H +#define VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H + +#include "../../../../game/filters/filter_tree_model.h" +#include "../general/layout_containers/flow_widget.h" + +#include +#include +#include +#include +#include +#include + +class VisualDatabaseDisplaySetFilterWidget : public QWidget +{ + Q_OBJECT +public: + explicit VisualDatabaseDisplaySetFilterWidget(QWidget *parent, FilterTreeModel *filterModel); + void createSetButtons(); + void updateSetButtonsVisibility(); + void handleSetToggled(const QString &setShortName, bool active); + + void updateSetFilter(); + void updateFilterMode(bool checked); + +private: + FilterTreeModel *filterModel; + QMap allMainCardTypesWithCount; + QVBoxLayout *layout; + QLineEdit *searchBox; + FlowWidget *flowWidget; + QPushButton *toggleButton; // Mode switch button + + QMap activeMainTypes; // Track active filters + QMap typeButtons; // Store toggle buttons + QMap setButtons; // Store set filter buttons + QMap activeSets; // Track active set filters + + bool exactMatchMode = false; // Toggle between "Exact Match" and "Includes" +}; + +#endif // VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp index 5c802c158..81e402656 100644 --- a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp @@ -1,12 +1,18 @@ #include "visual_database_display_widget.h" +#include "../../../../deck/custom_line_edit.h" #include "../../../../game/cards/card_database.h" #include "../../../../game/cards/card_database_manager.h" -#include "../../../../main.h" +#include "../../../../game/filters/filter_tree_model.h" +#include "../../../../settings/cache_settings.h" #include "../../../../utility/card_info_comparator.h" #include "../cards/card_info_picture_with_text_overlay_widget.h" +#include "../quick_settings/settings_button_widget.h" +#include "visual_database_display_color_filter_widget.h" +#include "visual_database_display_main_type_filter_widget.h" +#include "visual_database_display_set_filter_widget.h" -#include +#include #include #include @@ -23,9 +29,79 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent, // Set up main layout and widgets setMinimumSize(0, 0); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - main_layout = new QVBoxLayout(); + main_layout = new QVBoxLayout(this); setLayout(main_layout); + auto quickFilterWidget = new SettingsButtonWidget(this); + + auto searchLayout = new QHBoxLayout(this); + + searchEdit = new SearchLineEdit(); + searchEdit->setObjectName("searchEdit"); + searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)")); + searchEdit->setClearButtonEnabled(true); + // searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); + // auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); + searchEdit->installEventFilter(&searchKeySignals); + + setFocusProxy(searchEdit); + setFocusPolicy(Qt::ClickFocus); + + filterModel = new FilterTreeModel(); + filterModel->setObjectName("filterModel"); + databaseDisplayModel->setFilterTree(filterModel->filterTree()); + + auto mainTypeFilterWidget = new VisualDatabaseDisplayMainTypeFilterWidget(this, filterModel); + quickFilterWidget->addSettingsWidget(mainTypeFilterWidget); + + auto setFilterWidget = new VisualDatabaseDisplaySetFilterWidget(this, filterModel); + quickFilterWidget->addSettingsWidget(setFilterWidget); + + colorFilterWidget = new VisualDatabaseDisplayColorFilterWidget(this, filterModel); + + connect(filterModel, &FilterTreeModel::layoutChanged, this, &VisualDatabaseDisplayWidget::searchModelChanged); + + searchKeySignals.setObjectName("searchKeySignals"); + connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); + /*connect(&searchKeySignals, SIGNAL(onEnter()), this, SLOT(actAddCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actAddCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltRBracket()), this, SLOT(actAddCardToSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlC()), this, SLOT(copyDatabaseCellContents()));*/ + // connect(help, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::showSearchSyntaxHelp); + + /*databaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), true, this); + databaseModel->setObjectName("databaseModel"); + databaseDisplayModel = new CardDatabaseDisplayModel(this); + databaseDisplayModel->setSourceModel(databaseModel); + databaseDisplayModel->setFilterKeyColumn(0);*/ + + databaseView = new QTreeView(this); + databaseView->setObjectName("databaseView"); + databaseView->setFocusProxy(searchEdit); + databaseView->setRootIsDecorated(false); + databaseView->setItemDelegate(nullptr); + databaseView->setSortingEnabled(true); + databaseView->sortByColumn(0, Qt::AscendingOrder); + databaseView->setModel(databaseDisplayModel); + databaseView->setVisible(false); + + /*connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), + deckEditor, SLOT(updateCardInfoLeft(const QModelIndex &, const QModelIndex &))); + connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), + deckEditor, SLOT(updatePrintingSelectorDatabase(const QModelIndex &, const QModelIndex &)));*/ + + searchEdit->setTreeView(databaseView); + + searchLayout->addWidget(colorFilterWidget); + searchLayout->addWidget(quickFilterWidget); + searchLayout->addWidget(searchEdit); + + main_layout->addLayout(searchLayout); + flow_widget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarPolicy::ScrollBarAsNeeded); main_layout->addWidget(flow_widget); @@ -40,10 +116,7 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent, auto loadCardsTimer = new QTimer(this); loadCardsTimer->setSingleShot(true); // Ensure it only fires once after the timeout - connect(loadCardsTimer, &QTimer::timeout, this, [this]() { - qDebug() << "timer timed out"; - loadCurrentPage(); - }); + connect(loadCardsTimer, &QTimer::timeout, this, [this]() { loadCurrentPage(); }); loadCardsTimer->start(5000); } @@ -85,28 +158,37 @@ void VisualDatabaseDisplayWidget::populateCards() int start = currentPage * cardsPerPage; int end = qMin(start + cardsPerPage, rowCount); - qDebug() << "Fetching from " << start << " to " << end << " cards"; + qCDebug(VisualDatabaseDisplayLog) << "Fetching from " << start << " to " << end << " cards"; // Load more cards if we are at the end of the current list and can fetch more if (end >= rowCount && databaseDisplayModel->canFetchMore(QModelIndex())) { - qDebug() << "We gotta load more"; + qCDebug(VisualDatabaseDisplayLog) << "We gotta load more"; databaseDisplayModel->fetchMore(QModelIndex()); } for (int row = start; row < end; ++row) { - qDebug() << "Adding " << row; + qCDebug(VisualDatabaseDisplayLog) << "Adding " << row; QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn); QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole); - qDebug() << name.toString(); + qCDebug(VisualDatabaseDisplayLog) << name.toString(); CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(name.toString()); if (info) { addCard(info); } else { - qDebug() << "Card not found in database!"; + qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!"; } } currentPage++; } +void VisualDatabaseDisplayWidget::updateSearch(const QString &search) +{ + databaseDisplayModel->setStringFilter(search); + QModelIndexList sel = databaseView->selectionModel()->selectedRows(); + if (sel.isEmpty() && databaseDisplayModel->rowCount()) + databaseView->selectionModel()->setCurrentIndex(databaseDisplayModel->index(0, 0), + QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); +} + void VisualDatabaseDisplayWidget::searchModelChanged() { // Clear the current page and prepare for new data @@ -120,7 +202,7 @@ void VisualDatabaseDisplayWidget::searchModelChanged() currentPage = 0; loadCurrentPage(); - qDebug() << "Search model changed"; + qCDebug(VisualDatabaseDisplayLog) << "Search model changed"; } void VisualDatabaseDisplayWidget::loadNextPage() @@ -143,7 +225,7 @@ void VisualDatabaseDisplayWidget::loadNextPage() if (info) { addCard(info); } else { - qDebug() << "Card not found in database!"; + qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!"; } } @@ -156,7 +238,7 @@ void VisualDatabaseDisplayWidget::loadCurrentPage() // Ensure only the initial page is loaded if (currentPage == 0) { // Only load the first page initially - qDebug() << "Loading the first page"; + qCDebug(VisualDatabaseDisplayLog) << "Loading the first page"; populateCards(); } else { // If not the first page, just load the next page and append to the flow widget @@ -179,7 +261,7 @@ void VisualDatabaseDisplayWidget::databaseDataChanged(QModelIndex topLeft, QMode { (void)topLeft; (void)bottomRight; - qDebug() << "Database Data changed"; + qCDebug(VisualDatabaseDisplayLog) << "Database Data changed"; } void VisualDatabaseDisplayWidget::wheelEvent(QWheelEvent *event) @@ -195,7 +277,7 @@ void VisualDatabaseDisplayWidget::wheelEvent(QWheelEvent *event) event->accept(); // Accept the event as valid return; } - qDebug() << nextPageStartIndex << ":" << totalRows; + qCDebug(VisualDatabaseDisplayLog) << nextPageStartIndex << ":" << totalRows; } // Prevent overscrolling when there's no more data to load diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h index c39c19bd6..221ded26a 100644 --- a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h @@ -1,20 +1,28 @@ #ifndef VISUAL_DATABASE_DISPLAY_WIDGET_H #define VISUAL_DATABASE_DISPLAY_WIDGET_H +#include "../../../../deck/custom_line_edit.h" #include "../../../../deck/deck_list_model.h" #include "../../../../game/cards/card_database.h" #include "../../../../game/cards/card_database_model.h" +#include "../../../../game/filters/filter_tree_model.h" +#include "../../../game_logic/key_signals.h" #include "../../layouts/flow_layout.h" #include "../cards/card_info_picture_with_text_overlay_widget.h" #include "../cards/card_size_widget.h" #include "../general/layout_containers/flow_widget.h" #include "../general/layout_containers/overlap_control_widget.h" +#include "visual_database_display_color_filter_widget.h" +#include +#include #include #include #include #include +inline Q_LOGGING_CATEGORY(VisualDatabaseDisplayLog, "visual_database_display"); + class VisualDatabaseDisplayWidget : public QWidget { Q_OBJECT @@ -23,15 +31,21 @@ public: explicit VisualDatabaseDisplayWidget(QWidget *parent, CardDatabaseModel *database_model, CardDatabaseDisplayModel *database_display_model); - void updateDisplay(); + void adjustCardsPerPage(); void populateCards(); - void searchModelChanged(); void loadNextPage(); void loadCurrentPage(); void sortCardList(QStringList properties, Qt::SortOrder order); void setDeckList(const DeckList &new_deck_list_model); + SearchLineEdit *searchEdit; + FilterTreeModel *filterModel; + VisualDatabaseDisplayColorFilterWidget *colorFilterWidget; + +public slots: + void searchModelChanged(); + signals: void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void cardHoveredDatabaseDisplay(CardInfoPtr hoveredCard); @@ -43,10 +57,13 @@ protected slots: void databaseDataChanged(QModelIndex topLeft, QModelIndex bottomRight); void wheelEvent(QWheelEvent *event) override; void modelDirty(); + void updateSearch(const QString &search); private: + KeySignals searchKeySignals; CardDatabaseModel *databaseModel; CardDatabaseDisplayModel *databaseDisplayModel; + QTreeView *databaseView; QList *cards; QVBoxLayout *main_layout; QScrollArea *scrollArea; @@ -60,7 +77,7 @@ private: int debounce_time = 300; int currentPage = 0; // Current page index - int cardsPerPage = 200; // Number of cards per page + int cardsPerPage = 100; // Number of cards per page protected: void resizeEvent(QResizeEvent *event) override; diff --git a/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp index 9b7874796..2d8c40f6a 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp @@ -16,8 +16,8 @@ #include #include -VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *deck_list_model) - : QWidget(parent), deck_list_model(deck_list_model) +VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deck_list_model) + : QWidget(parent), deck_list_model(_deck_list_model) { this->mainDeckCards = new QList; this->sideboardCards = new QList;