Search bar, fancier display, resolve providerId

This commit is contained in:
Brübach, Lukas 2025-11-24 20:41:21 +01:00
parent 6edaa6db25
commit cbbe9e7da0
8 changed files with 274 additions and 25 deletions

View file

@ -19,6 +19,11 @@ public:
// Debug method for logging
void debugPrint() const;
QString getName() const
{
return userName;
}
private:
int id;
QString userName;

View file

@ -5,9 +5,7 @@
#include "../../../../cards/card_size_widget.h"
#include "../../../../cards/deck_card_zone_display_widget.h"
#include "../api_response/deck/archidekt_api_response_deck.h"
#include "custom_category_grouping_deck_list_proxy_model.h"
#include "deck_list_model.h"
#include "libcockatrice/card/database/card_database_manager.h"
#include <libcockatrice/card/database/card_database_manager.h>
#include <QSortFilterProxyModel>
@ -59,6 +57,8 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi
connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset);
model->getDeckList()->loadFromStream_Plain(deckStream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(model->getDeckList());
model->rebuildTree();
retranslateUi();

View file

@ -1,23 +1,122 @@
#include "archidekt_api_response_deck_entry_display_widget.h"
#include "../../../../cards/card_info_picture_with_text_overlay_widget.h"
#include <QLabel>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QHBoxLayout>
#include <QWidget>
#include <QPixmap>
ArchidektApiResponseDeckEntryDisplayWidget::ArchidektApiResponseDeckEntryDisplayWidget(
QWidget *parent,
ArchidektApiResponseDeckListingContainer _response)
: QWidget(parent), response(_response)
ArchidektApiResponseDeckListingContainer _response,
QNetworkAccessManager *_imageNetworkManager)
: QWidget(parent), response(_response), imageNetworkManager(_imageNetworkManager)
{
layout = new QHBoxLayout(this);
layout = new QVBoxLayout(this);
setLayout(layout);
// Add widgets for deck listings
this->setMaximumWidth(200);
this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
deckPreviewDisplayWidget = new CardInfoPictureWithTextOverlayWidget(this);
auto headerLayout = new QVBoxLayout();
QLabel *deckNameLabel = new QLabel(QString("%1 (%2)").arg(response.getName()).arg(response.getSize()));
deckNameLabel->setWordWrap(true); // Allow text to wrap
deckNameLabel->setStyleSheet("font-size: 14px; font-weight: bold;");
deckNameLabel->setMaximumWidth(200);
headerLayout->addWidget(deckNameLabel);
// Featured image (Deck Preview Image)
picture = new QLabel(this);
picture->setText("Loading Image...");
picture->setAlignment(Qt::AlignCenter);
picture->setMaximumWidth(200);
QNetworkRequest req(QUrl(response.getFeatured()));
imageNetworkManager->get(req);
connect(imageNetworkManager, &QNetworkAccessManager::finished,
this, &ArchidektApiResponseDeckEntryDisplayWidget::onPreviewImageLoadFinished);
headerLayout->addWidget(picture);
QLabel *ownerLabel = new QLabel(QString("Owner: %1").arg(response.getOwner().getName()));
ownerLabel->setWordWrap(true);
ownerLabel->setMaximumWidth(200);
headerLayout->addWidget(ownerLabel);
QLabel *edhBracketLabel = new QLabel(QString("EDH Bracket: %1").arg(response.getEDHBracket()));
edhBracketLabel->setWordWrap(true);
edhBracketLabel->setMaximumWidth(200);
headerLayout->addWidget(edhBracketLabel);
QLabel *viewCountLabel = new QLabel(QString("Views: %1").arg(response.getViewCount()));
viewCountLabel->setWordWrap(true);
viewCountLabel->setMaximumWidth(200);
headerLayout->addWidget(viewCountLabel);
QLabel *createdAtLabel = new QLabel(QString("Created: %1").arg(response.getCreatedAt()));
createdAtLabel->setWordWrap(true);
createdAtLabel->setMaximumWidth(200);
QLabel *updatedAtLabel = new QLabel(QString("Updated: %1").arg(response.getUpdatedAt()));
updatedAtLabel->setWordWrap(true);
updatedAtLabel->setMaximumWidth(200);
headerLayout->addWidget(createdAtLabel);
headerLayout->addWidget(updatedAtLabel);
QLabel *colorsLabel = new QLabel("Colors: ");
QStringList colorNames;
for (auto color : response.getColors().keys()) {
colorNames << color;
}
if (colorNames.isEmpty()) {
colorsLabel->setText("Colors: None");
} else {
colorsLabel->setText("Colors: " + colorNames.join(", "));
}
colorsLabel->setWordWrap(true);
colorsLabel->setMaximumWidth(200);
headerLayout->addWidget(colorsLabel);
layout->addLayout(headerLayout);
/*deckPreviewDisplayWidget = new CardInfoPictureWithTextOverlayWidget(this);
deckPreviewDisplayWidget->setOverlayText(QString("%1 (%2)").arg(response.getName()).arg(response.getSize()));
connect(deckPreviewDisplayWidget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
&ArchidektApiResponseDeckEntryDisplayWidget::actRequestNavigationToDeck);
layout->addWidget(deckPreviewDisplayWidget);*/
}
layout->addWidget(deckPreviewDisplayWidget);
void ArchidektApiResponseDeckEntryDisplayWidget::mousePressEvent(QMouseEvent *event)
{
actRequestNavigationToDeck();
}
void ArchidektApiResponseDeckEntryDisplayWidget::onPreviewImageLoadFinished(QNetworkReply *reply)
{
if (reply->url() != response.getFeatured()) {
return;
}
if (reply->error() != QNetworkReply::NoError) {
picture->setText("Error loading");
reply->deleteLater();
return;
}
QByteArray imageData = reply->readAll();
QPixmap pixmap;
if (pixmap.loadFromData(imageData)) {
picture->setPixmap(pixmap.scaled(150, 150, Qt::KeepAspectRatio, Qt::SmoothTransformation));
} else {
picture->setText("Invalid image");
}
reply->deleteLater();
}
void ArchidektApiResponseDeckEntryDisplayWidget::resizeEvent(QResizeEvent *event)

View file

@ -2,11 +2,11 @@
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_ENTRY_DISPLAY_WIDGET_H
#include "../../../../cards/card_info_picture_with_text_overlay_widget.h"
#include "../../../../general/layout_containers/flow_widget.h"
#include "../api_response/deck_listings/archidekt_api_response_deck_listing_container.h"
#include <QLabel>
#include <QNetworkAccessManager>
#include <QResizeEvent>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
@ -19,15 +19,22 @@ signals:
public:
explicit ArchidektApiResponseDeckEntryDisplayWidget(QWidget *parent,
ArchidektApiResponseDeckListingContainer response);
ArchidektApiResponseDeckListingContainer response,
QNetworkAccessManager *imageNetworkManager);
void onPreviewImageLoadFinished(QNetworkReply *reply);
void resizeEvent(QResizeEvent *event) override;
public slots:
void actRequestNavigationToDeck();
protected:
void mousePressEvent(QMouseEvent *event) override;
private:
QHBoxLayout *layout;
QVBoxLayout *layout;
ArchidektApiResponseDeckListingContainer response;
QNetworkAccessManager *imageNetworkManager;
QLabel *picture;
CardInfoPictureWithTextOverlayWidget *deckPreviewDisplayWidget;
};

View file

@ -13,10 +13,17 @@ ArchidektApiResponseDeckListingsDisplayWidget::ArchidektApiResponseDeckListingsD
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
imageNetworkManager = new QNetworkAccessManager(this);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
imageNetworkManager->setTransferTimeout(); // Use Qt's default timeout
#endif
imageNetworkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
// Add widgets for deck listings
auto deckListings = response.results;
for (const auto &deckListing : deckListings) {
auto cardListDisplayWidget = new ArchidektApiResponseDeckEntryDisplayWidget(this, deckListing);
auto cardListDisplayWidget = new ArchidektApiResponseDeckEntryDisplayWidget(this, deckListing, imageNetworkManager);
connect(cardListDisplayWidget, &ArchidektApiResponseDeckEntryDisplayWidget::requestNavigation, this,
&ArchidektApiResponseDeckListingsDisplayWidget::requestNavigation);
flowWidget->addWidget(cardListDisplayWidget);

View file

@ -4,6 +4,7 @@
#include "../../../../general/layout_containers/flow_widget.h"
#include "../api_response/archidekt_deck_listing_api_response.h"
#include <QNetworkAccessManager>
#include <QResizeEvent>
#include <QScrollArea>
#include <QVBoxLayout>
@ -23,6 +24,7 @@ public:
private:
QHBoxLayout *layout;
FlowWidget *flowWidget;
QNetworkAccessManager *imageNetworkManager;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_LISTINGS_DISPLAY_WIDGET_H

View file

@ -16,6 +16,7 @@
#include <QPushButton>
#include <QRegularExpression>
#include <QResizeEvent>
#include <QUrlQuery>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/models/database/card/card_completer_proxy_model.h>
#include <libcockatrice/models/database/card/card_search_model.h>
@ -77,6 +78,58 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
searchPushButton = new QPushButton(navigationContainer);
connect(searchPushButton, &QPushButton::clicked, this, [=, this]() { doSearch(); });
searchOptionsContainer = new QWidget(container);
searchOptionsLayout = new QHBoxLayout(searchOptionsContainer);
mainLayout->addWidget(searchOptionsContainer);
nameField = new QLineEdit(searchOptionsContainer);
nameField->setPlaceholderText(tr("Deck name contains..."));
searchOptionsLayout->addWidget(nameField);
ownerField = new QLineEdit(searchOptionsContainer);
ownerField->setPlaceholderText(tr("Owner name contains..."));
searchOptionsLayout->addWidget(ownerField);
QHBoxLayout *colorLayout = new QHBoxLayout();
QStringList colors = {"White", "Blue", "Black", "Green", "Red", "Colorless"};
for (const auto &c : colors) {
QCheckBox *chk = new QCheckBox(c, searchOptionsContainer);
colorChecks << chk;
colorLayout->addWidget(chk);
}
searchOptionsLayout->addLayout(colorLayout);
logicalAndCheck = new QCheckBox("Require ALL colors", searchOptionsContainer);
searchOptionsLayout->addWidget(logicalAndCheck);
QHBoxLayout *formatLayout = new QHBoxLayout();
QStringList formatNames = {"Standard", "Modern", "Commander", "Legacy", "Vintage",
"Pauper", "Custom", "Frontier", "Future Std", "Penny Dreadful",
"1v1 Commander", "Dual Commander", "Brawl"};
for (int i = 0; i < formatNames.size(); ++i) {
QCheckBox *formatCheckBox = new QCheckBox(formatNames[i], searchOptionsContainer);
formatChecks << formatCheckBox;
formatLayout->addWidget(formatCheckBox);
}
searchOptionsLayout->addLayout(formatLayout);
pageSizeSpin = new QSpinBox(searchOptionsContainer);
pageSizeSpin->setRange(1, 500);
pageSizeSpin->setValue(50);
searchOptionsLayout->addWidget(pageSizeSpin);
cardsField = new QLineEdit(searchOptionsContainer);
cardsField->setPlaceholderText("Cards (comma separated)");
searchOptionsLayout->addWidget(cardsField);
commandersField = new QLineEdit(searchOptionsContainer);
commandersField->setPlaceholderText("Commanders (comma separated)");
searchOptionsLayout->addWidget(commandersField);
settingsButton = new SettingsButtonWidget(this);
cardSizeSlider = new CardSizeWidget(this);
@ -97,7 +150,8 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
// Ensure navigation stays at the top and currentPageDisplay takes remaining space
mainLayout->setStretch(0, 0); // navigationContainer gets minimum space
mainLayout->setStretch(1, 1); // currentPageDisplay expands as much as possible
mainLayout->setStretch(1, 0);
mainLayout->setStretch(2, 1); // currentPageDisplay expands as much as possible
setCentralWidget(container);
@ -113,14 +167,61 @@ void TabArchidekt::retranslateUi()
searchPushButton->setText(tr("Search"));
}
QString TabArchidekt::buildSearchUrl()
{
QUrlQuery query;
// required
query.addQueryItem("name", nameField->text());
// colors
QStringList selectedColors;
for (auto *chk : colorChecks)
if (chk->isChecked())
selectedColors << chk->text();
if (!selectedColors.isEmpty())
query.addQueryItem("colors", selectedColors.join(","));
// logicalAnd
if (logicalAndCheck->isChecked())
query.addQueryItem("logicalAnd", "true");
// owner
if (!ownerField->text().isEmpty())
query.addQueryItem("owner", ownerField->text());
// cards
if (!cardsField->text().isEmpty())
query.addQueryItem("cards", cardsField->text());
// commanders
if (!commandersField->text().isEmpty())
query.addQueryItem("commanders", commandersField->text());
// formats
QStringList formatIds;
for (int i = 0; i < formatChecks.size(); ++i)
if (formatChecks[i]->isChecked())
formatIds << QString::number(i + 1);
if (!formatIds.isEmpty())
query.addQueryItem("formats", formatIds.join(","));
// page size
if (pageSizeSpin->value() != 50)
query.addQueryItem("pageSize", QString::number(pageSizeSpin->value()));
// build final URL
QUrl url("https://archidekt.com/api/decks/v3/");
url.setQuery(query);
return url.toString();
}
void TabArchidekt::doSearch()
{
CardInfoPtr searchedCard = CardDatabaseManager::query()->getCardInfo(searchBar->text());
if (!searchedCard) {
return;
}
// setCard(searchedCard, canBeCommander(searchedCard));
QString url = buildSearchUrl();
QNetworkRequest req{QUrl(url)};
networkManager->get(req);
}
void TabArchidekt::actNavigatePage(QString url)
@ -160,7 +261,7 @@ void TabArchidekt::processApiJson(QNetworkReply *reply)
QString responseUrl = reply->url().toString();
// Check if the response URL matches a commander request
if (responseUrl == "https://archidekt.com/api/decks/v3/?name=") {
if (responseUrl.startsWith("https://archidekt.com/api/decks/v3/")) {
processTopDecksResponse(jsonObj);
} else if (responseUrl.startsWith("https://archidekt.com/api/decks/")) {
processDeckResponse(jsonObj);
@ -197,7 +298,8 @@ void TabArchidekt::processTopDecksResponse(QJsonObject reply)
// **Ensure layout stays correct**
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
mainLayout->setStretch(1, 0); // Keep searchOptionsContainer at the top
mainLayout->setStretch(2, 1); // Make sure currentPageDisplay takes remaining space
}
void TabArchidekt::processDeckResponse(QJsonObject reply)
@ -227,7 +329,8 @@ void TabArchidekt::processDeckResponse(QJsonObject reply)
// **Ensure layout stays correct**
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
mainLayout->setStretch(1, 0); // Keep searchOptionsContainer at the top
mainLayout->setStretch(2, 1); // Make sure currentPageDisplay takes remaining space
}
void TabArchidekt::prettyPrintJson(const QJsonValue &value, int indentLevel)

View file

@ -5,10 +5,12 @@
#include "../../interface/widgets/quick_settings/settings_button_widget.h"
#include "../../tab.h"
#include <QCheckBox>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QNetworkAccessManager>
#include <QPushButton>
#include <QSpinBox>
#include <QString>
#include <libcockatrice/card/database/card_database.h>
@ -21,6 +23,7 @@ public:
explicit TabArchidekt(TabSupervisor *_tabSupervisor);
void retranslateUi() override;
QString buildSearchUrl();
void doSearch();
QString getTabText() const override
{
@ -53,6 +56,29 @@ private:
QPushButton *decksPushButton;
QLineEdit *searchBar;
QPushButton *searchPushButton;
// --- Search options UI ---
QWidget *searchOptionsContainer;
QHBoxLayout *searchOptionsLayout;
// Required / basic fields
QLineEdit *nameField; // Deck name substring
QLineEdit *ownerField; // Owner substring
// Colors
QVector<QCheckBox*> colorChecks; // White, Blue, Black, Green, Red, Colorless
QCheckBox *logicalAndCheck; // Require ALL colors instead of ANY
// Formats
QVector<QCheckBox*> formatChecks; // Format checkboxes (13 total)
// Page size
QSpinBox *pageSizeSpin;
// Cards and Commanders
QLineEdit *cardsField; // comma-separated quoted card names
QLineEdit *commandersField; // comma-separated quoted commander names
SettingsButtonWidget *settingsButton;
CardSizeWidget *cardSizeSlider;
CardInfoPtr cardToQuery;