diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 4ca3b07fa..4fbba62e2 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -24,6 +24,7 @@ set(cockatrice_SOURCES src/client/ui/widgets/cards/card_info_picture_widget.cpp src/client/ui/widgets/cards/card_info_text_widget.cpp src/client/ui/widgets/cards/card_info_display_widget.cpp + src/client/ui/widgets/cards/card_size_widget.cpp src/game/cards/card_item.cpp src/game/cards/card_list.cpp src/game/zones/card_zone.cpp @@ -80,6 +81,9 @@ set(cockatrice_SOURCES src/client/ui/widgets/cards/card_info_picture_enlarged_widget.cpp src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp src/client/ui/widgets/general/display/labeled_input.cpp + src/client/ui/widgets/general/display/dynamic_font_size_label.cpp + src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp + src/client/ui/widgets/general/display/shadow_background_label.cpp src/main.cpp src/server/message_log_widget.cpp src/client/ui/layouts/overlap_layout.cpp @@ -94,6 +98,17 @@ set(cockatrice_SOURCES src/game/player/player.cpp src/game/player/player_list_widget.cpp src/game/player/player_target.cpp + src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp + src/client/ui/widgets/printing_selector/card_amount_widget.cpp + src/client/ui/widgets/printing_selector/printing_selector.cpp + src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp + src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp + src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp + src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp + src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp + src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.cpp + src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.cpp + src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp src/client/network/release_channel.cpp src/server/remote/remote_client.cpp src/server/remote/remote_decklist_tree_widget.cpp diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp index 1e2284186..999f22580 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp @@ -15,6 +15,7 @@ #include "../../settings/cache_settings.h" #include "../ui/picture_loader.h" #include "../ui/pixel_map_generator.h" +#include "../ui/widgets/printing_selector/printing_selector.h" #include "pb/command_deck_upload.pb.h" #include "pb/response.pb.h" #include "tab_supervisor.h" @@ -78,9 +79,13 @@ void TabDeckEditor::createDeckDock() deckView->sortByColumn(1, Qt::AscendingOrder); deckView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); deckView->installEventFilter(&deckViewKeySignals); + deckView->setContextMenuPolicy(Qt::CustomContextMenu); connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &))); + connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, + SLOT(updatePrintingSelectorDeckView(const QModelIndex &, const QModelIndex &))); connect(deckView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actSwapCard())); + connect(deckView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(decklistCustomMenu(QPoint))); connect(&deckViewKeySignals, SIGNAL(onShiftS()), this, SLOT(actSwapCard())); connect(&deckViewKeySignals, SIGNAL(onEnter()), this, SLOT(actIncrement())); connect(&deckViewKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actIncrement())); @@ -172,7 +177,6 @@ void TabDeckEditor::createDeckDock() deckDock = new QDockWidget(this); deckDock->setObjectName("deckDock"); - deckDock->setMinimumSize(QSize(200, 41)); deckDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); deckDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); @@ -196,7 +200,6 @@ void TabDeckEditor::createCardInfoDock() cardInfoDock = new QDockWidget(this); cardInfoDock->setObjectName("cardInfoDock"); - cardInfoDock->setMinimumSize(QSize(200, 41)); cardInfoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); cardInfoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); @@ -271,6 +274,32 @@ void TabDeckEditor::createFiltersDock() connect(filterDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool))); } +void TabDeckEditor::createPrintingSelectorDock() +{ + printingSelector = new PrintingSelector(this, this, deckModel, deckView); + printingSelector->setObjectName("printingSelector"); + auto *printingSelectorFrame = new QVBoxLayout; + printingSelectorFrame->setObjectName("printingSelectorFrame"); + printingSelectorFrame->addWidget(printingSelector); + + printingSelectorDock = new QDockWidget(this); + printingSelectorDock->setObjectName("printingSelectorDock"); + + printingSelectorDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + printingSelectorDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | + QDockWidget::DockWidgetMovable); + auto *printingSelectorDockContents = new QWidget(); + printingSelectorDockContents->setObjectName("printingSelectorDockContents"); + printingSelectorDockContents->setLayout(printingSelectorFrame); + printingSelectorDock->setWidget(printingSelectorDockContents); + + printingSelectorDock->installEventFilter(this); + connect(printingSelectorDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool))); + + addDockWidget(Qt::RightDockWidgetArea, printingSelectorDock); + printingSelectorDock->setFloating(false); +} + void TabDeckEditor::createMenus() { aNewDeck = new QAction(QString(), this); @@ -349,6 +378,7 @@ void TabDeckEditor::createMenus() cardInfoDockMenu = viewMenu->addMenu(QString()); deckDockMenu = viewMenu->addMenu(QString()); filterDockMenu = viewMenu->addMenu(QString()); + printingSelectorDockMenu = viewMenu->addMenu(QString()); aCardInfoDockVisible = cardInfoDockMenu->addAction(QString()); aCardInfoDockVisible->setCheckable(true); @@ -371,6 +401,13 @@ void TabDeckEditor::createMenus() aFilterDockFloating->setCheckable(true); connect(aFilterDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered())); + aPrintingSelectorDockVisible = printingSelectorDockMenu->addAction(QString()); + aPrintingSelectorDockVisible->setCheckable(true); + connect(aPrintingSelectorDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered())); + aPrintingSelectorDockFloating = printingSelectorDockMenu->addAction(QString()); + aPrintingSelectorDockFloating->setCheckable(true); + connect(aPrintingSelectorDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered())); + viewMenu->addSeparator(); aResetLayout = viewMenu->addAction(QString()); @@ -426,6 +463,8 @@ void TabDeckEditor::createCentralFrame() 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->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, + SLOT(updatePrintingSelectorDatabase(const QModelIndex &, const QModelIndex &))); connect(databaseView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actAddCard())); QByteArray dbHeaderState = SettingsCache::instance().layouts().getDeckEditorDbHeaderState(); @@ -465,6 +504,7 @@ void TabDeckEditor::createCentralFrame() centralWidget = new QWidget(this); centralWidget->setObjectName("centralWidget"); centralWidget->setLayout(centralFrame); + centralWidget->setMaximumSize(900, 5000); setCentralWidget(centralWidget); setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks); } @@ -475,11 +515,14 @@ void TabDeckEditor::databaseCustomMenu(QPoint point) const CardInfoPtr info = currentCardInfo(); // add to deck and sideboard options - QAction *addToDeck, *addToSideboard; + QAction *addToDeck, *addToSideboard, *selectPrinting; addToDeck = menu.addAction(tr("Add to Deck")); addToSideboard = menu.addAction(tr("Add to Sideboard")); + selectPrinting = menu.addAction(tr("Select Printing")); + connect(addToDeck, SIGNAL(triggered()), this, SLOT(actAddCard())); connect(addToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); + connect(selectPrinting, &QAction::triggered, this, [this, info] { this->showPrintingSelector(); }); // filling out the related cards submenu auto *relatedMenu = new QMenu(tr("Show Related cards")); @@ -498,36 +541,58 @@ void TabDeckEditor::databaseCustomMenu(QPoint point) menu.exec(databaseView->mapToGlobal(point)); } +void TabDeckEditor::decklistCustomMenu(QPoint point) +{ + QMenu menu; + const CardInfoPtr info = cardInfo->getInfo(); + + QAction *selectPrinting = menu.addAction(tr("Select Printing")); + + connect(selectPrinting, &QAction::triggered, this, &TabDeckEditor::showPrintingSelector); + + menu.exec(deckView->mapToGlobal(point)); +} + +void TabDeckEditor::showPrintingSelector() +{ + printingSelector->setCard(cardInfo->getInfo(), DECK_ZONE_MAIN); + printingSelector->updateDisplay(); + aPrintingSelectorDockVisible->setChecked(true); + printingSelectorDock->setVisible(true); +} + void TabDeckEditor::restartLayout() { deckDock->setVisible(true); cardInfoDock->setVisible(true); filterDock->setVisible(true); + printingSelectorDock->setVisible(false); deckDock->setFloating(false); cardInfoDock->setFloating(false); filterDock->setFloating(false); + printingSelectorDock->setFloating(false); aCardInfoDockVisible->setChecked(true); aDeckDockVisible->setChecked(true); aFilterDockVisible->setChecked(true); + aPrintingSelectorDockVisible->setChecked(false); aCardInfoDockFloating->setChecked(false); aDeckDockFloating->setChecked(false); aFilterDockFloating->setChecked(false); + aPrintingSelectorDockFloating->setChecked(false); addDockWidget(static_cast(2), deckDock); addDockWidget(static_cast(2), cardInfoDock); addDockWidget(static_cast(2), filterDock); + addDockWidget(static_cast(2), printingSelectorDock); - splitDockWidget(cardInfoDock, deckDock, Qt::Horizontal); + splitDockWidget(cardInfoDock, printingSelectorDock, Qt::Horizontal); + splitDockWidget(printingSelectorDock, deckDock, Qt::Horizontal); + splitDockWidget(cardInfoDock, printingSelectorDock, Qt::Horizontal); splitDockWidget(cardInfoDock, filterDock, Qt::Vertical); - deckDock->setMinimumWidth(360); - deckDock->setMaximumWidth(360); - - cardInfoDock->setMinimumSize(250, 360); - cardInfoDock->setMaximumSize(250, 360); QTimer::singleShot(100, this, SLOT(freeDocksSize())); } @@ -541,6 +606,11 @@ void TabDeckEditor::freeDocksSize() filterDock->setMinimumSize(100, 100); filterDock->setMaximumSize(5000, 5000); + + printingSelectorDock->setMinimumSize(525, 100); + printingSelectorDock->setMaximumSize(5000, 5000); + + centralWidget->setMaximumSize(900, 5000); } void TabDeckEditor::refreshShortcuts() @@ -583,14 +653,17 @@ void TabDeckEditor::loadLayout() aCardInfoDockVisible->setChecked(cardInfoDock->isVisible()); aFilterDockVisible->setChecked(filterDock->isVisible()); aDeckDockVisible->setChecked(deckDock->isVisible()); + aPrintingSelectorDockVisible->setChecked(printingSelectorDock->isVisible()); aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked()); aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked()); aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked()); + aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked()); aCardInfoDockFloating->setChecked(cardInfoDock->isFloating()); aFilterDockFloating->setChecked(filterDock->isFloating()); aDeckDockFloating->setChecked(deckDock->isFloating()); + aPrintingSelectorDockFloating->setChecked(printingSelectorDock->isFloating()); cardInfoDock->setMinimumSize(layouts.getDeckEditorCardSize()); cardInfoDock->setMaximumSize(layouts.getDeckEditorCardSize()); @@ -601,6 +674,9 @@ void TabDeckEditor::loadLayout() deckDock->setMinimumSize(layouts.getDeckEditorDeckSize()); deckDock->setMaximumSize(layouts.getDeckEditorDeckSize()); + printingSelectorDock->setMinimumSize(layouts.getDeckEditorPrintingSelectorSize()); + printingSelectorDock->setMaximumSize(layouts.getDeckEditorPrintingSelectorSize()); + QTimer::singleShot(100, this, SLOT(freeDocksSize())); } @@ -616,6 +692,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) createDeckDock(); createCardInfoDock(); createFiltersDock(); + createPrintingSelectorDock(); this->installEventFilter(this); @@ -675,11 +752,13 @@ void TabDeckEditor::retranslateUi() cardInfoDock->setWindowTitle(tr("Card Info")); deckDock->setWindowTitle(tr("Deck")); filterDock->setWindowTitle(tr("Filters")); + printingSelectorDock->setWindowTitle(tr("Printing Selector")); viewMenu->setTitle(tr("&View")); cardInfoDockMenu->setTitle(tr("Card Info")); deckDockMenu->setTitle(tr("Deck")); filterDockMenu->setTitle(tr("Filters")); + printingSelectorDockMenu->setTitle(tr("Printing")); aCardInfoDockVisible->setText(tr("Visible")); aCardInfoDockFloating->setText(tr("Floating")); @@ -690,6 +769,9 @@ void TabDeckEditor::retranslateUi() aFilterDockVisible->setText(tr("Visible")); aFilterDockFloating->setText(tr("Floating")); + aPrintingSelectorDockVisible->setText(tr("Visible")); + aPrintingSelectorDockFloating->setText(tr("Floating")); + aResetLayout->setText(tr("Reset layout")); } @@ -715,6 +797,11 @@ void TabDeckEditor::updateComments() setSaveStatus(true); } +void TabDeckEditor::updateCardInfo(CardInfoPtr _card) +{ + cardInfo->setCard(_card); +} + void TabDeckEditor::updateCardInfoLeft(const QModelIndex ¤t, const QModelIndex & /*previous*/) { cardInfo->setCard(current.sibling(current.row(), 0).data().toString()); @@ -730,6 +817,43 @@ void TabDeckEditor::updateCardInfoRight(const QModelIndex ¤t, const QModel } } +void TabDeckEditor::updatePrintingSelectorDatabase(const QModelIndex ¤t, const QModelIndex & /*previous*/) +{ + const QString cardName = current.sibling(current.row(), 0).data().toString(); + const QString cardProviderID = CardDatabaseManager::getInstance()->getPreferredPrintingProviderIdForCard(cardName); + + if (!current.isValid()) { + return; + } + + if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { + printingSelector->setCard( + CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderID), DECK_ZONE_MAIN); + } +} + +void TabDeckEditor::updatePrintingSelectorDeckView(const QModelIndex ¤t, const QModelIndex & /*previous*/) +{ + const QString cardName = current.sibling(current.row(), 1).data().toString(); + const QString cardProviderID = current.sibling(current.row(), 4).data().toString(); + const QModelIndex gparent = current.parent().parent(); + + if (!gparent.isValid()) { + return; + } + + const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString(); + + if (!current.isValid()) { + return; + } + + if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { + printingSelector->setCard( + CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderID), zoneName); + } +} + void TabDeckEditor::updateSearch(const QString &search) { databaseDisplayModel->setStringFilter(search); @@ -1096,7 +1220,7 @@ void TabDeckEditor::actSwapCard() if (!currentIndex.isValid()) return; const QString cardName = currentIndex.sibling(currentIndex.row(), 1).data().toString(); - const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 2).data().toString(); + const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 4).data().toString(); const QModelIndex gparent = currentIndex.parent().parent(); if (!gparent.isValid()) @@ -1270,6 +1394,9 @@ bool TabDeckEditor::eventFilter(QObject *o, QEvent *e) } else if (o == filterDock) { aFilterDockVisible->setChecked(false); aFilterDockFloating->setEnabled(false); + } else if (o == printingSelectorDock) { + aPrintingSelectorDockVisible->setChecked(false); + aPrintingSelectorDockFloating->setEnabled(false); } } if (o == this && e->type() == QEvent::Hide) { @@ -1279,6 +1406,7 @@ bool TabDeckEditor::eventFilter(QObject *o, QEvent *e) layouts.setDeckEditorCardSize(cardInfoDock->size()); layouts.setDeckEditorFilterSize(filterDock->size()); layouts.setDeckEditorDeckSize(deckDock->size()); + layouts.setDeckEditorPrintingSelectorSize(printingSelectorDock->size()); } return false; } @@ -1303,6 +1431,12 @@ void TabDeckEditor::dockVisibleTriggered() aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked()); return; } + + if (o == aPrintingSelectorDockVisible) { + printingSelectorDock->setVisible(aPrintingSelectorDockVisible->isChecked()); + aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked()); + return; + } } void TabDeckEditor::dockFloatingTriggered() @@ -1322,6 +1456,11 @@ void TabDeckEditor::dockFloatingTriggered() filterDock->setFloating(aFilterDockFloating->isChecked()); return; } + + if (o == aPrintingSelectorDockFloating) { + printingSelectorDock->setFloating(aPrintingSelectorDockFloating->isChecked()); + return; + } } void TabDeckEditor::dockTopLevelChanged(bool topLevel) @@ -1341,6 +1480,11 @@ void TabDeckEditor::dockTopLevelChanged(bool topLevel) aFilterDockFloating->setChecked(topLevel); return; } + + if (o == printingSelectorDock) { + aPrintingSelectorDockFloating->setChecked(topLevel); + return; + } } void TabDeckEditor::saveDbHeaderState() diff --git a/cockatrice/src/client/tabs/tab_deck_editor.h b/cockatrice/src/client/tabs/tab_deck_editor.h index 6aff1cc31..fe1405312 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.h +++ b/cockatrice/src/client/tabs/tab_deck_editor.h @@ -4,6 +4,7 @@ #include "../../deck/custom_line_edit.h" #include "../../game/cards/card_database.h" #include "../game_logic/key_signals.h" +#include "../ui/widgets/printing_selector/printing_selector.h" #include "tab.h" #include @@ -55,8 +56,11 @@ private slots: void updateHash(); void updateCardInfoLeft(const QModelIndex ¤t, const QModelIndex &previous); void updateCardInfoRight(const QModelIndex ¤t, const QModelIndex &previous); + void updatePrintingSelectorDatabase(const QModelIndex ¤t, const QModelIndex &previous); + void updatePrintingSelectorDeckView(const QModelIndex ¤t, const QModelIndex &previous); void updateSearch(const QString &search); void databaseCustomMenu(QPoint point); + void decklistCustomMenu(QPoint point); void actNewDeck(); void actLoadDeck(); @@ -129,6 +133,7 @@ private: QTreeView *deckView; KeySignals deckViewKeySignals; CardInfoFrameWidget *cardInfo; + PrintingSelector *printingSelector; SearchLineEdit *searchEdit; KeySignals searchKeySignals; @@ -143,8 +148,8 @@ private: KeySignals filterViewKeySignals; QWidget *filterBox; - QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *analyzeDeckMenu, - *saveDeckToClipboardMenu; + QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu, + *analyzeDeckMenu, *saveDeckToClipboardMenu; QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aSaveDeckToClipboardRaw, *aPrintDeck, *aExportDeckDecklist, *aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout, *aClose; @@ -152,7 +157,7 @@ private: QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement; QAction *aResetLayout; QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating, *aFilterDockVisible, - *aFilterDockFloating; + *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating; bool modified; QVBoxLayout *centralFrame; @@ -160,6 +165,7 @@ private: QDockWidget *cardInfoDock; QDockWidget *deckDock; QDockWidget *filterDock; + QDockWidget *printingSelectorDock; QWidget *centralWidget; public: @@ -173,11 +179,14 @@ public: void createDeckDock(); void createCardInfoDock(); void createFiltersDock(); + void createPrintingSelectorDock(); void createMenus(); void createCentralFrame(); + void updateCardInfo(CardInfoPtr _card); public slots: void closeRequest() override; + void showPrintingSelector(); signals: void openDeckEditor(const DeckLoader *deckLoader); void deckEditorClosing(TabDeckEditor *tab); diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index a37e05567..76dd82ff5 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -1807,9 +1807,9 @@ void TabGame::createDeckViewContainerWidget(bool bReplay) deckViewContainerWidget->setLayout(deckViewContainerLayout); } -void TabGame::viewCardInfo(const QString &cardName) +void TabGame::viewCardInfo(const QString &cardName, const QString &providerId) const { - cardInfoFrameWidget->setCard(cardName); + cardInfoFrameWidget->setCard(cardName, providerId); } void TabGame::createCardInfoDock(bool bReplay) diff --git a/cockatrice/src/client/tabs/tab_game.h b/cockatrice/src/client/tabs/tab_game.h index 0d9d1d0db..3d8ebaa70 100644 --- a/cockatrice/src/client/tabs/tab_game.h +++ b/cockatrice/src/client/tabs/tab_game.h @@ -313,7 +313,7 @@ public: public slots: void sendGameCommand(PendingCommand *pend, int playerId = -1); void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1); - void viewCardInfo(const QString &cardName); + void viewCardInfo(const QString &cardName, const QString &providerId = "") const; }; #endif diff --git a/cockatrice/src/client/ui/picture_loader.cpp b/cockatrice/src/client/ui/picture_loader.cpp index a28a98eac..8eb4a7fb9 100644 --- a/cockatrice/src/client/ui/picture_loader.cpp +++ b/cockatrice/src/client/ui/picture_loader.cpp @@ -32,20 +32,28 @@ PictureToLoad::PictureToLoad(CardInfoPtr _card) : card(std::move(_card)), urlTemplates(SettingsCache::instance().downloads().getAllURLs()) { if (card) { - for (const auto &set : card->getSets()) { - sortedSets << set.getPtr(); + for (const auto &cardInfoPerSetList : card->getSets()) { + for (const auto &set : cardInfoPerSetList) { + sortedSets << set.getPtr(); + } } if (sortedSets.empty()) { sortedSets << CardSet::newInstance("", "", "", QDate()); } std::sort(sortedSets.begin(), sortedSets.end(), SetDownloadPriorityComparator()); - // If the pixmapCacheKey corresponds to a specific set, we have to try to load it first. - for (const auto &set : card->getSets()) { - if (QLatin1String("card_") + card->getName() + QString("_") + QString(set.getProperty("uuid")) == - card->getPixmapCacheKey()) { - long long setIndex = sortedSets.indexOf(set.getPtr()); - CardSetPtr setForCardProviderID = sortedSets.takeAt(setIndex); - sortedSets.prepend(setForCardProviderID); + + // If the user hasn't disabled arts other than their personal preference... + if (!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) { + // If the pixmapCacheKey corresponds to a specific set, we have to try to load it first. + for (const auto &cardInfoPerSetList : card->getSets()) { + for (const auto &set : cardInfoPerSetList) { + if (QLatin1String("card_") + card->getName() + QString("_") + QString(set.getProperty("uuid")) == + card->getPixmapCacheKey()) { + long long setIndex = sortedSets.indexOf(set.getPtr()); + CardSetPtr setForCardProviderID = sortedSets.takeAt(setIndex); + sortedSets.prepend(setForCardProviderID); + } + } } } // The first time called, nextSet will also populate the Urls for the first set. @@ -717,6 +725,26 @@ void PictureLoader::getCardBackPixmap(QPixmap &pixmap, QSize size) } } +void PictureLoader::getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size) +{ + QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height()); + if (!QPixmapCache::find(backCacheKey, &pixmap)) { + qDebug() << "PictureLoader: cache fail for" << backCacheKey; + pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QPixmapCache::insert(backCacheKey, pixmap); + } +} + +void PictureLoader::getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size) +{ + QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height()); + if (!QPixmapCache::find(backCacheKey, &pixmap)) { + qDebug() << "PictureLoader: cache fail for" << backCacheKey; + pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QPixmapCache::insert(backCacheKey, pixmap); + } +} + void PictureLoader::getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size) { if (card == nullptr) { diff --git a/cockatrice/src/client/ui/picture_loader.h b/cockatrice/src/client/ui/picture_loader.h index 245114358..e781efd91 100644 --- a/cockatrice/src/client/ui/picture_loader.h +++ b/cockatrice/src/client/ui/picture_loader.h @@ -142,6 +142,8 @@ private: public: static void getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size); static void getCardBackPixmap(QPixmap &pixmap, QSize size); + static void getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size); + static void getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size); static void clearPixmapCache(CardInfoPtr card); static void clearPixmapCache(); static void cacheCardPixmaps(QList cards); diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h index a9a4a0597..fd354c31c 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h +++ b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h @@ -30,7 +30,12 @@ public: TextOnlyView, ImageAndTextView }; + explicit CardInfoFrameWidget(const QString &cardName = QString(), QWidget *parent = nullptr); + CardInfoPtr getInfo() + { + return info; + } void retranslateUi(); public slots: diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp index 8b71c9000..284660037 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp +++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp @@ -92,13 +92,15 @@ void CardInfoPictureWidget::resizeEvent(QResizeEvent *event) */ void CardInfoPictureWidget::setScaleFactor(const int scale) { - const int newWidth = baseWidth + scale * 20; + const int newWidth = baseWidth * scale / 100; const int newHeight = static_cast(newWidth * aspectRatio); scaleFactor = scale; setFixedSize(newWidth, newHeight); updatePixmap(); + + emit cardScaleFactorChanged(scale); } /** @@ -119,10 +121,11 @@ void CardInfoPictureWidget::updatePixmap() */ void CardInfoPictureWidget::loadPixmap() { + PictureLoader::getCardBackLoadingInProgressPixmap(resizedPixmap, size()); if (info) { PictureLoader::getPixmap(resizedPixmap, info, size()); } else { - PictureLoader::getCardBackPixmap(resizedPixmap, size()); + PictureLoader::getCardBackLoadingFailedPixmap(resizedPixmap, size()); } pixmapDirty = false; diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h index e18df00d8..f274d8ad1 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h +++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h @@ -29,6 +29,7 @@ public slots: signals: void hoveredOnCard(CardInfoPtr hoveredCard); + void cardScaleFactorChanged(int _scale); protected: void resizeEvent(QResizeEvent *event) override; diff --git a/cockatrice/src/client/ui/widgets/cards/card_size_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_size_widget.cpp new file mode 100644 index 000000000..2557aba1f --- /dev/null +++ b/cockatrice/src/client/ui/widgets/cards/card_size_widget.cpp @@ -0,0 +1,53 @@ +#include "card_size_widget.h" + +#include "../../../../settings/cache_settings.h" + +/** + * @class CardSizeWidget + * @brief A widget for adjusting card sizes using a slider. + * + * This widget allows users to dynamically change the card size in a linked FlowWidget + * and updates the application's settings accordingly. + */ +CardSizeWidget::CardSizeWidget(QWidget *parent, FlowWidget *flowWidget, int defaultValue) + : parent(parent), flowWidget(flowWidget) +{ + cardSizeLayout = new QHBoxLayout(this); + setLayout(cardSizeLayout); + + cardSizeLabel = new QLabel(tr("Card Size"), this); + cardSizeLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + cardSizeSlider = new QSlider(Qt::Horizontal, this); + cardSizeSlider->setRange(50, 250); ///< Slider range for card size adjustment. + cardSizeSlider->setValue(defaultValue); ///< Initial slider value. + + cardSizeLayout->addWidget(cardSizeLabel); + cardSizeLayout->addWidget(cardSizeSlider); + + if (flowWidget != nullptr) { + connect(cardSizeSlider, &QSlider::valueChanged, flowWidget, &FlowWidget::setMinimumSizeToMaxSizeHint); + } + + connect(cardSizeSlider, &QSlider::valueChanged, this, &CardSizeWidget::updateCardSizeSetting); +} + +/** + * @brief Updates the card size setting in the application's cache. + * + * @param newValue The new card size value set by the slider. + */ +void CardSizeWidget::updateCardSizeSetting(int newValue) +{ + SettingsCache::instance().setPrintingSelectorCardSize(newValue); +} + +/** + * @brief Gets the slider widget used for adjusting the card size. + * + * @return A pointer to the QSlider object. + */ +QSlider *CardSizeWidget::getSlider() const +{ + return cardSizeSlider; +} diff --git a/cockatrice/src/client/ui/widgets/cards/card_size_widget.h b/cockatrice/src/client/ui/widgets/cards/card_size_widget.h new file mode 100644 index 000000000..aaafc2f45 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/cards/card_size_widget.h @@ -0,0 +1,29 @@ +#ifndef CARD_SIZE_WIDGET_H +#define CARD_SIZE_WIDGET_H + +#include "../general/layout_containers/flow_widget.h" + +#include +#include +#include +#include + +class CardSizeWidget : public QWidget +{ + Q_OBJECT + +public: + explicit CardSizeWidget(QWidget *parent, FlowWidget *flowWidget = nullptr, int defaultValue = 100); + [[nodiscard]] QSlider *getSlider() const; +public slots: + static void updateCardSizeSetting(int newValue); + +private: + QWidget *parent; + FlowWidget *flowWidget; + QHBoxLayout *cardSizeLayout; + QLabel *cardSizeLabel; + QSlider *cardSizeSlider; +}; + +#endif // CARD_SIZE_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.cpp b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.cpp new file mode 100644 index 000000000..ed4db1fd3 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.cpp @@ -0,0 +1,137 @@ +#include "dynamic_font_size_label.h" +#define FONT_PRECISION (0.5) + +#include +#include + +DynamicFontSizeLabel::DynamicFontSizeLabel(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f) +{ + setIndent(0); +} + +void DynamicFontSizeLabel::mousePressEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + emit clicked(); +} + +void DynamicFontSizeLabel::paintEvent(QPaintEvent *event) +{ + // QElapsedTimer timer; + // timer.start(); + + QFont newFont = font(); + float fontSize = getWidgetMaximumFontSize(this, this->text()); + newFont.setPointSizeF(fontSize); + setFont(newFont); + // qDebug() << "Font size set to" << fontSize; + + QLabel::paintEvent(event); + // LOG(true, "Paint delay" << ((float)timer.nsecsElapsed())/1000000.0 << " mS"); +} + +float DynamicFontSizeLabel::getWidgetMaximumFontSize(QWidget *widget, const QString &text) +{ + QFont font = widget->font(); + const QRect widgetRect = widget->contentsRect(); + const float widgetWidth = widgetRect.width(); + const float widgetHeight = widgetRect.height(); + + QRectF newFontSizeRect; + float currentSize = font.pointSizeF(); + + float step = currentSize / 2.0; + + /* If too small, increase step */ + if (step <= FONT_PRECISION) { + step = FONT_PRECISION * 4.0; + } + + float lastTestedSize = currentSize; + + float currentHeight = 0; + float currentWidth = 0; + if (text == "") { + return currentSize; + } + + if (currentSize < 0) { + return 1; + } + + /* Only stop when step is small enough and new size is smaller than QWidget */ + while (step > FONT_PRECISION || (currentHeight > widgetHeight) || (currentWidth > widgetWidth)) { + /* Keep last tested value */ + lastTestedSize = currentSize; + + /* Test label with its font */ + font.setPointSizeF(currentSize); + /* Use font metrics to test */ + QFontMetricsF fm(font); + + /* Check if widget is QLabel */ + QLabel *label = qobject_cast(widget); + if (label) { + newFontSizeRect = + fm.boundingRect(widgetRect, (label->wordWrap() ? Qt::TextWordWrap : 0) | label->alignment(), text); + } else { + newFontSizeRect = fm.boundingRect(widgetRect, 0, text); + } + + currentHeight = newFontSizeRect.height(); + currentWidth = newFontSizeRect.width(); + + /* If new font size is too big, decrease it */ + if ((currentHeight > widgetHeight) || (currentWidth > widgetWidth)) { + // qDebug() << "-- contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect" + // << newFontSizeRect << "Tight" << text << currentSize; + currentSize -= step; + /* if step is small enough, keep it constant, so it converge to biggest font size */ + if (step > FONT_PRECISION) { + step /= 2.0; + } + /* Do not allow negative size */ + if (currentSize <= 0) { + break; + } + } + /* If new font size is smaller than maximum possible size, increase it */ + else { + // qDebug() << "++ contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect" + // << newFontSizeRect << "Tight" << text << currentSize; + currentSize += step; + } + } + return lastTestedSize; +} + +void DynamicFontSizeLabel::setTextColor(QColor color) +{ + if (color.isValid() && color != textColor) { + textColor = color; + setStyleSheet("color : " + color.name() + ";"); + } +} + +QColor DynamicFontSizeLabel::getTextColor() +{ + return textColor; +} + +void DynamicFontSizeLabel::setTextAndColor(const QString &text, QColor color) +{ + setTextColor(color); + setText(text); +} + +/* Do not give any size hint as it it changes during paintEvent */ +QSize DynamicFontSizeLabel::minimumSizeHint() const +{ + return QWidget::minimumSizeHint(); +} + +/* Do not give any size hint as it it changes during paintEvent */ +QSize DynamicFontSizeLabel::sizeHint() const +{ + return QWidget::sizeHint(); +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.h b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.h new file mode 100644 index 000000000..2f66227e9 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.h @@ -0,0 +1,41 @@ +#ifndef DYNAMICFONTSIZELABEL_H +#define DYNAMICFONTSIZELABEL_H + +#include +#include + +class DynamicFontSizeLabel : public QLabel +{ + Q_OBJECT + +public: + explicit DynamicFontSizeLabel(QWidget *parent = NULL, Qt::WindowFlags f = Qt::WindowFlags()); + + ~DynamicFontSizeLabel() + { + } + + static float getWidgetMaximumFontSize(QWidget *widget, const QString &text); + + /* This method overwrite stylesheet */ + void setTextColor(QColor color); + QColor getTextColor(); + void setTextAndColor(const QString &text, QColor color = QColor::Invalid); +signals: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *event); + QColor textColor; + + // QWidget interface +protected: + void paintEvent(QPaintEvent *event); + + // QWidget interface +public: + QSize minimumSizeHint() const; + QSize sizeHint() const; +}; + +#endif // DYNAMICFONTSIZELABEL_H \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp new file mode 100644 index 000000000..fce4512e8 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp @@ -0,0 +1,80 @@ +#include "dynamic_font_size_push_button.h" + +#include "dynamic_font_size_label.h" + +#include +#include + +DynamicFontSizePushButton::DynamicFontSizePushButton(QWidget *parent) : QPushButton(parent) +{ +} + +void DynamicFontSizePushButton::paintEvent(QPaintEvent *event) +{ + // Call the base class paintEvent to preserve any other painting behavior + QPushButton::paintEvent(event); + + // Adjust the font size dynamically based on the text + QFont newFont = font(); + float fontSize = DynamicFontSizeLabel::getWidgetMaximumFontSize(this, this->text()); + newFont.setPointSizeF(fontSize); + setFont(newFont); + + // Get painter for custom painting + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // Paint the background with a linear gradient (normal state) + QLinearGradient gradient(0, 0, 0, height()); + if (isDown()) { + // Pressed state + gradient.setColorAt(0, QColor(128, 128, 128)); + gradient.setColorAt(1, QColor(64, 64, 64)); + } else if (underMouse()) { + // Hover state + gradient.setColorAt(0, QColor(96, 96, 96)); + gradient.setColorAt(1, QColor(48, 48, 48)); + } else { + // Normal state + gradient.setColorAt(0, QColor(64, 64, 64)); // start color + gradient.setColorAt(1, QColor(32, 32, 32)); // end color + } + painter.setBrush(gradient); + painter.setPen(Qt::NoPen); // No border + painter.drawRect(rect()); + + // Paint the button text + painter.setPen(QPen(textColor.isValid() ? textColor : QColor(255, 255, 255))); // Set text color + painter.drawText(rect(), Qt::AlignCenter, text()); +} + +void DynamicFontSizePushButton::setTextColor(QColor color) +{ + if (color.isValid() && color != textColor) { + textColor = color; + update(); // Request a repaint to update the text color + } +} + +void DynamicFontSizePushButton::setTextAndColor(const QString &text, QColor color) +{ + setTextColor(color); + setText(text); +} + +QColor DynamicFontSizePushButton::getTextColor() +{ + return textColor; +} + +/* Do not give any size hint as it it changes during paintEvent */ +QSize DynamicFontSizePushButton::minimumSizeHint() const +{ + return QWidget::minimumSizeHint(); +} + +/* Do not give any size hint as it it changes during paintEvent */ +QSize DynamicFontSizePushButton::sizeHint() const +{ + return QWidget::sizeHint(); +} diff --git a/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.h b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.h new file mode 100644 index 000000000..eb33a7d80 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.h @@ -0,0 +1,29 @@ +#ifndef DYNAMICFONTSIZEPUSHBUTTON_H +#define DYNAMICFONTSIZEPUSHBUTTON_H + +#include +#include +#include + +class DynamicFontSizePushButton : public QPushButton +{ +public: + explicit DynamicFontSizePushButton(QWidget *parent = NULL); + + /* This method overwrite stylesheet */ + void setTextColor(QColor color); + QColor getTextColor(); + void setTextAndColor(const QString &text, QColor color = QColor::Invalid); + + // QWidget interface + QSize minimumSizeHint() const; + QSize sizeHint() const; + +protected: + void paintEvent(QPaintEvent *event); + +private: + QColor textColor; +}; + +#endif // DYNAMICFONTSIZEPUSHBUTTON_H \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/general/display/shadow_background_label.cpp b/cockatrice/src/client/ui/widgets/general/display/shadow_background_label.cpp new file mode 100644 index 000000000..fbca8df8d --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/shadow_background_label.cpp @@ -0,0 +1,63 @@ +#include "shadow_background_label.h" + +#include +#include + +/** + * @class ShadowBackgroundLabel + * @brief A QLabel with a semi-transparent black shadowed background and rounded corners. + * + * This label provides a styled appearance with centered white text and a translucent + * rounded background, making it suitable for overlay or emphasis in a UI. + */ +ShadowBackgroundLabel::ShadowBackgroundLabel(QWidget *parent, const QString &text) : QLabel(parent) +{ + setAttribute(Qt::WA_TranslucentBackground); // Allows transparency. + setText("" + text + ""); ///< Ensures the text is rendered in white. + setAlignment(Qt::AlignCenter); ///< Centers the text within the label. + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); ///< Ensures minimum size constraints. +} + +/** + * @brief Handles resizing of the label. + * + * Ensures the label updates its appearance when resized by triggering a repaint. + * + * @param event The resize event containing new size information. + */ +void ShadowBackgroundLabel::resizeEvent(QResizeEvent *event) +{ + QLabel::resizeEvent(event); + update(); // Repaint borders explicitly. +} + +/** + * @brief Custom paint event for drawing the label's background. + * + * Renders a semi-transparent black rounded rectangle as the background + * and then delegates text rendering to QLabel. + * + * @param event The paint event for the widget. + */ +void ShadowBackgroundLabel::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // Enable antialiasing for smoother edges. + painter.setRenderHint(QPainter::Antialiasing, true); + + // Set semi-transparent black brush and disable border pen. + painter.setBrush(QColor(0, 0, 0, 128)); // Semi-transparent black. + painter.setPen(Qt::NoPen); // No border. + + // Adjust the rectangle to account for margins. + QRect adjustedRect = this->rect(); + int margin = contentsMargins().left(); // Assuming equal margins. + adjustedRect.adjust(margin, margin, -margin, -margin); + + // Draw a rounded rectangle with a corner radius of 5. + painter.drawRoundedRect(adjustedRect, 5, 5); + + // Delegate text rendering to QLabel. + QLabel::paintEvent(event); +} diff --git a/cockatrice/src/client/ui/widgets/general/display/shadow_background_label.h b/cockatrice/src/client/ui/widgets/general/display/shadow_background_label.h new file mode 100644 index 000000000..992c8e81c --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/shadow_background_label.h @@ -0,0 +1,18 @@ +#ifndef STYLEDLABEL_H +#define STYLEDLABEL_H + +#include + +class ShadowBackgroundLabel : public QLabel +{ + Q_OBJECT + +public: + explicit ShadowBackgroundLabel(QWidget *parent, const QString &text); + +protected: + void resizeEvent(QResizeEvent *event) override; + void paintEvent(QPaintEvent *event) override; // Custom painting logic +}; + +#endif // STYLEDLABEL_H diff --git a/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp b/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp index 7bcb6ea75..9475cd598 100644 --- a/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp +++ b/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp @@ -5,7 +5,6 @@ #include "flow_widget.h" -#include "../../../layouts/flow_layout.h" #include "../../../layouts/horizontal_flow_layout.h" #include "../../../layouts/vertical_flow_layout.h" @@ -29,21 +28,21 @@ FlowWidget::FlowWidget(QWidget *parent, // Main Widget and Layout this->setMinimumSize(0, 0); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - main_layout = new QHBoxLayout(); - this->setLayout(main_layout); + mainLayout = new QHBoxLayout(); + this->setLayout(mainLayout); // Flow Layout inside the scroll area container = new QWidget(); if (horizontalPolicy != Qt::ScrollBarAlwaysOff && verticalPolicy == Qt::ScrollBarAlwaysOff) { - flow_layout = new HorizontalFlowLayout(container); + flowLayout = new HorizontalFlowLayout(container); } else if (horizontalPolicy == Qt::ScrollBarAlwaysOff && verticalPolicy != Qt::ScrollBarAlwaysOff) { - flow_layout = new VerticalFlowLayout(container); + flowLayout = new VerticalFlowLayout(container); } else { - flow_layout = new FlowLayout(container, 0, 0, 0); + flowLayout = new FlowLayout(container, 0, 0, 0); } - container->setLayout(flow_layout); + container->setLayout(flowLayout); // The container should expand as much as possible, trusting the scrollArea to constrain it. container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); container->setMinimumSize(0, 0); @@ -60,10 +59,10 @@ FlowWidget::FlowWidget(QWidget *parent, // Use the FlowLayout container directly if we disable the ScrollArea if (horizontalPolicy == Qt::ScrollBarAlwaysOff && verticalPolicy == Qt::ScrollBarAlwaysOff) { - main_layout->addWidget(container); + mainLayout->addWidget(container); } else { scrollArea->setWidget(container); - main_layout->addWidget(scrollArea); + mainLayout->addWidget(scrollArea); } } @@ -85,7 +84,7 @@ void FlowWidget::addWidget(QWidget *widget_to_add) const } // Add the widget to the flow layout - this->flow_layout->addWidget(widget_to_add); + flowLayout->addWidget(widget_to_add); } /** @@ -95,23 +94,23 @@ void FlowWidget::addWidget(QWidget *widget_to_add) const */ void FlowWidget::clearLayout() { - if (flow_layout != nullptr) { + if (flowLayout != nullptr) { QLayoutItem *item; - while ((item = flow_layout->takeAt(0)) != nullptr) { + while ((item = flowLayout->takeAt(0)) != nullptr) { item->widget()->deleteLater(); // Delete the widget delete item; // Delete the layout item } } else { if (scrollArea->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff && scrollArea->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) { - flow_layout = new HorizontalFlowLayout(container); + flowLayout = new HorizontalFlowLayout(container); } else if (scrollArea->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff && scrollArea->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) { - flow_layout = new VerticalFlowLayout(container); + flowLayout = new VerticalFlowLayout(container); } else { - flow_layout = new FlowLayout(container, 0, 0, 0); + flowLayout = new FlowLayout(container, 0, 0, 0); } - this->container->setLayout(flow_layout); + container->setLayout(flowLayout); } } @@ -127,15 +126,52 @@ void FlowWidget::resizeEvent(QResizeEvent *event) QWidget::resizeEvent(event); // Trigger the layout to recalculate - if (flow_layout != nullptr) { - flow_layout->invalidate(); // Marks the layout as dirty and requires recalculation - flow_layout->activate(); // Recalculate the layout based on the new size + if (flowLayout != nullptr) { + flowLayout->invalidate(); // Marks the layout as dirty and requires recalculation + flowLayout->activate(); // Recalculate the layout based on the new size } // Ensure the scroll area and its content adjust correctly - if (scrollArea != nullptr) { - if (scrollArea->widget() != nullptr) { - scrollArea->widget()->adjustSize(); + if (scrollArea != nullptr && scrollArea->widget() != nullptr) { + scrollArea->widget()->adjustSize(); + } +} + +/** + * @brief Sets the minimum size for all widgets inside the FlowWidget to the maximum sizeHint of all of them. + */ +void FlowWidget::setMinimumSizeToMaxSizeHint() +{ + QSize maxSize(0, 0); // Initialize to a zero size + + // Iterate over all widgets in the flow layout to find the maximum sizeHint + for (int i = 0; i < flowLayout->count(); ++i) { + if (QLayoutItem *item = flowLayout->itemAt(i)) { + if (QWidget *widget = item->widget()) { + // Update the max size based on the sizeHint of each widget + QSize widgetSizeHint = widget->sizeHint(); + maxSize.setWidth(qMax(maxSize.width(), widgetSizeHint.width())); + maxSize.setHeight(qMax(maxSize.height(), widgetSizeHint.height())); + } + } + } + + // Set the minimum size for all widgets to the max sizeHint + for (int i = 0; i < flowLayout->count(); ++i) { + if (QLayoutItem *item = flowLayout->itemAt(i)) { + if (QWidget *widget = item->widget()) { + widget->setMinimumSize(maxSize); + } } } } + +QLayoutItem *FlowWidget::itemAt(int index) const +{ + return flowLayout->itemAt(index); +} + +int FlowWidget::count() const +{ + return flowLayout->count(); +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.h b/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.h index a52cca6e2..66a6566c3 100644 --- a/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.h +++ b/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.h @@ -14,15 +14,20 @@ public: FlowWidget(QWidget *parent, Qt::ScrollBarPolicy horizontalPolicy, Qt::ScrollBarPolicy verticalPolicy); void addWidget(QWidget *widget_to_add) const; void clearLayout(); + [[nodiscard]] int count() const; + [[nodiscard]] QLayoutItem *itemAt(int index) const; QScrollArea *scrollArea; +public slots: + void setMinimumSizeToMaxSizeHint(); + protected: void resizeEvent(QResizeEvent *event) override; private: - QHBoxLayout *main_layout; - FlowLayout *flow_layout; + QHBoxLayout *mainLayout; + FlowLayout *flowLayout; QWidget *container; }; diff --git a/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp new file mode 100644 index 000000000..309c21c17 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp @@ -0,0 +1,120 @@ +#include "all_zones_card_amount_widget.h" + +#include "../general/display/shadow_background_label.h" + +#include + +/** + * @brief Constructor for the AllZonesCardAmountWidget class. + * + * Initializes the widget with its layout and sets up the connections and necessary + * UI elements for managing card counts in both the mainboard and sideboard zones. + * + * @param parent The parent widget. + * @param deckEditor Pointer to the TabDeckEditor. + * @param deckModel Pointer to the DeckListModel. + * @param deckView Pointer to the QTreeView for the deck display. + * @param cardSizeSlider Pointer to the QSlider used for dynamic font resizing. + * @param rootCard The root card for the widget. + * @param setInfoForCard The set information for the card. + */ +AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent, + TabDeckEditor *deckEditor, + DeckListModel *deckModel, + QTreeView *deckView, + QSlider *cardSizeSlider, + CardInfoPtr rootCard, + CardInfoPerSet setInfoForCard) + : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView), cardSizeSlider(cardSizeSlider), + rootCard(rootCard), setInfoForCard(setInfoForCard) +{ + layout = new QVBoxLayout(this); + layout->setAlignment(Qt::AlignHCenter); + setLayout(layout); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setContentsMargins(5, 5, 5, 5); // Padding around the text + + zoneLabelMainboard = new ShadowBackgroundLabel(this, tr("Mainboard")); + buttonBoxMainboard = new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, + setInfoForCard, DECK_ZONE_MAIN); + zoneLabelSideboard = new ShadowBackgroundLabel(this, tr("Sideboard")); + buttonBoxSideboard = new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, + setInfoForCard, DECK_ZONE_SIDE); + + layout->addWidget(zoneLabelMainboard, 0, Qt::AlignHCenter | Qt::AlignBottom); + layout->addWidget(buttonBoxMainboard, 0, Qt::AlignHCenter | Qt::AlignTop); + layout->addSpacing(25); + layout->addWidget(zoneLabelSideboard, 0, Qt::AlignHCenter | Qt::AlignBottom); + layout->addWidget(buttonBoxSideboard, 0, Qt::AlignHCenter | Qt::AlignTop); + + connect(cardSizeSlider, &QSlider::valueChanged, this, &AllZonesCardAmountWidget::adjustFontSize); + + QTimer::singleShot(10, this, [this]() { adjustFontSize(this->cardSizeSlider->value()); }); + + setMouseTracking(true); +} + +/** + * @brief Adjusts the font size of the zone labels based on the slider value. + * + * This method calculates the new font size as a percentage of the original font size + * based on the slider value and applies it to the zone label text. + * + * @param scalePercentage The scale percentage from the slider. + */ +void AllZonesCardAmountWidget::adjustFontSize(int scalePercentage) +{ + const int minFontSize = 8; // Minimum font size + const int maxFontSize = 32; // Maximum font size + const int basePercentage = 100; // Scale at 100% + + int newFontSize = minFontSize + (scalePercentage - basePercentage) * (maxFontSize - minFontSize) / 225; + newFontSize = std::clamp(newFontSize, minFontSize, maxFontSize); + + // Update the font labels + QFont zoneLabelFont = zoneLabelMainboard->font(); + zoneLabelFont.setPointSize(newFontSize); + zoneLabelMainboard->setFont(zoneLabelFont); + zoneLabelSideboard->setFont(zoneLabelFont); + + // Repaint the widget (if necessary) + repaint(); +} + +/** + * @brief Gets the card count in the mainboard zone. + * + * @return The number of cards in the mainboard. + */ +int AllZonesCardAmountWidget::getMainboardAmount() +{ + return buttonBoxMainboard->countCardsInZone(DECK_ZONE_MAIN); +} + +/** + * @brief Gets the card count in the sideboard zone. + * + * @return The number of cards in the sideboard. + */ +int AllZonesCardAmountWidget::getSideboardAmount() +{ + return buttonBoxSideboard->countCardsInZone(DECK_ZONE_SIDE); +} + +/** + * @brief Handles the event when the mouse enters the widget. + * + * This method is triggered when the mouse enters the widget's area, allowing for updates + * or interactions such as UI feedback or layout changes. + * + * @param event The event information for the mouse entry. + */ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +void AllZonesCardAmountWidget::enterEvent(QEnterEvent *event) +#else +void AllZonesCardAmountWidget::enterEvent(QEvent *event) +#endif +{ + QWidget::enterEvent(event); + update(); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h new file mode 100644 index 000000000..0e08f04a4 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h @@ -0,0 +1,47 @@ +#ifndef ALL_ZONES_CARD_AMOUNT_WIDGET_H +#define ALL_ZONES_CARD_AMOUNT_WIDGET_H +#include "../../../../deck/deck_list_model.h" +#include "../../../../deck/deck_loader.h" +#include "../../../../deck/deck_view.h" +#include "card_amount_widget.h" + +#include +#include + +class AllZonesCardAmountWidget : public QWidget +{ + Q_OBJECT +public: + explicit AllZonesCardAmountWidget(QWidget *parent, + TabDeckEditor *deckEditor, + DeckListModel *deckModel, + QTreeView *deckView, + QSlider *cardSizeSlider, + CardInfoPtr rootCard, + CardInfoPerSet setInfoForCard); + int getMainboardAmount(); + int getSideboardAmount(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + void enterEvent(QEnterEvent *event) override; +#else + void enterEvent(QEvent *event) override; +#endif + +public slots: + void adjustFontSize(int scalePercentage); + +private: + QVBoxLayout *layout; + TabDeckEditor *deckEditor; + DeckListModel *deckModel; + QTreeView *deckView; + QSlider *cardSizeSlider; + CardInfoPtr rootCard; + CardInfoPerSet setInfoForCard; + QLabel *zoneLabelMainboard; + CardAmountWidget *buttonBoxMainboard; + QLabel *zoneLabelSideboard; + CardAmountWidget *buttonBoxSideboard; +}; + +#endif // ALL_ZONES_CARD_AMOUNT_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp new file mode 100644 index 000000000..5f5ca3c5b --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp @@ -0,0 +1,293 @@ +#include "card_amount_widget.h" + +#include + +/** + * @brief Constructs a widget for displaying and controlling the card count in a specific zone. + * + * @param parent The parent widget. + * @param deckEditor Pointer to the TabDeckEditor instance. + * @param deckModel Pointer to the DeckListModel instance. + * @param deckView Pointer to the QTreeView displaying the deck. + * @param cardSizeSlider Pointer to the QSlider for adjusting font size. + * @param rootCard The root card to manage within the widget. + * @param setInfoForCard Card set information for the root card. + * @param zoneName The zone name (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE). + */ +CardAmountWidget::CardAmountWidget(QWidget *parent, + TabDeckEditor *deckEditor, + DeckListModel *deckModel, + QTreeView *deckView, + QSlider *cardSizeSlider, + CardInfoPtr &rootCard, + CardInfoPerSet &setInfoForCard, + const QString &zoneName) + : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView), cardSizeSlider(cardSizeSlider), + rootCard(rootCard), setInfoForCard(setInfoForCard), zoneName(zoneName), hovered(false) +{ + layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(10); + this->setLayout(layout); + this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + layout->setAlignment(Qt::AlignHCenter); + + incrementButton = new DynamicFontSizePushButton(this); + incrementButton->setTextAndColor("+", Qt::white); + decrementButton = new DynamicFontSizePushButton(this); + decrementButton->setTextAndColor("-", Qt::white); + + incrementButton->setFixedSize(parentWidget()->size().width() / 3, parentWidget()->size().height() / 9); + decrementButton->setFixedSize(parentWidget()->size().width() / 3, parentWidget()->size().height() / 9); + + // Set up connections based on the zone (Mainboard or Sideboard) + if (zoneName == DECK_ZONE_MAIN) { + connect(incrementButton, &QPushButton::clicked, this, &CardAmountWidget::addPrintingMainboard); + connect(decrementButton, &QPushButton::clicked, this, &CardAmountWidget::removePrintingMainboard); + } else if (zoneName == DECK_ZONE_SIDE) { + connect(incrementButton, &QPushButton::clicked, this, &CardAmountWidget::addPrintingSideboard); + connect(decrementButton, &QPushButton::clicked, this, &CardAmountWidget::removePrintingSideboard); + } + + cardCountInZone = new QLabel(QString::number(countCardsInZone(zoneName)), this); + cardCountInZone->setAlignment(Qt::AlignCenter); + + layout->addWidget(decrementButton); + layout->addWidget(cardCountInZone); + layout->addWidget(incrementButton); + + // React to model changes + connect(deckModel, &DeckListModel::dataChanged, this, &CardAmountWidget::updateCardCount); + connect(deckModel, &QAbstractItemModel::rowsRemoved, this, &CardAmountWidget::updateCardCount); + + // Connect slider for dynamic font size adjustment + connect(cardSizeSlider, &QSlider::valueChanged, this, &CardAmountWidget::adjustFontSize); +} + +/** + * @brief Handles the painting of the widget, drawing a semi-transparent background. + * + * @param event The paint event. + */ +void CardAmountWidget::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // Draw semi-transparent black background + painter.setBrush(QBrush(QColor(0, 0, 0, 128))); + painter.setPen(Qt::NoPen); + painter.drawRect(rect()); + + QWidget::paintEvent(event); +} + +void CardAmountWidget::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + + adjustFontSize(this->cardSizeSlider->value()); + updateCardCount(); + + if (parentWidget()) { + int width = parentWidget()->size().width(); + int height = parentWidget()->size().height(); + + incrementButton->setFixedSize(width / 3, height / 9); + decrementButton->setFixedSize(width / 3, height / 9); + } +} + +/** + * @brief Adjusts the font size of the card count label based on the slider value. + * + * @param scalePercentage The percentage value from the slider for scaling the font size. + */ +void CardAmountWidget::adjustFontSize(int scalePercentage) +{ + const int minFontSize = 8; ///< Minimum font size + const int maxFontSize = 32; ///< Maximum font size + const int basePercentage = 100; ///< Scale at 100% + + int newFontSize = minFontSize + (scalePercentage - basePercentage) * (maxFontSize - minFontSize) / 225; + newFontSize = std::clamp(newFontSize, minFontSize, maxFontSize); + + // Update the font for card count label + QFont cardCountFont = cardCountInZone->font(); + cardCountFont.setPointSize(newFontSize); + cardCountInZone->setFont(cardCountFont); + + incrementButton->setFixedSize(parentWidget()->size().width() / 3, parentWidget()->size().height() / 9); + decrementButton->setFixedSize(parentWidget()->size().width() / 3, parentWidget()->size().height() / 9); + + // Repaint the widget + repaint(); +} + +/** + * @brief Updates the card count display in the widget. + */ +void CardAmountWidget::updateCardCount() +{ + cardCountInZone->setText("" + QString::number(countCardsInZone(zoneName)) + ""); + layout->invalidate(); + layout->activate(); +} + +/** + * @brief Adds a printing of the card to the specified zone (Mainboard or Sideboard). + * + * @param zone The zone to add the card to (DECK_ZONE_MAIN or DECK_ZONE_SIDE). + */ +void CardAmountWidget::addPrinting(const QString &zone) +{ + auto newCardIndex = deckModel->addCard(rootCard->getName(), setInfoForCard, zone); + recursiveExpand(newCardIndex); + QModelIndex find_card = deckModel->findCard(rootCard->getName(), zone); + if (find_card.isValid() && find_card != newCardIndex) { + auto amount = deckModel->data(find_card, Qt::DisplayRole); + for (int i = 0; i < amount.toInt() - 1; i++) { + deckModel->addCard(rootCard->getName(), setInfoForCard, zone); + } + deckModel->removeRow(find_card.row(), find_card.parent()); + } + newCardIndex = deckModel->findCard(rootCard->getName(), zone, setInfoForCard.getProperty("uuid"), + setInfoForCard.getProperty("num")); + deckView->setCurrentIndex(newCardIndex); + deckView->setFocus(Qt::FocusReason::MouseFocusReason); +} + +/** + * @brief Adds a printing to the mainboard zone. + */ +void CardAmountWidget::addPrintingMainboard() +{ + addPrinting(DECK_ZONE_MAIN); +} + +/** + * @brief Adds a printing to the sideboard zone. + */ +void CardAmountWidget::addPrintingSideboard() +{ + addPrinting(DECK_ZONE_SIDE); +} + +/** + * @brief Removes a printing from the mainboard zone. + */ +void CardAmountWidget::removePrintingMainboard() +{ + decrementCardHelper(DECK_ZONE_MAIN); +} + +/** + * @brief Removes a printing from the sideboard zone. + */ +void CardAmountWidget::removePrintingSideboard() +{ + decrementCardHelper(DECK_ZONE_SIDE); +} + +/** + * @brief Recursively expands the card in the deck view starting from the given index. + * + * @param index The model index of the card to expand. + */ +void CardAmountWidget::recursiveExpand(const QModelIndex &index) +{ + if (index.parent().isValid()) { + recursiveExpand(index.parent()); + } + deckView->expand(index); +} + +/** + * @brief Offsets the card count at the specified index by the given amount. + * + * @param idx The model index of the card. + * @param offset The amount to add or subtract from the card count. + */ +void CardAmountWidget::offsetCountAtIndex(const QModelIndex &idx, int offset) +{ + if (!idx.isValid() || offset == 0) { + return; + } + + const QModelIndex numberIndex = idx.sibling(idx.row(), 0); + const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); + const int new_count = count + offset; + deckView->setCurrentIndex(numberIndex); + if (new_count <= 0) { + deckModel->removeRow(idx.row(), idx.parent()); + } else { + deckModel->setData(numberIndex, new_count, Qt::EditRole); + } + deckEditor->setModified(true); +} + +/** + * @brief Helper function to decrement the card count for a given zone. + * + * @param zone The zone from which to remove the card (DECK_ZONE_MAIN or DECK_ZONE_SIDE). + */ +void CardAmountWidget::decrementCardHelper(const QString &zone) +{ + QModelIndex idx = deckModel->findCard(rootCard->getName(), zone, setInfoForCard.getProperty("uuid"), + setInfoForCard.getProperty("num")); + offsetCountAtIndex(idx, -1); +} + +/** + * @brief Counts the number of cards in a specific zone (mainboard or sideboard). + * + * @param deckZone The name of the zone (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE). + * @return The number of cards in the zone. + */ +int CardAmountWidget::countCardsInZone(const QString &deckZone) +{ + if (setInfoForCard.getProperty("uuid").isEmpty()) { + return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us. + } + + if (!deckModel) { + return -1; + } + + DeckList *decklist = deckModel->getDeckList(); + if (!decklist) { + return -1; + } + + InnerDecklistNode *listRoot = decklist->getRoot(); + if (!listRoot) { + return -1; + } + + int count = 0; + + for (auto *i : *listRoot) { + auto *countCurrentZone = dynamic_cast(i); + if (!countCurrentZone) { + continue; + } + + if (countCurrentZone->getName() != deckZone) { + continue; + } + + for (auto *cardNode : *countCurrentZone) { + auto *currentCard = dynamic_cast(cardNode); + if (!currentCard) { + continue; + } + + for (int k = 0; k < currentCard->getNumber(); ++k) { + if (currentCard->getCardProviderId() == setInfoForCard.getProperty("uuid")) { + count++; + } + } + } + } + return count; +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h new file mode 100644 index 000000000..3d4e5c4b8 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h @@ -0,0 +1,67 @@ +#ifndef CARD_AMOUNT_WIDGET_H +#define CARD_AMOUNT_WIDGET_H + +#include "../../../../deck/deck_list_model.h" +#include "../../../../deck/deck_loader.h" +#include "../../../../deck/deck_view.h" +#include "../../../../game/cards/card_database.h" +#include "../../../tabs/tab_deck_editor.h" +#include "../general/display/dynamic_font_size_push_button.h" + +#include +#include +#include +#include +#include +#include + +class CardAmountWidget : public QWidget +{ + Q_OBJECT +public: + explicit CardAmountWidget(QWidget *parent, + TabDeckEditor *deckEditor, + DeckListModel *deckModel, + QTreeView *deckView, + QSlider *cardSizeSlider, + CardInfoPtr &rootCard, + CardInfoPerSet &setInfoForCard, + const QString &zoneName); + int countCardsInZone(const QString &deckZone); + +public slots: + void updateCardCount(); + void addPrinting(const QString &zone); + +protected: + void paintEvent(QPaintEvent *event) override; + void showEvent(QShowEvent *event) override; + +private: + TabDeckEditor *deckEditor; + DeckListModel *deckModel; + QTreeView *deckView; + QSlider *cardSizeSlider; + CardInfoPtr rootCard; + CardInfoPerSet setInfoForCard; + QString zoneName; + QHBoxLayout *layout; + DynamicFontSizePushButton *incrementButton; + DynamicFontSizePushButton *decrementButton; + QLabel *cardCountInZone; + + bool hovered; + + void offsetCountAtIndex(const QModelIndex &idx, int offset); + void decrementCardHelper(const QString &zoneName); + void recursiveExpand(const QModelIndex &index); + +private slots: + void addPrintingMainboard(); + void addPrintingSideboard(); + void removePrintingMainboard(); + void removePrintingSideboard(); + void adjustFontSize(int scalePercentage); +}; + +#endif // CARD_AMOUNT_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp new file mode 100644 index 000000000..5d3adccd2 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp @@ -0,0 +1,234 @@ +#include "printing_selector.h" + +#include "../../../../settings/cache_settings.h" +#include "printing_selector_card_display_widget.h" +#include "printing_selector_card_search_widget.h" +#include "printing_selector_card_selection_widget.h" +#include "printing_selector_card_sorting_widget.h" +#include "printing_selector_view_options_toolbar_widget.h" + +#include + +/** + * @brief Constructs a PrintingSelector widget to display and manage card printings. + * + * This constructor initializes the PrintingSelector widget, setting up various child widgets + * such as sorting tools, search bar, card size options, and navigation controls. It also connects + * signals and slots to update the display when the deck model changes, and loads available printings + * for the selected card. + * + * @param parent The parent widget for the PrintingSelector. + * @param deckEditor The TabDeckEditor instance used for managing the deck. + * @param deckModel The DeckListModel instance that provides data for the deck's contents. + * @param deckView The QTreeView instance used to display the deck and its contents. + */ +PrintingSelector::PrintingSelector(QWidget *parent, + TabDeckEditor *deckEditor, + DeckListModel *deckModel, + QTreeView *deckView) + : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout = new QVBoxLayout(); + setLayout(layout); + widgetLoadingBufferTimer = new QTimer(this); + + // Initialize toolbar and widgets + viewOptionsToolbar = new PrintingSelectorViewOptionsToolbarWidget(this, this); + layout->addWidget(viewOptionsToolbar); + + sortToolBar = new PrintingSelectorCardSortingWidget(this); + sortToolBar->setVisible(SettingsCache::instance().getPrintingSelectorSortOptionsVisible()); + layout->addWidget(sortToolBar); + + searchBar = new PrintingSelectorCardSearchWidget(this); + searchBar->setVisible(SettingsCache::instance().getPrintingSelectorSearchBarVisible()); + layout->addWidget(searchBar); + + flowWidget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); + layout->addWidget(flowWidget); + + cardSizeWidget = new CardSizeWidget(this, flowWidget, SettingsCache::instance().getPrintingSelectorCardSize()); + cardSizeWidget->setVisible(SettingsCache::instance().getPrintingSelectorCardSizeSliderVisible()); + layout->addWidget(cardSizeWidget); + + cardSelectionBar = new PrintingSelectorCardSelectionWidget(this); + cardSelectionBar->setVisible(SettingsCache::instance().getPrintingSelectorNavigationButtonsVisible()); + layout->addWidget(cardSelectionBar); + + // Connect deck model data change signal to update display + connect(deckModel, &DeckListModel::dataChanged, this, [this]() { + // Delay the update to avoid race conditions + QTimer::singleShot(100, this, &PrintingSelector::updateDisplay); + }); +} + +/** + * @brief Updates the display by clearing the layout and loading new sets for the current card. + */ +void PrintingSelector::updateDisplay() +{ + widgetLoadingBufferTimer->stop(); + widgetLoadingBufferTimer->deleteLater(); + widgetLoadingBufferTimer = new QTimer(this); + flowWidget->clearLayout(); + if (selectedCard != nullptr) { + setWindowTitle(selectedCard->getName()); + } + getAllSetsForCurrentCard(); +} + +/** + * @brief Sets the current card for the selector and updates the display. + * + * @param newCard The new card to set. + * @param _currentZone The current zone the card is in. + */ +void PrintingSelector::setCard(const CardInfoPtr &newCard, const QString &_currentZone) +{ + if (newCard.isNull()) { + return; + } + selectedCard = newCard; + currentZone = _currentZone; + if (isVisible()) { + updateDisplay(); + } + flowWidget->setMinimumSizeToMaxSizeHint(); + flowWidget->scrollArea->verticalScrollBar()->setValue(0); + flowWidget->repaint(); +} + +/** + * @brief Selects the previous card in the list. + */ +void PrintingSelector::selectPreviousCard() +{ + selectCard(-1); +} + +/** + * @brief Selects the next card in the list. + */ +void PrintingSelector::selectNextCard() +{ + selectCard(1); +} + +/** + * @brief Selects a card based on the change direction. + * + * @param changeBy The direction to change, -1 for previous, 1 for next. + */ +void PrintingSelector::selectCard(const int changeBy) +{ + if (changeBy == 0) { + return; + } + + // Get the current index of the selected item + auto deckViewCurrentIndex = deckView->currentIndex(); + + auto nextIndex = deckViewCurrentIndex.siblingAtRow(deckViewCurrentIndex.row() + changeBy); + if (!nextIndex.isValid()) { + nextIndex = deckViewCurrentIndex; + + // Increment to the next valid index, skipping header rows + AbstractDecklistNode *node; + do { + if (changeBy > 0) { + nextIndex = deckView->indexBelow(nextIndex); + } else { + nextIndex = deckView->indexAbove(nextIndex); + } + node = static_cast(nextIndex.internalPointer()); + } while (node && node->isDeckHeader()); + } + + if (nextIndex.isValid()) { + deckView->setCurrentIndex(nextIndex); + deckView->setFocus(Qt::FocusReason::MouseFocusReason); + } +} + +/** + * @brief Loads and displays all sets for the current selected card. + */ +void PrintingSelector::getAllSetsForCurrentCard() +{ + if (selectedCard.isNull()) { + return; + } + + CardInfoPerSetMap cardInfoPerSets = selectedCard->getSets(); + const QList sortedSets = sortToolBar->sortSets(cardInfoPerSets); + const QList filteredSets = + sortToolBar->filterSets(sortedSets, searchBar->getSearchText().trimmed().toLower()); + QList setsToUse; + + if (SettingsCache::instance().getBumpSetsWithCardsInDeckToTop()) { + setsToUse = sortToolBar->prependPrintingsInDeck(filteredSets, selectedCard, deckModel); + } else { + setsToUse = filteredSets; + } + + // Defer widget creation + currentIndex = 0; + + connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=]() mutable { + for (int i = 0; i < BATCH_SIZE && currentIndex < setsToUse.size(); ++i, ++currentIndex) { + auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckModel, deckView, + cardSizeWidget->getSlider(), selectedCard, + setsToUse[currentIndex], currentZone); + flowWidget->addWidget(cardDisplayWidget); + cardDisplayWidget->clampSetNameToPicture(); + } + + // Stop timer when done + if (currentIndex >= setsToUse.size()) { + widgetLoadingBufferTimer->stop(); + } + }); + currentIndex = 0; + widgetLoadingBufferTimer->start(0); // Process as soon as possible +} + +/** + * @brief Toggles the visibility of the sorting options toolbar. + * + * @param _state The visibility state to set. + */ +void PrintingSelector::toggleVisibilitySortOptions(bool _state) +{ + sortToolBar->setVisible(_state); +} + +/** + * @brief Toggles the visibility of the search bar. + * + * @param _state The visibility state to set. + */ +void PrintingSelector::toggleVisibilitySearchBar(bool _state) +{ + searchBar->setVisible(_state); +} + +/** + * @brief Toggles the visibility of the card size slider. + * + * @param _state The visibility state to set. + */ +void PrintingSelector::toggleVisibilityCardSizeSlider(bool _state) +{ + cardSizeWidget->setVisible(_state); +} + +/** + * @brief Toggles the visibility of the navigation buttons. + * + * @param _state The visibility state to set. + */ +void PrintingSelector::toggleVisibilityNavigationButtons(bool _state) +{ + cardSelectionBar->setVisible(_state); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h new file mode 100644 index 000000000..1e9b01b29 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h @@ -0,0 +1,58 @@ +#ifndef PRINTING_SELECTOR_H +#define PRINTING_SELECTOR_H + +#include "../../../../deck/deck_list_model.h" +#include "../../../../deck/deck_view.h" +#include "../../../../game/cards/card_database.h" +#include "../cards/card_size_widget.h" +#include "../general/layout_containers/flow_widget.h" + +#include +#include +#include +#include + +#define BATCH_SIZE 10 + +class PrintingSelectorCardSearchWidget; +class PrintingSelectorCardSelectionWidget; +class PrintingSelectorCardSortingWidget; +class PrintingSelectorViewOptionsToolbarWidget; +class TabDeckEditor; +class PrintingSelector : public QWidget +{ + Q_OBJECT + +public: + PrintingSelector(QWidget *parent, TabDeckEditor *deckEditor, DeckListModel *deckModel, QTreeView *deckView); + void setCard(const CardInfoPtr &newCard, const QString &_currentZone); + void getAllSetsForCurrentCard(); + +public slots: + void updateDisplay(); + void selectPreviousCard(); + void selectNextCard(); + void toggleVisibilitySortOptions(bool _state); + void toggleVisibilitySearchBar(bool _state); + void toggleVisibilityCardSizeSlider(bool _state); + void toggleVisibilityNavigationButtons(bool _state); + +private: + QVBoxLayout *layout; + PrintingSelectorViewOptionsToolbarWidget *viewOptionsToolbar; + PrintingSelectorCardSortingWidget *sortToolBar; + PrintingSelectorCardSearchWidget *searchBar; + FlowWidget *flowWidget; + CardSizeWidget *cardSizeWidget; + PrintingSelectorCardSelectionWidget *cardSelectionBar; + TabDeckEditor *deckEditor; + DeckListModel *deckModel; + QTreeView *deckView; + CardInfoPtr selectedCard; + QString currentZone; + QTimer *widgetLoadingBufferTimer; + int currentIndex = 0; + void selectCard(int changeBy); +}; + +#endif // PRINTING_SELECTOR_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp new file mode 100644 index 000000000..176cc793f --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp @@ -0,0 +1,74 @@ +#include "printing_selector_card_display_widget.h" + +#include "card_amount_widget.h" +#include "printing_selector_card_overlay_widget.h" +#include "set_name_and_collectors_number_display_widget.h" + +#include +#include +#include +#include + +/** + * @brief Constructs a PrintingSelectorCardDisplayWidget to display card information. + * + * This widget is responsible for displaying the selected card's printing information, including + * the card's image and set details. It also handles the layout of the card's display, including + * its size, set name, and collectors number. The card is displayed within a `QVBoxLayout` with + * two main components: the overlay (which combines the card image and buttons) and the set name and collectors number + * display. + * + * @param parent The parent widget for this display. + * @param deckEditor The TabDeckEditor instance for deck management. + * @param deckModel The DeckListModel instance providing deck data. + * @param deckView The QTreeView instance displaying the deck. + * @param cardSizeSlider The slider controlling the size of the displayed card. + * @param rootCard The root card object, representing the card to be displayed. + * @param setInfoForCard The set-specific information for the card being displayed. + * @param currentZone The current zone in which the card is located. + */ +PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent, + TabDeckEditor *_deckEditor, + DeckListModel *_deckModel, + QTreeView *_deckView, + QSlider *_cardSizeSlider, + CardInfoPtr _rootCard, + const CardInfoPerSet &_setInfoForCard, + QString &_currentZone) + : QWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), deckView(_deckView), + cardSizeSlider(_cardSizeSlider), rootCard(std::move(_rootCard)), setInfoForCard(_setInfoForCard), + currentZone(_currentZone) +{ + layout = new QVBoxLayout(this); + setLayout(layout); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + // Create the overlay widget for the card display + overlayWidget = new PrintingSelectorCardOverlayWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, + rootCard, setInfoForCard); + + // Create the widget to display the set name and collector's number + const QString combinedSetName = + QString(setInfoForCard.getPtr()->getLongName() + " (" + setInfoForCard.getPtr()->getShortName() + ")"); + setNameAndCollectorsNumberDisplayWidget = new SetNameAndCollectorsNumberDisplayWidget( + this, combinedSetName, setInfoForCard.getProperty("num"), cardSizeSlider); + + // Add the widgets to the layout + layout->addWidget(overlayWidget, 0, Qt::AlignHCenter); + layout->addWidget(setNameAndCollectorsNumberDisplayWidget, 1, Qt::AlignHCenter | Qt::AlignBottom); +} + +/** + * @brief Adjusts the width of the set name display to fit the card overlay widget. + * + * This method ensures that the set name and collector's number display widget does not exceed + * the width of the card's overlay widget. It clamps the set name widget to match the width of + * the overlay widget and updates the display. + */ +void PrintingSelectorCardDisplayWidget::clampSetNameToPicture() +{ + if (overlayWidget != nullptr && setNameAndCollectorsNumberDisplayWidget != nullptr) { + setNameAndCollectorsNumberDisplayWidget->setMaximumWidth(overlayWidget->width()); + } + update(); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h new file mode 100644 index 000000000..f8b351c84 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h @@ -0,0 +1,52 @@ +#ifndef PRINTING_SELECTOR_CARD_DISPLAY_WIDGET_H +#define PRINTING_SELECTOR_CARD_DISPLAY_WIDGET_H + +#include "../../../../client/ui/widgets/cards/card_info_picture_widget.h" +#include "../../../../deck/deck_list_model.h" +#include "../../../../deck/deck_view.h" +#include "../../../../game/cards/card_database.h" +#include "../../../tabs/tab_deck_editor.h" +#include "all_zones_card_amount_widget.h" +#include "card_amount_widget.h" +#include "printing_selector_card_overlay_widget.h" +#include "set_name_and_collectors_number_display_widget.h" + +#include +#include +#include +#include +#include +#include + +class PrintingSelectorCardDisplayWidget : public QWidget +{ + Q_OBJECT + +public: + PrintingSelectorCardDisplayWidget(QWidget *parent, + TabDeckEditor *_deckEditor, + DeckListModel *_deckModel, + QTreeView *_deckView, + QSlider *_cardSizeSlider, + CardInfoPtr _rootCard, + const CardInfoPerSet &_setInfoForCard, + QString &_currentZone); + +public slots: + void clampSetNameToPicture(); + +private: + QVBoxLayout *layout; + SetNameAndCollectorsNumberDisplayWidget *setNameAndCollectorsNumberDisplayWidget; + TabDeckEditor *deckEditor; + DeckListModel *deckModel; + QTreeView *deckView; + QSlider *cardSizeSlider; + CardInfoPtr rootCard; + CardInfoPtr setCard; + CardInfoPerSet setInfoForCard; + QString currentZone; + PrintingSelectorCardOverlayWidget *overlayWidget; +}; + +#endif // PRINTING_SELECTOR_CARD_DISPLAY_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp new file mode 100644 index 000000000..b996a6aff --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp @@ -0,0 +1,185 @@ +#include "printing_selector_card_overlay_widget.h" + +#include "../../../../game/cards/card_database_manager.h" +#include "printing_selector_card_display_widget.h" + +#include +#include +#include +#include + +/** + * @brief Constructs a PrintingSelectorCardOverlayWidget for displaying a card overlay. + * + * This widget is responsible for showing the card's image and providing interactive features such + * as a context menu and the ability to adjust the card's scale. It includes the card's image as well + * as a widget that displays the card amounts in different zones (mainboard, sideboard, etc.). + * + * @param parent The parent widget for this overlay. + * @param deckEditor The TabDeckEditor instance for deck management. + * @param deckModel The DeckListModel instance providing deck data. + * @param deckView The QTreeView instance displaying the deck. + * @param cardSizeSlider The slider controlling the size of the card. + * @param rootCard The root card object that contains information about the card. + * @param setInfoForCard The set-specific information for the card being displayed. + */ +PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent, + TabDeckEditor *_deckEditor, + DeckListModel *_deckModel, + QTreeView *_deckView, + QSlider *_cardSizeSlider, + CardInfoPtr _rootCard, + const CardInfoPerSet &_setInfoForCard) + : QWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), deckView(_deckView), + cardSizeSlider(_cardSizeSlider), rootCard(std::move(_rootCard)), setInfoForCard(_setInfoForCard) +{ + // Set up the main layout + auto *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + setLayout(mainLayout); + + // Add CardInfoPictureWidget + cardInfoPicture = new CardInfoPictureWidget(this); + cardInfoPicture->setMinimumSize(0, 0); + cardInfoPicture->setScaleFactor(cardSizeSlider->value()); + setCard = CardDatabaseManager::getInstance()->getCardByNameAndProviderId(rootCard->getName(), + setInfoForCard.getProperty("uuid")); + cardInfoPicture->setCard(setCard); + mainLayout->addWidget(cardInfoPicture); + + // Add AllZonesCardAmountWidget + allZonesCardAmountWidget = + new AllZonesCardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, setCard, setInfoForCard); + + allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture + // Set initial visibility based on amounts + if (allZonesCardAmountWidget->getMainboardAmount() > 0 || allZonesCardAmountWidget->getSideboardAmount() > 0) { + allZonesCardAmountWidget->setVisible(true); + } else { + allZonesCardAmountWidget->setVisible(false); + } + + // Attempt to cast the parent to PrintingSelectorCardDisplayWidget + if (const auto *parentWidget = qobject_cast(parent)) { + connect(cardInfoPicture, &CardInfoPictureWidget::cardScaleFactorChanged, parentWidget, + &PrintingSelectorCardDisplayWidget::clampSetNameToPicture); + } + + connect(cardSizeSlider, &QSlider::valueChanged, cardInfoPicture, &CardInfoPictureWidget::setScaleFactor); +} + +/** + * @brief Handles the mouse press event for right-clicks to show the context menu. + * + * If the right mouse button is pressed, a custom context menu will appear. For other mouse buttons, + * the event is passed to the base class for default handling. + * + * @param event The mouse event triggered by the user. + */ +void PrintingSelectorCardOverlayWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::RightButton) { + customMenu(event->pos()); + } else { + QWidget::mousePressEvent(event); // Pass other events to the base class + } +} + +/** + * @brief Resizes the overlay widget to match the card's size. + * + * This method ensures that the amount widget matches the card's size when the overlay widget is resized. + * It also resizes the card info picture widget to match the new size. + * + * @param event The resize event triggered when the widget is resized. + */ +void PrintingSelectorCardOverlayWidget::resizeEvent(QResizeEvent *event) +{ + // Ensure the amount widget matches the parent size + QWidget::resizeEvent(event); + if (allZonesCardAmountWidget) { + allZonesCardAmountWidget->resize(cardInfoPicture->size()); + } + resize(cardInfoPicture->size()); +} + +/** + * @brief Handles the mouse enter event when the cursor enters the overlay widget area. + * + * When the cursor enters the widget, the card information is updated, and the card amount widget + * is displayed if the amounts are zero for both the mainboard and sideboard. + * + * @param event The event triggered when the mouse enters the widget. + */ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void PrintingSelectorCardOverlayWidget::enterEvent(QEnterEvent *event) +#else +void PrintingSelectorCardOverlayWidget::enterEvent(QEvent *event) +#endif +{ + QWidget::enterEvent(event); + deckEditor->updateCardInfo(setCard); + + // Check if either mainboard or sideboard amount is greater than 0 + if (allZonesCardAmountWidget->getMainboardAmount() > 0 || allZonesCardAmountWidget->getSideboardAmount() > 0) { + // Don't change visibility if amounts are greater than 0 + return; + } + + // Show the widget if amounts are 0 + allZonesCardAmountWidget->setVisible(true); +} + +/** + * @brief Handles the mouse leave event when the cursor leaves the overlay widget area. + * + * When the cursor leaves the widget, the card amount widget is hidden if both the mainboard and sideboard + * amounts are zero. + * + * @param event The event triggered when the mouse leaves the widget. + */ +void PrintingSelectorCardOverlayWidget::leaveEvent(QEvent *event) +{ + QWidget::leaveEvent(event); + + // Check if either mainboard or sideboard amount is greater than 0 + if (allZonesCardAmountWidget->getMainboardAmount() > 0 || allZonesCardAmountWidget->getSideboardAmount() > 0) { + // Don't hide the widget if amounts are greater than 0 + return; + } + + // Hide the widget if amounts are 0 + allZonesCardAmountWidget->setVisible(false); +} + +/** + * @brief Creates and shows a custom context menu when the right mouse button is clicked. + * + * The context menu includes an option to show related cards, which displays a submenu with actions + * for each related card. When an action is triggered, the card information is updated, and the + * printing selector is shown. + * + * @param point The position of the mouse when the right-click occurred. + */ +void PrintingSelectorCardOverlayWidget::customMenu(QPoint point) +{ + QMenu menu; + // filling out the related cards submenu + auto *relatedMenu = new QMenu(tr("Show Related cards")); + menu.addMenu(relatedMenu); + auto relatedCards = rootCard->getAllRelatedCards(); + if (relatedCards.isEmpty()) { + relatedMenu->setDisabled(true); + } else { + for (const CardRelation *rel : relatedCards) { + const QString &relatedCardName = rel->getName(); + QAction *relatedCard = relatedMenu->addAction(relatedCardName); + connect(relatedCard, &QAction::triggered, deckEditor, [this, relatedCardName] { + deckEditor->updateCardInfo(CardDatabaseManager::getInstance()->getCard(relatedCardName)); + deckEditor->showPrintingSelector(); + }); + } + } + menu.exec(this->mapToGlobal(point)); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h new file mode 100644 index 000000000..03e1b0c95 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h @@ -0,0 +1,49 @@ +#ifndef PRINTING_SELECTOR_CARD_OVERLAY_WIDGET_H +#define PRINTING_SELECTOR_CARD_OVERLAY_WIDGET_H + +#include "../../../../client/ui/widgets/cards/card_info_picture_widget.h" +#include "../../../../deck/deck_list_model.h" +#include "../../../../deck/deck_view.h" +#include "../../../../game/cards/card_database.h" +#include "../../../tabs/tab_deck_editor.h" +#include "all_zones_card_amount_widget.h" +#include "card_amount_widget.h" +#include "set_name_and_collectors_number_display_widget.h" + +class PrintingSelectorCardOverlayWidget : public QWidget +{ + Q_OBJECT + +public: + explicit PrintingSelectorCardOverlayWidget(QWidget *parent, + TabDeckEditor *_deckEditor, + DeckListModel *_deckModel, + QTreeView *_deckView, + QSlider *_cardSizeSlider, + CardInfoPtr _rootCard, + const CardInfoPerSet &_setInfoForCard); + +protected: + void resizeEvent(QResizeEvent *event) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void enterEvent(QEnterEvent *event) override; +#else + void enterEvent(QEvent *event) override; +#endif + void leaveEvent(QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void customMenu(QPoint point); + +private: + CardInfoPictureWidget *cardInfoPicture; + AllZonesCardAmountWidget *allZonesCardAmountWidget; + TabDeckEditor *deckEditor; + DeckListModel *deckModel; + QTreeView *deckView; + QSlider *cardSizeSlider; + CardInfoPtr rootCard; + CardInfoPtr setCard; + CardInfoPerSet setInfoForCard; +}; + +#endif // PRINTING_SELECTOR_CARD_OVERLAY_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp new file mode 100644 index 000000000..3ec552b4b --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp @@ -0,0 +1,38 @@ +#include "printing_selector_card_search_widget.h" + +/** + * @brief Constructs a PrintingSelectorCardSearchWidget for searching cards by set name or set code. + * + * This widget provides a search bar that allows users to search for cards by either their set name + * or set code. It uses a debounced timer to trigger the search action after the user stops typing. + * + * @param parent The parent PrintingSelector widget that will handle the search results. + */ +PrintingSelectorCardSearchWidget::PrintingSelectorCardSearchWidget(PrintingSelector *parent) : parent(parent) +{ + layout = new QHBoxLayout(this); + setLayout(layout); + + searchBar = new QLineEdit(this); + searchBar->setPlaceholderText(tr("Search by set name or set code")); + layout->addWidget(searchBar); + + // Add a debounce timer for the search bar to limit frequent updates + searchDebounceTimer = new QTimer(this); + searchDebounceTimer->setSingleShot(true); + connect(searchBar, &QLineEdit::textChanged, this, [this]() { + searchDebounceTimer->start(300); // 300ms debounce + }); + + connect(searchDebounceTimer, &QTimer::timeout, parent, &PrintingSelector::updateDisplay); +} + +/** + * @brief Retrieves the current text in the search bar. + * + * @return The text entered by the user in the search bar. + */ +QString PrintingSelectorCardSearchWidget::getSearchText() +{ + return searchBar->text(); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.h new file mode 100644 index 000000000..737d9b7ba --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.h @@ -0,0 +1,25 @@ +#ifndef PRINTING_SELECTOR_CARD_SEARCH_WIDGET_H +#define PRINTING_SELECTOR_CARD_SEARCH_WIDGET_H + +#include "printing_selector.h" + +#include +#include +#include + +class PrintingSelectorCardSearchWidget : public QWidget +{ + Q_OBJECT + +public: + explicit PrintingSelectorCardSearchWidget(PrintingSelector *parent); + QString getSearchText(); + +private: + QHBoxLayout *layout; + PrintingSelector *parent; + QLineEdit *searchBar; + QTimer *searchDebounceTimer; +}; + +#endif // PRINTING_SELECTOR_CARD_SEARCH_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp new file mode 100644 index 000000000..1b6c4eb76 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp @@ -0,0 +1,37 @@ +#include "printing_selector_card_selection_widget.h" + +/** + * @brief Constructs a PrintingSelectorCardSelectionWidget for navigating through cards in the deck. + * + * This widget provides buttons that allow users to navigate between cards in the deck. + * It includes buttons for moving to the previous and next card in the deck. + * + * @param parent The parent PrintingSelector widget responsible for managing card selection. + */ +PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(PrintingSelector *parent) : parent(parent) +{ + cardSelectionBarLayout = new QHBoxLayout(this); + + previousCardButton = new QPushButton(this); + previousCardButton->setText(tr("Previous Card in Deck")); + + nextCardButton = new QPushButton(this); + nextCardButton->setText(tr("Next Card in Deck")); + + connectSignals(); + + cardSelectionBarLayout->addWidget(previousCardButton); + cardSelectionBarLayout->addWidget(nextCardButton); +} + +/** + * @brief Connects the signals from the buttons to the appropriate slots in the parent widget. + * + * This method connects the click signals of the previous and next card buttons to + * the selectPreviousCard and selectNextCard slots in the parent PrintingSelector widget. + */ +void PrintingSelectorCardSelectionWidget::connectSignals() +{ + connect(previousCardButton, &QPushButton::clicked, parent, &PrintingSelector::selectPreviousCard); + connect(nextCardButton, &QPushButton::clicked, parent, &PrintingSelector::selectNextCard); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.h new file mode 100644 index 000000000..4bb2b048f --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.h @@ -0,0 +1,26 @@ +#ifndef PRINTING_SELECTOR_CARD_SELECTION_WIDGET_H +#define PRINTING_SELECTOR_CARD_SELECTION_WIDGET_H + +#include "printing_selector.h" + +#include +#include +#include + +class PrintingSelectorCardSelectionWidget : public QWidget +{ + Q_OBJECT + +public: + explicit PrintingSelectorCardSelectionWidget(PrintingSelector *parent); + + void connectSignals(); + +private: + PrintingSelector *parent; + QHBoxLayout *cardSelectionBarLayout; + QPushButton *previousCardButton; + QPushButton *nextCardButton; +}; + +#endif // PRINTING_SELECTOR_CARD_SELECTION_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp new file mode 100644 index 000000000..aac777332 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp @@ -0,0 +1,210 @@ +#include "printing_selector_card_sorting_widget.h" + +#include "../../../../settings/cache_settings.h" +#include "../../../../utility/card_set_comparator.h" + +const QString PrintingSelectorCardSortingWidget::SORT_OPTIONS_ALPHABETICAL = tr("Alphabetical"); +const QString PrintingSelectorCardSortingWidget::SORT_OPTIONS_PREFERENCE = tr("Preference"); +const QString PrintingSelectorCardSortingWidget::SORT_OPTIONS_RELEASE_DATE = tr("Release Date"); + +const QStringList PrintingSelectorCardSortingWidget::SORT_OPTIONS = {SORT_OPTIONS_ALPHABETICAL, SORT_OPTIONS_PREFERENCE, + SORT_OPTIONS_RELEASE_DATE}; + +/** + * @brief A widget for sorting and filtering card sets in the Printing Selector. + * + * This widget allows users to choose sorting options for the card sets, such as alphabetical order, release date, or + * user-defined preferences. It also allows users to toggle the sorting order between ascending and descending. + */ +PrintingSelectorCardSortingWidget::PrintingSelectorCardSortingWidget(PrintingSelector *parent) : parent(parent) +{ + sortToolBar = new QHBoxLayout(this); + + sortOptionsSelector = new QComboBox(this); + sortOptionsSelector->addItems(SORT_OPTIONS); + sortOptionsSelector->setCurrentIndex(SettingsCache::instance().getPrintingSelectorSortOrder()); + connect(sortOptionsSelector, &QComboBox::currentTextChanged, this, + &PrintingSelectorCardSortingWidget::updateSortSetting); + connect(sortOptionsSelector, &QComboBox::currentTextChanged, parent, &PrintingSelector::updateDisplay); + sortToolBar->addWidget(sortOptionsSelector); + + toggleSortOrder = new QPushButton(this); + toggleSortOrder->setText(tr("Descending")); + descendingSort = true; + connect(toggleSortOrder, &QPushButton::clicked, this, &PrintingSelectorCardSortingWidget::updateSortOrder); + sortToolBar->addWidget(toggleSortOrder); +} + +/** + * @brief Updates the sorting order (ascending or descending). + * + * This function toggles the sort order between ascending and descending and updates the display. + */ +void PrintingSelectorCardSortingWidget::updateSortOrder() +{ + if (descendingSort) { + toggleSortOrder->setText(tr("Ascending")); + } else { + toggleSortOrder->setText(tr("Descending")); + } + descendingSort = !descendingSort; + parent->updateDisplay(); +} + +/** + * @brief Updates the sorting setting in the application settings. + * + * This function saves the selected sorting option (from the combobox) to the application settings. + */ +void PrintingSelectorCardSortingWidget::updateSortSetting() +{ + SettingsCache::instance().setPrintingSelectorSortOrder(sortOptionsSelector->currentIndex()); +} + +/** + * @brief Sorts a list of card sets based on the selected sorting option. + * + * This function sorts the card sets according to the selected sorting option in the combobox. The options include: + * - Alphabetical + * - Preference + * - Release Date + * - Contained in Deck + * - Potential Cards in Deck + * + * @param cardInfoPerSets The list of card sets to be sorted. + * @return A sorted list of card sets. + */ +QList PrintingSelectorCardSortingWidget::sortSets(CardInfoPerSetMap cardInfoPerSets) +{ + QList sortedSets; + + for (const auto &cardInfoPerSetList : cardInfoPerSets) { + for (const auto &set : cardInfoPerSetList) { + sortedSets << set.getPtr(); + break; + } + } + + if (sortedSets.empty()) { + sortedSets << CardSet::newInstance("", "", "", QDate()); + } + + if (sortOptionsSelector->currentText() == SORT_OPTIONS_PREFERENCE) { + std::sort(sortedSets.begin(), sortedSets.end(), SetPriorityComparator()); + std::reverse(sortedSets.begin(), sortedSets.end()); + } else if (sortOptionsSelector->currentText() == SORT_OPTIONS_RELEASE_DATE) { + std::sort(sortedSets.begin(), sortedSets.end(), SetReleaseDateComparator()); + } + + QList sortedCardInfoPerSets; + // Reconstruct sorted list of CardInfoPerSet + for (const auto &set : sortedSets) { + for (auto it = cardInfoPerSets.begin(); it != cardInfoPerSets.end(); ++it) { + for (const auto &cardInfoPerSet : it.value()) { + if (cardInfoPerSet.getPtr() == set) { + sortedCardInfoPerSets << it.value(); + break; + } + } + } + } + + if (descendingSort) { + std::reverse(sortedCardInfoPerSets.begin(), sortedCardInfoPerSets.end()); + } + + return sortedCardInfoPerSets; +} + +/** + * @brief Filters a list of card sets based on the search text. + * + * This function filters the given list of card sets by comparing their long and short names with the provided search + * text. If the search text matches either the long or short name of a card set, that set is included in the filtered + * list. + * + * @param sets The list of card sets to be filtered. + * @param searchText The search text used to filter the card sets. + * @return A filtered list of card sets. + */ +QList PrintingSelectorCardSortingWidget::filterSets(const QList &sets, + const QString &searchText) +{ + if (searchText.isEmpty()) { + return sets; + } + + QList filteredSets; + + for (const auto &set : sets) { + const QString longName = set.getPtr()->getLongName().toLower(); + const QString shortName = set.getPtr()->getShortName().toLower(); + + if (longName.contains(searchText) || shortName.contains(searchText)) { + filteredSets << set; + } + } + + return filteredSets; +} + +/** + * @brief Prepend card printings that are contained in the deck to the list of card sets. + * + * This function adjusts the list of card sets by moving the printings that are already contained in the deck to the + * beginning of the list, sorted by the count of cards in the deck. + * + * @param sets The original list of card sets. + * @param selectedCard The currently selected card. + * @param deckModel The model representing the deck. + * @return A list of card sets with the printings contained in the deck prepended. + */ +QList PrintingSelectorCardSortingWidget::prependPrintingsInDeck(const QList &sets, + const CardInfoPtr &selectedCard, + DeckListModel *deckModel) +{ + if (!selectedCard) { + return {}; + } + + CardInfoPerSetMap cardInfoPerSets = selectedCard->getSets(); + QList> countList; + + // Collect sets with their counts + for (const auto &cardInfoPerSetList : cardInfoPerSets) { + for (const auto &cardInfoPerSet : cardInfoPerSetList) { + QModelIndex find_card = + deckModel->findCard(selectedCard->getName(), DECK_ZONE_MAIN, cardInfoPerSet.getProperty("uuid")); + if (find_card.isValid()) { + int count = + deckModel->data(find_card, Qt::DisplayRole).toInt(); // Ensure the count is treated as an integer + if (count > 0) { + countList.append(qMakePair(cardInfoPerSet, count)); + } + } + break; + } + } + + // Sort sets by count in descending numerical order + std::sort(countList.begin(), countList.end(), + [](const QPair &a, const QPair &b) { + return a.second > b.second; // Ensure numerical comparison + }); + + // Create a copy of the original list to modify + QList result = sets; + + // Prepend sorted sets and remove them from the original list + for (const auto &pair : countList) { + auto it = std::find_if(result.begin(), result.end(), [&pair](const CardInfoPerSet &item) { + return item.getProperty("uuid") == pair.first.getProperty("uuid"); + }); + if (it != result.end()) { + result.erase(it); // Remove the matching entry + } + result.prepend(pair.first); // Prepend the sorted item + } + + return result; +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.h new file mode 100644 index 000000000..6c9efa4e6 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.h @@ -0,0 +1,39 @@ +#ifndef PRINTING_SELECTOR_CARD_SORTING_WIDGET_H +#define PRINTING_SELECTOR_CARD_SORTING_WIDGET_H + +#include "printing_selector.h" + +#include +#include +#include + +class PrintingSelectorCardSortingWidget : public QWidget +{ + Q_OBJECT +public: + explicit PrintingSelectorCardSortingWidget(PrintingSelector *parent); + QList sortSets(CardInfoPerSetMap cardInfoPerSets); + static QList filterSets(const QList &sets, const QString &searchText); + static QList prependPrintingsInDeck(const QList &sets, + const CardInfoPtr &selectedCard, + DeckListModel *deckModel); + +public slots: + void updateSortOrder(); + void updateSortSetting(); + +private: + PrintingSelector *parent; + QHBoxLayout *sortToolBar; + static const QString SORT_OPTIONS_ALPHABETICAL; + static const QString SORT_OPTIONS_PREFERENCE; + static const QString SORT_OPTIONS_RELEASE_DATE; + static const QString SORT_OPTIONS_CONTAINED_IN_DECK; + static const QString SORT_OPTIONS_POTENTIAL_CARDS; + static const QStringList SORT_OPTIONS; + QComboBox *sortOptionsSelector; + bool descendingSort; + QPushButton *toggleSortOrder; +}; + +#endif // PRINTING_SELECTOR_CARD_SORTING_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.cpp new file mode 100644 index 000000000..d580db9e4 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.cpp @@ -0,0 +1,140 @@ +#include "printing_selector_view_options_toolbar_widget.h" + +#include +#include +#include + +/** + * @class PrintingSelectorViewOptionsToolbarWidget + * @brief A widget that provides a toolbar for view options with collapsible and expandable functionality. + * + * This widget allows the user to collapse or expand the view options for the PrintingSelector, + * providing a more compact interface when collapsed and a full view of options when expanded. + */ +PrintingSelectorViewOptionsToolbarWidget::PrintingSelectorViewOptionsToolbarWidget(QWidget *_parent, + PrintingSelector *_printingSelector) + : QWidget(_parent), printingSelector(_printingSelector) +{ + // Set up layout for the widget + layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); + + // Set up the expanded widget with its layout + expandedWidget = new QWidget(this); + auto *expandedLayout = new QVBoxLayout(expandedWidget); + expandedLayout->setContentsMargins(0, 0, 0, 0); + expandedLayout->setSpacing(0); + + // Collapse button to toggle between expanded and collapsed states + collapseButton = new QPushButton("▼", this); + collapseButton->setFixedSize(20, 20); + collapseButton->setToolTip("Collapse"); + collapseButton->setStyleSheet("border: none;"); + connect(collapseButton, &QPushButton::clicked, this, &PrintingSelectorViewOptionsToolbarWidget::collapse); + expandedLayout->addWidget(collapseButton, 0, Qt::AlignLeft); + + // View options widget + viewOptions = new PrintingSelectorViewOptionsWidget(expandedWidget, printingSelector); + expandedLayout->addWidget(viewOptions); + + expandedWidget->setLayout(expandedLayout); + + // Set up the collapsed widget with its layout + collapsedWidget = new QWidget(this); + auto *collapsedLayout = new QHBoxLayout(collapsedWidget); + collapsedLayout->setContentsMargins(5, 0, 5, 0); + collapsedLayout->setSpacing(0); + + // Expand button to show full options + expandButton = new QPushButton("▲", this); + expandButton->setFixedSize(20, 20); + expandButton->setToolTip("Expand"); + expandButton->setStyleSheet("border: none;"); + connect(expandButton, &QPushButton::clicked, this, &PrintingSelectorViewOptionsToolbarWidget::expand); + collapsedLayout->addWidget(expandButton); + + // Label for collapsed state + auto *collapsedLabel = new QLabel(tr("Display Options"), this); + collapsedLayout->addWidget(collapsedLabel); + + collapsedWidget->setLayout(collapsedLayout); + + // Stack widget to switch between expanded and collapsed states + stackedWidget = new QStackedWidget(this); + stackedWidget->addWidget(expandedWidget); + stackedWidget->addWidget(collapsedWidget); + + layout->addWidget(stackedWidget); + + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + + // Default to the expanded widget + stackedWidget->setCurrentWidget(expandedWidget); + + // Connect the stacked widget to update the layout when it changes + connect(stackedWidget, &QStackedWidget::currentChanged, this, + &PrintingSelectorViewOptionsToolbarWidget::onWidgetChanged); +} + +/** + * @brief Toggles the widget to the collapsed state. + */ +void PrintingSelectorViewOptionsToolbarWidget::collapse() +{ + stackedWidget->setCurrentWidget(collapsedWidget); + updateGeometry(); +} + +/** + * @brief Toggles the widget to the expanded state. + */ +void PrintingSelectorViewOptionsToolbarWidget::expand() +{ + stackedWidget->setCurrentWidget(expandedWidget); + updateGeometry(); +} + +/** + * @brief Handles the geometry update when the stacked widget changes. + * + * This ensures that the parent layout is also updated when the widget's display state changes. + */ +void PrintingSelectorViewOptionsToolbarWidget::onWidgetChanged(int) +{ + updateGeometry(); + if (parentWidget() && parentWidget()->layout()) { + parentWidget()->layout()->invalidate(); + } +} + +/** + * @brief Provides the recommended size for the widget based on the current view. + * + * @return QSize The suggested size for the widget. + */ +QSize PrintingSelectorViewOptionsToolbarWidget::sizeHint() const +{ + return stackedWidget->currentWidget()->sizeHint(); +} + +/** + * @brief Provides the minimum size required for the widget based on the current view. + * + * @return QSize The minimum size required for the widget. + */ +QSize PrintingSelectorViewOptionsToolbarWidget::minimumSizeHint() const +{ + return stackedWidget->currentWidget()->minimumSizeHint(); +} + +/** + * @brief Returns the view options widget contained within this toolbar. + * + * @return PrintingSelectorViewOptionsWidget* The view options widget. + */ +PrintingSelectorViewOptionsWidget *PrintingSelectorViewOptionsToolbarWidget::getViewOptionsWidget() const +{ + return viewOptions; +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.h new file mode 100644 index 000000000..875f669f6 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.h @@ -0,0 +1,36 @@ +#ifndef PRINTING_SELECTOR_SORT_AND_SEARCH_TOOLBAR_WIDGET_H +#define PRINTING_SELECTOR_SORT_AND_SEARCH_TOOLBAR_WIDGET_H + +#include "printing_selector.h" +#include "printing_selector_view_options_widget.h" + +#include +#include +#include +#include + +class PrintingSelectorViewOptionsToolbarWidget : public QWidget +{ + Q_OBJECT + +public: + explicit PrintingSelectorViewOptionsToolbarWidget(QWidget *parent, PrintingSelector *printingSelector); + void collapse(); + void expand(); + void onWidgetChanged(int); + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + PrintingSelectorViewOptionsWidget *getViewOptionsWidget() const; + +private: + QVBoxLayout *layout; + PrintingSelector *printingSelector; + PrintingSelectorViewOptionsWidget *viewOptions; + QWidget *expandedWidget; + QPushButton *collapseButton; + QWidget *collapsedWidget; + QPushButton *expandButton; + QStackedWidget *stackedWidget; +}; + +#endif // PRINTING_SELECTOR_SORT_AND_SEARCH_TOOLBAR_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.cpp new file mode 100644 index 000000000..e04f1e38d --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.cpp @@ -0,0 +1,69 @@ +#include "printing_selector_view_options_widget.h" + +#include "../../../../settings/cache_settings.h" + +/** + * @class PrintingSelectorViewOptionsWidget + * @brief A widget that provides the view options for the PrintingSelector, including checkboxes + * for sorting, search bar, card size slider, and navigation buttons. + * + * This widget allows the user to toggle the visibility of various interface components of the + * PrintingSelector through checkboxes. The state of the checkboxes is saved and restored using + * the `SettingsCache`. + */ +PrintingSelectorViewOptionsWidget::PrintingSelectorViewOptionsWidget(QWidget *parent, + PrintingSelector *_printingSelector) + : QWidget(parent), printingSelector(_printingSelector) +{ + // Set up the layout for the widget + layout = new QHBoxLayout(this); + setLayout(layout); + + // Create the flow widget to hold the checkboxes + flowWidget = new FlowWidget(this, Qt::ScrollBarPolicy::ScrollBarAlwaysOff, Qt::ScrollBarPolicy::ScrollBarAsNeeded); + + // Create the checkbox for sorting options visibility + sortCheckBox = new QCheckBox(flowWidget); + sortCheckBox->setText(tr("Display Sorting Options")); + sortCheckBox->setChecked(SettingsCache::instance().getPrintingSelectorSortOptionsVisible()); + connect(sortCheckBox, &QCheckBox::QT_STATE_CHANGED, printingSelector, + &PrintingSelector::toggleVisibilitySortOptions); + connect(sortCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPrintingSelectorSortOptionsVisible); + + // Create the checkbox for search bar visibility + searchCheckBox = new QCheckBox(flowWidget); + searchCheckBox->setText(tr("Display Search Bar")); + searchCheckBox->setChecked(SettingsCache::instance().getPrintingSelectorSearchBarVisible()); + connect(searchCheckBox, &QCheckBox::QT_STATE_CHANGED, printingSelector, + &PrintingSelector::toggleVisibilitySearchBar); + connect(searchCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPrintingSelectorSearchBarVisible); + + // Create the checkbox for card size slider visibility + cardSizeCheckBox = new QCheckBox(flowWidget); + cardSizeCheckBox->setText(tr("Display Card Size Slider")); + cardSizeCheckBox->setChecked(SettingsCache::instance().getPrintingSelectorCardSizeSliderVisible()); + connect(cardSizeCheckBox, &QCheckBox::QT_STATE_CHANGED, printingSelector, + &PrintingSelector::toggleVisibilityCardSizeSlider); + connect(cardSizeCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPrintingSelectorCardSizeSliderVisible); + + // Create the checkbox for navigation buttons visibility + navigationCheckBox = new QCheckBox(flowWidget); + navigationCheckBox->setText(tr("Display Navigation Buttons")); + navigationCheckBox->setChecked(SettingsCache::instance().getPrintingSelectorNavigationButtonsVisible()); + connect(navigationCheckBox, &QCheckBox::QT_STATE_CHANGED, printingSelector, + &PrintingSelector::toggleVisibilityNavigationButtons); + connect(navigationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPrintingSelectorNavigationButtonsVisible); + + // Add checkboxes to the flow widget + flowWidget->addWidget(sortCheckBox); + flowWidget->addWidget(searchCheckBox); + flowWidget->addWidget(cardSizeCheckBox); + flowWidget->addWidget(navigationCheckBox); + + // Add flow widget to the main layout + layout->addWidget(flowWidget); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.h new file mode 100644 index 000000000..16d12d0b9 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.h @@ -0,0 +1,28 @@ +#ifndef PRINTING_SELECTOR_VIEW_OPTIONS_WIDGET_H +#define PRINTING_SELECTOR_VIEW_OPTIONS_WIDGET_H + +#include "../general/layout_containers/flow_widget.h" +#include "printing_selector.h" + +#include +#include +#include + +class PrintingSelectorViewOptionsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit PrintingSelectorViewOptionsWidget(QWidget *parent, PrintingSelector *_printingSelector); + +private: + QHBoxLayout *layout; + FlowWidget *flowWidget; + PrintingSelector *printingSelector; + QCheckBox *sortCheckBox; + QCheckBox *searchCheckBox; + QCheckBox *cardSizeCheckBox; + QCheckBox *navigationCheckBox; +}; + +#endif // PRINTING_SELECTOR_VIEW_OPTIONS_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp new file mode 100644 index 000000000..b1a6a61c1 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp @@ -0,0 +1,102 @@ +#include "set_name_and_collectors_number_display_widget.h" + +#include + +/** + * @class SetNameAndCollectorsNumberDisplayWidget + * @brief A widget to display the set name and collectors number with adjustable font size. + * + * This widget displays the set name and collectors number on two separate labels. The font size is resized dynamically + * when the card size is changed. + */ +SetNameAndCollectorsNumberDisplayWidget::SetNameAndCollectorsNumberDisplayWidget(QWidget *parent, + const QString &_setName, + const QString &_collectorsNumber, + QSlider *_cardSizeSlider) + : QWidget(parent) +{ + // Set up the layout for the widget + layout = new QVBoxLayout(this); + setLayout(layout); + + // Set the widget's size policy and minimum size + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + setMinimumSize(QWidget::sizeHint()); + + // Create and configure the set name label + setName = new QLabel(_setName); + setName->setWordWrap(true); + setName->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + setName->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + + // Create and configure the collectors number label + collectorsNumber = new QLabel(_collectorsNumber); + collectorsNumber->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + collectorsNumber->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + // Store the card size slider and connect its signal to the font size adjustment slot + cardSizeSlider = _cardSizeSlider; + connect(cardSizeSlider, &QSlider::valueChanged, this, &SetNameAndCollectorsNumberDisplayWidget::adjustFontSize); + + // Add labels to the layout + layout->addWidget(setName); + layout->addWidget(collectorsNumber); +} + +/** + * @brief Adjusts the font size of the labels based on the slider value. + * + * This method adjusts the font size of the set name and collectors number labels + * according to the scale percentage provided by the slider. The font size is clamped + * to a range between the defined minimum and maximum font sizes. + * + * @param scalePercentage The scale percentage from the slider. + */ +void SetNameAndCollectorsNumberDisplayWidget::adjustFontSize(int scalePercentage) +{ + // Define the base font size and the range + const int minFontSize = 8; // Minimum font size + const int maxFontSize = 32; // Maximum font size + const int basePercentage = 100; // Scale at 100% + + // Calculate the new font size + int newFontSize = minFontSize + (scalePercentage - basePercentage) * (maxFontSize - minFontSize) / 225; + + // Clamp the font size to the defined range + newFontSize = std::clamp(newFontSize, minFontSize, maxFontSize); + + // Update the fonts for both labels + QFont setNameFont = setName->font(); + setNameFont.setPointSize(newFontSize); + setName->setFont(setNameFont); + + QFont collectorsNumberFont = collectorsNumber->font(); + collectorsNumberFont.setPointSize(newFontSize); + collectorsNumber->setFont(collectorsNumberFont); + + // Optionally trigger a resize to accommodate new font size + adjustSize(); +} + +/** + * @brief Handles resize events to adjust the height of the set name label. + * + * This method calculates the height required to display the set name label with word wrapping. + * It adjusts the minimum height of the set name label to fit the text. + * + * @param event The resize event. + */ +void SetNameAndCollectorsNumberDisplayWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); // Ensure the parent class handles the event first + + QFontMetrics fm(setName->font()); + int labelWidth = setName->width(); // Get the current width of the QLabel + QString text = setName->text(); // The text to be rendered + + // Calculate the height required to render the text with word wrapping + int textHeight = fm.boundingRect(0, 0, labelWidth, 0, Qt::TextWordWrap, text).height(); + + // Set the minimum height to accommodate the required text height + setName->setMinimumHeight(textHeight); +} diff --git a/cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.h new file mode 100644 index 000000000..dfaa98caa --- /dev/null +++ b/cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.h @@ -0,0 +1,29 @@ +#ifndef SET_NAME_AND_COLLECTORS_NUMBER_DISPLAY_WIDGET_H +#define SET_NAME_AND_COLLECTORS_NUMBER_DISPLAY_WIDGET_H + +#include +#include +#include +#include + +class SetNameAndCollectorsNumberDisplayWidget : public QWidget +{ + Q_OBJECT +public: + SetNameAndCollectorsNumberDisplayWidget(QWidget *parent, + const QString &setName, + const QString &collectorsNumber, + QSlider *cardSizeSlider); + void resizeEvent(QResizeEvent *event) override; + +public slots: + void adjustFontSize(int scalePercentage); + +private: + QVBoxLayout *layout; + QLabel *setName; + QLabel *collectorsNumber; + QSlider *cardSizeSlider; +}; + +#endif // SET_NAME_AND_COLLECTORS_NUMBER_DISPLAY_WIDGET_H diff --git a/cockatrice/src/deck/deck_list_model.cpp b/cockatrice/src/deck/deck_list_model.cpp index a33e72c7e..fefed4b3f 100644 --- a/cockatrice/src/deck/deck_list_model.cpp +++ b/cockatrice/src/deck/deck_list_model.cpp @@ -1,13 +1,11 @@ #include "deck_list_model.h" -#include "../game/cards/card_database.h" #include "../game/cards/card_database_manager.h" #include "../main.h" #include "../settings/cache_settings.h" #include "deck_loader.h" #include -#include #include #include #include @@ -309,8 +307,10 @@ InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerD return newNode; } -DecklistModelCardNode * -DeckListModel::findCardNode(const QString &cardName, const QString &zoneName, const QString &providerId) const +DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, + const QString &zoneName, + const QString &providerId, + const QString &cardNumber) const { InnerDecklistNode *zoneNode, *typeNode; CardInfoPtr info; @@ -332,17 +332,18 @@ DeckListModel::findCardNode(const QString &cardName, const QString &zoneName, co return nullptr; } - if (providerId.isEmpty()) { - return dynamic_cast(typeNode->findChild(cardName)); - } - return dynamic_cast(typeNode->findCardChildByNameAndProviderId(cardName, providerId)); + return dynamic_cast( + typeNode->findCardChildByNameProviderIdAndNumber(cardName, providerId, cardNumber)); } -QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zoneName, const QString &providerId) const +QModelIndex DeckListModel::findCard(const QString &cardName, + const QString &zoneName, + const QString &providerId, + const QString &cardNumber) const { DecklistModelCardNode *cardNode; - cardNode = findCardNode(cardName, zoneName, providerId); + cardNode = findCardNode(cardName, zoneName, providerId, cardNumber); if (!cardNode) { return {}; } @@ -357,7 +358,7 @@ QModelIndex DeckListModel::addPreferredPrintingCard(const QString &cardName, con } QModelIndex DeckListModel::addCard(const QString &cardName, - const CardInfoPerSet cardInfoSet, + const CardInfoPerSet &cardInfoSet, const QString &zoneName, bool abAddAnyway) { @@ -382,18 +383,19 @@ QModelIndex DeckListModel::addCard(const QString &cardName, InnerDecklistNode *cardTypeNode = createNodeIfNeeded(cardType, zoneNode); const QModelIndex parentIndex = nodeToIndex(cardTypeNode); - auto *cardNode = dynamic_cast( - cardTypeNode->findCardChildByNameAndProviderId(cardName, cardInfoSet.getProperty("uuid"))); + auto *cardNode = dynamic_cast(cardTypeNode->findCardChildByNameProviderIdAndNumber( + cardName, cardInfoSet.getProperty("uuid"), cardInfoSet.getProperty("num"))); + const auto cardSetName = cardInfoSet.getPtr().isNull() ? "" : cardInfoSet.getPtr()->getCorrectedShortName(); + if (!cardNode) { - auto *decklistCard = - deckList->addCard(cardInfo->getName(), zoneName, cardInfoSet.getPtr()->getCorrectedShortName(), - cardInfoSet.getProperty("num"), cardInfoSet.getProperty("uuid")); + auto *decklistCard = deckList->addCard(cardInfo->getName(), zoneName, cardSetName, + cardInfoSet.getProperty("num"), cardInfoSet.getProperty("uuid")); beginInsertRows(parentIndex, static_cast(cardTypeNode->size()), static_cast(cardTypeNode->size())); cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode); endInsertRows(); } else { cardNode->setNumber(cardNode->getNumber() + 1); - cardNode->setCardSetShortName(cardInfoSet.getPtr()->getCorrectedShortName()); + cardNode->setCardSetShortName(cardSetName); cardNode->setCardCollectorNumber(cardInfoSet.getProperty("num")); cardNode->setCardProviderId(cardInfoSet.getProperty("uuid")); deckList->updateDeckHash(); diff --git a/cockatrice/src/deck/deck_list_model.h b/cockatrice/src/deck/deck_list_model.h index 24eaea612..c7194a5da 100644 --- a/cockatrice/src/deck/deck_list_model.h +++ b/cockatrice/src/deck/deck_list_model.h @@ -66,6 +66,10 @@ public: { return dataNode; } + [[nodiscard]] bool isDeckHeader() const override + { + return false; + } }; class DeckListModel : public QAbstractItemModel @@ -90,10 +94,15 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent) override; - QModelIndex findCard(const QString &cardName, const QString &zoneName, const QString &providerId = "") const; + QModelIndex findCard(const QString &cardName, + const QString &zoneName, + const QString &providerId = "", + const QString &cardNumber = "") const; QModelIndex addPreferredPrintingCard(const QString &cardName, const QString &zoneName, bool abAddAnyway); - QModelIndex - addCard(const ::QString &cardName, CardInfoPerSet cardInfoSet, const QString &zoneName, bool abAddAnyway = false); + QModelIndex addCard(const ::QString &cardName, + const CardInfoPerSet &cardInfoSet, + const QString &zoneName, + bool abAddAnyway = false); void sort(int column, Qt::SortOrder order) override; void cleanList(); DeckLoader *getDeckList() const @@ -109,8 +118,10 @@ private: Qt::SortOrder lastKnownOrder; InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); QModelIndex nodeToIndex(AbstractDecklistNode *node) const; - DecklistModelCardNode * - findCardNode(const QString &cardName, const QString &zoneName, const QString &providerId = "") const; + DecklistModelCardNode *findCardNode(const QString &cardName, + const QString &zoneName, + const QString &providerId = "", + const QString &cardNumber = "") const; void emitRecursiveUpdates(const QModelIndex &index); void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); diff --git a/cockatrice/src/dialogs/dlg_edit_tokens.cpp b/cockatrice/src/dialogs/dlg_edit_tokens.cpp index 7d47f642c..884c1c225 100644 --- a/cockatrice/src/dialogs/dlg_edit_tokens.cpp +++ b/cockatrice/src/dialogs/dlg_edit_tokens.cpp @@ -163,7 +163,7 @@ void DlgEditTokens::actAddToken() QString setName = CardDatabase::TOKENS_SETNAME; CardInfoPerSetMap sets; - sets.insert(setName, CardInfoPerSet(databaseModel->getDatabase()->getSet(setName))); + sets[setName].append(CardInfoPerSet(databaseModel->getDatabase()->getSet(setName))); CardInfoPtr card = CardInfo::newInstance(name, "", true, QVariantHash(), QList(), QList(), sets, false, -1, false); card->setCardType("Token"); diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index 82c9f5ff3..2ca36aea6 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -338,6 +338,14 @@ AppearanceSettingsPage::AppearanceSettingsPage() displayCardNamesCheckBox.setChecked(settings.getDisplayCardNames()); connect(&displayCardNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setDisplayCardNames); + overrideAllCardArtWithPersonalPreferenceCheckBox.setChecked(settings.getOverrideAllCardArtWithPersonalPreference()); + connect(&overrideAllCardArtWithPersonalPreferenceCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setOverrideAllCardArtWithPersonalPreference); + + bumpSetsWithCardsInDeckToTopCheckBox.setChecked(settings.getBumpSetsWithCardsInDeckToTop()); + connect(&bumpSetsWithCardsInDeckToTopCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setBumpSetsWithCardsInDeckToTop); + cardScalingCheckBox.setChecked(settings.getScaleCards()); connect(&cardScalingCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setCardScaling); @@ -354,10 +362,12 @@ AppearanceSettingsPage::AppearanceSettingsPage() auto *cardsGrid = new QGridLayout; cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2); cardsGrid->addWidget(&cardScalingCheckBox, 1, 0, 1, 2); - cardsGrid->addWidget(&verticalCardOverlapPercentLabel, 2, 0, 1, 1); - cardsGrid->addWidget(&verticalCardOverlapPercentBox, 2, 1, 1, 1); - cardsGrid->addWidget(&cardViewInitialRowsMaxLabel, 3, 0); - cardsGrid->addWidget(&cardViewInitialRowsMaxBox, 3, 1); + cardsGrid->addWidget(&overrideAllCardArtWithPersonalPreferenceCheckBox, 2, 0, 1, 2); + cardsGrid->addWidget(&bumpSetsWithCardsInDeckToTopCheckBox, 3, 0, 1, 2); + cardsGrid->addWidget(&verticalCardOverlapPercentLabel, 4, 0, 1, 1); + cardsGrid->addWidget(&verticalCardOverlapPercentBox, 4, 1, 1, 1); + cardsGrid->addWidget(&cardViewInitialRowsMaxLabel, 5, 0); + cardsGrid->addWidget(&cardViewInitialRowsMaxBox, 5, 1); cardsGroupBox = new QGroupBox; cardsGroupBox->setLayout(cardsGrid); @@ -452,6 +462,11 @@ void AppearanceSettingsPage::retranslateUi() cardsGroupBox->setTitle(tr("Card rendering")); displayCardNamesCheckBox.setText(tr("Display card names on cards having a picture")); + overrideAllCardArtWithPersonalPreferenceCheckBox.setText( + tr("Override all card art with personal set preference (Pre-ProviderID change behavior) [Requires Client " + "restart]")); + bumpSetsWithCardsInDeckToTopCheckBox.setText( + tr("Bump sets that the deck contains cards from to the top in the printing selector")); cardScalingCheckBox.setText(tr("Scale cards on mouse over")); verticalCardOverlapPercentLabel.setText( tr("Minimum overlap percentage of cards on the stack and in vertical hand")); diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h index 9db288128..969abc3c1 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -93,6 +93,8 @@ private: QLabel maxFontSizeForCardsLabel; QCheckBox showShortcutsCheckBox; QCheckBox displayCardNamesCheckBox; + QCheckBox overrideAllCardArtWithPersonalPreferenceCheckBox; + QCheckBox bumpSetsWithCardsInDeckToTopCheckBox; QCheckBox cardScalingCheckBox; QLabel verticalCardOverlapPercentLabel; QSpinBox verticalCardOverlapPercentBox; diff --git a/cockatrice/src/game/cards/card_database.cpp b/cockatrice/src/game/cards/card_database.cpp index 5372bc1ff..3e58440fd 100644 --- a/cockatrice/src/game/cards/card_database.cpp +++ b/cockatrice/src/game/cards/card_database.cpp @@ -266,8 +266,11 @@ CardInfoPtr CardInfo::newInstance(const QString &_name, _sets, _cipt, _tableRow, _upsideDownArt)); ptr->setSmartPointer(ptr); - for (const CardInfoPerSet &set : _sets) { - set.getPtr()->append(ptr); + for (const auto &cardInfoPerSetList : _sets) { + for (const CardInfoPerSet &set : cardInfoPerSetList) { + set.getPtr()->append(ptr); + break; + } } return ptr; @@ -288,7 +291,7 @@ QString CardInfo::getCorrectedName() const void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info) { _set->append(smartThis); - sets.insert(_set->getShortName(), _info); + sets[_set->getShortName()].append(_info); refreshCachedSetNames(); } @@ -297,9 +300,12 @@ void CardInfo::refreshCachedSetNames() { QStringList setList; // update the cached list of set names - for (const auto &set : sets) { - if (set.getPtr()->getEnabled()) { - setList << set.getPtr()->getShortName(); + for (const auto &cardInfoPerSetList : sets) { + for (const auto &set : cardInfoPerSetList) { + if (set.getPtr()->getEnabled()) { + setList << set.getPtr()->getShortName(); + } + break; } } setsNames = setList.join(", "); @@ -396,8 +402,10 @@ void CardDatabase::addCard(CardInfoPtr card) // if card already exists just add the new set property if (cards.contains(card->getName())) { CardInfoPtr sameCard = cards[card->getName()]; - for (const CardInfoPerSet &set : card->getSets()) { - sameCard->addToSet(set.getPtr(), set); + for (const auto &cardInfoPerSetList : card->getSets()) { + for (const CardInfoPerSet &set : cardInfoPerSetList) { + sameCard->addToSet(set.getPtr(), set); + } } return; } @@ -456,12 +464,14 @@ CardInfoPtr CardDatabase::getCardByNameAndProviderId(const QString &cardName, co return info; } - for (const auto &set : info->getSets()) { - if (set.getProperty("uuid") == providerId) { - CardInfoPtr cardFromSpecificSet = info->clone(); - cardFromSpecificSet->setPixmapCacheKey(QLatin1String("card_") + QString(info->getName()) + QString("_") + - QString(set.getProperty("uuid"))); - return cardFromSpecificSet; + for (const auto &cardInfoPerSetList : info->getSets()) { + for (const auto &set : cardInfoPerSetList) { + if (set.getProperty("uuid") == providerId) { + CardInfoPtr cardFromSpecificSet = info->clone(); + cardFromSpecificSet->setPixmapCacheKey(QLatin1String("card_") + QString(info->getName()) + + QString("_") + QString(set.getProperty("uuid"))); + return cardFromSpecificSet; + } } } return {}; @@ -614,7 +624,7 @@ void CardDatabase::refreshPreferredPrintings() } } -CardInfoPerSet CardDatabase::getPreferredSetForCard(const QString &cardName) +CardInfoPerSet CardDatabase::getPreferredSetForCard(const QString &cardName) const { CardInfoPtr cardInfo = getCard(cardName); if (!cardInfo) { @@ -630,11 +640,13 @@ CardInfoPerSet CardDatabase::getPreferredSetForCard(const QString &cardName) CardInfoPerSet preferredCard; SetPriorityComparator comparator; - for (auto &cardInfoForSet : setMap) { - CardSetPtr currentSet = cardInfoForSet.getPtr(); - if (!preferredSet || comparator(currentSet, preferredSet)) { - preferredSet = currentSet; - preferredCard = cardInfoForSet; + for (const auto &cardInfoPerSetList : setMap) { + for (auto &cardInfoForSet : cardInfoPerSetList) { + CardSetPtr currentSet = cardInfoForSet.getPtr(); + if (!preferredSet || comparator(currentSet, preferredSet)) { + preferredSet = currentSet; + preferredCard = cardInfoForSet; + } } } @@ -657,12 +669,18 @@ CardInfoPerSet CardDatabase::getSpecificSetForCard(const QString &cardName, cons return CardInfoPerSet(nullptr); } - for (auto &cardInfoForSet : setMap) { - if (cardInfoForSet.getProperty("uuid") == providerId) { - return cardInfoForSet; + for (const auto &cardInfoPerSetList : setMap) { + for (auto &cardInfoForSet : cardInfoPerSetList) { + if (cardInfoForSet.getProperty("uuid") == providerId) { + return cardInfoForSet; + } } } + if (providerId.isNull()) { + return getPreferredSetForCard(cardName); + } + return CardInfoPerSet(nullptr); } @@ -689,6 +707,25 @@ bool CardDatabase::isProviderIdForPreferredPrinting(const QString &cardName, con return providerId == getPreferredPrintingProviderIdForCard(cardName); } +CardInfoPerSet CardDatabase::getSetInfoForCard(const CardInfoPtr &_card) +{ + const CardInfoPerSetMap &setMap = _card->getSets(); + if (setMap.empty()) { + return CardInfoPerSet(nullptr); + } + + for (const auto &cardInfoPerSetList : setMap) { + for (const auto &cardInfoForSet : cardInfoPerSetList) { + if (QLatin1String("card_") + _card->getName() + QString("_") + cardInfoForSet.getProperty("uuid") == + _card->getPixmapCacheKey()) { + return cardInfoForSet; + } + } + } + + return CardInfoPerSet(nullptr); +} + void CardDatabase::refreshCachedReverseRelatedCards() { for (const CardInfoPtr &card : cards) diff --git a/cockatrice/src/game/cards/card_database.h b/cockatrice/src/game/cards/card_database.h index 6f73f16a4..78d573d99 100644 --- a/cockatrice/src/game/cards/card_database.h +++ b/cockatrice/src/game/cards/card_database.h @@ -23,7 +23,7 @@ class ICardDatabaseParser; typedef QMap QStringMap; typedef QSharedPointer CardInfoPtr; typedef QSharedPointer CardSetPtr; -typedef QMap CardInfoPerSetMap; +typedef QMap> CardInfoPerSetMap; Q_DECLARE_METATYPE(CardInfoPtr) @@ -306,15 +306,15 @@ public: { if (!sets.contains(setName)) return ""; - return sets[setName].getProperty(propertyName); - } - void setSetProperty(const QString &setName, const QString &_name, const QString &_value) - { - if (!sets.contains(setName)) - return; - sets[setName].setProperty(_name, _value); - emit cardInfoChanged(smartThis); + for (const auto &set : sets[setName]) { + if (QLatin1String("card_") + this->getName() + QString("_") + QString(set.getProperty("uuid")) == + this->getPixmapCacheKey()) { + return set.getProperty(propertyName); + } + } + + return sets[setName][0].getProperty(propertyName); } // related cards @@ -450,22 +450,23 @@ public: ~CardDatabase() override; void clear(); void removeCard(CardInfoPtr card); - CardInfoPtr getCard(const QString &cardName) const; - QList getCards(const QStringList &cardNames) const; - CardInfoPtr getCardByNameAndProviderId(const QString &cardName, const QString &providerId) const; - CardInfoPerSet getPreferredSetForCard(const QString &cardName); - CardInfoPerSet getSpecificSetForCard(const QString &cardName, const QString &providerId) const; + [[nodiscard]] CardInfoPtr getCard(const QString &cardName) const; + [[nodiscard]] QList getCards(const QStringList &cardNames) const; + [[nodiscard]] CardInfoPtr getCardByNameAndProviderId(const QString &cardName, const QString &providerId) const; + [[nodiscard]] CardInfoPerSet getPreferredSetForCard(const QString &cardName) const; + [[nodiscard]] CardInfoPerSet getSpecificSetForCard(const QString &cardName, const QString &providerId) const; QString getPreferredPrintingProviderIdForCard(const QString &cardName); - CardInfoPtr guessCard(const QString &cardName) const; + [[nodiscard]] CardInfoPtr guessCard(const QString &cardName) const; /* * Get a card by its simple name. The name will be simplified in this * function, so you don't need to simplify it beforehand. */ - CardInfoPtr getCardBySimpleName(const QString &cardName) const; + [[nodiscard]] CardInfoPtr getCardBySimpleName(const QString &cardName) const; CardSetPtr getSet(const QString &setName); bool isProviderIdForPreferredPrinting(const QString &cardName, const QString &providerId); + static CardInfoPerSet getSetInfoForCard(const CardInfoPtr &_card); QList getCardList() const { return cards.values(); diff --git a/cockatrice/src/game/cards/card_database_model.cpp b/cockatrice/src/game/cards/card_database_model.cpp index abfd25128..a14a9d494 100644 --- a/cockatrice/src/game/cards/card_database_model.cpp +++ b/cockatrice/src/game/cards/card_database_model.cpp @@ -97,9 +97,11 @@ bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(CardInfoPtr card) if (!showOnlyCardsFromEnabledSets) return true; - for (const auto &set : card->getSets()) { - if (set.getPtr()->getEnabled()) - return true; + for (const auto &cardInfoPerSetList : card->getSets()) { + for (const auto &set : cardInfoPerSetList) { + if (set.getPtr()->getEnabled()) + return true; + } } return false; diff --git a/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp b/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp index 24106a3f4..52f284cc2 100644 --- a/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp +++ b/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp @@ -221,7 +221,7 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) if (attrs.hasAttribute("rarity")) { setInfo.setProperty("rarity", attrs.value("rarity").toString()); } - _sets.insert(setName, setInfo); + _sets[setName].append(setInfo); // related cards } else if (xmlName == "related" || xmlName == "reverse-related") { CardRelation::AttachType attach = CardRelation::DoesNotAttach; @@ -331,24 +331,26 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in // sets const CardInfoPerSetMap sets = info->getSets(); - for (CardInfoPerSet set : sets) { - xml.writeStartElement("set"); - xml.writeAttribute("rarity", set.getProperty("rarity")); - xml.writeAttribute("muId", set.getProperty("muid")); - xml.writeAttribute("uuId", set.getProperty("uuid")); + for (const auto &cardInfoPerSetList : sets) { + for (const CardInfoPerSet &set : cardInfoPerSetList) { + xml.writeStartElement("set"); + xml.writeAttribute("rarity", set.getProperty("rarity")); + xml.writeAttribute("muId", set.getProperty("muid")); + xml.writeAttribute("uuId", set.getProperty("uuid")); - tmpString = set.getProperty("num"); - if (!tmpString.isEmpty()) { - xml.writeAttribute("num", tmpString); + tmpString = set.getProperty("num"); + if (!tmpString.isEmpty()) { + xml.writeAttribute("num", tmpString); + } + + tmpString = set.getProperty("picurl"); + if (!tmpString.isEmpty()) { + xml.writeAttribute("picURL", tmpString); + } + + xml.writeCharacters(set.getPtr()->getShortName()); + xml.writeEndElement(); } - - tmpString = set.getProperty("picurl"); - if (!tmpString.isEmpty()) { - xml.writeAttribute("picURL", tmpString); - } - - xml.writeCharacters(set.getPtr()->getShortName()); - xml.writeEndElement(); } // related cards diff --git a/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp b/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp index f2c50d8d4..c6c3921f7 100644 --- a/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp +++ b/cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp @@ -181,7 +181,7 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) attrName = "picurl"; setInfo.setProperty(attrName, attr.value().toString()); } - _sets.insert(setName, setInfo); + _sets[setName].append(setInfo); } // related cards } else if (xmlName == "related" || xmlName == "reverse-related") { @@ -284,14 +284,16 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in xml.writeEndElement(); // sets - for (CardInfoPerSet set : info->getSets()) { - xml.writeStartElement("set"); - for (QString propName : set.getProperties()) { - xml.writeAttribute(propName, set.getProperty(propName)); - } + for (const auto &cardInfoPerSetList : info->getSets()) { + for (const CardInfoPerSet &set : cardInfoPerSetList) { + xml.writeStartElement("set"); + for (const QString &propName : set.getProperties()) { + xml.writeAttribute(propName, set.getProperty(propName)); + } - xml.writeCharacters(set.getPtr()->getShortName()); - xml.writeEndElement(); + xml.writeCharacters(set.getPtr()->getShortName()); + xml.writeEndElement(); + } } // related cards diff --git a/cockatrice/src/game/filters/filter_string.cpp b/cockatrice/src/game/filters/filter_string.cpp index 1bd10485a..8427967f6 100644 --- a/cockatrice/src/game/filters/filter_string.cpp +++ b/cockatrice/src/game/filters/filter_string.cpp @@ -108,10 +108,12 @@ static void setupParserRules() }; search["RarityQuery"] = [](const peg::SemanticValues &sv) -> Filter { StringMatcher matcher = sv[0].get(); - return [=](CardData x) -> bool { - for (const auto &set : x->getSets().values()) { - if (matcher(set.getProperty("rarity"))) - return true; + return [=](const CardData &x) -> bool { + for (const auto &cardInfoPerSetList : x->getSets().values()) { + for (const auto &set : cardInfoPerSetList) { + if (matcher(set.getProperty("rarity"))) + return true; + } } return false; }; diff --git a/cockatrice/src/game/filters/filter_tree.cpp b/cockatrice/src/game/filters/filter_tree.cpp index 1ed61f1e3..d36a1d253 100644 --- a/cockatrice/src/game/filters/filter_tree.cpp +++ b/cockatrice/src/game/filters/filter_tree.cpp @@ -203,11 +203,13 @@ bool FilterItem::acceptText(const CardInfoPtr info) const bool FilterItem::acceptSet(const CardInfoPtr info) const { bool status = false; - for (const auto &set : info->getSets()) { - if (set.getPtr()->getShortName().compare(term, Qt::CaseInsensitive) == 0 || - set.getPtr()->getLongName().compare(term, Qt::CaseInsensitive) == 0) { - status = true; - break; + for (const auto &cardInfoPerSetList : info->getSets()) { + for (const auto &set : cardInfoPerSetList) { + if (set.getPtr()->getShortName().compare(term, Qt::CaseInsensitive) == 0 || + set.getPtr()->getLongName().compare(term, Qt::CaseInsensitive) == 0) { + status = true; + break; + } } } @@ -336,9 +338,11 @@ bool FilterItem::acceptRarity(const CardInfoPtr info) const } } - for (const auto &set : info->getSets()) { - if (set.getProperty("rarity").compare(converted_term, Qt::CaseInsensitive) == 0) { - return true; + for (const auto &cardInfoPerSetList : info->getSets()) { + for (const auto &set : cardInfoPerSetList) { + if (set.getProperty("rarity").compare(converted_term, Qt::CaseInsensitive) == 0) { + return true; + } } } return false; diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index 446f633c3..a42880ea9 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -1764,7 +1764,8 @@ void Player::actCreateRelatedCard() * then let's allow it to be created via "create another token" */ if (createRelatedFromRelation(sourceCard, cardRelation) && cardRelation->getCanCreateAnother()) { - CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCard(cardRelation->getName()); + CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCardByNameAndProviderId( + cardRelation->getName(), sourceCard->getProviderId()); setLastToken(cardInfo); } } @@ -1927,11 +1928,13 @@ void Player::createCard(const CardItem *sourceCard, case CardRelation::AttachTo: cmd.set_target_card_id(sourceCard->getId()); cmd.set_target_mode(Command_CreateToken::ATTACH_TO); + cmd.set_card_provider_id(sourceCard->getProviderId().toStdString()); break; case CardRelation::TransformInto: cmd.set_target_card_id(sourceCard->getId()); cmd.set_target_mode(Command_CreateToken::TRANSFORM_INTO); + cmd.set_card_provider_id(sourceCard->getProviderId().toStdString()); break; } @@ -2090,7 +2093,8 @@ void Player::eventCreateToken(const Event_CreateToken &event) return; } - CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()), QString(), event.card_id()); + CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()), + QString::fromStdString(event.card_provider_id()), event.card_id()); // use db PT if not provided in event if (!QString::fromStdString(event.pt()).isEmpty()) { card->setPT(QString::fromStdString(event.pt())); @@ -3820,13 +3824,17 @@ void Player::addRelatedCardView(const CardItem *card, QMenu *cardMenu) return; } + const auto ¤tCardSet = CardDatabase::getSetInfoForCard(cardInfo); + cardMenu->addSeparator(); auto viewRelatedCards = new QMenu(tr("View related cards")); cardMenu->addMenu(viewRelatedCards); for (const CardRelation *relatedCard : relatedCards) { QString relatedCardName = relatedCard->getName(); QAction *viewCard = viewRelatedCards->addAction(relatedCardName); - connect(viewCard, &QAction::triggered, game, [this, relatedCardName] { game->viewCardInfo(relatedCardName); }); + connect(viewCard, &QAction::triggered, game, [this, relatedCardName, currentCardSet] { + game->viewCardInfo(relatedCardName, currentCardSet.getProperty("uuid")); + }); } } @@ -3845,13 +3853,20 @@ void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu) return; } + const auto ¤tCardSet = CardDatabase::getSetInfoForCard(cardInfo); + cardMenu->addSeparator(); int index = 0; QAction *createRelatedCards = nullptr; for (const CardRelation *cardRelation : relatedCards) { - CardInfoPtr relatedCard = CardDatabaseManager::getInstance()->getCard(cardRelation->getName()); - if (relatedCard == nullptr) + CardInfoPtr relatedCard = CardDatabaseManager::getInstance()->getCardByNameAndProviderId( + cardRelation->getName(), currentCardSet.getProperty("uuid")); + if (relatedCard == nullptr) { + relatedCard = CardDatabaseManager::getInstance()->getCard(cardRelation->getName()); + } + if (relatedCard == nullptr) { continue; + } QString relatedCardName; if (relatedCard->getPowTough().size() > 0) { diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index 538607f80..af4d9bbe5 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -243,6 +243,17 @@ SettingsCache::SettingsCache() showShortcuts = settings->value("menu/showshortcuts", true).toBool(); displayCardNames = settings->value("cards/displaycardnames", true).toBool(); + overrideAllCardArtWithPersonalPreference = + settings->value("cards/overrideallcardartwithpersonalpreference", false).toBool(); + bumpSetsWithCardsInDeckToTop = settings->value("cards/bumpsetswithcardsindecktotop", true).toBool(); + printingSelectorSortOrder = settings->value("cards/printingselectorsortorder", 1).toInt(); + printingSelectorCardSize = settings->value("cards/printingselectorcardsize", 100).toInt(); + printingSelectorSortOptionsVisible = settings->value("cards/printingselectorsortoptionsvisible", true).toBool(); + printingSelectorSearchBarVisible = settings->value("cards/printingselectorcardsearchbarvisible", true).toBool(); + printingSelectorCardSizeSliderVisible = + settings->value("cards/printingselectorcardsizeslidervisible", true).toBool(); + printingSelectorNavigationButtonsVisible = + settings->value("cards/printingselectornavigationbuttonsvisible", true).toBool(); horizontalHand = settings->value("hand/horizontal", true).toBool(); invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool(); minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt(); @@ -534,6 +545,62 @@ void SettingsCache::setDisplayCardNames(QT_STATE_CHANGED_T _displayCardNames) emit displayCardNamesChanged(); } +void SettingsCache::setOverrideAllCardArtWithPersonalPreference(QT_STATE_CHANGED_T _overrideAllCardArt) +{ + overrideAllCardArtWithPersonalPreference = static_cast(_overrideAllCardArt); + settings->setValue("cards/overrideallcardartwithpersonalpreference", overrideAllCardArtWithPersonalPreference); + emit overrideAllCardArtWithPersonalPreferenceChanged(); +} + +void SettingsCache::setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T _bumpSetsWithCardsInDeckToTop) +{ + bumpSetsWithCardsInDeckToTop = static_cast(_bumpSetsWithCardsInDeckToTop); + settings->setValue("cards/bumpsetswithcardsindecktotop", bumpSetsWithCardsInDeckToTop); + emit bumpSetsWithCardsInDeckToTopChanged(); +} + +void SettingsCache::setPrintingSelectorSortOrder(int _printingSelectorSortOrder) +{ + printingSelectorSortOrder = _printingSelectorSortOrder; + settings->setValue("cards/printingselectorsortorder", printingSelectorSortOrder); + emit printingSelectorSortOrderChanged(); +} + +void SettingsCache::setPrintingSelectorCardSize(int _printingSelectorCardSize) +{ + printingSelectorCardSize = _printingSelectorCardSize; + settings->setValue("cards/printingselectorcardsize", printingSelectorCardSize); + emit printingSelectorCardSizeChanged(); +} + +void SettingsCache::setPrintingSelectorSortOptionsVisible(QT_STATE_CHANGED_T _sortOptionsVisible) +{ + printingSelectorSortOptionsVisible = _sortOptionsVisible; + settings->setValue("cards/printingselectorsortoptionsvisible", printingSelectorSortOptionsVisible); + emit printingSelectorSortOptionsVisibleChanged(); +} + +void SettingsCache::setPrintingSelectorSearchBarVisible(QT_STATE_CHANGED_T _searchBarVisible) +{ + printingSelectorSearchBarVisible = _searchBarVisible; + settings->setValue("cards/printingselectorsearchbarvisible", printingSelectorSearchBarVisible); + emit printingSelectorSearchBarVisibleChanged(); +} + +void SettingsCache::setPrintingSelectorCardSizeSliderVisible(QT_STATE_CHANGED_T _cardSizeSliderVisible) +{ + printingSelectorCardSizeSliderVisible = _cardSizeSliderVisible; + settings->setValue("cards/printingselectorcardsizeslidervisible", printingSelectorCardSizeSliderVisible); + emit printingSelectorCardSizeSliderVisibleChanged(); +} + +void SettingsCache::setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T _navigationButtonsVisible) +{ + printingSelectorNavigationButtonsVisible = _navigationButtonsVisible; + settings->setValue("cards/printingselectornavigationbuttonsvisible", printingSelectorNavigationButtonsVisible); + emit printingSelectorNavigationButtonsVisibleChanged(); +} + void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand) { horizontalHand = static_cast(_horizontalHand); diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index 5ea1cdf68..ace3dd49f 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -48,6 +48,14 @@ signals: void themeChanged(); void picDownloadChanged(); void displayCardNamesChanged(); + void overrideAllCardArtWithPersonalPreferenceChanged(); + void bumpSetsWithCardsInDeckToTopChanged(); + void printingSelectorSortOrderChanged(); + void printingSelectorCardSizeChanged(); + void printingSelectorSortOptionsVisibleChanged(); + void printingSelectorSearchBarVisibleChanged(); + void printingSelectorCardSizeSliderVisibleChanged(); + void printingSelectorNavigationButtonsVisibleChanged(); void horizontalHandChanged(); void handJustificationChanged(); void invertVerticalCoordinateChanged(); @@ -100,6 +108,14 @@ private: QByteArray tabGameSplitterSizes; bool showShortcuts; bool displayCardNames; + bool overrideAllCardArtWithPersonalPreference; + bool bumpSetsWithCardsInDeckToTop; + int printingSelectorSortOrder; + int printingSelectorCardSize; + bool printingSelectorSortOptionsVisible; + bool printingSelectorSearchBarVisible; + bool printingSelectorCardSizeSliderVisible; + bool printingSelectorNavigationButtonsVisible; bool horizontalHand; bool invertVerticalCoordinate; int minPlayersForMultiColumnLayout; @@ -304,6 +320,38 @@ public: { return displayCardNames; } + bool getOverrideAllCardArtWithPersonalPreference() const + { + return overrideAllCardArtWithPersonalPreference; + } + bool getBumpSetsWithCardsInDeckToTop() const + { + return bumpSetsWithCardsInDeckToTop; + } + int getPrintingSelectorSortOrder() const + { + return printingSelectorSortOrder; + } + int getPrintingSelectorCardSize() const + { + return printingSelectorCardSize; + } + bool getPrintingSelectorSortOptionsVisible() const + { + return printingSelectorSortOptionsVisible; + } + bool getPrintingSelectorSearchBarVisible() const + { + return printingSelectorSearchBarVisible; + } + bool getPrintingSelectorCardSizeSliderVisible() const + { + return printingSelectorCardSizeSliderVisible; + } + bool getPrintingSelectorNavigationButtonsVisible() const + { + return printingSelectorNavigationButtonsVisible; + } bool getHorizontalHand() const { return horizontalHand; @@ -584,6 +632,14 @@ public slots: void setTabGameSplitterSizes(const QByteArray &_tabGameSplitterSizes); void setShowShortcuts(QT_STATE_CHANGED_T _showShortcuts); void setDisplayCardNames(QT_STATE_CHANGED_T _displayCardNames); + void setOverrideAllCardArtWithPersonalPreference(QT_STATE_CHANGED_T _overrideAllCardArt); + void setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T _bumpSetsWithCardsInDeckToTop); + void setPrintingSelectorSortOrder(int _printingSelectorSortOrder); + void setPrintingSelectorCardSize(int _printingSelectorCardSize); + void setPrintingSelectorSortOptionsVisible(QT_STATE_CHANGED_T _sortOptionsVisible); + void setPrintingSelectorSearchBarVisible(QT_STATE_CHANGED_T _searchBarVisible); + void setPrintingSelectorCardSizeSliderVisible(QT_STATE_CHANGED_T _cardSizeSliderVisible); + void setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T _navigationButtonsVisible); void setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand); void setInvertVerticalCoordinate(QT_STATE_CHANGED_T _invertVerticalCoordinate); void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout); diff --git a/cockatrice/src/settings/layouts_settings.cpp b/cockatrice/src/settings/layouts_settings.cpp index 7e17451b9..5791cbfe9 100644 --- a/cockatrice/src/settings/layouts_settings.cpp +++ b/cockatrice/src/settings/layouts_settings.cpp @@ -1,6 +1,6 @@ #include "layouts_settings.h" -LayoutsSettings::LayoutsSettings(QString settingPath, QObject *parent) +LayoutsSettings::LayoutsSettings(const QString &settingPath, QObject *parent) : SettingsManager(settingPath + "layouts.ini", parent) { } @@ -25,7 +25,7 @@ void LayoutsSettings::setDeckEditorGeometry(const QByteArray &value) setValue(value, "layouts/deckEditor_geometry"); } -const QSize LayoutsSettings::getDeckEditorCardSize() +QSize LayoutsSettings::getDeckEditorCardSize() { QVariant previous = getValue("layouts/deckEditor_CardSize"); return previous == QVariant() ? QSize(250, 500) : previous.toSize(); @@ -36,7 +36,7 @@ void LayoutsSettings::setDeckEditorCardSize(const QSize &value) setValue(value, "layouts/deckEditor_CardSize"); } -const QSize LayoutsSettings::getDeckEditorDeckSize() +QSize LayoutsSettings::getDeckEditorDeckSize() { QVariant previous = getValue("layouts/deckEditor_DeckSize"); return previous == QVariant() ? QSize(250, 360) : previous.toSize(); @@ -47,7 +47,18 @@ void LayoutsSettings::setDeckEditorDeckSize(const QSize &value) setValue(value, "layouts/deckEditor_DeckSize"); } -const QSize LayoutsSettings::getDeckEditorFilterSize() +QSize LayoutsSettings::getDeckEditorPrintingSelectorSize() +{ + QVariant previous = getValue("layouts/deckEditor_PrintingSelectorSize"); + return previous == QVariant() ? QSize(525, 250) : previous.toSize(); +} + +void LayoutsSettings::setDeckEditorPrintingSelectorSize(const QSize &value) +{ + setValue(value, "layouts/deckEditor_PrintingSelectorSize"); +} + +QSize LayoutsSettings::getDeckEditorFilterSize() { QVariant previous = getValue("layouts/deckEditor_FilterSize"); return previous == QVariant() ? QSize(250, 250) : previous.toSize(); diff --git a/cockatrice/src/settings/layouts_settings.h b/cockatrice/src/settings/layouts_settings.h index 80bd71fe8..0464bec01 100644 --- a/cockatrice/src/settings/layouts_settings.h +++ b/cockatrice/src/settings/layouts_settings.h @@ -15,6 +15,7 @@ public: void setDeckEditorGeometry(const QByteArray &value); void setDeckEditorCardSize(const QSize &value); void setDeckEditorDeckSize(const QSize &value); + void setDeckEditorPrintingSelectorSize(const QSize &value); void setDeckEditorFilterSize(const QSize &value); void setDeckEditorDbHeaderState(const QByteArray &value); void setSetsDialogHeaderState(const QByteArray &value); @@ -34,9 +35,10 @@ public: const QByteArray getDeckEditorLayoutState(); const QByteArray getDeckEditorGeometry(); - const QSize getDeckEditorCardSize(); - const QSize getDeckEditorDeckSize(); - const QSize getDeckEditorFilterSize(); + QSize getDeckEditorCardSize(); + QSize getDeckEditorDeckSize(); + QSize getDeckEditorPrintingSelectorSize(); + QSize getDeckEditorFilterSize(); const QByteArray getDeckEditorDbHeaderState(); const QByteArray getSetsDialogHeaderState(); @@ -57,7 +59,7 @@ signals: public slots: private: - explicit LayoutsSettings(QString settingPath, QObject *parent = nullptr); + explicit LayoutsSettings(const QString &settingPath, QObject *parent = nullptr); LayoutsSettings(const LayoutsSettings & /*other*/); }; diff --git a/cockatrice/src/utility/card_set_comparator.h b/cockatrice/src/utility/card_set_comparator.h index 1aa1500fb..107b618fb 100644 --- a/cockatrice/src/utility/card_set_comparator.h +++ b/cockatrice/src/utility/card_set_comparator.h @@ -21,4 +21,40 @@ public: } }; +class SetReleaseDateComparator +{ +public: + /* + * Returns true if a has higher download priority than b + * Enabled sets have priority over disabled sets + * Both groups follow the user-defined order + */ + inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const + { + if (a->getEnabled()) { + return !b->getEnabled() || a->getReleaseDate() < b->getReleaseDate(); + } else { + return !b->getEnabled() && a->getReleaseDate() < b->getReleaseDate(); + } + } +}; + +class CardSetPriorityComparator +{ +public: + /* + * Returns true if a has higher download priority than b + * Enabled sets have priority over disabled sets + * Both groups follow the user-defined order + */ + inline bool operator()(const CardInfoPerSet &a, const CardInfoPerSet &b) const + { + if (a.getPtr()->getEnabled()) { + return !b.getPtr()->getEnabled() || a.getPtr()->getSortKey() < b.getPtr()->getSortKey(); + } else { + return !b.getPtr()->getEnabled() && a.getPtr()->getSortKey() < b.getPtr()->getSortKey(); + } + } +}; + #endif // SET_PRIORITY_COMPARATOR_H \ No newline at end of file diff --git a/common/decklist.cpp b/common/decklist.cpp index 011c9e0f3..08b98a730 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -156,12 +156,17 @@ AbstractDecklistNode *InnerDecklistNode::findChild(const QString &_name) return nullptr; } -AbstractDecklistNode *InnerDecklistNode::findCardChildByNameAndProviderId(const QString &_name, - const QString &_providerId) +AbstractDecklistNode *InnerDecklistNode::findCardChildByNameProviderIdAndNumber(const QString &_name, + const QString &_providerId, + const QString &_cardNumber) { - for (int i = 0; i < size(); i++) { - if (at(i) != nullptr && at(i)->getName() == _name && at(i)->getCardProviderId() == _providerId) { - return at(i); + for (const auto &i : *this) { + if (i != nullptr && i->getName() == _name) { + if (i->getCardCollectorNumber() == _cardNumber) { + if (i->getCardProviderId() == _providerId) { + return i; + } + } } } return nullptr; @@ -319,13 +324,13 @@ void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml) xml->writeAttribute("number", QString::number(getNumber())); xml->writeAttribute("name", getName()); - if (getCardSetShortName().isEmpty()) { + if (!getCardSetShortName().isEmpty()) { xml->writeAttribute("setShortName", getCardSetShortName()); } - if (getCardCollectorNumber().isEmpty()) { + if (!getCardCollectorNumber().isEmpty()) { xml->writeAttribute("collectorNumber", getCardCollectorNumber()); } - if (getCardProviderId().isEmpty()) { + if (!getCardProviderId().isEmpty()) { xml->writeAttribute("uuid", getCardProviderId()); } } diff --git a/common/decklist.h b/common/decklist.h index ad5f59b22..87596a884 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -66,6 +66,7 @@ public: virtual QString getCardProviderId() const = 0; virtual QString getCardSetShortName() const = 0; virtual QString getCardCollectorNumber() const = 0; + [[nodiscard]] virtual bool isDeckHeader() const = 0; InnerDecklistNode *getParent() const { return parent; @@ -128,10 +129,16 @@ public: { cardCollectorNumber = _cardCollectorNumber; } + [[nodiscard]] bool isDeckHeader() const override + { + return true; + } void clearTree(); AbstractDecklistNode *findChild(const QString &_name); - AbstractDecklistNode *findCardChildByNameAndProviderId(const QString &_name, const QString &_providerId); + AbstractDecklistNode *findCardChildByNameProviderIdAndNumber(const QString &_name, + const QString &_providerId, + const QString &_cardNumber = ""); int height() const override; int recursiveCount(bool countTotalCards = false) const; bool compare(AbstractDecklistNode *other) const override; @@ -233,6 +240,10 @@ public: { cardSetNumber = _cardSetNumber; } + [[nodiscard]] bool isDeckHeader() const override + { + return false; + } }; class DeckList : public QObject diff --git a/common/pb/command_create_token.proto b/common/pb/command_create_token.proto index 4ee60b289..671ce3fe3 100644 --- a/common/pb/command_create_token.proto +++ b/common/pb/command_create_token.proto @@ -25,4 +25,6 @@ message Command_CreateToken { // What to do with the target card. Ignored if there is no target card. optional TargetMode target_mode = 11; + + optional string card_provider_id = 12; } diff --git a/common/pb/event_create_token.proto b/common/pb/event_create_token.proto index 42c32eff8..bc88a744c 100644 --- a/common/pb/event_create_token.proto +++ b/common/pb/event_create_token.proto @@ -14,4 +14,5 @@ message Event_CreateToken { optional bool destroy_on_zone_change = 7; optional sint32 x = 8; optional sint32 y = 9; + optional string card_provider_id = 10; } diff --git a/common/server_player.cpp b/common/server_player.cpp index adf1a66e0..218ab2fdd 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -391,6 +391,7 @@ static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card event.set_zone_name(zone->getName().toStdString()); event.set_card_id(card->getId()); event.set_card_name(card->getName().toStdString()); + event.set_card_provider_id(card->getProviderId().toStdString()); event.set_color(card->getColor().toStdString()); event.set_pt(card->getPT().toStdString()); event.set_annotation(card->getAnnotation().toStdString()); @@ -1400,7 +1401,8 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer } } - QString cardName = nameFromStdString(cmd.card_name()); + const QString cardName = nameFromStdString(cmd.card_name()); + const QString cardProviderId = nameFromStdString(cmd.card_provider_id()); if (zone->hasCoords()) { xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, false); } @@ -1411,7 +1413,7 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer yCoord = 0; } - auto *card = new Server_Card(cardName, QString(), newCardId(), xCoord, yCoord); + auto *card = new Server_Card(cardName, cardProviderId, newCardId(), xCoord, yCoord); card->moveToThread(thread()); card->setPT(nameFromStdString(cmd.pt())); card->setColor(nameFromStdString(cmd.color())); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index 4f3624c73..14618b977 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -160,6 +160,30 @@ void SettingsCache::setShowShortcuts(QT_STATE_CHANGED_T /* _showShortcuts */) void SettingsCache::setDisplayCardNames(QT_STATE_CHANGED_T /* _displayCardNames */) { } +void SettingsCache::setOverrideAllCardArtWithPersonalPreference(QT_STATE_CHANGED_T /* _displayCardNames */) +{ +} +void SettingsCache::setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T /* _bumpSetsWithCardsInDeckToTop */) +{ +} +void SettingsCache::setPrintingSelectorSortOrder(int /* _printingSelectorSortOrder */) +{ +} +void SettingsCache::setPrintingSelectorCardSize(int /* _printingSelectorCardSize */) +{ +} +void SettingsCache::setPrintingSelectorSortOptionsVisible(QT_STATE_CHANGED_T /* _sortOptionsVisible */) +{ +} +void SettingsCache::setPrintingSelectorSearchBarVisible(QT_STATE_CHANGED_T /* _searchBarVisible */) +{ +} +void SettingsCache::setPrintingSelectorCardSizeSliderVisible(QT_STATE_CHANGED_T /* _cardSizeSliderVisible */) +{ +} +void SettingsCache::setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T /* _navigationButtonsVisible */) +{ +} void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */) { } diff --git a/dbconverter/src/mocks.h b/dbconverter/src/mocks.h index bcbaf0342..23210810a 100644 --- a/dbconverter/src/mocks.h +++ b/dbconverter/src/mocks.h @@ -10,9 +10,9 @@ #define PICTURELOADER_H -#include "../cockatrice/src/game/cards/card_database.h" -#include "../cockatrice/src/settings/cache_settings.h" -#include "../cockatrice/src/utility/macros.h" +#include "../../cockatrice/src/game/cards/card_database.h" +#include "../../cockatrice/src/settings/cache_settings.h" +#include "../../cockatrice/src/utility/macros.h" extern SettingsCache *settingsCache; diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index bfa68e64e..1e1d41c14 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -3,7 +3,8 @@ #include "game/cards/card_database_parser/cockatrice_xml_4.h" #include "qt-json/json.h" -#include +#include +#include #include #include @@ -42,7 +43,7 @@ bool OracleImporter::readSetsFromByteArray(const QByteArray &data) return false; } - QListIterator it(setsMap.values()); + QListIterator it(setsMap.values()); QVariantMap map; QString shortName; @@ -176,7 +177,7 @@ CardInfoPtr OracleImporter::addCard(QString name, // insert the card and its properties QList reverseRelatedCards; CardInfoPerSetMap setsInfo; - setsInfo.insert(setInfo.getPtr()->getShortName(), setInfo); + setsInfo[setInfo.getPtr()->getShortName()].append(setInfo); CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards, setsInfo, cipt, tableRow, upsideDown); @@ -193,9 +194,7 @@ QString OracleImporter::getStringPropertyFromMap(const QVariantMap &card, const return card.contains(propertyName) ? card.value(propertyName).toString() : QString(""); } -int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, - const QList &cardsList, - bool skipSpecialCards) +int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList &cardsList) { // mtgjson name => xml name static const QMap cardProperties{ @@ -213,23 +212,17 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, QMap> splitCards; QString ptSeparator("/"); QVariantMap card; - QString layout, name, text, colors, colorIdentity, maintype, faceName; - static const bool isToken = false; + QString layout, name, text, colors, colorIdentity, faceName; + static constexpr bool isToken = false; + static const QList setsWithCardsWithSameNameButDifferentText = {"UST"}; QVariantHash properties; CardInfoPerSet setInfo; QList relatedCards; - static const QList specialNumChars = {"★", "s", "†"}; - QMap specialPromoCards; QList allNameProps; for (const QVariant &cardVar : cardsList) { card = cardVar.toMap(); - // skip alternatives - if (getStringPropertyFromMap(card, "isAlternative") == "true") { - continue; - } - /* Currently used layouts are: * augment, double_faced_token, flip, host, leveler, meld, normal, planar, * saga, scheme, split, token, transform, vanguard @@ -251,7 +244,7 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, // card properties properties.clear(); - QMapIterator it(cardProperties); + QMapIterator it(cardProperties); while (it.hasNext()) { it.next(); QString mtgjsonProperty = it.key(); @@ -263,7 +256,7 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, // per-set properties setInfo = CardInfoPerSet(currentSet); - QMapIterator it2(setInfoProperties); + QMapIterator it2(setInfoProperties); while (it2.hasNext()) { it2.next(); QString mtgjsonProperty = it2.key(); @@ -274,7 +267,7 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, } // Identifiers - QMapIterator it3(identifierProperties); + QMapIterator it3(identifierProperties); while (it3.hasNext()) { it3.next(); auto mtgjsonProperty = it3.key(); @@ -285,43 +278,16 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, } } - QString numComponent{}; - if (skipSpecialCards) { - QString numProperty = setInfo.getProperty("num"); - // skip promo cards if it's not the only print, cards with two faces are different cards - if (allNameProps.contains(faceName)) { - // check for alternative versions - if (layout != "normal") - continue; + QString numComponent; + const QString numProperty = setInfo.getProperty("num"); + const QChar lastChar = numProperty.at(numProperty.size() - 1); - // alternative versions have a letter in the end of num like abc - // note this will also catch p and s, those will get removed later anyway - QChar lastChar = numProperty.at(numProperty.size() - 1); - if (!lastChar.isLetter()) - continue; - - numComponent = " (" + QString(lastChar) + ")"; - faceName += numComponent; // add to facename to make it unique - } - if (getStringPropertyFromMap(card, "isPromo") == "true") { - specialPromoCards.insert(faceName, cardVar); - continue; - } - bool skip = false; - // skip cards containing special stuff in the collectors number like promo cards - for (const QString &specialChar : specialNumChars) { - if (numProperty.contains(specialChar)) { - skip = true; - break; - } - } - if (skip) { - specialPromoCards.insert(faceName, cardVar); - continue; - } else { - allNameProps.append(faceName); - } + // Un-Sets do some wonky stuff. Split up these cards as individual entries. + if (setsWithCardsWithSameNameButDifferentText.contains(currentSet->getShortName()) && + allNameProps.contains(faceName) && layout == "normal" && lastChar.isLetter()) { + numComponent = " (" + QString(lastChar).toLower() + ")"; } + allNameProps.append(faceName); // special handling properties colors = card.value("colors").toStringList().join(""); @@ -463,18 +429,6 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, numCards++; } - // only add the unique promo cards that didn't already exist in the set - if (skipSpecialCards) { - QList nonDuplicatePromos; - for (auto cardIter = specialPromoCards.constBegin(); cardIter != specialPromoCards.constEnd(); ++cardIter) { - if (!allNameProps.contains(cardIter.key())) { - nonDuplicatePromos.append(cardIter.value()); - } - } - if (!nonDuplicatePromos.isEmpty()) { - numCards += importCardsFromSet(currentSet, nonDuplicatePromos, false); - } - } return numCards; } diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index 48b609e2f..e4caf9fbb 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -143,7 +143,7 @@ public: bool readSetsFromByteArray(const QByteArray &data); int startImport(); bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion); - int importCardsFromSet(const CardSetPtr ¤tSet, const QList &cards, bool skipSpecialNums = true); + int importCardsFromSet(const CardSetPtr ¤tSet, const QList &cards); QList &getSets() { return allSets; diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index 03437ae2a..63ecca936 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -164,6 +164,30 @@ void SettingsCache::setShowShortcuts(QT_STATE_CHANGED_T /* _showShortcuts */) void SettingsCache::setDisplayCardNames(QT_STATE_CHANGED_T /* _displayCardNames */) { } +void SettingsCache::setOverrideAllCardArtWithPersonalPreference(QT_STATE_CHANGED_T /* _overrideAllCardArt */) +{ +} +void SettingsCache::setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T /* _bumpSetsWithCardsInDeckToTop */) +{ +} +void SettingsCache::setPrintingSelectorSortOrder(int /* _printingSelectorSortOrder */) +{ +} +void SettingsCache::setPrintingSelectorCardSize(int /* _printingSelectorCardSize */) +{ +} +void SettingsCache::setPrintingSelectorSortOptionsVisible(QT_STATE_CHANGED_T /* _sortOptionsVisible */) +{ +} +void SettingsCache::setPrintingSelectorSearchBarVisible(QT_STATE_CHANGED_T /* _searchBarVisible */) +{ +} +void SettingsCache::setPrintingSelectorCardSizeSliderVisible(QT_STATE_CHANGED_T /* _cardSizeSliderVisible */) +{ +} +void SettingsCache::setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T /* _navigationButtonsVisible */) +{ +} void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */) { } diff --git a/tests/carddatabase/mocks.h b/tests/carddatabase/mocks.h index 88a8d2d8a..23210810a 100644 --- a/tests/carddatabase/mocks.h +++ b/tests/carddatabase/mocks.h @@ -12,7 +12,7 @@ #include "../../cockatrice/src/game/cards/card_database.h" #include "../../cockatrice/src/settings/cache_settings.h" -#include "../cockatrice/src/utility/macros.h" +#include "../../cockatrice/src/utility/macros.h" extern SettingsCache *settingsCache;