diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp index fe6852362..83b884779 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp @@ -55,13 +55,11 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) orderByCombo = new QComboBox(navigationContainer); orderByCombo->addItems({"name", "updatedAt", "createdAt", "viewCount", "size", "edhBracket"}); orderByCombo->setCurrentText("updatedAt"); // Pre-select updatedAt - navigationLayout->addWidget(orderByCombo); // Asc/Desc toggle orderDirButton = new QPushButton(tr("Desc."), navigationContainer); orderDirButton->setCheckable(true); // checked = DESC, unchecked = ASC orderDirButton->setChecked(true); - navigationLayout->addWidget(orderDirButton); connect(orderByCombo, &QComboBox::currentTextChanged, this, [this]() { doSearch(); }); connect(orderDirButton, &QPushButton::clicked, this, [this](bool checked) { @@ -88,10 +86,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) }); } - navigationLayout->addLayout(colorLayout); - logicalAndCheck = new QCheckBox("Require ALL colors", navigationContainer); - navigationLayout->addWidget(logicalAndCheck); // Formats @@ -110,20 +105,15 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) formatSettingsWidget->addSettingsWidget(formatCheckBox); } - navigationLayout->addWidget(formatSettingsWidget); - navigationLayout->addWidget(formatLabel); - // EDH Bracket edhBracketCombo = new QComboBox(navigationContainer); edhBracketCombo->addItem(tr("Any Bracket")); edhBracketCombo->addItems({"1", "2", "3", "4", "5"}); - navigationLayout->addWidget(edhBracketCombo); connect(edhBracketCombo, &QComboBox::currentTextChanged, this, [this]() { doSearch(); }); // Search for Card Packages instead of Decks packagesCheck = new QCheckBox("Packages", navigationContainer); - navigationLayout->addWidget(packagesCheck); connect(packagesCheck, &QCheckBox::clicked, this, [this]() { bool disable = packagesCheck->isChecked(); @@ -139,25 +129,18 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) // Deck Name nameField = new QLineEdit(navigationContainer); nameField->setPlaceholderText(tr("Deck name contains...")); - navigationLayout->addWidget(nameField); // Owner Name - ownerField = new QLineEdit(navigationContainer); ownerField->setPlaceholderText(tr("Owner name contains...")); - navigationLayout->addWidget(ownerField); // Contained cards - cardsField = new QLineEdit(navigationContainer); cardsField->setPlaceholderText("Cards (comma separated)"); - navigationLayout->addWidget(cardsField); // Commanders - commandersField = new QLineEdit(navigationContainer); commandersField->setPlaceholderText("Commanders (comma separated)"); - navigationLayout->addWidget(commandersField); // DB supplemented card search auto cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this); @@ -201,14 +184,12 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) // Tag Name deckTagNameField = new QLineEdit(navigationContainer); deckTagNameField->setPlaceholderText("Deck tag"); - navigationLayout->addWidget(deckTagNameField); connect(deckTagNameField, &QLineEdit::textChanged, this, [this]() { doSearch(); }); // Search button searchPushButton = new QPushButton(navigationContainer); searchPushButton->setText("Search"); - navigationLayout->addWidget(searchPushButton); connect(searchPushButton, &QPushButton::clicked, this, [this]() { doSearch(); }); @@ -216,23 +197,18 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) settingsButton = new SettingsButtonWidget(this); cardSizeSlider = new CardSizeWidget(this); settingsButton->addSettingsWidget(cardSizeSlider); - navigationLayout->addWidget(settingsButton); - // Page size + // Min deck size + minDeckSizeLabel = new QLabel(navigationContainer); - pageSizeLabel = new QLabel(navigationContainer); - navigationLayout->addWidget(pageSizeLabel); - - pageSizeSpin = new QSpinBox(navigationContainer); - pageSizeSpin->setRange(1, 200); - pageSizeSpin->setValue(50); - navigationLayout->addWidget(pageSizeSpin); + minDeckSizeSpin = new QSpinBox(navigationContainer); + minDeckSizeSpin->setRange(1, 200); + minDeckSizeSpin->setValue(50); // Page number pageSpin = new QSpinBox(navigationContainer); pageSpin->setRange(1, 9999); pageSpin->setValue(1); - navigationLayout->addWidget(pageSpin); connect(pageSpin, &QSpinBox::valueChanged, this, [this]() { doSearch(); }); @@ -242,6 +218,54 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) currentPageLayout->setContentsMargins(0, 0, 0, 0); currentPageDisplay->setLayout(currentPageLayout); + // Layout composition + + // Sort section + navigationLayout->addWidget(orderByCombo); + navigationLayout->addWidget(orderDirButton); + + // Colors section + navigationLayout->addLayout(colorLayout); + navigationLayout->addWidget(logicalAndCheck); + + // Formats section + navigationLayout->addWidget(formatSettingsWidget); + navigationLayout->addWidget(formatLabel); + + // EDH Bracket + navigationLayout->addWidget(edhBracketCombo); + + // Packages toggle + navigationLayout->addWidget(packagesCheck); + + // Deck name + navigationLayout->addWidget(nameField); + + // Owner name + navigationLayout->addWidget(ownerField); + + // Contained cards + navigationLayout->addWidget(cardsField); + + // Commanders + navigationLayout->addWidget(commandersField); + + // Deck tag + navigationLayout->addWidget(deckTagNameField); + + // Search button + navigationLayout->addWidget(searchPushButton); + + // Card size settings + navigationLayout->addWidget(settingsButton); + + // Page size + navigationLayout->addWidget(minDeckSizeLabel); + navigationLayout->addWidget(minDeckSizeSpin); + + // Page number + navigationLayout->addWidget(pageSpin); + mainLayout->addWidget(navigationContainer); mainLayout->addWidget(currentPageDisplay); @@ -260,16 +284,21 @@ void TabArchidekt::retranslateUi() { searchPushButton->setText(tr("Search")); formatLabel->setText(tr("Formats")); - pageSizeLabel->setText(tr("Min. # of Cards:")); + minDeckSizeLabel->setText(tr("Min. # of Cards:")); } QString TabArchidekt::buildSearchUrl() { QUrlQuery query; - // Name - if (!nameField->text().isEmpty()) { - query.addQueryItem("name", nameField->text()); + // orderBy (field + direction) + { + QString field = orderByCombo->currentText(); + if (!field.isEmpty()) { + bool desc = orderDirButton->isChecked(); + QString final = desc ? "-" + field : field; + query.addQueryItem("orderBy", final); + } } // Colors @@ -277,7 +306,6 @@ QString TabArchidekt::buildSearchUrl() for (QChar c : activeColors) { selectedColors.append(c); } - if (!selectedColors.isEmpty()) { query.addQueryItem("colors", selectedColors.join(",")); } @@ -287,6 +315,38 @@ QString TabArchidekt::buildSearchUrl() query.addQueryItem("logicalAnd", "true"); } + // Formats + if (!packagesCheck->isChecked()) { + QStringList formatIds; + for (int i = 0; i < formatChecks.size(); ++i) + if (formatChecks[i]->isChecked()) { + formatIds << QString::number(i + 1); + } + + if (!formatIds.isEmpty()) { + query.addQueryItem("deckFormat", formatIds.join(",")); + } + } + + // edhBracket + if (!packagesCheck->isChecked()) { + if (!edhBracketCombo->currentText().isEmpty()) { + if (edhBracketCombo->currentText() != tr("Any Bracket")) { + query.addQueryItem("edhBracket", edhBracketCombo->currentText()); + } + } + } + + // Search for card packages instead of decks + if (packagesCheck->isChecked()) { + query.addQueryItem("packages", "true"); + } + + // Name + if (!nameField->text().isEmpty()) { + query.addQueryItem("name", nameField->text()); + } + // owner if (!ownerField->text().isEmpty()) { query.addQueryItem("ownerUsername", ownerField->text()); @@ -304,33 +364,6 @@ QString TabArchidekt::buildSearchUrl() } } - // Formats - if (!packagesCheck->isChecked()) { - QStringList formatIds; - for (int i = 0; i < formatChecks.size(); ++i) - if (formatChecks[i]->isChecked()) { - formatIds << QString::number(i + 1); - } - - if (!formatIds.isEmpty()) { - query.addQueryItem("deckFormat", formatIds.join(",")); - } - } - - // Search for card packages instead of decks - if (packagesCheck->isChecked()) { - query.addQueryItem("packages", "true"); - } - - // edhBracket - if (!packagesCheck->isChecked()) { - if (!edhBracketCombo->currentText().isEmpty()) { - if (edhBracketCombo->currentText() != tr("Any Bracket")) { - query.addQueryItem("edhBracket", edhBracketCombo->currentText()); - } - } - } - // deckTagName if (!packagesCheck->isChecked()) { if (!deckTagNameField->text().isEmpty()) { @@ -338,24 +371,14 @@ QString TabArchidekt::buildSearchUrl() } } - // orderBy (field + direction) - { - QString field = orderByCombo->currentText(); - if (!field.isEmpty()) { - bool desc = orderDirButton->isChecked(); - QString final = desc ? "-" + field : field; - query.addQueryItem("orderBy", final); - } - } - // page number if (pageSpin->value() <= 1) { query.addQueryItem("page", QString::number(pageSpin->value())); } - // page size - if (pageSizeSpin->value() != 50) { - query.addQueryItem("pageSize", QString::number(pageSizeSpin->value())); + // Min deck size + if (minDeckSizeSpin->value() != 50) { + query.addQueryItem("size", QString::number(minDeckSizeSpin->value())); } // build final URL diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.h b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.h index 05d8c9f2f..66837565c 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.h +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.h @@ -14,79 +14,231 @@ #include #include +/** Base API link for Archidekt deck search */ inline QString archidektApiLink = "https://archidekt.com/api/decks/v3/?name="; +/** + * @brief Tab for browsing, searching, and filtering Archidekt decks. + * + * This class provides a comprehensive interface for querying decks from the Archidekt API. + * Users can filter decks by name, owner, included cards, commanders, deck tags, colors, EDH bracket, + * and formats. It also provides sorting and pagination, as well as a card size adjustment widget. + */ class TabArchidekt : public Tab { Q_OBJECT public: + /** + * @brief Construct a new TabArchidekt object + * @param _tabSupervisor Parent tab supervisor responsible for tab management and callbacks + * + * Initializes the network manager, creates all UI components, sets up layouts, + * connects signals and slots, and triggers an initial fetch of top decks. + */ explicit TabArchidekt(TabSupervisor *_tabSupervisor); + /** + * @brief Update all UI text to reflect the current language or translation + * + * This function re-applies translations to all labels, buttons, and placeholders. + * It should be called after a language change. + */ void retranslateUi() override; + + /** + * @brief Construct the search URL from all current filters + * @return QString Fully constructed URL including all query parameters + * + * The search URL is dynamically built using the state of all filter widgets. + * Parameters included: + * - Deck name + * - Owner + * - Included cards + * - Commander cards + * - Deck tag + * - Colors and logical AND requirement + * - Formats + * - EDH bracket + * - Packages toggle + * - Sorting field and direction + * - Pagination (page and page size) + */ QString buildSearchUrl(); + + /** + * @brief Trigger a search using the current filters + * + * Sends a network request to the Archidekt API using the URL generated by buildSearchUrl(). + * Updates the current page display with results asynchronously. + */ void doSearch(); + + /** + * @brief Retrieve the tab display text + * @return QString Human-readable title for the tab + * + * If a card is pre-selected (cardToQuery), its name is appended to the tab title. + */ QString getTabText() const override { auto cardName = cardToQuery.isNull() ? QString() : cardToQuery->getName(); return tr("Archidekt: ") + cardName; } + /** + * @brief Get the card size slider widget + * @return CardSizeWidget* Pointer to the card size adjustment slider + * + * Allows external code to read or manipulate the current card size or hook up the sliders signals. + */ CardSizeWidget *getCardSizeSlider() { return cardSizeSlider; } + /** @brief Network manager for handling API requests */ QNetworkAccessManager *networkManager; public slots: + /** + * @brief Process a network reply containing JSON data + * @param reply QNetworkReply object with the API response + * + * Determines whether the response corresponds to a top decks query or a single deck, + * and dispatches it to the appropriate handler. + */ void processApiJson(QNetworkReply *reply); + + /** + * @brief Handle a JSON response containing multiple decks + * @param reply QJsonObject containing top deck listings + * + * Clears the previous page display and creates a new display widget for the results. + */ void processTopDecksResponse(QJsonObject reply); + + /** + * @brief Handle a JSON response for a single deck + * @param reply QJsonObject containing deck data + * + * Clears the previous page display and creates a new display widget for the deck details. + */ void processDeckResponse(QJsonObject reply); + + /** + * @brief Pretty-print a QJsonValue for debugging + * @param value The JSON value to print + * @param indentLevel The indentation depth (number of levels) + */ void prettyPrintJson(const QJsonValue &value, int indentLevel); + + /** + * @brief Navigate to a specified page URL + * @param url The URL to request + * + * Typically called when a navigation button is clicked in a deck listing. + */ void actNavigatePage(QString url); + + /** + * @brief Fetch top decks from the Archidekt API + * + * Called on initialization to populate the initial page display. + */ void getTopDecks(); private: - QWidget *container; - QWidget *navigationContainer; - QWidget *currentPageDisplay; - QVBoxLayout *mainLayout; - QHBoxLayout *navigationLayout; - QVBoxLayout *currentPageLayout; - // QLineEdit *searchBar; - QPushButton *searchPushButton; + // --------------------------------------------------------------------- + // Layout Containers + // --------------------------------------------------------------------- - // Required / basic fields - QLineEdit *nameField; // Deck name substring - QLineEdit *ownerField; // Owner substring + QWidget *container; ///< Root container for the entire tab + QVBoxLayout *mainLayout; ///< Outer vertical layout containing navigation and page display + QWidget *navigationContainer; ///< Container for all navigation/filter controls + QHBoxLayout *navigationLayout; ///< Layout for horizontal arrangement of filter widgets + QWidget *currentPageDisplay; ///< Widget containing the currently displayed deck(s) + QVBoxLayout *currentPageLayout; ///< Layout for deck display widgets - // Colors - QSet activeColors; - QCheckBox *logicalAndCheck; // Require ALL colors instead of ANY + // --------------------------------------------------------------------- + // Sorting Controls + // --------------------------------------------------------------------- - // Formats - QLabel *formatLabel; - SettingsButtonWidget *formatSettingsWidget; - QVector formatChecks; // Format checkboxes (13 total) + QComboBox *orderByCombo; ///< Dropdown for selecting the sort field + QPushButton *orderDirButton; ///< Toggle button for ascending/descending sort - QComboBox *edhBracketCombo; - QLineEdit *deckTagNameField; - QComboBox *orderByCombo; - QPushButton *orderDirButton; - QCheckBox *packagesCheck; - QSpinBox *pageSpin; + // --------------------------------------------------------------------- + // Color Filters + // --------------------------------------------------------------------- - // Page size - QLabel *pageSizeLabel; - QSpinBox *pageSizeSpin; + QSet activeColors; ///< Set of currently active mana colors + QCheckBox *logicalAndCheck; ///< Require ALL selected colors instead of ANY - // Cards and Commanders - QLineEdit *cardsField; // comma-separated quoted card names - QLineEdit *commandersField; // comma-separated quoted commander names + // --------------------------------------------------------------------- + // Format Filters + // --------------------------------------------------------------------- - SettingsButtonWidget *settingsButton; - CardSizeWidget *cardSizeSlider; - CardInfoPtr cardToQuery; + QLabel *formatLabel; ///< Label displaying "Formats" + SettingsButtonWidget *formatSettingsWidget; ///< Collapsible widget containing format checkboxes + QVector formatChecks; ///< Individual checkboxes for each format + + // --------------------------------------------------------------------- + // EDH Bracket / Package Toggle + // --------------------------------------------------------------------- + + QComboBox *edhBracketCombo; ///< Dropdown for EDH bracket selection + QCheckBox *packagesCheck; ///< Toggle for searching card packages instead of full decks + + // --------------------------------------------------------------------- + // Basic Search Fields + // --------------------------------------------------------------------- + + QLineEdit *nameField; ///< Input for deck name filter + QLineEdit *ownerField; ///< Input for owner name filter + + // --------------------------------------------------------------------- + // Card Filters + // --------------------------------------------------------------------- + + QLineEdit *cardsField; ///< Input for cards included in the deck (comma-separated) + QLineEdit *commandersField; ///< Input for commander cards (comma-separated) + + // --------------------------------------------------------------------- + // Deck Tag + // --------------------------------------------------------------------- + + QLineEdit *deckTagNameField; ///< Input for deck tag filtering + + // --------------------------------------------------------------------- + // Search Trigger + // --------------------------------------------------------------------- + + QPushButton *searchPushButton; ///< Button to trigger the search manually + + // --------------------------------------------------------------------- + // UI Settings (Card Size) + // --------------------------------------------------------------------- + + SettingsButtonWidget *settingsButton; ///< Container for additional UI settings + CardSizeWidget *cardSizeSlider; ///< Slider to adjust card size in results + + // --------------------------------------------------------------------- + // Minimum Cards in Deck + // --------------------------------------------------------------------- + + QLabel *minDeckSizeLabel; ///< Label for minimum number of cards per deck + QSpinBox *minDeckSizeSpin; ///< Spinner to select minimum deck size + + // --------------------------------------------------------------------- + // Pagination + // --------------------------------------------------------------------- + + QSpinBox *pageSpin; ///< Spinner to select the page number for results + + // --------------------------------------------------------------------- + // Optional Context + // --------------------------------------------------------------------- + + CardInfoPtr cardToQuery; ///< Optional pre-selected card for initial filtering }; #endif // COCKATRICE_TAB_ARCHIDEKT_H