mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-09 01:23:57 -07:00
[Feature] TabArchidekt and Archidekt API integration (#6348)
* TabArchidekt and Archidekt API integration. Took 37 seconds Took 4 minutes Took 40 seconds Took 4 minutes * Lint. * Lont. * Search bar, fancier display, resolve providerId * Delegate click to base. * Be explicit for pedantic compilers. * Liiint. * Leave them default I guess * Leave them default I guess * Small fixes. * New utility display widgets. * New style for deck listing. * Lint. * Lont. * Scale things. * Delegate paint to base. * Use default Archidekt preview image for decks without featured. * Consistent sizes. * Increase font size, qt version guard. * More version guards. * Clean up filter layout, use mana symbols. * Set content margins. * Refresh on filter change. * Lint. * Better elision. * Query actual new endpoints, new query parameters. * Doxygen, reorder fields in constructor, readability. * Update page size doc to min size. * Update initial min deck size value. * Add label to page selection. * Okay, so, people upload a lot of 1 card decks frequently. * Whoops. * Add a selection combobox for sorting logic. * Debounce and limit searches. * Include. * Lint. * Don't imply that Archidekt supports multiple cards/commander names. * Let's not lambda it and slot it instead. * Overload. * Add button to home tab. Took 8 minutes * Adjust to selection model change. Took 5 minutes * Cleanup auto-generated comments. Took 8 minutes * Remember card sizes. Took 1 minute * Initialize with correct size. Took 3 minutes * Use correct placeholders. Took 2 minutes * Style lint. Took 16 minutes * Parse double-faced cards correctly. * Parse double-faced cards correctly. * Allow TabArchidekt to use VDE group/sort/display buttons * Lint. * Indicate that things are clickable. * Min treshold for nicer display. * Lint. * We have good labels at home. * We do a little linting. * Qt version guards. * Qt5 is the devil. * Update comments. * Lint comments. * More doxys. * One more doxy. * Lint. * Update. * Small fixes. Took 7 minutes Took 13 seconds --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
de13c22552
commit
eab4d435f8
43 changed files with 3285 additions and 141 deletions
|
|
@ -0,0 +1,562 @@
|
|||
#include "tab_archidekt.h"
|
||||
|
||||
#include "../../../../../client/settings/cache_settings.h"
|
||||
#include "../../../cards/additional_info/mana_symbol_widget.h"
|
||||
#include "../../tab_supervisor.h"
|
||||
#include "api_response/archidekt_deck_listing_api_response.h"
|
||||
#include "display/archidekt_api_response_deck_display_widget.h"
|
||||
#include "display/archidekt_api_response_deck_listings_display_widget.h"
|
||||
|
||||
#include <QCompleter>
|
||||
#include <QDebug>
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#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>
|
||||
|
||||
TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
networkManager->setTransferTimeout(); // Use Qt's default timeout
|
||||
#endif
|
||||
|
||||
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
||||
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *)));
|
||||
|
||||
searchDebounceTimer = new QTimer(this);
|
||||
searchDebounceTimer->setSingleShot(true); // We only want it to fire once after inactivity
|
||||
searchDebounceTimer->setInterval(300); // 300ms debounce
|
||||
|
||||
connect(searchDebounceTimer, &QTimer::timeout, this, [this]() { doSearchImmediate(); });
|
||||
|
||||
container = new QWidget(this);
|
||||
mainLayout = new QVBoxLayout(container);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
container->setLayout(mainLayout);
|
||||
|
||||
navigationContainer = new QWidget(container);
|
||||
navigationContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
navigationLayout = new QHBoxLayout(navigationContainer);
|
||||
navigationLayout->setSpacing(3);
|
||||
navigationContainer->setLayout(navigationLayout);
|
||||
|
||||
// Sort by
|
||||
|
||||
orderByCombo = new QComboBox(navigationContainer);
|
||||
orderByCombo->addItems({"name", "updatedAt", "createdAt", "viewCount", "size", "edhBracket"});
|
||||
orderByCombo->setCurrentText("updatedAt"); // Pre-select updatedAt
|
||||
|
||||
// Asc/Desc toggle
|
||||
orderDirButton = new QPushButton(tr("Desc."), navigationContainer);
|
||||
orderDirButton->setCheckable(true); // checked = DESC, unchecked = ASC
|
||||
orderDirButton->setChecked(true);
|
||||
|
||||
connect(orderByCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
|
||||
connect(orderDirButton, &QPushButton::clicked, this, [this](bool checked) {
|
||||
orderDirButton->setText(checked ? tr("Desc.") : tr("Asc."));
|
||||
doSearch();
|
||||
});
|
||||
|
||||
// Colors
|
||||
QHBoxLayout *colorLayout = new QHBoxLayout();
|
||||
QString colorIdentity = "WUBRG"; // Optionally include "C" for colorless once we have a symbol for it
|
||||
|
||||
for (const QChar &color : colorIdentity) {
|
||||
auto *manaSymbol = new ManaSymbolWidget(navigationContainer, color, false, true);
|
||||
manaSymbol->setFixedWidth(25);
|
||||
colorLayout->addWidget(manaSymbol);
|
||||
|
||||
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this, [this](QChar c, bool active) {
|
||||
if (active) {
|
||||
activeColors.insert(c);
|
||||
} else {
|
||||
activeColors.remove(c);
|
||||
}
|
||||
doSearch();
|
||||
});
|
||||
}
|
||||
|
||||
logicalAndCheck = new QCheckBox("Require ALL colors", navigationContainer);
|
||||
|
||||
// Formats
|
||||
|
||||
formatLabel = new QLabel(this);
|
||||
|
||||
formatSettingsWidget = new SettingsButtonWidget(this);
|
||||
|
||||
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], navigationContainer);
|
||||
connect(formatCheckBox, &QCheckBox::clicked, this, &TabArchidekt::doSearch);
|
||||
formatChecks << formatCheckBox;
|
||||
formatSettingsWidget->addSettingsWidget(formatCheckBox);
|
||||
}
|
||||
|
||||
// EDH Bracket
|
||||
edhBracketCombo = new QComboBox(navigationContainer);
|
||||
edhBracketCombo->addItem(tr("Any Bracket"));
|
||||
edhBracketCombo->addItems({"1", "2", "3", "4", "5"});
|
||||
|
||||
connect(edhBracketCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
|
||||
|
||||
// Search for Card Packages instead of Decks
|
||||
packagesCheck = new QCheckBox("Packages", navigationContainer);
|
||||
|
||||
connect(packagesCheck, &QCheckBox::clicked, this, [this]() {
|
||||
bool disable = packagesCheck->isChecked();
|
||||
for (auto *cb : formatChecks)
|
||||
cb->setEnabled(!disable);
|
||||
commandersField->setEnabled(!disable);
|
||||
deckTagNameField->setEnabled(!disable);
|
||||
edhBracketCombo->setCurrentIndex(0);
|
||||
edhBracketCombo->setEnabled(!disable);
|
||||
doSearch();
|
||||
});
|
||||
|
||||
// Deck Name
|
||||
nameField = new QLineEdit(navigationContainer);
|
||||
nameField->setPlaceholderText(tr("Deck name contains..."));
|
||||
|
||||
// Owner Name
|
||||
ownerField = new QLineEdit(navigationContainer);
|
||||
ownerField->setPlaceholderText(tr("Owner name contains..."));
|
||||
|
||||
// Contained cards
|
||||
cardsField = new QLineEdit(navigationContainer);
|
||||
cardsField->setPlaceholderText("Deck contains card...");
|
||||
|
||||
// Commanders
|
||||
commandersField = new QLineEdit(navigationContainer);
|
||||
commandersField->setPlaceholderText("Deck has commander...");
|
||||
|
||||
// DB supplemented card search
|
||||
auto cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
||||
auto displayModel = new CardDatabaseDisplayModel(this);
|
||||
displayModel->setSourceModel(cardDatabaseModel);
|
||||
auto *searchModel = new CardSearchModel(displayModel, this);
|
||||
|
||||
auto *proxyModel = new CardCompleterProxyModel(this);
|
||||
proxyModel->setSourceModel(searchModel);
|
||||
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
proxyModel->setFilterRole(Qt::DisplayRole);
|
||||
|
||||
auto *completer = new QCompleter(proxyModel, this);
|
||||
completer->setCompletionRole(Qt::DisplayRole);
|
||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setFilterMode(Qt::MatchContains);
|
||||
completer->setMaxVisibleItems(10);
|
||||
|
||||
cardsField->setCompleter(completer);
|
||||
commandersField->setCompleter(completer);
|
||||
|
||||
connect(cardsField, &QLineEdit::textChanged, searchModel, &CardSearchModel::updateSearchResults);
|
||||
|
||||
connect(cardsField, &QLineEdit::textChanged, this, [=](const QString &text) {
|
||||
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
|
||||
proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
|
||||
if (!text.isEmpty())
|
||||
completer->complete();
|
||||
});
|
||||
|
||||
connect(commandersField, &QLineEdit::textChanged, searchModel, &CardSearchModel::updateSearchResults);
|
||||
|
||||
connect(commandersField, &QLineEdit::textChanged, this, [=](const QString &text) {
|
||||
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
|
||||
proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
|
||||
if (!text.isEmpty())
|
||||
completer->complete();
|
||||
});
|
||||
|
||||
// Tag Name
|
||||
deckTagNameField = new QLineEdit(navigationContainer);
|
||||
deckTagNameField->setPlaceholderText("Deck tag");
|
||||
|
||||
connect(deckTagNameField, &QLineEdit::textChanged, this, &TabArchidekt::doSearch);
|
||||
|
||||
// Search button
|
||||
searchPushButton = new QPushButton(navigationContainer);
|
||||
searchPushButton->setText("Search");
|
||||
|
||||
connect(searchPushButton, &QPushButton::clicked, this, &TabArchidekt::doSearch);
|
||||
|
||||
// Card Size settings
|
||||
settingsButton = new SettingsButtonWidget(this);
|
||||
cardSizeSlider = new CardSizeWidget(this, nullptr, SettingsCache::instance().getArchidektPreviewSize());
|
||||
connect(cardSizeSlider, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance(),
|
||||
&SettingsCache::setArchidektPreviewCardSize);
|
||||
settingsButton->addSettingsWidget(cardSizeSlider);
|
||||
|
||||
// Min deck size
|
||||
minDeckSizeLabel = new QLabel(navigationContainer);
|
||||
|
||||
minDeckSizeSpin = new QSpinBox(navigationContainer);
|
||||
minDeckSizeSpin->setSpecialValueText(tr("Disabled"));
|
||||
minDeckSizeSpin->setRange(0, 200);
|
||||
minDeckSizeSpin->setValue(0);
|
||||
|
||||
// Size logic
|
||||
minDeckSizeLogicCombo = new QComboBox(navigationContainer);
|
||||
minDeckSizeLogicCombo->addItems({"Exact", "≥", "≤"}); // Exact = unset, ≥ = GTE, ≤ = LTE
|
||||
minDeckSizeLogicCombo->setCurrentIndex(1); // default GTE
|
||||
|
||||
connect(minDeckSizeSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
||||
connect(minDeckSizeLogicCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
|
||||
|
||||
// Page number
|
||||
pageLabel = new QLabel(navigationContainer);
|
||||
|
||||
pageSpin = new QSpinBox(navigationContainer);
|
||||
pageSpin->setRange(1, 9999);
|
||||
pageSpin->setValue(1);
|
||||
|
||||
connect(pageSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
||||
|
||||
// Page display
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
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);
|
||||
|
||||
// Min. # of cards in deck
|
||||
navigationLayout->addWidget(minDeckSizeLabel);
|
||||
navigationLayout->addWidget(minDeckSizeSpin);
|
||||
navigationLayout->addWidget(minDeckSizeLogicCombo);
|
||||
|
||||
// Page number
|
||||
navigationLayout->addWidget(pageLabel);
|
||||
navigationLayout->addWidget(pageSpin);
|
||||
|
||||
mainLayout->addWidget(navigationContainer);
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// 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
|
||||
|
||||
setCentralWidget(container);
|
||||
|
||||
TabArchidekt::retranslateUi();
|
||||
|
||||
getTopDecks();
|
||||
}
|
||||
|
||||
void TabArchidekt::retranslateUi()
|
||||
{
|
||||
searchPushButton->setText(tr("Search"));
|
||||
formatLabel->setText(tr("Formats"));
|
||||
minDeckSizeLabel->setText(tr("Min. # of Cards:"));
|
||||
pageLabel->setText(tr("Page:"));
|
||||
}
|
||||
|
||||
QString TabArchidekt::buildSearchUrl()
|
||||
{
|
||||
QUrlQuery query;
|
||||
|
||||
// orderBy (field + direction)
|
||||
{
|
||||
QString field = orderByCombo->currentText();
|
||||
if (!field.isEmpty()) {
|
||||
bool desc = orderDirButton->isChecked();
|
||||
QString final = desc ? "-" + field : field;
|
||||
query.addQueryItem("orderBy", final);
|
||||
}
|
||||
}
|
||||
|
||||
// Colors
|
||||
QStringList selectedColors;
|
||||
for (QChar c : activeColors) {
|
||||
selectedColors.append(c);
|
||||
}
|
||||
if (!selectedColors.isEmpty()) {
|
||||
query.addQueryItem("colors", selectedColors.join(","));
|
||||
}
|
||||
|
||||
// logicalAnd
|
||||
if (logicalAndCheck->isChecked()) {
|
||||
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());
|
||||
}
|
||||
|
||||
// cards
|
||||
if (!cardsField->text().isEmpty()) {
|
||||
query.addQueryItem("cardName", cardsField->text());
|
||||
}
|
||||
|
||||
// Commander Name
|
||||
if (!packagesCheck->isChecked()) {
|
||||
if (!commandersField->text().isEmpty()) {
|
||||
query.addQueryItem("commanderName", commandersField->text());
|
||||
}
|
||||
}
|
||||
|
||||
// deckTagName
|
||||
if (!packagesCheck->isChecked()) {
|
||||
if (!deckTagNameField->text().isEmpty()) {
|
||||
query.addQueryItem("deckTagName", deckTagNameField->text());
|
||||
}
|
||||
}
|
||||
|
||||
// page number
|
||||
if (pageSpin->value() <= 1) {
|
||||
query.addQueryItem("page", QString::number(pageSpin->value()));
|
||||
}
|
||||
|
||||
// Min deck size
|
||||
if (minDeckSizeSpin->value() != 0) {
|
||||
query.addQueryItem("size", QString::number(minDeckSizeSpin->value()));
|
||||
|
||||
QString logic = "GTE"; // default
|
||||
QString selected = minDeckSizeLogicCombo->currentText();
|
||||
if (selected == "≥")
|
||||
logic = "GTE";
|
||||
else if (selected == "≤")
|
||||
logic = "LTE";
|
||||
else
|
||||
logic = ""; // Exact = unset
|
||||
|
||||
if (!logic.isEmpty()) {
|
||||
query.addQueryItem("sizeLogic", logic);
|
||||
}
|
||||
}
|
||||
|
||||
// build final URL
|
||||
QUrl url("https://archidekt.com/api/decks/v3/");
|
||||
url.setQuery(query);
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
void TabArchidekt::doSearch()
|
||||
{
|
||||
searchDebounceTimer->start();
|
||||
}
|
||||
|
||||
void TabArchidekt::doSearchImmediate()
|
||||
{
|
||||
QString url = buildSearchUrl();
|
||||
QNetworkRequest req{QUrl(url)};
|
||||
networkManager->get(req);
|
||||
}
|
||||
|
||||
void TabArchidekt::actNavigatePage(QString url)
|
||||
{
|
||||
QNetworkRequest request{QUrl(url)};
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabArchidekt::getTopDecks()
|
||||
{
|
||||
QNetworkRequest request{QUrl(buildSearchUrl())};
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabArchidekt::processApiJson(QNetworkReply *reply)
|
||||
{
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qDebug() << "Network error occurred:" << reply->errorString();
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
|
||||
|
||||
if (!jsonDoc.isObject()) {
|
||||
qDebug() << "Invalid JSON response received.";
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject jsonObj = jsonDoc.object();
|
||||
|
||||
// Get the actual URL from the reply
|
||||
QString responseUrl = reply->url().toString();
|
||||
|
||||
// Check if the response URL matches a commander request
|
||||
if (responseUrl.startsWith("https://archidekt.com/api/decks/v3/")) {
|
||||
processTopDecksResponse(jsonObj);
|
||||
} else if (responseUrl.startsWith("https://archidekt.com/api/decks/")) {
|
||||
processDeckResponse(jsonObj);
|
||||
} else {
|
||||
prettyPrintJson(jsonObj, 4);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void TabArchidekt::processTopDecksResponse(QJsonObject reply)
|
||||
{
|
||||
ArchidektDeckListingApiResponse deckData;
|
||||
deckData.fromJson(reply);
|
||||
|
||||
// **Remove previous page display to prevent stacking**
|
||||
if (currentPageDisplay) {
|
||||
mainLayout->removeWidget(currentPageDisplay);
|
||||
delete currentPageDisplay;
|
||||
currentPageDisplay = nullptr;
|
||||
}
|
||||
|
||||
// **Create new currentPageDisplay**
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
currentPageDisplay->setLayout(currentPageLayout);
|
||||
|
||||
auto display = new ArchidektApiResponseDeckListingsDisplayWidget(currentPageDisplay, deckData, cardSizeSlider);
|
||||
connect(display, &ArchidektApiResponseDeckListingsDisplayWidget::requestNavigation, this,
|
||||
&TabArchidekt::actNavigatePage);
|
||||
currentPageLayout->addWidget(display);
|
||||
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// **Ensure layout stays correct**
|
||||
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
|
||||
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
|
||||
}
|
||||
|
||||
void TabArchidekt::processDeckResponse(QJsonObject reply)
|
||||
{
|
||||
ArchidektApiResponseDeck deckData;
|
||||
deckData.fromJson(reply);
|
||||
|
||||
// **Remove previous page display to prevent stacking**
|
||||
if (currentPageDisplay) {
|
||||
mainLayout->removeWidget(currentPageDisplay);
|
||||
delete currentPageDisplay;
|
||||
currentPageDisplay = nullptr;
|
||||
}
|
||||
|
||||
// **Create new currentPageDisplay**
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
currentPageDisplay->setLayout(currentPageLayout);
|
||||
|
||||
auto display = new ArchidektApiResponseDeckDisplayWidget(currentPageDisplay, deckData, cardSizeSlider);
|
||||
connect(display, &ArchidektApiResponseDeckDisplayWidget::requestNavigation, this, &TabArchidekt::actNavigatePage);
|
||||
connect(display, &ArchidektApiResponseDeckDisplayWidget::openInDeckEditor, tabSupervisor,
|
||||
&TabSupervisor::openDeckInNewTab);
|
||||
currentPageLayout->addWidget(display);
|
||||
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// **Ensure layout stays correct**
|
||||
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
|
||||
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
|
||||
}
|
||||
|
||||
void TabArchidekt::prettyPrintJson(const QJsonValue &value, int indentLevel)
|
||||
{
|
||||
const QString indent(indentLevel * 2, ' '); // Adjust spacing as needed for pretty printing
|
||||
|
||||
if (value.isObject()) {
|
||||
QJsonObject obj = value.toObject();
|
||||
for (auto it = obj.begin(); it != obj.end(); ++it) {
|
||||
qDebug().noquote() << indent + it.key() + ":";
|
||||
prettyPrintJson(it.value(), indentLevel + 1);
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
QJsonArray array = value.toArray();
|
||||
for (int i = 0; i < array.size(); ++i) {
|
||||
qDebug().noquote() << indent + QString("[%1]:").arg(i);
|
||||
prettyPrintJson(array[i], indentLevel + 1);
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
qDebug().noquote() << indent + "\"" + value.toString() + "\"";
|
||||
} else if (value.isDouble()) {
|
||||
qDebug().noquote() << indent + QString::number(value.toDouble());
|
||||
} else if (value.isBool()) {
|
||||
qDebug().noquote() << indent + (value.toBool() ? "true" : "false");
|
||||
} else if (value.isNull()) {
|
||||
qDebug().noquote() << indent + "null";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue