From 245d51caea38a46b550362dff7cc05df01afb8d4 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Thu, 19 Dec 2024 03:40:34 +0100 Subject: [PATCH] New printing selector (#5182) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Squashed Commits Lint things. Set focus back to deckView after selecting a card to enable keyboard navigation. Bump scrollbar to top when selecting a new card. Update card info on hover. Layout cleanups Add +- to buttons. Merge buttons into card picture. Cleanup size, min 2 cards by default in rows Support layout settings config and set min to 525 so two cols are visible by default for printings, when opened Move Printing Selector to before Deck, and visible true Null safety for setCard. Turn down the dropshadow a little. Make PrintingSelector dockable, don't duplicate sets when bumping them to the front of the list. When swapping cards between mainboard and sideboard, use preferred printing if no uuid is available (i.e. null). Reorder includes... Unwonk an include. Give the card widget a snazzy drop shadow, appease the linter gods. Handle jumping between segments Remember scale factor when initializing new widgets. Cleanup Select Card works (Not M->SB tho) Resize word-wrapped label properly. Fix the layouting, mostly. remove tx Build Fix Squashed Commits Load and store redirects properly. Layouting is fun :) * Group PrintingSelectorCardDisplayWidgets into distinct containers for alignment purposes. Override resizeEvent() properly. Word wrap properly. Keep widget sizes uniform for aesthetic reasons (grid pattern). Label stuff, center card picture widget, allow cardSizeSlider to scale down. Replace cards which have no uuid in the decklist when first selecting a printing. Add buttons for previous and next card in DeckList. Add a card size slider. Move sort options initialization to implementation file. Explicitly nullptr the parent for the PrintingSelector. Address PR comments (minor cleanups). Hook up to the rows removed signal to update card counts properly. Include QDebug. Add labels to the mainboard/sideboard button boxes. Implement a search bar. Expand node recursively when we add a new card. Only create image widgets for the printing selector if it is visible in order to not slow down image loading. Minor Tweaks Invert decklist export logic to write out setName, collectorNumber, providerId value if it is NOT empty. Linting. Update CardCounts properly, update PrintingSelector on Database selection. Initialize sideboard card count label properly. Split mainboard/sideboard display and increment/decrement buttons. Add button to sort all sortOptions by ascending or descending order. Add option to sort by release date in ascending or descending order. Add PrintingSelector to database view. Display placeholder image before loading. Fix deckEditor crash on mainboard/sideboard swap by correcting column index to providerId instead of shortName. Include currentZoneName, fix the column when updating from DeckView indexChanged to be UUID and not setShortName so cards are properly fetched again. The most minor linter change you've ever seen. Null checks are important. Linter again. Linter and refactor to providerId. Sort properly, (We don't need a map, we need a list, a map won't be ordered right [i.e. 1, 10, 11, 2, 3, 4, ..., 9]) Sort alphabetically or by preference. Hook printingSelector up to the CardInfoFrameWidget. Allow info from CardFrame to be retrieved, properly initialize PrintingSelector again. Refactors to reflect CardInfoPicture becoming CardInfoPictureWidget. Make PrintingSelector re-usable by introducing setCard(). Make PrintingSelector use the CardFrame, not the database index. Add a new selector widget for printings. * Support multiple tags per card within the database This will allow us to show off all different printings for cards that might appear multiple times in a set (alt arts, Secret Lairs, etc.) * Support Flip Cards with related art * Minor Cleanup * Minor Cleanup * Release Date DESC default * Load widgets in batches. * Refactor local batch variables to be class variables/defines. * Clear timer on updateDisplay. * Fix Timer & Builds on Qt5 * Not Override * Yes Override * Yes Override * Lint * Can't override in function definition. * Resize setName to picture width on initialization. Also add a new signal to card_info_picture_widget to emit when the scale factor changes. Hook this up to the setName resizing method to ensure card size updates trigger it appropriately after initialization. Clean up unused enter and resize methods that just delegated to base-class. * Add ability to force preferred set art to be loaded for every card. * Show related cards from printing selector by right-clicking card image. * fix build * Fix UST cards * Inc QDebug * Fix Qt5 Builds * Fix Qt5 Builds * Fix Qt5 Builds * Fix Qt5 Builds * Fix Qt5 Builds * Fix cards being able to jump between side and mainboard * Don't hide PrintingSelector button widgets if the deck contains a card from the set. * Update PrintingSelector properly on DeckListModel::dataChanged * Add option to disable bumping sets to the front of the list if the deck contains cards from the set. * Linter behave. * Linter behave. * Fix mocks. * Fix cards without providerIds being counted for all cards. * Flip preference sort so descending means "Most to least preferred". * Set the index correctly when removing a non-providerId printing for a providerId printing to avoid jumping to the next card. * Move the "Next/Previous" card buttons to their own widget. * Move the card size slider to its own widget. * Lint the makelist. * Linter * Crash fix * Move the sorting options to their own widget. * Move the search bar to its own widget. * Minor cleanup * Minor cleanup * Minor cleanup * Only overwrite card in deck if UUID _and_ Number missing * Adjust font size when adjusting card size. * Clean up some imports. * Pivot to a view options toolbar. * Persist sort options and change default to 'preference'. * Lint. * Remember how many cards were originally in deck when replacing with uuid version. * Relabel buttons for clarity. * Fix tests. * Fix tests properly. * Fix dbconverter mock. * Try to wrangle font sizes. * Update mainboard/sideboard labels correctly. * Initialize button sizes correctly. * Label texts are supposed to be white. * Adjust another deckModel->findCard call to include number parameter. * Style buttons again. * Negative currentSize means we don't render the widget yet, return a default value. * Clean up debug statements. * Boop the mainboard/sideboard label and the cardCount after a little bit of delay to make sure they initialize at the right size. * Persist card size slider selection in SettingsCache. * Good Lint Inc. * updateCardCount to get white color in initializer * Make the view display options functional. * Comment ALL the things. * Lint the things. * Brief accidentally nuked some constants. * Proper Qt slot for checkboxes. * Don't use timers, Qt provides ShowEvent for anything necessary before the widget is shown. * Cleanup from Reading * Cleanup Lints * Minor --------- Co-authored-by: Lukas Brübach Co-authored-by: Zach Halpern --- cockatrice/CMakeLists.txt | 15 + .../src/client/tabs/tab_deck_editor.cpp | 164 +++++++++- cockatrice/src/client/tabs/tab_deck_editor.h | 15 +- cockatrice/src/client/tabs/tab_game.cpp | 4 +- cockatrice/src/client/tabs/tab_game.h | 2 +- cockatrice/src/client/ui/picture_loader.cpp | 46 ++- cockatrice/src/client/ui/picture_loader.h | 2 + .../ui/widgets/cards/card_info_frame_widget.h | 5 + .../cards/card_info_picture_widget.cpp | 7 +- .../widgets/cards/card_info_picture_widget.h | 1 + .../ui/widgets/cards/card_size_widget.cpp | 53 ++++ .../ui/widgets/cards/card_size_widget.h | 29 ++ .../display/dynamic_font_size_label.cpp | 137 ++++++++ .../general/display/dynamic_font_size_label.h | 41 +++ .../display/dynamic_font_size_push_button.cpp | 80 +++++ .../display/dynamic_font_size_push_button.h | 29 ++ .../display/shadow_background_label.cpp | 63 ++++ .../general/display/shadow_background_label.h | 18 ++ .../general/layout_containers/flow_widget.cpp | 80 +++-- .../general/layout_containers/flow_widget.h | 9 +- .../all_zones_card_amount_widget.cpp | 120 +++++++ .../all_zones_card_amount_widget.h | 47 +++ .../printing_selector/card_amount_widget.cpp | 293 ++++++++++++++++++ .../printing_selector/card_amount_widget.h | 67 ++++ .../printing_selector/printing_selector.cpp | 234 ++++++++++++++ .../printing_selector/printing_selector.h | 58 ++++ .../printing_selector_card_display_widget.cpp | 74 +++++ .../printing_selector_card_display_widget.h | 52 ++++ .../printing_selector_card_overlay_widget.cpp | 185 +++++++++++ .../printing_selector_card_overlay_widget.h | 49 +++ .../printing_selector_card_search_widget.cpp | 38 +++ .../printing_selector_card_search_widget.h | 25 ++ ...rinting_selector_card_selection_widget.cpp | 37 +++ .../printing_selector_card_selection_widget.h | 26 ++ .../printing_selector_card_sorting_widget.cpp | 210 +++++++++++++ .../printing_selector_card_sorting_widget.h | 39 +++ ...g_selector_view_options_toolbar_widget.cpp | 140 +++++++++ ...ing_selector_view_options_toolbar_widget.h | 36 +++ .../printing_selector_view_options_widget.cpp | 69 +++++ .../printing_selector_view_options_widget.h | 28 ++ ...e_and_collectors_number_display_widget.cpp | 102 ++++++ ...ame_and_collectors_number_display_widget.h | 29 ++ cockatrice/src/deck/deck_list_model.cpp | 36 ++- cockatrice/src/deck/deck_list_model.h | 21 +- cockatrice/src/dialogs/dlg_edit_tokens.cpp | 2 +- cockatrice/src/dialogs/dlg_settings.cpp | 23 +- cockatrice/src/dialogs/dlg_settings.h | 2 + cockatrice/src/game/cards/card_database.cpp | 83 +++-- cockatrice/src/game/cards/card_database.h | 33 +- .../src/game/cards/card_database_model.cpp | 8 +- .../card_database_parser/cockatrice_xml_3.cpp | 36 ++- .../card_database_parser/cockatrice_xml_4.cpp | 18 +- cockatrice/src/game/filters/filter_string.cpp | 10 +- cockatrice/src/game/filters/filter_tree.cpp | 20 +- cockatrice/src/game/player/player.cpp | 25 +- cockatrice/src/settings/cache_settings.cpp | 67 ++++ cockatrice/src/settings/cache_settings.h | 56 ++++ cockatrice/src/settings/layouts_settings.cpp | 19 +- cockatrice/src/settings/layouts_settings.h | 10 +- cockatrice/src/utility/card_set_comparator.h | 36 +++ common/decklist.cpp | 21 +- common/decklist.h | 13 +- common/pb/command_create_token.proto | 2 + common/pb/event_create_token.proto | 1 + common/server_player.cpp | 6 +- dbconverter/src/mocks.cpp | 24 ++ dbconverter/src/mocks.h | 6 +- oracle/src/oracleimporter.cpp | 84 ++--- oracle/src/oracleimporter.h | 2 +- tests/carddatabase/mocks.cpp | 24 ++ tests/carddatabase/mocks.h | 2 +- 71 files changed, 3197 insertions(+), 251 deletions(-) create mode 100644 cockatrice/src/client/ui/widgets/cards/card_size_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/cards/card_size_widget.h create mode 100644 cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.cpp create mode 100644 cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_label.h create mode 100644 cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp create mode 100644 cockatrice/src/client/ui/widgets/general/display/dynamic_font_size_push_button.h create mode 100644 cockatrice/src/client/ui/widgets/general/display/shadow_background_label.cpp create mode 100644 cockatrice/src/client/ui/widgets/general/display/shadow_background_label.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_toolbar_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.h create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.h 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;