Revert UI.

Took 25 minutes

Took 5 seconds
This commit is contained in:
Lukas Brübach 2026-05-12 19:51:37 +02:00
parent fe12f4cbb9
commit 135beab531
174 changed files with 14 additions and 15702 deletions

View file

@ -51,7 +51,6 @@ set(cockatrice_SOURCES
src/interface/widgets/dialogs/dlg_view_log.cpp src/interface/widgets/dialogs/dlg_view_log.cpp
src/interface/widgets/dialogs/override_printing_warning.cpp src/interface/widgets/dialogs/override_printing_warning.cpp
src/interface/widgets/dialogs/tip_of_the_day.cpp src/interface/widgets/dialogs/tip_of_the_day.cpp
src/filters/deck_filter_string.cpp
src/filters/filter_builder.cpp src/filters/filter_builder.cpp
src/filters/filter_tree_model.cpp src/filters/filter_tree_model.cpp
src/filters/syntax_help.cpp src/filters/syntax_help.cpp
@ -145,33 +144,6 @@ set(cockatrice_SOURCES
src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
src/interface/widgets/cards/card_info_text_widget.cpp src/interface/widgets/cards/card_info_text_widget.cpp
src/interface/widgets/cards/card_size_widget.cpp src/interface/widgets/cards/card_size_widget.cpp
src/interface/widgets/cards/deck_card_zone_display_widget.cpp
src/interface/widgets/cards/deck_preview_card_picture_widget.cpp
src/interface/widgets/deck_analytics/abstract_analytics_panel_widget.cpp
src/interface/widgets/deck_analytics/add_analytics_panel_dialog.cpp
src/interface/widgets/deck_analytics/analytics_panel_widget_factory.cpp
src/interface/widgets/deck_analytics/analytics_panel_widget_registrar.cpp
src/interface/widgets/deck_analytics/deck_analytics_widget.cpp
src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp
src/interface/widgets/deck_analytics/resizable_panel.cpp
src/interface/widgets/deck_analytics/analyzer_modules/draw_probability/draw_probability_config.cpp
src/interface/widgets/deck_analytics/analyzer_modules/draw_probability/draw_probability_config_dialog.cpp
src/interface/widgets/deck_analytics/analyzer_modules/draw_probability/draw_probability_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_config.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_config_dialog.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_config.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_config_dialog.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_config.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_config_dialog.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_config.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_config_dialog.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_single_display_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp
src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_database_dock_widget.cpp src/interface/widgets/deck_editor/deck_editor_card_database_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
@ -182,7 +154,6 @@ set(cockatrice_SOURCES
src/interface/widgets/deck_editor/deck_list_style_proxy.cpp src/interface/widgets/deck_editor/deck_list_style_proxy.cpp
src/interface/widgets/deck_editor/deck_state_manager.cpp src/interface/widgets/deck_editor/deck_state_manager.cpp
src/interface/widgets/deck_editor/printing_disabled_info_widget.cpp src/interface/widgets/deck_editor/printing_disabled_info_widget.cpp
src/interface/widgets/general/background_sources.cpp
src/interface/widgets/general/display/background_plate_widget.cpp src/interface/widgets/general/display/background_plate_widget.cpp
src/interface/widgets/general/display/banner_widget.cpp src/interface/widgets/general/display/banner_widget.cpp
src/interface/widgets/general/display/dynamic_font_size_label.cpp src/interface/widgets/general/display/dynamic_font_size_label.cpp
@ -196,8 +167,6 @@ set(cockatrice_SOURCES
src/interface/widgets/general/display/charts/bars/bar_chart_background_widget.cpp src/interface/widgets/general/display/charts/bars/bar_chart_background_widget.cpp
src/interface/widgets/general/display/charts/bars/segmented_bar_widget.cpp src/interface/widgets/general/display/charts/bars/segmented_bar_widget.cpp
src/interface/widgets/general/display/charts/pies/color_pie.cpp src/interface/widgets/general/display/charts/pies/color_pie.cpp
src/interface/widgets/general/home_styled_button.cpp
src/interface/widgets/general/home_widget.cpp
src/interface/widgets/general/layout_containers/flow_widget.cpp src/interface/widgets/general/layout_containers/flow_widget.cpp
src/interface/widgets/general/layout_containers/overlap_control_widget.cpp src/interface/widgets/general/layout_containers/overlap_control_widget.cpp
src/interface/widgets/general/layout_containers/overlap_widget.cpp src/interface/widgets/general/layout_containers/overlap_widget.cpp
@ -234,99 +203,23 @@ set(cockatrice_SOURCES
src/interface/widgets/utility/sequence_edit.cpp src/interface/widgets/utility/sequence_edit.cpp
src/interface/widgets/utility/visibility_change_listener.cpp src/interface/widgets/utility/visibility_change_listener.cpp
src/interface/widgets/utility/visibility_change_listener.h src/interface/widgets/utility/visibility_change_listener.h
src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_format_legality_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_widget.cpp
src/interface/widgets/visual_database_display/visual_database_filter_display_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_placeholder_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_item_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
src/interface/window_main.cpp src/interface/window_main.cpp
src/main.cpp src/main.cpp
src/interface/widgets/tabs/abstract_tab_deck_editor.cpp src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp
src/interface/widgets/tabs/api/archidekt/api_response/archidekt_deck_listing_api_response.cpp
src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card_entry.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_edition.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck_category.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_listing_container.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_owner.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_deck_preview_image_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
src/interface/widgets/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp
src/interface/widgets/tabs/tab.cpp src/interface/widgets/tabs/tab.cpp
src/interface/widgets/tabs/tab_account.cpp src/interface/widgets/tabs/tab_account.cpp
src/interface/widgets/tabs/tab_admin.cpp src/interface/widgets/tabs/tab_admin.cpp
src/interface/widgets/tabs/tab_deck_editor.cpp src/interface/widgets/tabs/tab_deck_editor.cpp
src/interface/widgets/tabs/tab_deck_storage.cpp src/interface/widgets/tabs/tab_deck_storage.cpp
src/interface/widgets/tabs/tab_game.cpp src/interface/widgets/tabs/tab_game.cpp
src/interface/widgets/tabs/tab_home.cpp
src/interface/widgets/tabs/tab_logs.cpp src/interface/widgets/tabs/tab_logs.cpp
src/interface/widgets/tabs/tab_message.cpp src/interface/widgets/tabs/tab_message.cpp
src/interface/widgets/tabs/tab_replays.cpp src/interface/widgets/tabs/tab_replays.cpp
src/interface/widgets/tabs/tab_room.cpp src/interface/widgets/tabs/tab_room.cpp
src/interface/widgets/tabs/tab_server.cpp src/interface/widgets/tabs/tab_server.cpp
src/interface/widgets/tabs/tab_supervisor.cpp src/interface/widgets/tabs/tab_supervisor.cpp
src/interface/widgets/tabs/tab_visual_database_display.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
src/interface/key_signals.cpp src/interface/key_signals.cpp
src/interface/logger.cpp src/interface/logger.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h
) )
add_subdirectory(sounds) add_subdirectory(sounds)

View file

@ -1,213 +0,0 @@
#include "deck_filter_string.h"
#include <QFileInfo>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/utility/peglib.h>
static peg::parser search(R"(
Start <- QueryPartList
~ws <- [ ]+
QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws*
ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart
SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart
QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / FormatQuery / CommentQuery / GenericQuery
NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart
DeckContentQuery <- CardSearch NumericExpression?
CardSearch <- '[[' CardFilterString ']]'
CardFilterString <- (!']]'.)*
DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String
FileNameQuery <- [Ff] ([Nn] / 'ile' ([Nn] 'ame')?) [:] String
PathQuery <- [Pp] 'ath'? [:] String
FormatQuery <- [Ff] 'ormat'? [:] String
CommentQuery <- [Cc] ('omment' 's'?)? [:] String
GenericQuery <- String
NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["].
NonSingleQuoteUnlessEscaped <- "\\\'". / !['].
UnescapedStringListPart <- !['":<>()=! ].
SingleApostropheString <- (UnescapedStringListPart+ ws*)* ['] (UnescapedStringListPart+ ws*)*
String <- SingleApostropheString / UnescapedStringListPart+ / ["] <NonDoubleQuoteUnlessEscaped*> ["] / ['] <NonSingleQuoteUnlessEscaped*> [']
NumericExpression <- NumericOperator ws? NumericValue
NumericOperator <- [=:] / <[><!][=]?>
NumericValue <- [0-9]+
)");
static std::once_flag init;
static void setupParserRules()
{
// plumbing
auto passthru = [](const peg::SemanticValues &sv) -> DeckFilter {
return !sv.empty() ? std::any_cast<DeckFilter>(sv[0]) : nullptr;
};
search["Start"] = passthru;
search["QueryPartList"] = [](const peg::SemanticValues &sv) -> DeckFilter {
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) {
auto matchesFilter = [&deck, &info](const std::any &query) {
return std::any_cast<DeckFilter>(query)(deck, info);
};
return std::all_of(sv.begin(), sv.end(), matchesFilter);
};
};
search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> DeckFilter {
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) {
auto matchesFilter = [&deck, &info](const std::any &query) {
return std::any_cast<DeckFilter>(query)(deck, info);
};
return std::any_of(sv.begin(), sv.end(), matchesFilter);
};
};
search["SomewhatComplexQueryPart"] = passthru;
search["QueryPart"] = passthru;
search["NotQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
const auto dependent = std::any_cast<DeckFilter>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) -> bool {
return !dependent(deck, info);
};
};
search["String"] = [](const peg::SemanticValues &sv) -> QString {
if (sv.choice() == 0) {
return QString::fromStdString(std::string(sv.sv()));
}
return QString::fromStdString(std::string(sv.token(0)));
};
search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher {
const auto arg = std::any_cast<int>(sv[1]);
const auto op = std::any_cast<QString>(sv[0]);
if (op == ">")
return [=](const int s) { return s > arg; };
if (op == ">=")
return [=](const int s) { return s >= arg; };
if (op == "<")
return [=](const int s) { return s < arg; };
if (op == "<=")
return [=](const int s) { return s <= arg; };
if (op == "=")
return [=](const int s) { return s == arg; };
if (op == ":")
return [=](const int s) { return s == arg; };
if (op == "!=")
return [=](const int s) { return s != arg; };
return [](int) { return false; };
};
search["NumericValue"] = [](const peg::SemanticValues &sv) -> int {
return QString::fromStdString(std::string(sv.sv())).toInt();
};
search["NumericOperator"] = [](const peg::SemanticValues &sv) -> QString {
return QString::fromStdString(std::string(sv.sv()));
};
// actual functionality
search["DeckContentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto cardFilter = FilterString(std::any_cast<QString>(sv[0]));
auto numberMatcher = sv.size() > 1 ? std::any_cast<NumberMatcher>(sv[1]) : [](int count) { return count > 0; };
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) -> bool {
int count = 0;
auto cardNodes = deck->deckLoader->getDeck().deckList.getCardNodes();
for (auto node : cardNodes) {
auto cardInfoPtr = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) {
count += node->getNumber();
}
}
return numberMatcher(count);
};
};
search["CardSearch"] = [](const peg::SemanticValues &sv) -> QString { return std::any_cast<QString>(sv[0]); };
search["CardFilterString"] = [](const peg::SemanticValues &sv) -> QString {
return QString::fromStdString(std::string(sv.sv()));
};
search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
return deck->deckLoader->getDeck().deckList.getName().contains(name, Qt::CaseInsensitive);
};
};
search["FileNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
auto filename = QFileInfo(deck->filePath).fileName();
return filename.contains(name, Qt::CaseInsensitive);
};
};
search["PathQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *, const ExtraDeckSearchInfo &info) {
return info.relativeFilePath.contains(name, Qt::CaseInsensitive);
};
};
search["FormatQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto format = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
auto gameFormat = deck->deckLoader->getDeck().deckList.getGameFormat();
return QString::compare(format, gameFormat, Qt::CaseInsensitive) == 0;
};
};
search["CommentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto value = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
auto comments = deck->deckLoader->getDeck().deckList.getComments();
return comments.contains(value, Qt::CaseInsensitive);
};
};
search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
return deck->getDisplayName().contains(name, Qt::CaseInsensitive);
};
};
}
DeckFilterString::DeckFilterString()
{
filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return false; };
_error = "Not initialized";
}
DeckFilterString::DeckFilterString(const QString &expr)
{
QByteArray ba = expr.simplified().toUtf8();
std::call_once(init, setupParserRules);
_error = QString();
if (ba.isEmpty()) {
filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return true; };
return;
}
search.set_logger([&](size_t /*ln*/, size_t col, const std::string &msg) {
_error = QString("Error at position %1: %2").arg(col).arg(QString::fromStdString(msg));
});
if (!search.parse(ba.data(), filter)) {
qCInfo(DeckFilterStringLog).nospace() << "DeckFilterString error for " << expr << "; " << qPrintable(_error);
filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return false; };
}
}

View file

@ -1,55 +0,0 @@
/**
* @file deck_filter_string.h
* @ingroup DeckStorageWidgets
* @brief TODO: Document this.
*/
#ifndef DECK_FILTER_STRING_H
#define DECK_FILTER_STRING_H
#include "../interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h"
#include <QLoggingCategory>
#include <QString>
#include <functional>
inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string");
/**
* Extra info relevant to filtering that isn't present in the DeckPreviewWidget
*/
struct ExtraDeckSearchInfo
{
/**
* The relative filepath starting from the deck folder
*/
QString relativeFilePath;
};
typedef std::function<bool(const DeckPreviewWidget *, const ExtraDeckSearchInfo &)> DeckFilter;
class DeckFilterString
{
public:
DeckFilterString();
explicit DeckFilterString(const QString &expr);
bool check(const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) const
{
return filter(deck, info);
}
[[nodiscard]] bool valid() const
{
return _error.isEmpty();
}
QString error()
{
return _error;
}
private:
QString _error;
DeckFilter filter;
};
#endif // DECK_FILTER_STRING_H

View file

@ -46,7 +46,7 @@ void ToggleButton::setState(bool _state)
} }
DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent)
: QWidget(nullptr), visualDeckStorageWidget(nullptr), parentGame(parent), playerId(_playerId) : QWidget(nullptr), parentGame(parent), playerId(_playerId)
{ {
loadLocalButton = new QPushButton; loadLocalButton = new QPushButton;
loadRemoteButton = new QPushButton; loadRemoteButton = new QPushButton;
@ -101,23 +101,6 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent)
switchToDeckSelectView(); switchToDeckSelectView();
} }
/**
* Creates the VDS widget and inserts it into the layout. No-ops if the widget already exists
*/
void DeckViewContainer::tryCreateVisualDeckStorageWidget()
{
if (visualDeckStorageWidget) {
return;
}
visualDeckStorageWidget = new VisualDeckStorageWidget(this);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this,
&DeckViewContainer::loadDeckFromFile);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, parentGame, &TabGame::openDeckEditor);
deckViewLayout->addWidget(visualDeckStorageWidget);
}
void DeckViewContainer::retranslateUi() void DeckViewContainer::retranslateUi()
{ {
loadLocalButton->setText(tr("Load deck...")); loadLocalButton->setText(tr("Load deck..."));
@ -138,17 +121,7 @@ static void setVisibility(QPushButton *button, bool visible)
void DeckViewContainer::switchToDeckSelectView() void DeckViewContainer::switchToDeckSelectView()
{ {
if (SettingsCache::instance().getVisualDeckStorageInGame()) { deckView->setHidden(false);
deckView->setHidden(true);
tryCreateVisualDeckStorageWidget();
visualDeckStorageWidget->setHidden(false);
} else {
deckView->setHidden(false);
if (visualDeckStorageWidget) {
visualDeckStorageWidget->setHidden(true);
}
}
deckViewLayout->update(); deckViewLayout->update();
@ -172,9 +145,6 @@ void DeckViewContainer::switchToDeckSelectView()
void DeckViewContainer::switchToDeckLoadedView() void DeckViewContainer::switchToDeckLoadedView()
{ {
deckView->setHidden(false); deckView->setHidden(false);
if (visualDeckStorageWidget) {
visualDeckStorageWidget->setHidden(true);
}
deckViewLayout->update(); deckViewLayout->update();
@ -227,15 +197,9 @@ void DeckViewContainer::setVisualDeckStorageExists(bool exists)
if (loadLocalButton->isEnabled()) { if (loadLocalButton->isEnabled()) {
// We only need to handle the setting changing while in deck select state; tryCreate already gets called // We only need to handle the setting changing while in deck select state; tryCreate already gets called
// when switching from deck loaded to deck select state // when switching from deck loaded to deck select state
tryCreateVisualDeckStorageWidget();
visualDeckStorageWidget->setHidden(false);
deckView->setHidden(true); deckView->setHidden(true);
} }
} else { } else {
if (visualDeckStorageWidget) {
visualDeckStorageWidget->deleteLater();
visualDeckStorageWidget = nullptr;
}
deckView->setHidden(false); deckView->setHidden(false);
} }

View file

@ -54,11 +54,9 @@ private:
QPushButton *unloadDeckButton, *forceStartGameButton; QPushButton *unloadDeckButton, *forceStartGameButton;
ToggleButton *readyStartButton, *sideboardLockButton; ToggleButton *readyStartButton, *sideboardLockButton;
DeckView *deckView; DeckView *deckView;
VisualDeckStorageWidget *visualDeckStorageWidget;
TabGame *parentGame; TabGame *parentGame;
int playerId; int playerId;
void tryCreateVisualDeckStorageWidget();
void sendReadyStartCommand(bool ready); void sendReadyStartCommand(bool ready);
private slots: private slots:
void switchToDeckSelectView(); void switchToDeckSelectView();

View file

@ -1,7 +1,6 @@
#include "card_size_widget.h" #include "card_size_widget.h"
#include "../printing_selector/printing_selector.h" #include "../printing_selector/printing_selector.h"
#include "../visual_deck_storage/visual_deck_storage_widget.h"
/** /**
* @class CardSizeWidget * @class CardSizeWidget

View file

@ -1,251 +0,0 @@
#include "deck_card_zone_display_widget.h"
#include "card_group_display_widgets/flat_card_group_display_widget.h"
#include "card_group_display_widgets/overlapped_card_group_display_widget.h"
#include "libcockatrice/card/database/card_database_manager.h"
#include <QResizeEvent>
#include <libcockatrice/models/deck_list/deck_list_model.h>
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QItemSelectionModel *_selectionModel,
QPersistentModelIndex _trackedIndex,
QString _zoneName,
QString _activeGroupCriteria,
QStringList _activeSortCriteria,
DisplayType _displayType,
int bannerOpacity,
int subBannerOpacity,
CardSizeWidget *_cardSizeWidget)
: QWidget(parent), deckListModel(_deckListModel), selectionModel(_selectionModel), trackedIndex(_trackedIndex),
zoneName(_zoneName), activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria),
displayType(_displayType), bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity),
cardSizeWidget(_cardSizeWidget)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
layout = new QVBoxLayout(this);
setLayout(layout);
banner = new BannerWidget(this, zoneName, Qt::Orientation::Vertical, bannerOpacity);
layout->addWidget(banner);
cardGroupContainer = new QWidget(this);
cardGroupLayout = new QVBoxLayout(cardGroupContainer);
cardGroupContainer->setLayout(cardGroupLayout);
layout->addWidget(cardGroupContainer);
banner->setBuddy(cardGroupContainer);
displayCards();
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &DeckCardZoneDisplayWidget::onCategoryAddition);
if (selectionModel) {
connect(selectionModel, &QItemSelectionModel::selectionChanged, this,
&DeckCardZoneDisplayWidget::onSelectionChanged);
}
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &DeckCardZoneDisplayWidget::onCategoryRemoval);
}
// =====================================================================================================================
// User Interaction
// =====================================================================================================================
void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
{
emit cardClicked(event, card, zoneName);
}
void DeckCardZoneDisplayWidget::onHover(const ExactCard &card)
{
emit cardHovered(card);
}
void DeckCardZoneDisplayWidget::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
for (auto &range : selected) {
for (int row = range.top(); row <= range.bottom(); ++row) {
QModelIndex idx = range.model()->index(row, 0, range.parent());
auto it = indexToWidgetMap.find(QPersistentModelIndex(idx));
if (it != indexToWidgetMap.end()) {
// it.value()->setHighlighted(true);
}
}
}
for (auto &range : deselected) {
for (int row = range.top(); row <= range.bottom(); ++row) {
QModelIndex idx = range.model()->index(row, 0, range.parent());
auto it = indexToWidgetMap.find(QPersistentModelIndex(idx));
if (it != indexToWidgetMap.end()) {
// it.value()->setHighlighted(false);
}
}
}
}
// =====================================================================================================================
// Display Widget Management
// =====================================================================================================================
void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex index)
{
if (indexToWidgetMap.contains(index)) {
return;
}
auto categoryName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
if (displayType == DisplayType::Overlap) {
auto *displayWidget = new OverlappedCardGroupDisplayWidget(
cardGroupContainer, deckListModel, selectionModel, index, zoneName, categoryName, activeGroupCriteria,
activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this,
&DeckCardZoneDisplayWidget::onClick);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardHovered, this,
&DeckCardZoneDisplayWidget::onHover);
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
&DeckCardZoneDisplayWidget::cleanupInvalidCardGroup);
connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, displayWidget,
&CardGroupDisplayWidget::onActiveSortCriteriaChanged);
cardGroupLayout->addWidget(displayWidget);
indexToWidgetMap.insert(index, displayWidget);
} else if (displayType == DisplayType::Flat) {
auto *displayWidget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, selectionModel, index,
zoneName, categoryName, activeGroupCriteria,
activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &FlatCardGroupDisplayWidget::cardClicked, this, &DeckCardZoneDisplayWidget::onClick);
connect(displayWidget, &FlatCardGroupDisplayWidget::cardHovered, this, &DeckCardZoneDisplayWidget::onHover);
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
&DeckCardZoneDisplayWidget::cleanupInvalidCardGroup);
connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, displayWidget,
&CardGroupDisplayWidget::onActiveSortCriteriaChanged);
cardGroupLayout->addWidget(displayWidget);
indexToWidgetMap.insert(index, displayWidget);
}
}
void DeckCardZoneDisplayWidget::displayCards()
{
QSortFilterProxyModel proxy;
proxy.setSourceModel(deckListModel);
proxy.setSortRole(Qt::EditRole);
proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
// 1. trackedIndex is a source index → map it to proxy space
QModelIndex proxyParent = proxy.mapFromSource(trackedIndex);
// 2. iterate children under the proxy parent
for (int i = 0; i < proxy.rowCount(proxyParent); ++i) {
QModelIndex proxyIndex = proxy.index(i, 0, proxyParent);
// 3. map back to source
QModelIndex sourceIndex = proxy.mapToSource(proxyIndex);
// 4. persist the source index
QPersistentModelIndex persistent(sourceIndex);
constructAppropriateWidget(persistent);
}
}
void DeckCardZoneDisplayWidget::refreshDisplayType(const DisplayType &_displayType)
{
displayType = _displayType;
QLayoutItem *item;
while ((item = cardGroupLayout->takeAt(0)) != nullptr) {
if (item->widget()) {
item->widget()->deleteLater();
} else if (item->layout()) {
item->layout()->deleteLater();
}
delete item;
}
indexToWidgetMap.clear();
// We gotta wait for all the deleteLater's to finish so we fire after the next event cycle
auto timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [this]() { displayCards(); });
timer->start();
}
void DeckCardZoneDisplayWidget::cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget)
{
cardGroupLayout->removeWidget(displayWidget);
displayWidget->setParent(nullptr);
for (auto idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
indexToWidgetMap.remove(idx);
}
}
delete displayWidget;
}
// =====================================================================================================================
// DeckListModel Signal Responses
// =====================================================================================================================
void DeckCardZoneDisplayWidget::onCategoryAddition(const QModelIndex &parent, int first, int last)
{
if (!trackedIndex.isValid()) {
emit requestCleanup(this);
return;
}
if (parent == trackedIndex) {
for (int i = first; i <= last; i++) {
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(i, 0, trackedIndex));
constructAppropriateWidget(index);
}
}
}
void DeckCardZoneDisplayWidget::onCategoryRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(parent);
Q_UNUSED(first);
Q_UNUSED(last);
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
cardGroupLayout->removeWidget(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
if (!trackedIndex.isValid()) {
emit requestCleanup(this);
}
}
void DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged(QString _activeGroupCriteria)
{
activeGroupCriteria = _activeGroupCriteria;
displayCards();
}
void DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSortCriteria)
{
activeSortCriteria = _activeSortCriteria;
emit activeSortCriteriaChanged(activeSortCriteria);
}
QList<QString> DeckCardZoneDisplayWidget::getGroupCriteriaValueList()
{
QList<QString> groupCriteriaValues;
QList<const DecklistCardNode *> nodes = deckListModel->getCardNodesForZone(zoneName);
for (auto node : nodes) {
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
if (info) {
groupCriteriaValues.append(info->getProperty(activeGroupCriteria));
}
}
groupCriteriaValues.removeDuplicates();
groupCriteriaValues.sort();
return groupCriteriaValues;
}

View file

@ -1,78 +0,0 @@
/**
* @file deck_card_zone_display_widget.h
* @ingroup DeckEditorWidgets
* @brief TODO: Document this.
*/
#ifndef DECK_CARD_ZONE_DISPLAY_WIDGET_H
#define DECK_CARD_ZONE_DISPLAY_WIDGET_H
#include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h"
#include "../visual_deck_editor/visual_deck_editor_widget.h"
#include "card_group_display_widgets/card_group_display_widget.h"
#include "card_info_picture_with_text_overlay_widget.h"
#include "card_size_widget.h"
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/models/deck_list/deck_list_model.h>
class DeckCardZoneDisplayWidget : public QWidget
{
Q_OBJECT
public:
DeckCardZoneDisplayWidget(QWidget *parent,
DeckListModel *deckListModel,
QItemSelectionModel *selectionModel,
QPersistentModelIndex trackedIndex,
QString zoneName,
QString activeGroupCriteria,
QStringList activeSortCriteria,
DisplayType displayType,
int bannerOpacity,
int subBannerOpacity,
CardSizeWidget *_cardSizeWidget);
void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
DeckListModel *deckListModel;
QItemSelectionModel *selectionModel;
QPersistentModelIndex trackedIndex;
QString zoneName;
void addCardsToOverlapWidget();
public slots:
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void onHover(const ExactCard &card);
void cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget);
void constructAppropriateWidget(QPersistentModelIndex index);
void displayCards();
void refreshDisplayType(const DisplayType &displayType);
void onActiveGroupCriteriaChanged(QString activeGroupCriteria);
void onActiveSortCriteriaChanged(QStringList activeSortCriteria);
QList<QString> getGroupCriteriaValueList();
void onCategoryAddition(const QModelIndex &parent, int first, int last);
void onCategoryRemoval(const QModelIndex &parent, int first, int last);
signals:
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card, QString zoneName);
void cardHovered(const ExactCard &card);
void activeSortCriteriaChanged(QStringList activeSortCriteria);
void requestCleanup(DeckCardZoneDisplayWidget *displayWidget);
private:
QString activeGroupCriteria;
QStringList activeSortCriteria;
DisplayType displayType = DisplayType::Overlap;
int bannerOpacity = 20;
int subBannerOpacity = 10;
CardSizeWidget *cardSizeWidget;
QVBoxLayout *layout;
BannerWidget *banner;
QWidget *cardGroupContainer;
QVBoxLayout *cardGroupLayout;
OverlapWidget *overlapWidget;
QHash<QPersistentModelIndex, QWidget *> indexToWidgetMap;
};
#endif // DECK_CARD_ZONE_DISPLAY_WIDGET_H

View file

@ -1,62 +0,0 @@
#include "deck_preview_card_picture_widget.h"
#include "../../../client/settings/cache_settings.h"
#include <QApplication>
#include <QFileInfo>
#include <QFontMetrics>
#include <QMouseEvent>
#include <QPainterPath>
#include <QStylePainter>
/**
* @brief Constructs a CardPictureWithTextOverlay widget.
* @param parent The parent widget.
* @param hoverToZoomEnabled If this widget will spawn a larger widget when hovered over.
* @param textColor The color of the overlay text.
* @param outlineColor The color of the outline around the text.
* @param fontSize The font size of the overlay text.
* @param alignment The alignment of the text within the overlay.
*
* Sets the widget's size policy and default border style.
*/
DeckPreviewCardPictureWidget::DeckPreviewCardPictureWidget(QWidget *parent,
const bool hoverToZoomEnabled,
const bool raiseOnEnter,
const QColor &textColor,
const QColor &outlineColor,
const int fontSize,
const Qt::Alignment alignment)
: CardInfoPictureWithTextOverlayWidget(parent,
hoverToZoomEnabled,
raiseOnEnter,
textColor,
outlineColor,
fontSize,
alignment)
{
singleClickTimer = new QTimer(this);
singleClickTimer->setSingleShot(true);
connect(singleClickTimer, &QTimer::timeout, this, [this]() { emit imageClicked(lastMouseEvent, this); });
connect(&SettingsCache::instance(), &SettingsCache::visualDeckStorageSelectionAnimationChanged, this,
&CardInfoPictureWidget::setRaiseOnEnterEnabled);
}
void DeckPreviewCardPictureWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
lastMouseEvent = event;
singleClickTimer->start(QApplication::doubleClickInterval());
} else {
emit imageClicked(event, this);
event->accept();
}
}
void DeckPreviewCardPictureWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
singleClickTimer->stop(); // Prevent single-click logic
emit imageDoubleClicked(lastMouseEvent, this);
}
}

View file

@ -1,44 +0,0 @@
/**
* @file deck_preview_card_picture_widget.h
* @ingroup CardWidgets
* @ingroup Lobby
* @brief TODO: Document this.
*/
#ifndef DECK_PREVIEW_CARD_PICTURE_WIDGET_H
#define DECK_PREVIEW_CARD_PICTURE_WIDGET_H
#include "card_info_picture_with_text_overlay_widget.h"
#include <QColor>
#include <QSize>
#include <QTextOption>
#include <QTimer>
class DeckPreviewCardPictureWidget final : public CardInfoPictureWithTextOverlayWidget
{
Q_OBJECT
public:
explicit DeckPreviewCardPictureWidget(QWidget *parent,
bool hoverToZoomEnabled = false,
bool raiseOnEnter = false,
const QColor &textColor = Qt::white,
const QColor &outlineColor = Qt::black,
int fontSize = 12,
Qt::Alignment alignment = Qt::AlignCenter);
signals:
void imageClicked(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
void imageDoubleClicked(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
private:
QTimer *singleClickTimer;
QMouseEvent *lastMouseEvent = nullptr; // Store the last mouse event
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
};
#endif // DECK_PREVIEW_CARD_PICTURE_WIDGET_H

View file

@ -14,13 +14,6 @@
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h> #include <libcockatrice/card/relation/card_relation.h>
static bool canBeCommander(const CardInfo &cardInfo)
{
return (cardInfo.getCardType().contains("Legendary", Qt::CaseInsensitive) &&
cardInfo.getCardType().contains("Creature", Qt::CaseInsensitive)) ||
cardInfo.getText().contains("can be your commander", Qt::CaseInsensitive);
}
DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent, AbstractTabDeckEditor *deckEditor) DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent, AbstractTabDeckEditor *deckEditor)
: QWidget(parent), deckEditor(deckEditor) : QWidget(parent), deckEditor(deckEditor)
{ {
@ -186,22 +179,14 @@ void DeckEditorDatabaseDisplayWidget::databaseCustomMenu(QPoint point)
if (card) { if (card) {
// add to deck and sideboard options // add to deck and sideboard options
QAction *addToDeck, *addToSideboard, *selectPrinting, *edhRecCommander, *edhRecCard; QAction *addToDeck, *addToSideboard, *selectPrinting;
addToDeck = menu.addAction(tr("Add to Deck")); addToDeck = menu.addAction(tr("Add to Deck"));
addToSideboard = menu.addAction(tr("Add to Sideboard")); addToSideboard = menu.addAction(tr("Add to Sideboard"));
selectPrinting = menu.addAction(tr("Select Printing")); selectPrinting = menu.addAction(tr("Select Printing"));
connect(selectPrinting, &QAction::triggered, this, [this, card] { deckEditor->showPrintingSelector(); }); connect(selectPrinting, &QAction::triggered, this, [this, card] { deckEditor->showPrintingSelector(); });
if (canBeCommander(card.getInfo())) {
edhRecCommander = menu.addAction(tr("Show on EDHRec (Commander)"));
connect(edhRecCommander, &QAction::triggered, this,
[this, card] { deckEditor->getTabSupervisor()->addEdhrecTab(card.getCardPtr(), true); });
}
edhRecCard = menu.addAction(tr("Show on EDHRec (Card)"));
connect(addToDeck, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck); connect(addToDeck, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck);
connect(addToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard); connect(addToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard);
connect(edhRecCard, &QAction::triggered, this,
[this, card] { deckEditor->getTabSupervisor()->addEdhrecTab(card.getCardPtr()); });
// filling out the related cards submenu // filling out the related cards submenu
auto *relatedMenu = new QMenu(tr("Show Related cards")); auto *relatedMenu = new QMenu(tr("Show Related cards"));

View file

@ -119,8 +119,6 @@ void DeckEditorDeckDockWidget::createDeckDock()
showTagsWidgetCheckBox->setChecked(SettingsCache::instance().getDeckEditorTagsWidgetVisible()); showTagsWidgetCheckBox->setChecked(SettingsCache::instance().getDeckEditorTagsWidgetVisible());
connect(showTagsWidgetCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(showTagsWidgetCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setDeckEditorTagsWidgetVisible); &SettingsCache::setDeckEditorTagsWidgetVisible);
connect(&SettingsCache::instance(), &SettingsCache::deckEditorTagsWidgetVisibleChanged, this,
&DeckEditorDeckDockWidget::updateShowTagsWidget);
quickSettingsWidget->addSettingsWidget(showBannerCardCheckBox); quickSettingsWidget->addSettingsWidget(showBannerCardCheckBox);
quickSettingsWidget->addSettingsWidget(showTagsWidgetCheckBox); quickSettingsWidget->addSettingsWidget(showTagsWidgetCheckBox);
@ -164,11 +162,6 @@ void DeckEditorDeckDockWidget::createDeckDock()
&DeckEditorDeckDockWidget::writeBannerCard); &DeckEditorDeckDockWidget::writeBannerCard);
bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible()); bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, {});
deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible());
connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, deckStateManager,
&DeckStateManager::setTags);
activeGroupCriteriaLabel = new QLabel(this); activeGroupCriteriaLabel = new QLabel(this);
activeGroupCriteriaComboBox = new QComboBox(this); activeGroupCriteriaComboBox = new QComboBox(this);
@ -219,8 +212,6 @@ void DeckEditorDeckDockWidget::createDeckDock()
upperLayout->addWidget(bannerCardLabel, 3, 0); upperLayout->addWidget(bannerCardLabel, 3, 0);
upperLayout->addWidget(bannerCardComboBox, 3, 1); upperLayout->addWidget(bannerCardComboBox, 3, 1);
upperLayout->addWidget(deckTagsDisplayWidget, 4, 1);
upperLayout->addWidget(activeGroupCriteriaLabel, 5, 0); upperLayout->addWidget(activeGroupCriteriaLabel, 5, 0);
upperLayout->addWidget(activeGroupCriteriaComboBox, 5, 1); upperLayout->addWidget(activeGroupCriteriaComboBox, 5, 1);
@ -439,11 +430,6 @@ void DeckEditorDeckDockWidget::updateShowBannerCardComboBox(const bool visible)
bannerCardComboBox->setHidden(!visible); bannerCardComboBox->setHidden(!visible);
} }
void DeckEditorDeckDockWidget::updateShowTagsWidget(const bool visible)
{
deckTagsDisplayWidget->setHidden(!visible);
}
void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck() void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
{ {
if (deckStateManager->getMetadata().bannerCard.name == "") { if (deckStateManager->getMetadata().bannerCard.name == "") {
@ -488,10 +474,6 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
formatComboBox->blockSignals(true); formatComboBox->blockSignals(true);
formatComboBox->setCurrentIndex(formatComboBox->findData(deckStateManager->getMetadata().gameFormat)); formatComboBox->setCurrentIndex(formatComboBox->findData(deckStateManager->getMetadata().gameFormat));
formatComboBox->blockSignals(false); formatComboBox->blockSignals(false);
deckTagsDisplayWidget->blockSignals(true);
deckTagsDisplayWidget->setTags(deckStateManager->getMetadata().tags);
deckTagsDisplayWidget->blockSignals(false);
} }
/** /**

View file

@ -11,10 +11,11 @@
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h" #include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../../key_signals.h" #include "../../key_signals.h"
#include "../utility/custom_line_edit.h" #include "../utility/custom_line_edit.h"
#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include "deck_list_history_manager_widget.h" #include "deck_list_history_manager_widget.h"
#include "deck_list_style_proxy.h" #include "deck_list_style_proxy.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDockWidget> #include <QDockWidget>
#include <QLabel> #include <QLabel>
#include <QTextEdit> #include <QTextEdit>
@ -79,7 +80,6 @@ private:
QTextEdit *commentsEdit; QTextEdit *commentsEdit;
QTimer *commentsDebounceTimer; QTimer *commentsDebounceTimer;
QLabel *bannerCardLabel; QLabel *bannerCardLabel;
DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget;
QLabel *hashLabel1; QLabel *hashLabel1;
LineEditUnfocusable *hashLabel; LineEditUnfocusable *hashLabel;
QLabel *activeGroupCriteriaLabel; QLabel *activeGroupCriteriaLabel;
@ -104,7 +104,6 @@ private slots:
void updateHash(); void updateHash();
void refreshShortcuts(); void refreshShortcuts();
void updateShowBannerCardComboBox(bool visible); void updateShowBannerCardComboBox(bool visible);
void updateShowTagsWidget(bool visible);
void syncBannerCardComboBoxSelectionWithDeck(); void syncBannerCardComboBoxSelectionWithDeck();
void changeSelectedCard(int changeBy); void changeSelectedCard(int changeBy);
void recursiveExpand(const QModelIndex &parent); void recursiveExpand(const QModelIndex &parent);

View file

@ -7,7 +7,6 @@
#include "../client/sound_engine.h" #include "../client/sound_engine.h"
#include "../interface/card_picture_loader/card_picture_loader.h" #include "../interface/card_picture_loader/card_picture_loader.h"
#include "../interface/theme_manager.h" #include "../interface/theme_manager.h"
#include "../interface/widgets/general/background_sources.h"
#include "../interface/widgets/tabs/tab_supervisor.h" #include "../interface/widgets/tabs/tab_supervisor.h"
#include "../interface/widgets/utility/custom_line_edit.h" #include "../interface/widgets/utility/custom_line_edit.h"
#include "../interface/widgets/utility/get_text_with_max.h" #include "../interface/widgets/utility/get_text_with_max.h"
@ -425,45 +424,10 @@ AppearanceSettingsPage::AppearanceSettingsPage()
connect(&themeBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppearanceSettingsPage::themeBoxChanged); connect(&themeBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppearanceSettingsPage::themeBoxChanged);
connect(&openThemeButton, &QPushButton::clicked, this, &AppearanceSettingsPage::openThemeLocation); connect(&openThemeButton, &QPushButton::clicked, this, &AppearanceSettingsPage::openThemeLocation);
for (const auto &entry : BackgroundSources::all()) {
homeTabBackgroundSourceBox.addItem(QObject::tr(entry.trKey), QVariant::fromValue(entry.type));
}
QString homeTabBackgroundSource = SettingsCache::instance().getHomeTabBackgroundSource();
int homeTabBackgroundSourceId =
homeTabBackgroundSourceBox.findData(BackgroundSources::fromId(homeTabBackgroundSource));
if (homeTabBackgroundSourceId != -1) {
homeTabBackgroundSourceBox.setCurrentIndex(homeTabBackgroundSourceId);
}
connect(&homeTabBackgroundSourceBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this]() {
auto type = homeTabBackgroundSourceBox.currentData().value<BackgroundSources::Type>();
SettingsCache::instance().setHomeTabBackgroundSource(BackgroundSources::toId(type));
updateHomeTabSettingsVisibility();
});
homeTabBackgroundShuffleFrequencySpinBox.setRange(0, 3600);
homeTabBackgroundShuffleFrequencySpinBox.setSuffix(tr(" seconds"));
homeTabBackgroundShuffleFrequencySpinBox.setValue(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency());
connect(&homeTabBackgroundShuffleFrequencySpinBox, qOverload<int>(&QSpinBox::valueChanged),
&SettingsCache::instance(), &SettingsCache::setHomeTabBackgroundShuffleFrequency);
homeTabDisplayCardNameCheckBox.setChecked(settings.getHomeTabDisplayCardName());
connect(&homeTabDisplayCardNameCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings,
&SettingsCache::setHomeTabDisplayCardName);
updateHomeTabSettingsVisibility();
auto *themeGrid = new QGridLayout; auto *themeGrid = new QGridLayout;
themeGrid->addWidget(&themeLabel, 0, 0); themeGrid->addWidget(&themeLabel, 0, 0);
themeGrid->addWidget(&themeBox, 0, 1); themeGrid->addWidget(&themeBox, 0, 1);
themeGrid->addWidget(&openThemeButton, 1, 1); themeGrid->addWidget(&openThemeButton, 1, 1);
themeGrid->addWidget(&homeTabBackgroundSourceLabel, 2, 0);
themeGrid->addWidget(&homeTabBackgroundSourceBox, 2, 1);
themeGrid->addWidget(&homeTabBackgroundShuffleFrequencyLabel, 3, 0);
themeGrid->addWidget(&homeTabBackgroundShuffleFrequencySpinBox, 3, 1);
themeGrid->addWidget(&homeTabDisplayCardNameLabel, 4, 0);
themeGrid->addWidget(&homeTabDisplayCardNameCheckBox, 4, 1);
themeGroupBox = new QGroupBox; themeGroupBox = new QGroupBox;
themeGroupBox->setLayout(themeGrid); themeGroupBox->setLayout(themeGrid);
@ -660,17 +624,6 @@ void AppearanceSettingsPage::openThemeLocation()
} }
} }
void AppearanceSettingsPage::updateHomeTabSettingsVisibility()
{
bool visible =
SettingsCache::instance().getHomeTabBackgroundSource() != BackgroundSources::toId(BackgroundSources::Theme);
homeTabBackgroundShuffleFrequencyLabel.setVisible(visible);
homeTabBackgroundShuffleFrequencySpinBox.setVisible(visible);
homeTabDisplayCardNameLabel.setVisible(visible);
homeTabDisplayCardNameCheckBox.setVisible(visible);
}
void AppearanceSettingsPage::showShortcutsChanged(QT_STATE_CHANGED_T value) void AppearanceSettingsPage::showShortcutsChanged(QT_STATE_CHANGED_T value)
{ {
SettingsCache::instance().setShowShortcuts(value); SettingsCache::instance().setShowShortcuts(value);
@ -724,10 +677,6 @@ void AppearanceSettingsPage::retranslateUi()
themeGroupBox->setTitle(tr("Theme settings")); themeGroupBox->setTitle(tr("Theme settings"));
themeLabel.setText(tr("Current theme:")); themeLabel.setText(tr("Current theme:"));
openThemeButton.setText(tr("Open themes folder")); openThemeButton.setText(tr("Open themes folder"));
homeTabBackgroundSourceLabel.setText(tr("Home tab background source:"));
homeTabBackgroundShuffleFrequencyLabel.setText(tr("Home tab background shuffle frequency:"));
homeTabBackgroundShuffleFrequencySpinBox.setSpecialValueText(tr("Disabled"));
homeTabDisplayCardNameLabel.setText(tr("Display card name of background in bottom right:"));
menuGroupBox->setTitle(tr("Menu settings")); menuGroupBox->setTitle(tr("Menu settings"));
showShortcutsCheckBox.setText(tr("Show keyboard shortcuts in right-click menus")); showShortcutsCheckBox.setText(tr("Show keyboard shortcuts in right-click menus"));
@ -872,47 +821,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
connect(&openDeckInNewTabCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(&openDeckInNewTabCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setOpenDeckInNewTab); &SettingsCache::setOpenDeckInNewTab);
visualDeckStorageInGameCheckBox.setChecked(SettingsCache::instance().getVisualDeckStorageInGame());
connect(&visualDeckStorageInGameCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageInGame);
visualDeckStorageSelectionAnimationCheckBox.setChecked(
SettingsCache::instance().getVisualDeckStorageSelectionAnimation());
connect(&visualDeckStorageSelectionAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageSelectionAnimation);
visualDeckStoragePromptForConversionSelector.addItem(""); // these will be set in retranslateUI
visualDeckStoragePromptForConversionSelector.addItem("");
visualDeckStoragePromptForConversionSelector.addItem("");
if (SettingsCache::instance().getVisualDeckStoragePromptForConversion()) {
visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexPrompt);
} else if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) {
visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexAlways);
} else {
visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexNone);
}
connect(&visualDeckStoragePromptForConversionSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[](int index) {
SettingsCache::instance().setVisualDeckStoragePromptForConversion(
index == visualDeckStoragePromptForConversionIndexPrompt);
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(
index == visualDeckStoragePromptForConversionIndexAlways);
});
defaultDeckEditorTypeSelector.addItem(""); // these will be set in retranslateUI
defaultDeckEditorTypeSelector.addItem("");
defaultDeckEditorTypeSelector.setCurrentIndex(SettingsCache::instance().getDefaultDeckEditorType());
connect(&defaultDeckEditorTypeSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
&SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType);
auto *deckEditorGrid = new QGridLayout; auto *deckEditorGrid = new QGridLayout;
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0); deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0);
deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1);
deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0);
deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1);
deckEditorGroupBox = new QGroupBox; deckEditorGroupBox = new QGroupBox;
deckEditorGroupBox->setLayout(deckEditorGrid); deckEditorGroupBox->setLayout(deckEditorGrid);
@ -976,19 +886,6 @@ void UserInterfaceSettingsPage::retranslateUi()
tapAnimationCheckBox.setText(tr("&Tap/untap animation")); tapAnimationCheckBox.setText(tr("&Tap/untap animation"));
deckEditorGroupBox->setTitle(tr("Deck editor/storage settings")); deckEditorGroupBox->setTitle(tr("Deck editor/storage settings"));
openDeckInNewTabCheckBox.setText(tr("Open deck in new tab by default")); openDeckInNewTabCheckBox.setText(tr("Open deck in new tab by default"));
visualDeckStorageInGameCheckBox.setText(tr("Use visual deck storage in game lobby"));
visualDeckStorageSelectionAnimationCheckBox.setText(tr("Use selection animation for Visual Deck Storage"));
visualDeckStoragePromptForConversionLabel.setText(
tr("When adding a tag in the visual deck storage to a .txt deck:"));
visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexNone,
tr("do nothing"));
visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexPrompt,
tr("ask to convert to .cod"));
visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways,
tr("always convert to .cod"));
defaultDeckEditorTypeLabel.setText(tr("Default deck editor type"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor"));
replayGroupBox->setTitle(tr("Replay settings")); replayGroupBox->setTitle(tr("Replay settings"));
rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:")); rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:"));
rewindBufferingMsBox.setSuffix(" ms"); rewindBufferingMsBox.setSuffix(" ms");

View file

@ -103,7 +103,6 @@ class AppearanceSettingsPage : public AbstractSettingsPage
private slots: private slots:
void themeBoxChanged(int index); void themeBoxChanged(int index);
void openThemeLocation(); void openThemeLocation();
void updateHomeTabSettingsVisibility();
void showShortcutsChanged(QT_STATE_CHANGED_T enabled); void showShortcutsChanged(QT_STATE_CHANGED_T enabled);
void overrideAllCardArtWithPersonalPreferenceToggled(QT_STATE_CHANGED_T enabled); void overrideAllCardArtWithPersonalPreferenceToggled(QT_STATE_CHANGED_T enabled);
@ -114,12 +113,6 @@ private:
QLabel themeLabel; QLabel themeLabel;
QComboBox themeBox; QComboBox themeBox;
QPushButton openThemeButton; QPushButton openThemeButton;
QLabel homeTabBackgroundSourceLabel;
QComboBox homeTabBackgroundSourceBox;
QLabel homeTabBackgroundShuffleFrequencyLabel;
QSpinBox homeTabBackgroundShuffleFrequencySpinBox;
QLabel homeTabDisplayCardNameLabel;
QCheckBox homeTabDisplayCardNameCheckBox;
QLabel minPlayersForMultiColumnLayoutLabel; QLabel minPlayersForMultiColumnLayoutLabel;
QLabel maxFontSizeForCardsLabel; QLabel maxFontSizeForCardsLabel;
QCheckBox showShortcutsCheckBox; QCheckBox showShortcutsCheckBox;
@ -176,12 +169,6 @@ private:
QCheckBox useTearOffMenusCheckBox; QCheckBox useTearOffMenusCheckBox;
QCheckBox tapAnimationCheckBox; QCheckBox tapAnimationCheckBox;
QCheckBox openDeckInNewTabCheckBox; QCheckBox openDeckInNewTabCheckBox;
QLabel visualDeckStoragePromptForConversionLabel;
QComboBox visualDeckStoragePromptForConversionSelector;
QCheckBox visualDeckStorageInGameCheckBox;
QCheckBox visualDeckStorageSelectionAnimationCheckBox;
QLabel defaultDeckEditorTypeLabel;
QComboBox defaultDeckEditorTypeSelector;
QLabel rewindBufferingMsLabel; QLabel rewindBufferingMsLabel;
QSpinBox rewindBufferingMsBox; QSpinBox rewindBufferingMsBox;
QGroupBox *generalGroupBox; QGroupBox *generalGroupBox;

View file

@ -1,3 +0,0 @@
#include "background_sources.h"
// Required so moc generates Q_OBJECT macros

View file

@ -1,68 +0,0 @@
/**
* @file background_sources.h
* @ingroup UI
* @brief TODO: Document this.
*/
#ifndef COCKATRICE_BACKGROUND_SOURCES_H
#define COCKATRICE_BACKGROUND_SOURCES_H
#include <QList>
#include <QObject>
#include <QString>
class BackgroundSources
{
Q_GADGET
public:
enum Type
{
Theme,
RandomCardArt,
DeckFileArt
};
Q_ENUM(Type)
struct Entry
{
Type type;
const char *id; // stable ID for settings
const char *trKey; // key for translation
};
static QList<Entry> all()
{
return {{Theme, "theme", QT_TR_NOOP("Theme")},
{RandomCardArt, "random_card_art", QT_TR_NOOP("Art crop of random card")},
{DeckFileArt, "deck_file_art", QT_TR_NOOP("Art crop of background.cod deck file")}};
}
static QString toId(Type type)
{
for (const auto &e : all()) {
if (e.type == type)
return e.id;
}
return {};
}
static Type fromId(const QString &id)
{
for (const auto &e : all()) {
if (id == e.id)
return e.type;
}
return Theme; // default
}
static QString toDisplay(Type type)
{
for (const auto &e : all()) {
if (e.type == type)
return QObject::tr(e.trKey);
}
return {};
}
};
#endif // COCKATRICE_BACKGROUND_SOURCES_H

View file

@ -1,103 +0,0 @@
#include "home_styled_button.h"
#include <QPainter>
#include <QPainterPath>
#include <qgraphicseffect.h>
#include <qstyleoption.h>
HomeStyledButton::HomeStyledButton(const QString &text, QPair<QColor, QColor> _gradientColors, QWidget *parent)
: QPushButton(text, parent), gradientColors(_gradientColors)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMinimumHeight(25);
setStyleSheet(generateButtonStylesheet(gradientColors));
}
void HomeStyledButton::updateStylesheet(const QPair<QColor, QColor> &colors)
{
gradientColors = colors;
setStyleSheet(generateButtonStylesheet(gradientColors));
}
QString HomeStyledButton::generateButtonStylesheet(const QPair<QColor, QColor> &colors)
{
QColor baseGradientStart = colors.first;
QColor baseGradientEnd = colors.second;
QColor hoverGradientStart = baseGradientStart.lighter(120); // 20% lighter
QColor hoverGradientEnd = baseGradientEnd.lighter(120);
QColor pressedGradientStart = baseGradientStart.darker(130); // 30% darker
QColor pressedGradientEnd = baseGradientEnd.darker(130);
// Disabled: more gray, less saturated
QColor disabledGradientStart = baseGradientStart.toHsv();
disabledGradientStart.setHsv(disabledGradientStart.hue(), disabledGradientStart.saturation() * 0.2,
disabledGradientStart.value() * 0.6);
QColor disabledGradientEnd = baseGradientEnd.toHsv();
disabledGradientEnd.setHsv(disabledGradientEnd.hue(), disabledGradientEnd.saturation() * 0.2,
disabledGradientEnd.value() * 0.6);
return QString(R"(
QPushButton {
font-size: 34px;
font-weight: bold;
font-family: sans-serif, "Segoe UI", "Helvetica Neue";
padding: 30px;
color: white;
border: 2px solid %1;
border-radius: 20px;
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
stop:0 %2, stop:1 %3);
}
QPushButton:hover {
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
stop:0 %4, stop:1 %5);
}
QPushButton:pressed {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 %6, stop:1 %7);
}
QPushButton:disabled {
color: #aaaaaa;
border: 2px solid #888888;
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
stop:0 %8, stop:1 %9);
}
)")
.arg(baseGradientStart.name())
.arg(baseGradientStart.name())
.arg(baseGradientEnd.name())
.arg(hoverGradientStart.name())
.arg(hoverGradientEnd.name())
.arg(pressedGradientStart.name())
.arg(pressedGradientEnd.name())
.arg(disabledGradientStart.name())
.arg(disabledGradientEnd.name());
}
void HomeStyledButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event); // Event is just used for update clipping, we redraw the whole widget.
QStyleOptionButton opt;
initStyleOption(&opt);
opt.text.clear(); // prevent style from drawing text
QPainter painter(this);
style()->drawControl(QStyle::CE_PushButton, &opt, &painter, this);
// Draw white text with a black outline
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
QFontMetrics fm(font());
QSize textSize = fm.size(Qt::TextSingleLine, text());
QPointF center((width() - textSize.width()) / 2.0, (height() + textSize.height() / 2.0) / 2.0);
QPainterPath path;
path.addText(center, font(), text());
QPen pen(Qt::black, 4.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
painter.strokePath(path, pen);
painter.fillPath(path, Qt::white);
}

View file

@ -1,25 +0,0 @@
/**
* @file home_styled_button.h
* @ingroup Widgets
* @brief TODO: Document this.
*/
#ifndef HOME_STYLED_BUTTON_H
#define HOME_STYLED_BUTTON_H
#include <QPushButton>
class HomeStyledButton : public QPushButton
{
Q_OBJECT
public:
HomeStyledButton(const QString &text, QPair<QColor, QColor> gradientColors, QWidget *parent = nullptr);
void updateStylesheet(const QPair<QColor, QColor> &colors);
QString generateButtonStylesheet(const QPair<QColor, QColor> &colors);
public slots:
void paintEvent(QPaintEvent *event) override;
private:
QPair<QColor, QColor> gradientColors;
};
#endif // HOME_STYLED_BUTTON_H

View file

@ -1,377 +0,0 @@
#include "home_widget.h"
#include "../../../client/settings/cache_settings.h"
#include "../../../interface/widgets/tabs/tab_supervisor.h"
#include "../../theme_manager.h"
#include "../../window_main.h"
#include "background_sources.h"
#include "home_styled_button.h"
#include <QGroupBox>
#include <QPainter>
#include <QPainterPath>
#include <QPushButton>
#include <QVBoxLayout>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/network/client/remote/remote_client.h>
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
: QWidget(parent), tabSupervisor(_tabSupervisor), background("theme:backgrounds/home"), overlay("theme:cockatrice")
{
layout = new QGridLayout(this);
backgroundSourceCard = new CardInfoPictureArtCropWidget(this);
gradientColors = extractDominantColors(background);
layout->addWidget(createButtons(), 1, 1, Qt::AlignVCenter | Qt::AlignHCenter);
layout->setRowStretch(0, 1);
layout->setRowStretch(2, 1);
layout->setColumnStretch(0, 1);
layout->setColumnStretch(2, 1);
setLayout(layout);
cardChangeTimer = new QTimer(this);
connect(cardChangeTimer, &QTimer::timeout, this, &HomeWidget::updateRandomCard);
initializeBackgroundFromSource();
updateConnectButton(tabSupervisor->getClient()->getStatus());
connect(tabSupervisor->getClient(), &RemoteClient::statusChanged, this, &HomeWidget::updateConnectButton);
connect(&SettingsCache::instance(), &SettingsCache::homeTabBackgroundSourceChanged, this,
&HomeWidget::initializeBackgroundFromSource);
connect(&SettingsCache::instance(), &SettingsCache::homeTabBackgroundShuffleFrequencyChanged, this,
&HomeWidget::onBackgroundShuffleFrequencyChanged);
// Lambda is cleaner to read than overloading this
connect(&SettingsCache::instance(), &SettingsCache::homeTabDisplayCardNameChanged, this, [this] { repaint(); });
connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this,
&HomeWidget::initializeBackgroundFromSource);
connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this,
&HomeWidget::updateButtonsToBackgroundColor);
}
void HomeWidget::initializeBackgroundFromSource()
{
if (CardDatabaseManager::getInstance()->getLoadStatus() != LoadStatus::Ok) {
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&HomeWidget::initializeBackgroundFromSource);
return;
}
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
switch (backgroundSourceType) {
case BackgroundSources::Theme:
cardChangeTimer->stop();
background = QPixmap("theme:backgrounds/home");
backgroundSourceDeck = DeckList();
backgroundSourceCard->setCard(ExactCard());
updateButtonsToBackgroundColor();
update();
break;
case BackgroundSources::RandomCardArt:
backgroundSourceDeck = DeckList();
updateRandomCard();
onBackgroundShuffleFrequencyChanged();
break;
case BackgroundSources::DeckFileArt:
loadBackgroundSourceDeck();
updateRandomCard();
onBackgroundShuffleFrequencyChanged();
break;
}
}
void HomeWidget::loadBackgroundSourceDeck()
{
std::optional<LoadedDeck> deckOpt = DeckLoader::loadFromFile(
SettingsCache::instance().getDeckPath() + "background.cod", DeckFileFormat::Cockatrice, false);
backgroundSourceDeck = deckOpt.has_value() ? deckOpt.value().deckList : DeckList();
}
void HomeWidget::setRandomCard(ExactCard &newCard)
{
static constexpr int ATTEMPTS = 10;
for (int i = 0; i < ATTEMPTS; ++i) {
ExactCard tmpCard = CardDatabaseManager::query()->getRandomCard();
if (tmpCard != backgroundSourceCard->getCard() && tmpCard.getCardPtr()->getProperty("layout") == "normal" &&
tmpCard.getPrinting().getSet() != nullptr) {
newCard = tmpCard;
return;
}
}
qWarning() << "failed to set random card image after" << ATTEMPTS << "attempts";
}
void HomeWidget::updateRandomCard()
{
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
ExactCard newCard;
switch (backgroundSourceType) {
case BackgroundSources::Theme:
break;
case BackgroundSources::RandomCardArt:
setRandomCard(newCard);
break;
case BackgroundSources::DeckFileArt:
QList<CardRef> cardRefs = backgroundSourceDeck.getCardRefList();
ExactCard oldCard = backgroundSourceCard->getCard();
if (!cardRefs.empty()) {
if (cardRefs.size() == 1) {
newCard = CardDatabaseManager::query()->getCard(cardRefs.first());
} else {
// Keep picking until different
do {
int idx = QRandomGenerator::global()->bounded(cardRefs.size());
newCard = CardDatabaseManager::query()->getCard(cardRefs.at(idx));
} while (newCard == oldCard);
}
} else {
do {
newCard = CardDatabaseManager::query()->getRandomCard();
} while (newCard == oldCard);
}
break;
}
if (!newCard)
return;
connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties);
backgroundSourceCard->setCard(newCard);
background = backgroundSourceCard->getBackground();
}
void HomeWidget::onBackgroundShuffleFrequencyChanged()
{
cardChangeTimer->stop();
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() > 0) {
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
}
}
void HomeWidget::updateBackgroundProperties()
{
background = backgroundSourceCard->getBackground();
updateButtonsToBackgroundColor();
update(); // Triggers repaint
}
void HomeWidget::updateButtonsToBackgroundColor()
{
gradientColors = extractDominantColors(background);
for (HomeStyledButton *button : findChildren<HomeStyledButton *>()) {
button->updateStylesheet(gradientColors);
button->update();
}
}
QGroupBox *HomeWidget::createButtons()
{
QGroupBox *box = new QGroupBox(this);
box->setStyleSheet(R"(
QGroupBox {
font-size: 20px;
color: white; /* Title text color */
background: transparent;
}
QGroupBox::title {
color: white;
subcontrol-origin: margin;
subcontrol-position: top center; /* or top left / right */
}
)");
box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout *boxLayout = new QVBoxLayout;
boxLayout->setAlignment(Qt::AlignHCenter);
QLabel *logoLabel = new QLabel;
logoLabel->setPixmap(overlay.scaledToWidth(200, Qt::SmoothTransformation));
logoLabel->setAlignment(Qt::AlignCenter);
boxLayout->addWidget(logoLabel);
boxLayout->addSpacing(25);
connectButton = new HomeStyledButton("Connect/Play", gradientColors);
boxLayout->addWidget(connectButton);
auto visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors);
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->openDeckInNewTab(LoadedDeck()); });
boxLayout->addWidget(visualDeckEditorButton);
auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->actTabVisualDeckStorage(true); });
boxLayout->addWidget(visualDeckStorageButton);
auto visualDatabaseDisplayButton = new HomeStyledButton(tr("Browse Card Database"), gradientColors);
connect(visualDatabaseDisplayButton, &QPushButton::clicked, tabSupervisor,
&TabSupervisor::addVisualDatabaseDisplayTab);
boxLayout->addWidget(visualDatabaseDisplayButton);
auto edhrecButton = new HomeStyledButton(tr("Browse EDHRec"), gradientColors);
connect(edhrecButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addEdhrecMainTab);
boxLayout->addWidget(edhrecButton);
auto archidektButton = new HomeStyledButton(tr("Browse Archidekt"), gradientColors);
connect(archidektButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addArchidektTab);
boxLayout->addWidget(archidektButton);
auto replaybutton = new HomeStyledButton(tr("View Replays"), gradientColors);
connect(replaybutton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
boxLayout->addWidget(replaybutton);
if (qobject_cast<MainWindow *>(tabSupervisor->parentWidget())) {
auto exitButton = new HomeStyledButton(tr("Quit"), gradientColors);
connect(exitButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
&MainWindow::actExit);
boxLayout->addWidget(exitButton);
}
box->setLayout(boxLayout);
return box;
}
void HomeWidget::updateConnectButton(const ClientStatus status)
{
disconnect(connectButton, &QPushButton::clicked, nullptr, nullptr);
switch (status) {
case StatusConnecting:
connectButton->setText(tr("Connecting..."));
connectButton->setEnabled(false);
break;
case StatusDisconnected:
connectButton->setText(tr("Connect"));
connectButton->setEnabled(true);
connect(connectButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
&MainWindow::actConnect);
break;
case StatusLoggedIn:
connectButton->setText(tr("Play"));
connectButton->setEnabled(true);
connect(connectButton, &QPushButton::clicked, tabSupervisor,
&TabSupervisor::switchToFirstAvailableNetworkTab);
break;
default:
break;
}
}
QPair<QColor, QColor> HomeWidget::extractDominantColors(const QPixmap &pixmap)
{
if (themeManager->isBuiltInTheme() &&
SettingsCache::instance().getHomeTabBackgroundSource() == BackgroundSources::toId(BackgroundSources::Theme)) {
return QPair<QColor, QColor>(QColor::fromRgb(20, 140, 60), QColor::fromRgb(120, 200, 80));
}
// Step 1: Downscale image for performance
QImage image = pixmap.toImage()
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)
.convertToFormat(QImage::Format_RGB32);
QMap<QRgb, int> colorCount;
// Step 2: Count quantized colors
for (int y = 0; y < image.height(); ++y) {
const QRgb *scanLine = reinterpret_cast<const QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); ++x) {
QColor color = QColor::fromRgb(scanLine[x]);
int r = color.red() & 0xF0;
int g = color.green() & 0xF0;
int b = color.blue() & 0xF0;
QRgb quantized = qRgb(r, g, b);
colorCount[quantized]++;
}
}
// Step 3: Sort by frequency
QVector<QPair<QRgb, int>> sortedColors;
for (auto it = colorCount.constBegin(); it != colorCount.constEnd(); ++it) {
sortedColors.append(qMakePair(it.key(), it.value()));
}
std::sort(sortedColors.begin(), sortedColors.end(),
[](const QPair<QRgb, int> &a, const QPair<QRgb, int> &b) { return a.second > b.second; });
// Step 4: Pick top two distinct colors
QColor first = QColor(sortedColors.value(0).first);
QColor second = first;
for (int i = 1; i < sortedColors.size(); ++i) {
QColor candidate = QColor(sortedColors[i].first);
if (candidate != first) {
second = candidate;
break;
}
}
return QPair<QColor, QColor>(first, second);
}
void HomeWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
if (!background.isNull()) {
QSize widgetSize = size() * devicePixelRatio();
QPixmap toDraw = background.scaled(widgetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Draw scaled background centered
QSize bgSize = toDraw.size();
QPoint topLeft((widgetSize.width() - bgSize.width()) / (devicePixelRatio() * 2), // undo scaling for painter
(widgetSize.height() - bgSize.height()) / (devicePixelRatio() * 2));
painter.drawPixmap(topLeft, toDraw);
}
// Draw translucent black overlay with rounded corners
QRectF overlayRect(5, 5, width() - 10, height() - 10);
QPainterPath roundedRectPath;
roundedRectPath.addRoundedRect(overlayRect, 20, 20);
QColor semiTransparentBlack(0, 0, 0, static_cast<int>(255 * 0.33));
painter.fillPath(roundedRectPath, semiTransparentBlack);
// Card name overlay (bottom-right)
QString cardName;
ExactCard card = backgroundSourceCard->getCard();
if (card) {
cardName = card.getCardPtr()->getName();
if (card.getPrinting().getSet() != nullptr) {
cardName += " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " +
card.getPrinting().getProperty("num");
}
}
if (!cardName.isEmpty() && SettingsCache::instance().getHomeTabDisplayCardName()) {
QFont font = painter.font();
font.setPointSize(14);
font.setBold(true);
painter.setFont(font);
QFontMetrics fm(font);
constexpr int padding = 10;
constexpr int margin = 15;
QRect textRect = fm.boundingRect(cardName);
QRect bgRect(width() - textRect.width() - padding * 2 - margin,
height() - textRect.height() - padding * 2 - margin, textRect.width() + padding * 2,
textRect.height() + padding * 2);
// Background bubble
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 160));
painter.drawRoundedRect(bgRect, 8, 8);
// Text
painter.setPen(Qt::white);
painter.drawText(bgRect.adjusted(padding, padding, -padding, -padding), Qt::AlignRight | Qt::AlignVCenter,
cardName);
}
QWidget::paintEvent(event);
}

View file

@ -1,52 +0,0 @@
/**
* @file home_widget.h
* @ingroup Core
* @ingroup Widgets
* @brief TODO: Document this.
*/
#ifndef HOME_WIDGET_H
#define HOME_WIDGET_H
#include "../../../interface/widgets/tabs/tab_supervisor.h"
#include "../cards/card_info_picture_art_crop_widget.h"
#include "home_styled_button.h"
#include <QGridLayout>
#include <QWidget>
#include <libcockatrice/network/client/abstract/abstract_client.h>
class HomeWidget : public QWidget
{
Q_OBJECT
public:
HomeWidget(QWidget *parent, TabSupervisor *tabSupervisor);
void updateRandomCard();
QPair<QColor, QColor> extractDominantColors(const QPixmap &pixmap);
public slots:
void paintEvent(QPaintEvent *event) override;
void initializeBackgroundFromSource();
void onBackgroundShuffleFrequencyChanged();
void updateBackgroundProperties();
void updateButtonsToBackgroundColor();
QGroupBox *createButtons();
void updateConnectButton(const ClientStatus status);
private:
QGridLayout *layout;
QTimer *cardChangeTimer;
TabSupervisor *tabSupervisor;
QPixmap background;
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
DeckList backgroundSourceDeck;
QPixmap overlay;
QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton;
void setRandomCard(ExactCard &newCard);
void loadBackgroundSourceDeck();
};
#endif // HOME_WIDGET_H

View file

@ -1,6 +1,7 @@
#include "printing_selector_card_overlay_widget.h" #include "printing_selector_card_overlay_widget.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../cards/card_info_picture_widget.h"
#include "printing_selector_card_display_widget.h" #include "printing_selector_card_display_widget.h"
#include <QImageReader> #include <QImageReader>

View file

@ -8,6 +8,8 @@
#ifndef TAB_GENERIC_DECK_EDITOR_H #ifndef TAB_GENERIC_DECK_EDITOR_H
#define TAB_GENERIC_DECK_EDITOR_H #define TAB_GENERIC_DECK_EDITOR_H
#include "../../deck_loader/deck_loader.h"
#include "../../deck_loader/loaded_deck.h"
#include "../interface/widgets/deck_editor/deck_editor_card_database_dock_widget.h" #include "../interface/widgets/deck_editor/deck_editor_card_database_dock_widget.h"
#include "../interface/widgets/deck_editor/deck_editor_card_info_dock_widget.h" #include "../interface/widgets/deck_editor/deck_editor_card_info_dock_widget.h"
#include "../interface/widgets/deck_editor/deck_editor_database_display_widget.h" #include "../interface/widgets/deck_editor/deck_editor_database_display_widget.h"
@ -15,7 +17,6 @@
#include "../interface/widgets/deck_editor/deck_editor_filter_dock_widget.h" #include "../interface/widgets/deck_editor/deck_editor_filter_dock_widget.h"
#include "../interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h" #include "../interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h"
#include "../interface/widgets/menus/deck_editor_menu.h" #include "../interface/widgets/menus/deck_editor_menu.h"
#include "../interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include "tab.h" #include "tab.h"
#include <libcockatrice/deck_list/deck_list_history_manager.h> #include <libcockatrice/deck_list/deck_list_history_manager.h>

View file

@ -1,29 +0,0 @@
#include "archidekt_deck_listing_api_response.h"
#include <QDebug>
#include <QJsonArray>
void ArchidektDeckListingApiResponse::fromJson(const QJsonObject &json)
{
count = json.value("count").toInt();
next = QUrl(json.value("next").toString());
QJsonArray containerJson = json.value("results").toArray();
for (const QJsonValue &deckListingValue : containerJson) {
ArchidektApiResponseDeckListingContainer listingResult;
listingResult.fromJson(deckListingValue.toObject());
results.append(listingResult);
}
}
void ArchidektDeckListingApiResponse::debugPrint() const
{
qDebug() << "Count:" << count;
qDebug() << "Next:" << next;
qDebug() << "Results:";
for (const auto &deckListing : results) {
deckListing.debugPrint();
}
}

View file

@ -1,22 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_DECK_LISTING_API_RESPONSE_H
#define COCKATRICE_ARCHIDEKT_DECK_LISTING_API_RESPONSE_H
#include "deck_listings/archidekt_api_response_deck_listing_container.h"
#include <QJsonObject>
#include <QString>
#include <QUrl>
class ArchidektDeckListingApiResponse
{
public:
int count;
QUrl next;
QVector<ArchidektApiResponseDeckListingContainer> results;
void fromJson(const QJsonObject &json);
void debugPrint() const;
};
#endif // COCKATRICE_DECK_LISTING_API_RESPONSE_H

View file

@ -1,227 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_FORMATS_H
#define COCKATRICE_ARCHIDEKT_FORMATS_H
#include <QHash>
#include <QString>
namespace ArchidektFormats
{
enum class DeckFormat
{
Standard = 0,
Modern = 1,
Commander = 2,
Legacy = 3,
Vintage = 4,
Pauper = 5,
Custom = 6,
Frontier = 7,
FutureStandard = 8,
PennyDreadful = 9,
Commander1v1 = 10,
DualCommander = 11,
Brawl = 12,
// Values outside Archidekt range
Alchemy = 1000,
Historic = 1001,
Gladiator = 1002,
Oathbreaker = 1003,
OldSchool = 1004,
PauperCommander = 1005,
Pioneer = 1006,
PreDH = 1007,
Premodern = 1008,
StandardBrawl = 1009,
Timeless = 1010,
Unknown = 1011
};
inline static QString formatToApiName(DeckFormat format)
{
switch (format) {
case DeckFormat::Standard:
return "Standard";
case DeckFormat::Modern:
return "Modern";
case DeckFormat::Commander:
return "Commander";
case DeckFormat::Legacy:
return "Legacy";
case DeckFormat::Vintage:
return "Vintage";
case DeckFormat::Pauper:
return "Pauper";
case DeckFormat::Custom:
return "Custom";
case DeckFormat::Frontier:
return "Frontier";
case DeckFormat::FutureStandard:
return "Future Std";
case DeckFormat::PennyDreadful:
return "Penny Dreadful";
case DeckFormat::Commander1v1:
return "1v1 Commander";
case DeckFormat::DualCommander:
return "Dual Commander";
case DeckFormat::Brawl:
return "Brawl";
case DeckFormat::Alchemy:
return "Alchemy";
case DeckFormat::Historic:
return "Historic";
case DeckFormat::Gladiator:
return "Gladiator";
case DeckFormat::Oathbreaker:
return "Oathbreaker";
case DeckFormat::OldSchool:
return "Old School";
case DeckFormat::PauperCommander:
return "Pauper Commander";
case DeckFormat::Pioneer:
return "Pioneer";
case DeckFormat::PreDH:
return "PreDH";
case DeckFormat::Premodern:
return "Premodern";
case DeckFormat::StandardBrawl:
return "Standard Brawl";
case DeckFormat::Timeless:
return "Timeless";
default:
return "Unknown";
}
}
inline static DeckFormat apiNameToFormat(const QString &name)
{
const QString n = name.trimmed();
if (n.compare("Standard", Qt::CaseInsensitive) == 0)
return DeckFormat::Standard;
if (n.compare("Modern", Qt::CaseInsensitive) == 0)
return DeckFormat::Modern;
if (n.compare("Commander", Qt::CaseInsensitive) == 0)
return DeckFormat::Commander;
if (n.compare("Legacy", Qt::CaseInsensitive) == 0)
return DeckFormat::Legacy;
if (n.compare("Vintage", Qt::CaseInsensitive) == 0)
return DeckFormat::Vintage;
if (n.compare("Pauper", Qt::CaseInsensitive) == 0)
return DeckFormat::Pauper;
if (n.compare("Custom", Qt::CaseInsensitive) == 0)
return DeckFormat::Custom;
if (n.compare("Frontier", Qt::CaseInsensitive) == 0)
return DeckFormat::Frontier;
if (n.compare("Future Std", Qt::CaseInsensitive) == 0)
return DeckFormat::FutureStandard;
if (n.compare("Penny Dreadful", Qt::CaseInsensitive) == 0)
return DeckFormat::PennyDreadful;
if (n.compare("1v1 Commander", Qt::CaseInsensitive) == 0)
return DeckFormat::Commander1v1;
if (n.compare("Dual Commander", Qt::CaseInsensitive) == 0)
return DeckFormat::DualCommander;
if (n.compare("Brawl", Qt::CaseInsensitive) == 0)
return DeckFormat::Brawl;
if (n.compare("Alchemy", Qt::CaseInsensitive) == 0)
return DeckFormat::Alchemy;
if (n.compare("Historic", Qt::CaseInsensitive) == 0)
return DeckFormat::Historic;
if (n.compare("Gladiator", Qt::CaseInsensitive) == 0)
return DeckFormat::Gladiator;
if (n.compare("Oathbreaker", Qt::CaseInsensitive) == 0)
return DeckFormat::Oathbreaker;
if (n.compare("Old School", Qt::CaseInsensitive) == 0)
return DeckFormat::OldSchool;
if (n.compare("Pauper Commander", Qt::CaseInsensitive) == 0)
return DeckFormat::PauperCommander;
if (n.compare("Pioneer", Qt::CaseInsensitive) == 0)
return DeckFormat::Pioneer;
if (n.compare("PreDH", Qt::CaseInsensitive) == 0)
return DeckFormat::PreDH;
if (n.compare("Premodern", Qt::CaseInsensitive) == 0)
return DeckFormat::Premodern;
if (n.compare("Standard Brawl", Qt::CaseInsensitive) == 0)
return DeckFormat::StandardBrawl;
if (n.compare("Timeless", Qt::CaseInsensitive) == 0)
return DeckFormat::Timeless;
return DeckFormat::Unknown;
}
inline static QString formatToCockatriceName(DeckFormat format)
{
switch (format) {
case DeckFormat::Standard:
return "standard";
case DeckFormat::Modern:
return "modern";
case DeckFormat::Commander:
return "commander";
case DeckFormat::Legacy:
return "legacy";
case DeckFormat::Vintage:
return "vintage";
case DeckFormat::Pauper:
return "pauper";
case DeckFormat::Brawl:
return "brawl";
case DeckFormat::PennyDreadful:
return "penny";
case DeckFormat::FutureStandard:
return "future";
case DeckFormat::Commander1v1:
return "duel";
case DeckFormat::DualCommander:
return "duel";
case DeckFormat::Alchemy:
return "alchemy";
case DeckFormat::Historic:
return "historic";
case DeckFormat::Gladiator:
return "gladiator";
case DeckFormat::Oathbreaker:
return "oathbreaker";
case DeckFormat::OldSchool:
return "oldschool";
case DeckFormat::PauperCommander:
return "paupercommander";
case DeckFormat::Pioneer:
return "pioneer";
case DeckFormat::PreDH:
return "predh";
case DeckFormat::Premodern:
return "premodern";
case DeckFormat::StandardBrawl:
return "standardbrawl";
case DeckFormat::Timeless:
return "timeless";
default:
return {};
}
}
inline static DeckFormat cockatriceNameToFormat(const QString &apiName)
{
static const QHash<QString, DeckFormat> map = {
{"standard", DeckFormat::Standard}, {"modern", DeckFormat::Modern},
{"commander", DeckFormat::Commander}, {"legacy", DeckFormat::Legacy},
{"vintage", DeckFormat::Vintage}, {"pauper", DeckFormat::Pauper},
{"brawl", DeckFormat::Brawl}, {"penny", DeckFormat::PennyDreadful},
{"future", DeckFormat::FutureStandard}, {"duel", DeckFormat::Commander1v1},
{"alchemy", DeckFormat::Alchemy}, {"historic", DeckFormat::Historic},
{"gladiator", DeckFormat::Gladiator}, {"oathbreaker", DeckFormat::Oathbreaker},
{"oldschool", DeckFormat::OldSchool}, {"paupercommander", DeckFormat::PauperCommander},
{"pioneer", DeckFormat::Pioneer}, {"predh", DeckFormat::PreDH},
{"premodern", DeckFormat::Premodern}, {"standardbrawl", DeckFormat::StandardBrawl},
{"timeless", DeckFormat::Timeless}};
return map.value(apiName.toLower(), DeckFormat::Unknown);
}
} // namespace ArchidektFormats
#endif // COCKATRICE_ARCHIDEKT_FORMATS_H

View file

@ -1,61 +0,0 @@
#include "archidekt_api_response_card.h"
void ArchidektApiResponseCard::fromJson(const QJsonObject &json)
{
id = json.value("id").toInt();
artist = json.value("artist").toString();
tcgProductId = json.value("tcgProductId").toInt();
ckFoilId = json.value("ckFoilId").toInt();
ckNormalId = json.value("ckNormalId").toInt();
cmEd = json.value("cmEd").toString();
scgSku = json.value("scgSku").toString();
scgFoilSku = json.value("scgFoilSku").toString();
collectorNumber = json.value("collectorNumber").toString();
multiverseId = json.value("multiverseId").toInt();
mtgoFoilId = json.value("mtgoFoilId").toInt();
mtgoNormalId = json.value("mtgoNormalId").toInt();
uid = json.value("uid").toString();
displayName = json.value("displayName").toString();
releasedAt = json.value("releasedAt").toString();
edition.fromJson(json.value("edition").toObject());
flavor = json.value("flavor").toString();
//! \todo but not really important
//! \todo games = {""};
//! \todo options = {""};
scryfallImageHash = json.value("scryfallImageHash").toString();
oracleCard = json.value("oracleCard").toObject();
owned = json.value("owned").toInt();
pinnedStatus = json.value("pinnedStatus").toInt();
rarity = json.value("rarity").toString();
//! \todo but not really important
//! \todo globalCategories = {""};
}
void ArchidektApiResponseCard::debugPrint() const
{
qDebug() << "Id:" << id;
qDebug() << "id:" << artist;
qDebug() << "artist:" << tcgProductId;
qDebug() << "tcgProductId:" << ckFoilId;
qDebug() << "ckFoilId:" << ckNormalId;
qDebug() << "ckNormalId:" << cmEd;
qDebug() << "cmEd:" << scgSku;
qDebug() << "scgSku:" << scgFoilSku;
qDebug() << "scgFoilSku:" << collectorNumber;
qDebug() << "collectorNumber:" << multiverseId;
qDebug() << "multiverseId:" << mtgoFoilId;
qDebug() << "mtgoFoilId:" << mtgoNormalId;
qDebug() << "mtgoNormalId:" << uid;
qDebug() << "uid:" << displayName;
qDebug() << "displayName:" << releasedAt;
qDebug() << "releasedAt:" << flavor;
qDebug() << "flavor:" << games;
qDebug() << "games:" << options;
qDebug() << "options:" << scryfallImageHash;
qDebug() << "scryfallImageHash:" << owned;
qDebug() << "owned:" << pinnedStatus;
qDebug() << "pinnedStatus:" << rarity;
qDebug() << "rarity:" << globalCategories;
}

View file

@ -1,68 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_CARD_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_CARD_H
#include "archidekt_api_response_edition.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
class ArchidektApiResponseCard
{
public:
// Constructor
ArchidektApiResponseCard() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
QJsonObject getOracleCard() const
{
return oracleCard;
};
QString getCollectorNumber() const
{
return collectorNumber;
}
ArchidektApiResponseEdition getEdition() const
{
return edition;
}
private:
int id;
QString artist;
int tcgProductId;
int ckFoilId;
int ckNormalId;
QString cmEd;
QString scgSku;
QString scgFoilSku;
QString collectorNumber;
int multiverseId;
int mtgoFoilId;
int mtgoNormalId;
QString uid;
QString displayName;
QString releasedAt;
ArchidektApiResponseEdition edition;
QString flavor;
QStringList games;
QStringList options;
QString scryfallImageHash;
QJsonObject oracleCard;
int owned;
int pinnedStatus;
// ArchidektApiResponsePrices prices;
QString rarity;
QStringList globalCategories;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_CARD_H

View file

@ -1,68 +0,0 @@
#include "archidekt_api_response_card_entry.h"
void ArchidektApiResponseCardEntry::fromJson(const QJsonObject &json)
{
id = json.value("id").toInt();
categories.clear();
auto categoriesJson = json.value("categories").toArray();
for (const auto &categoryValue : categoriesJson) {
Category cat;
if (categoryValue.isObject()) {
QJsonObject obj = categoryValue.toObject();
cat.id = obj.value("id").toInt();
cat.name = obj.value("name").toString();
cat.isPremier = obj.value("isPremier").toBool();
cat.includedInDeck = obj.value("includedInDeck").toBool();
cat.includedInPrice = obj.value("includedInPrice").toBool();
} else if (categoryValue.isString()) {
cat.name = categoryValue.toString();
// assume mainboard unless known otherwise
cat.includedInDeck = true;
}
categories.append(cat);
}
companion = json.value("companion").toBool();
flippedDefault = json.value("flippedDefault").toBool();
label = json.value("label").toString();
modifier = json.value("modifier").toString();
quantity = json.value("quantity").toInt();
customCmc = json.value("customCmc").toInt();
// removedCategories = {""};
createdAt = json.value("createdAt").toString();
updatedAt = json.value("updatedAt").toString();
deletedAt = json.value("deletedAt").toString();
notes = json.value("notes").toString();
card.fromJson(json.value("card").toObject());
}
void ArchidektApiResponseCardEntry::debugPrint() const
{
qDebug() << "Id:" << id;
for (auto category : categories) {
qDebug() << "Category ID:" << category.id;
qDebug() << "Category Name:" << category.name;
qDebug() << "Category Premier:" << category.isPremier;
qDebug() << "Category Included in Deck:" << category.includedInDeck;
qDebug() << "Category Included in Price:" << category.includedInPrice;
}
qDebug() << "Companion:" << companion;
qDebug() << "FlippedDefault:" << flippedDefault;
qDebug() << "Label:" << label;
qDebug() << "Modifier:" << modifier;
qDebug() << "Quantity:" << quantity;
qDebug() << "CustomCmc:" << customCmc;
qDebug() << "RemovedCategories:" << removedCategories;
qDebug() << "CreatedAt:" << createdAt;
qDebug() << "UpdatedAt:" << updatedAt;
qDebug() << "DeletedAt:" << deletedAt;
qDebug() << "Notes:" << notes;
card.debugPrint();
}

View file

@ -1,65 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_CARD_ENTRY_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_CARD_ENTRY_H
#include "archidekt_api_response_card.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
struct Category
{
int id;
QString name;
bool isPremier;
bool includedInDeck;
bool includedInPrice;
};
class ArchidektApiResponseCardEntry
{
public:
// Constructor
ArchidektApiResponseCardEntry() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
ArchidektApiResponseCard getCard() const
{
return card;
};
QList<Category> getCategories() const
{
return categories;
}
int getQuantity() const
{
return quantity;
}
private:
int id;
QList<Category> categories;
bool companion;
bool flippedDefault;
QString label;
QString modifier;
int quantity;
int customCmc;
QStringList removedCategories;
QString createdAt;
QString updatedAt;
QString deletedAt;
QString notes;
ArchidektApiResponseCard card;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_CARD_ENTRY_H

View file

@ -1,19 +0,0 @@
#include "archidekt_api_response_edition.h"
void ArchidektApiResponseEdition::fromJson(const QJsonObject &json)
{
editionCode = json.value("editioncode").toString();
editionName = json.value("editionname").toString();
editionDate = json.value("editiondate").toString();
editionType = json.value("editiontype").toString();
mtgoCode = json.value("mtgocode").toString();
}
void ArchidektApiResponseEdition::debugPrint() const
{
qDebug() << "Edition Code: " << editionCode;
qDebug() << "Edition Name: " << editionName;
qDebug() << "Edition Date: " << editionDate;
qDebug() << "Edition Type: " << editionType;
qDebug() << "Mtgo Code: " << mtgoCode;
}

View file

@ -1,51 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_EDITION_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_EDITION_H
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
class ArchidektApiResponseEdition
{
public:
// Constructor
ArchidektApiResponseEdition() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
[[nodiscard]] QString getEditionCode() const
{
return editionCode;
}
[[nodiscard]] QString getEditionName() const
{
return editionName;
}
[[nodiscard]] QString getEditionDate() const
{
return editionDate;
}
[[nodiscard]] QString getEditionType() const
{
return editionType;
}
[[nodiscard]] QString getMtgoCode() const
{
return mtgoCode;
}
private:
QString editionCode;
QString editionName;
QString editionDate;
QString editionType;
QString mtgoCode;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_EDITION_H

View file

@ -1,79 +0,0 @@
#include "archidekt_api_response_deck.h"
#include "../card/archidekt_api_response_card_entry.h"
void ArchidektApiResponseDeck::fromJson(const QJsonObject &json)
{
id = json.value("id").toInt();
name = json.value("name").toString();
size = json.value("size").toInt();
updatedAt = json.value("updatedAt").toString();
createdAt = json.value("createdAt").toString();
deckFormat = json.value("deckFormat").toInt();
edhBracket = json.value("edhBracket").toInt();
featured = json.value("featured").toString();
customFeatured = json.value("customFeatured").toString();
viewCount = json.value("viewCount").toInt();
privateDeck = json.value("private").toBool();
unlisted = json.value("unlisted").toBool();
theoryCrafted = json.value("theoryCrafted").toBool();
points = json.value("points").toInt();
userInput = json.value("userInput").toInt();
owner.fromJson(json.value("owner").toObject());
commentRoot = json.value("commentRoot").toInt();
editors = json.value("editors").toString();
parentFolderId = json.value("parentFolderId").toInt();
bookmarked = json.value("bookmarked").toBool();
auto categoriesJson = json.value("categories").toArray();
for (auto category : categoriesJson) {
ArchidektApiResponseDeckCategory categoryEntry;
categoryEntry.fromJson(category.toObject());
categories.append(categoryEntry);
}
// deckTags = {""};
playgroupDeckUrl = json.value("playgroupDeckUrl").toString();
cardPackage = json.value("cardPackage").toString();
auto cardsObject = json.value("cards").toArray();
for (auto card : cardsObject) {
ArchidektApiResponseCardEntry entry;
entry.fromJson(card.toObject());
cards.append(entry);
}
// TODO but not really important
// customCards = {""};
}
void ArchidektApiResponseDeck::debugPrint() const
{
qDebug() << "Id:" << id;
qDebug() << "Name:" << name;
qDebug() << "Size:" << size;
qDebug() << "UpdatedAt:" << updatedAt;
qDebug() << "CreatedAt:" << createdAt;
qDebug() << "DeckFormat:" << deckFormat;
qDebug() << "EdhBracket:" << edhBracket;
qDebug() << "Featured:" << featured;
qDebug() << "CustomFeatured:" << customFeatured;
qDebug() << "ViewCount:" << viewCount;
qDebug() << "Private:" << privateDeck;
qDebug() << "Unlisted:" << unlisted;
qDebug() << "TheoryCrafted:" << theoryCrafted;
qDebug() << "Points:" << points;
qDebug() << "UserInput:" << userInput;
owner.debugPrint();
qDebug() << "CommentRoot:" << commentRoot;
qDebug() << "Editors:" << editors;
qDebug() << "ParentFolderId:" << parentFolderId;
qDebug() << "Bookmarked:" << bookmarked;
qDebug() << "DeckTags:" << deckTags;
qDebug() << "PlaygroupDeckUrl:" << playgroupDeckUrl;
qDebug() << "CardPackage:" << cardPackage;
for (auto card : cards) {
card.debugPrint();
}
}

View file

@ -1,76 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_H
#include "../card/archidekt_api_response_card.h"
#include "../card/archidekt_api_response_card_entry.h"
#include "../deck_listings/archidekt_api_response_deck_owner.h"
#include "archidekt_api_response_deck_category.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
class ArchidektApiResponseDeck
{
public:
// Constructor
ArchidektApiResponseDeck() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
QVector<ArchidektApiResponseCardEntry> getCards() const
{
return cards;
};
QVector<ArchidektApiResponseDeckCategory> getCategories() const
{
return categories;
}
QString getDeckName() const
{
return name;
};
int getDeckFormat() const
{
return deckFormat;
}
private:
int id;
QString name;
int size;
QString updatedAt;
QString createdAt;
int deckFormat;
int edhBracket;
QString featured;
QString customFeatured;
int viewCount;
bool privateDeck;
bool unlisted;
bool theoryCrafted;
int points;
int userInput;
ArchidektApiResponseDeckOwner owner;
int commentRoot;
QString editors;
int parentFolderId;
bool bookmarked;
QVector<ArchidektApiResponseDeckCategory> categories;
QStringList deckTags;
QString playgroupDeckUrl;
QString cardPackage;
QVector<ArchidektApiResponseCardEntry> cards;
QStringList customCards;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_H

View file

@ -1,19 +0,0 @@
#include "archidekt_api_response_deck_category.h"
void ArchidektApiResponseDeckCategory::fromJson(const QJsonObject &json)
{
id = json.value("id").toInt();
name = json.value("name").toString();
isPremier = json.value("isPremier").toBool();
includedInDeck = json.value("includedInDeck").toBool();
includedInPrice = json.value("includedInPrice").toBool();
}
void ArchidektApiResponseDeckCategory::debugPrint() const
{
qDebug() << "Id:" << id;
qDebug() << "Name:" << name;
qDebug() << "isPremier:" << isPremier;
qDebug() << "IncludedInDeck:" << includedInDeck;
qDebug() << "IncludedInPrice:" << includedInPrice;
}

View file

@ -1,55 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_CATEGORY_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_CATEGORY_H
#include "../card/archidekt_api_response_card.h"
#include "../card/archidekt_api_response_card_entry.h"
#include "../deck_listings/archidekt_api_response_deck_owner.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
class ArchidektApiResponseDeckCategory
{
public:
// Constructor
ArchidektApiResponseDeckCategory() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
[[nodiscard]] int getId() const
{
return id;
}
[[nodiscard]] QString getName() const
{
return name;
}
[[nodiscard]] bool getIsPremier() const
{
return isPremier;
}
[[nodiscard]] bool getIncludedInDeck() const
{
return includedInDeck;
}
[[nodiscard]] bool getIncludedInPrice() const
{
return includedInPrice;
}
private:
int id;
QString name;
bool isPremier;
bool includedInDeck;
bool includedInPrice;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_CATEGORY_H

View file

@ -1,64 +0,0 @@
#include "archidekt_api_response_deck_listing_container.h"
#include "archidekt_api_response_deck_owner.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
void ArchidektApiResponseDeckListingContainer::fromJson(const QJsonObject &json)
{
id = json.value("id").toInt();
name = json.value("name").toString();
size = json.value("size").toInt();
updatedAt = json.value("updatedAt").toString();
createdAt = json.value("createdAt").toString();
deckFormat = json.value("deckFormat").toInt();
edhBracket = json.value("edhBracket").toInt();
featured = json.value("featured").toString();
customFeatured = json.value("customFeatured").toString();
viewCount = json.value("viewCount").toInt();
privateDeck = json.value("private").toBool();
unlisted = json.value("unlisted").toBool();
theoryCrafted = json.value("theoryCrafted").toBool();
game = json.value("game").toString();
hasDescription = json.value("hasDescription").toBool();
// TODO
// tags = {""};
parentFolderId = json.value("parentFolderId").toInt();
owner.fromJson(json.value("owner").toObject());
auto colorsJson = json.value("colors").toObject();
for (auto color : colorsJson.keys()) {
colors[color] = colorsJson[color].toInt();
}
cardPackage = json.value("cardPackage").toString();
contest = json.value("contest").toString();
}
void ArchidektApiResponseDeckListingContainer::debugPrint() const
{
qDebug() << "Id:" << id;
qDebug() << "Name:" << name;
qDebug() << "Size:" << size;
qDebug() << "UpdatedAt:" << updatedAt;
qDebug() << "CreatedAt:" << createdAt;
qDebug() << "DeckFormat:" << deckFormat;
qDebug() << "EdhBracket:" << edhBracket;
qDebug() << "Featured:" << featured;
qDebug() << "CustomFeatured:" << customFeatured;
qDebug() << "ViewCount:" << viewCount;
qDebug() << "Private:" << privateDeck;
qDebug() << "Unlisted:" << unlisted;
qDebug() << "TheoryCrafted:" << theoryCrafted;
qDebug() << "Game:" << game;
qDebug() << "HasDescription:" << hasDescription;
qDebug() << "Tags:" << tags;
qDebug() << "ParentFolderId:" << parentFolderId;
owner.debugPrint();
qDebug() << "Colors:" << colors;
qDebug() << "CardPackage" << cardPackage;
qDebug() << "Contest:" << contest;
}

View file

@ -1,133 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_DECK_LISTING_API_RESPONSE_CONTAINER_H
#define COCKATRICE_ARCHIDEKT_DECK_LISTING_API_RESPONSE_CONTAINER_H
#include "archidekt_api_response_deck_owner.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
class ArchidektApiResponseDeckListingContainer
{
public:
// Constructor
ArchidektApiResponseDeckListingContainer() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
[[nodiscard]] int getId() const
{
return id;
}
[[nodiscard]] QString getName() const
{
return name;
}
[[nodiscard]] int getSize() const
{
return size;
}
[[nodiscard]] QString getUpdatedAt() const
{
return updatedAt;
}
[[nodiscard]] QString getCreatedAt() const
{
return createdAt;
}
[[nodiscard]] int getDeckFormat() const
{
return deckFormat;
}
[[nodiscard]] int getEDHBracket() const
{
return edhBracket;
}
[[nodiscard]] QString getFeatured() const
{
return featured;
}
[[nodiscard]] QString getCustomFeatured() const
{
return customFeatured;
}
[[nodiscard]] int getViewCount() const
{
return viewCount;
}
[[nodiscard]] bool getPrivateDeck() const
{
return privateDeck;
}
[[nodiscard]] bool getUnlisted() const
{
return unlisted;
}
[[nodiscard]] bool getTheoryCrafted() const
{
return theoryCrafted;
}
[[nodiscard]] QString getGame() const
{
return game;
}
[[nodiscard]] bool getHasDescription() const
{
return hasDescription;
}
[[nodiscard]] QStringList getTags() const
{
return tags;
}
[[nodiscard]] int getParentFolderId() const
{
return parentFolderId;
}
[[nodiscard]] ArchidektApiResponseDeckOwner getOwner() const
{
return owner;
}
[[nodiscard]] QMap<QString, int> getColors() const
{
return colors;
}
[[nodiscard]] QString getCardPackage() const
{
return cardPackage;
}
[[nodiscard]] QString getContest() const
{
return contest;
}
private:
int id;
QString name;
int size;
QString updatedAt;
QString createdAt;
int deckFormat;
int edhBracket;
QString featured;
QString customFeatured;
int viewCount;
bool privateDeck;
bool unlisted;
bool theoryCrafted;
QString game;
bool hasDescription;
QStringList tags;
int parentFolderId;
ArchidektApiResponseDeckOwner owner;
QMap<QString, int> colors;
QString cardPackage;
QString contest;
};
#endif // COCKATRICE_ARCHIDEKT_DECK_LISTING_API_RESPONSE_CONTAINER_H

View file

@ -1,22 +0,0 @@
#include "archidekt_api_response_deck_owner.h"
void ArchidektApiResponseDeckOwner::fromJson(const QJsonObject &json)
{
id = json.value("id").toInt();
userName = json.value("username").toString();
avatar = QUrl(json.value("avatar").toString());
moderator = json.value("moderator").toBool();
pledgeLevel = json.value("pledgeLevel").toInt();
// TODO but not really important
// roles = {""};
}
void ArchidektApiResponseDeckOwner::debugPrint() const
{
qDebug() << "Id:" << id;
qDebug() << "UserName:" << userName;
qDebug() << "Avatar:" << avatar;
qDebug() << "Moderator:" << moderator;
qDebug() << "PledgeLevel:" << pledgeLevel;
qDebug() << "Roles:" << roles;
}

View file

@ -1,36 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_OWNER_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_OWNER_H
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
class ArchidektApiResponseDeckOwner
{
public:
// Constructor
ArchidektApiResponseDeckOwner() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
[[nodiscard]] QString getName() const
{
return userName;
}
private:
int id;
QString userName;
QUrl avatar;
bool moderator;
int pledgeLevel;
QStringList roles;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_OWNER_H

View file

@ -1,218 +0,0 @@
#include "archidekt_api_response_deck_display_widget.h"
#include "../../../../../deck_loader/card_node_function.h"
#include "../../../../../deck_loader/deck_loader.h"
#include "../../../../cards/card_size_widget.h"
#include "../../../../cards/deck_card_zone_display_widget.h"
#include "../../../../visual_deck_editor/visual_deck_display_options_widget.h"
#include "../api_response/archidekt_formats.h"
#include "../api_response/deck/archidekt_api_response_deck.h"
#include <QSortFilterProxyModel>
#include <libcockatrice/card/import/card_name_normalizer.h>
ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWidget *parent,
ArchidektApiResponseDeck _response,
CardSizeWidget *_cardSizeSlider)
: QWidget(parent), response(_response), cardSizeSlider(_cardSizeSlider)
{
layout = new QVBoxLayout(this);
setLayout(layout);
navigationContainer = new QWidget(this);
navigationContainerLayout = new QHBoxLayout(navigationContainer);
homeButton = new QPushButton(navigationContainer);
navigationContainerLayout->addWidget(homeButton);
connect(homeButton, &QPushButton::clicked, this, &ArchidektApiResponseDeckDisplayWidget::requestSearch);
openInEditorButton = new QPushButton(navigationContainer);
navigationContainerLayout->addWidget(openInEditorButton);
connect(openInEditorButton, &QPushButton::clicked, this,
&ArchidektApiResponseDeckDisplayWidget::actOpenInDeckEditor);
layout->addWidget(navigationContainer);
displayOptionsWidget = new VisualDeckDisplayOptionsWidget(this);
layout->addWidget(displayOptionsWidget);
connect(displayOptionsWidget, &VisualDeckDisplayOptionsWidget::groupCriteriaChanged, this,
&ArchidektApiResponseDeckDisplayWidget::onGroupCriteriaChange);
scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
layout->addWidget(scrollArea);
container = new QWidget(scrollArea);
scrollArea->setWidget(container);
containerLayout = new QVBoxLayout(container);
container->setLayout(containerLayout);
zoneContainer = new QWidget(container);
containerLayout->addWidget(zoneContainer);
zoneContainerLayout = new QVBoxLayout(zoneContainer);
zoneContainer->setLayout(zoneContainerLayout);
QString tempDeck;
QTextStream deckStream(&tempDeck);
QString mainboardText;
QString sideboardText;
QTextStream mainStream(&mainboardText);
QTextStream sideStream(&sideboardText);
for (const auto &card : response.getCards()) {
QString fullName = card.getCard().getOracleCard().value("name").toString();
// We don't really care about the second card, the card database already has it as a relation
QString cleanName = fullName.split("//").first().trimmed();
QString line = QString("%1 %2 (%3) %4\n")
.arg(card.getQuantity())
.arg(cleanName)
.arg(card.getCard().getEdition().getEditionCode().toUpper())
.arg(card.getCard().getCollectorNumber());
bool isCommander = false;
bool isSideboardCategory = false;
bool includedInDeck = false;
for (const auto &cat : card.getCategories()) {
if (cat.name.compare("Commander", Qt::CaseInsensitive) == 0) {
isCommander = true;
}
if (cat.name.compare("Sideboard", Qt::CaseInsensitive) == 0 ||
cat.name.compare("Maybeboard", Qt::CaseInsensitive) == 0) {
isSideboardCategory = true;
}
if (cat.includedInDeck) {
includedInDeck = true;
}
}
QString target;
if (isCommander || isSideboardCategory) {
sideStream << line;
} else if (includedInDeck) {
mainStream << line;
} else {
sideStream << line;
}
}
// Combine with blank line separator
tempDeck = mainboardText;
if (!sideboardText.isEmpty()) {
tempDeck += "\n";
tempDeck += sideboardText;
}
model = new DeckListModel(this);
connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset);
auto decklist = QSharedPointer<DeckList>(new DeckList);
decklist->loadFromStream_Plain(deckStream, false, CardNameNormalizer());
model->setDeckList(decklist);
model->forEachCard(CardNodeFunction::ResolveProviderId());
retranslateUi();
}
void ArchidektApiResponseDeckDisplayWidget::retranslateUi()
{
homeButton->setText(tr("Back to results"));
openInEditorButton->setText(tr("Open Deck in Deck Editor"));
}
void ArchidektApiResponseDeckDisplayWidget::onGroupCriteriaChange(const QString &activeGroupCriteria)
{
model->setActiveGroupCriteria(DeckListModelGroupCriteria::fromString(activeGroupCriteria));
model->sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
}
void ArchidektApiResponseDeckDisplayWidget::actOpenInDeckEditor()
{
DeckList deckList(*model->getDeckList());
deckList.setName(response.getDeckName());
deckList.setGameFormat(
ArchidektFormats::formatToCockatriceName(ArchidektFormats::DeckFormat(response.getDeckFormat() - 1)));
LoadedDeck loadedDeck = {deckList, {}};
emit openInDeckEditor(loadedDeck);
}
void ArchidektApiResponseDeckDisplayWidget::clearAllDisplayWidgets()
{
for (auto idx : indexToWidgetMap.keys()) {
auto displayWidget = indexToWidgetMap.value(idx);
zoneContainerLayout->removeWidget(displayWidget);
indexToWidgetMap.remove(idx);
delete displayWidget;
}
}
void ArchidektApiResponseDeckDisplayWidget::decklistModelReset()
{
clearAllDisplayWidgets();
constructZoneWidgetsFromDeckListModel();
}
void ArchidektApiResponseDeckDisplayWidget::constructZoneWidgetsFromDeckListModel()
{
qDebug() << model->rowCount(model->getRoot());
QSortFilterProxyModel proxy;
proxy.setSourceModel(model);
proxy.setSortRole(Qt::EditRole);
proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
for (int i = 0; i < proxy.rowCount(); ++i) {
QModelIndex proxyIndex = proxy.index(i, 0);
QModelIndex sourceIndex = proxy.mapToSource(proxyIndex);
// Make a persistent index from the *source* model
QPersistentModelIndex persistent(sourceIndex);
if (indexToWidgetMap.contains(persistent)) {
continue;
}
QString zoneName =
persistent.sibling(persistent.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
DeckCardZoneDisplayWidget *zoneDisplayWidget =
new DeckCardZoneDisplayWidget(zoneContainer, model, nullptr, persistent, zoneName, "maintype", {"name"},
DisplayType::Overlap, 20, 10, cardSizeSlider);
connect(displayOptionsWidget, &VisualDeckDisplayOptionsWidget::sortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
connect(displayOptionsWidget, &VisualDeckDisplayOptionsWidget::groupCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged);
connect(displayOptionsWidget, &VisualDeckDisplayOptionsWidget::displayTypeChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::refreshDisplayType);
zoneContainerLayout->addWidget(zoneDisplayWidget);
indexToWidgetMap.insert(persistent, zoneDisplayWidget);
}
}
void ArchidektApiResponseDeckDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
layout->invalidate();
layout->activate();
layout->update();
}

View file

@ -1,129 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_DISPLAY_WIDGET_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_DISPLAY_WIDGET_H
#include "../../../../../deck_loader/deck_loader.h"
#include "../../../../cards/card_size_widget.h"
#include "../../../../general/layout_containers/flow_widget.h"
#include "../../../../visual_deck_editor/visual_deck_display_options_widget.h"
#include "../api_response/deck/archidekt_api_response_deck.h"
#include "deck_list_model.h"
#include <QPushButton>
#include <QResizeEvent>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
/**
* @class ArchidektApiResponseDeckDisplayWidget
* @brief Displays a full deck fetched from an Archidekt API response.
*
* This widget visualizes all cards in a deck retrieved from the Archidekt API.
* It supports:
* - Interactive display options via a VisualDeckDisplayOptionsWidget.
* - Scrollable display of deck zones/cards with DeckCardZoneDisplayWidget.
* - Integration with a CardSizeWidget slider for card scaling.
* - Opening the deck in a deck editor.
*
* The widget internally constructs a DeckListModel from the Archidekt API response,
* then builds zone widgets for each group of cards according to the active group
* criteria. It also responds dynamically to model resets or sorting/grouping changes.
*
* ### Signals
* - `requestNavigation(QString url)` triggered when navigation to a deck URL is requested.
* - `openInDeckEditor(const LoadedDeck &deck)` emitted when the user chooses to open the deck
* in the deck editor.
*
* ### Features
* - Automatically generates DeckCardZoneDisplayWidget instances for each card group.
* - Provides a scrollable layout for decks of arbitrary size.
* - Updates layouts dynamically when resized or when display/group/sort criteria change.
*/
class ArchidektApiResponseDeckDisplayWidget : public QWidget
{
Q_OBJECT
signals:
/**
* @brief Emitted when navigation to a deck URL is requested.
* @param url URL of the deck on Archidekt.
*/
void requestNavigation(QString url);
void requestSearch();
/**
* @brief Emitted when the deck should be opened in the deck editor.
* @param deck LoadedDeck containing the deck data.
*/
void openInDeckEditor(const LoadedDeck &deck);
public:
/**
* @brief Constructs a display widget for an Archidekt deck.
* @param parent Parent widget.
* @param response API deck data container.
* @param cardSizeSlider Slider controlling card scaling.
*/
explicit ArchidektApiResponseDeckDisplayWidget(QWidget *parent,
ArchidektApiResponseDeck response,
CardSizeWidget *cardSizeSlider);
/**
* @brief Updates all UI text for retranslation/localization.
*
* Called when the application language changes.
*/
void retranslateUi();
/**
* @brief Opens the deck in the deck editor.
*/
void actOpenInDeckEditor();
/**
* @brief Clears all dynamically generated card zone display widgets.
*/
void clearAllDisplayWidgets();
/**
* @brief Handles model reset by clearing and reconstructing display widgets.
*/
void decklistModelReset();
/**
* @brief Builds DeckCardZoneDisplayWidget instances from the current DeckListModel.
*/
void constructZoneWidgetsFromDeckListModel();
private slots:
/**
* @brief Slot triggered when the active group criteria change.
* @param activeGroupCriteria Name of the new grouping criteria.
*/
void onGroupCriteriaChange(const QString &activeGroupCriteria);
private:
ArchidektApiResponseDeck response; ///< API deck data container
CardSizeWidget *cardSizeSlider; ///< Slider for adjusting card sizes
QVBoxLayout *layout; ///< Main vertical layout
QWidget *navigationContainer;
QHBoxLayout *navigationContainerLayout;
QPushButton *homeButton;
QPushButton *openInEditorButton; ///< Button to open deck in editor
VisualDeckDisplayOptionsWidget *displayOptionsWidget; ///< Controls grouping/sorting/display
QScrollArea *scrollArea; ///< Scrollable area for deck zones
QWidget *zoneContainer; ///< Container for deck zones
QVBoxLayout *zoneContainerLayout; ///< Layout for deck zones
QWidget *container; ///< Outer container for scroll area
QHash<QPersistentModelIndex, QWidget *> indexToWidgetMap; ///< Maps model indices to widgets
QVBoxLayout *containerLayout; ///< Layout for container
DeckListModel *model; ///< Deck list model
protected slots:
/**
* @brief Updates layout and display on resize.
* @param event Resize event.
*/
void resizeEvent(QResizeEvent *event) override;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_DISPLAY_WIDGET_H

View file

@ -1,237 +0,0 @@
#include "archidekt_api_response_deck_entry_display_widget.h"
#include "../../../../../card_picture_loader/card_picture_loader.h"
#include "../../../../cards/card_info_picture_with_text_overlay_widget.h"
#include "../../../../general/display/background_plate_widget.h"
#include "../../../../general/display/charts/bars/color_bar.h"
#include "archidekt_deck_preview_image_display_widget.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QPixmap>
#include <QWidget>
#include <version_string.h>
#define ARCHIDEKT_DEFAULT_IMAGE "https://storage.googleapis.com/topdekt-user/images/archidekt_deck_card_shadow.jpg"
static QString timeAgo(const QString &timestamp)
{
QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate);
if (!dt.isValid())
return timestamp; // fallback if parsing fails
qint64 secs = dt.secsTo(QDateTime::currentDateTimeUtc());
if (secs < 60)
return QString("%1 seconds ago").arg(secs);
if (secs < 3600)
return QString("%1 minutes ago").arg(secs / 60);
if (secs < 86400)
return QString("%1 hours ago").arg(secs / 3600);
if (secs < 30 * 86400)
return QString("%1 days ago").arg(secs / 86400);
if (secs < 365 * 86400)
return QString("%1 months ago").arg(secs / (30 * 86400));
return QString("%1 years ago").arg(secs / (365 * 86400));
}
ArchidektApiResponseDeckEntryDisplayWidget::ArchidektApiResponseDeckEntryDisplayWidget(
QWidget *parent,
ArchidektApiResponseDeckListingContainer _response,
QNetworkAccessManager *_imageNetworkManager)
: QWidget(parent), response(_response), imageNetworkManager(_imageNetworkManager)
{
layout = new QVBoxLayout(this);
setLayout(layout);
this->setMaximumWidth(400);
this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
auto headerLayout = new QVBoxLayout();
previewWidget = new ArchidektDeckPreviewImageDisplayWidget(this);
previewWidget->setMaximumWidth(400);
previewWidget->setMinimumHeight(300); // consistent height
// Set deck name (ellided)
QFontMetrics fm(previewWidget->topLeftLabel->font());
QString elided = fm.elidedText(response.getName(), Qt::ElideRight, 280);
previewWidget->topLeftLabel->setLabelText(elided);
previewWidget->topLeftLabel->setToolTip(response.getName());
// Set count
previewWidget->topRightLabel->setLabelText(QString::number(response.getSize()));
// EDH bracket (skip if 0)
if (response.getEDHBracket() != 0) {
previewWidget->bottomLeftLabel->setLabelText(QString("EDH: %1").arg(response.getEDHBracket()));
} else {
previewWidget->bottomLeftLabel->hide();
}
// Views
previewWidget->bottomRightLabel->setLabelText(QString("Views: %1").arg(response.getViewCount()));
// Use preview->imageLabel for image loading
picture = previewWidget->imageLabel;
imageUrl = response.getFeatured().isEmpty() ? QUrl(ARCHIDEKT_DEFAULT_IMAGE) : QUrl(response.getFeatured());
QNetworkRequest req(imageUrl);
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
QNetworkReply *reply = imageNetworkManager->get(req);
// tag the reply with "this" so we know it belongs to us later
reply->setProperty("deckWidget", QVariant::fromValue<void *>(this));
reply->setProperty("requestedUrl", imageUrl);
connect(imageNetworkManager, &QNetworkAccessManager::finished, this,
&ArchidektApiResponseDeckEntryDisplayWidget::onPreviewImageLoadFinished);
headerLayout->addWidget(previewWidget);
auto colors = response.getColors();
ColorBar *colorBar = new ColorBar(colors, this);
colorBar->setMinPercentThreshold(3);
colorBar->setFixedHeight(22);
headerLayout->addWidget(colorBar);
// Create a shared plate for the labels
backgroundPlateWidget = new BackgroundPlateWidget(this);
backgroundPlateWidget->setFixedHeight(120); // Adjust height to fit all labels
QVBoxLayout *plateLayout = new QVBoxLayout(backgroundPlateWidget);
// Add labels to the plate layout
QLabel *ownerLabel = new QLabel(QString("Owner: %1").arg(response.getOwner().getName()));
plateLayout->addWidget(ownerLabel);
QLabel *createdAtLabel = new QLabel(QString("Created: %1").arg(timeAgo(response.getCreatedAt())));
plateLayout->addWidget(createdAtLabel);
QLabel *updatedAtLabel = new QLabel(QString("Updated: %1").arg(timeAgo(response.getUpdatedAt())));
plateLayout->addWidget(updatedAtLabel);
// Add the shared plate to the header layout
headerLayout->addWidget(backgroundPlateWidget);
layout->addLayout(headerLayout);
}
void ArchidektApiResponseDeckEntryDisplayWidget::mousePressEvent(QMouseEvent *event)
{
QWidget::mousePressEvent(event);
actRequestNavigationToDeck();
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void ArchidektApiResponseDeckEntryDisplayWidget::enterEvent(QEnterEvent *event)
#else
void ArchidektApiResponseDeckEntryDisplayWidget::enterEvent(QEvent *event)
#endif
{
QWidget::enterEvent(event);
backgroundPlateWidget->setFocused(true);
}
void ArchidektApiResponseDeckEntryDisplayWidget::leaveEvent(QEvent *event)
{
QWidget::leaveEvent(event);
backgroundPlateWidget->setFocused(false);
}
void ArchidektApiResponseDeckEntryDisplayWidget::setScaleFactor(int scale)
{
scaleFactor = scale;
updateScaledPreview();
}
void ArchidektApiResponseDeckEntryDisplayWidget::onPreviewImageLoadFinished(QNetworkReply *reply)
{
// Check if this is our reply
void *owner = reply->property("deckWidget").value<void *>();
if (owner != this) {
return; // not our reply
}
// Check that the requested URL matches what we asked
QUrl requestedUrl = reply->property("requestedUrl").toUrl();
if (requestedUrl != imageUrl) {
reply->deleteLater();
return;
}
QPixmap loaded;
if (reply->error() != QNetworkReply::NoError || !loaded.loadFromData(reply->readAll())) {
CardPictureLoader::getCardBackLoadingFailedPixmap(loaded, QSize(400, 400));
}
originalPixmap = loaded;
// Always scale preview widget to this ratio
previewWidget->setAspectRatio(DESIGN_RATIO);
previewWidget->setPreviewWidth(400);
// Initial scaling
updateScaledPreview();
reply->deleteLater();
}
void ArchidektApiResponseDeckEntryDisplayWidget::updateScaledPreview()
{
if (originalPixmap.isNull()) {
return;
}
int baseWidth = 400;
int newWidth = baseWidth * scaleFactor / 100;
int newHeight = static_cast<int>(newWidth * DESIGN_RATIO);
previewWidget->setFixedSize(newWidth, newHeight);
// Scale image to fill the preview area (crop edges)
QPixmap scaled =
originalPixmap.scaled(newWidth, newHeight, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Crop to exact target size
QRect cropRect((scaled.width() - newWidth) / 2, (scaled.height() - newHeight) / 2, newWidth, newHeight);
QPixmap cropped = scaled.copy(cropRect);
picture->setPixmap(cropped);
picture->setFixedSize(newWidth, newHeight);
// Update the elided deck name based on new width
int textMaxWidth = int(newWidth * 0.7); // allow 70% of width for text
QFontMetrics fm(previewWidget->topLeftLabel->font());
QString elided = fm.elidedText(response.getName(), Qt::ElideRight, textMaxWidth);
previewWidget->topLeftLabel->setLabelText(elided);
previewWidget->topLeftLabel->setToolTip(response.getName());
setFixedWidth(newWidth);
layout->invalidate();
layout->activate();
updateGeometry();
}
void ArchidektApiResponseDeckEntryDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
layout->invalidate();
layout->activate();
layout->update();
}
void ArchidektApiResponseDeckEntryDisplayWidget::actRequestNavigationToDeck()
{
emit requestNavigation(QString("https://archidekt.com/api/decks/%1/").arg(response.getId()));
}

View file

@ -1,121 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_ENTRY_DISPLAY_WIDGET_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_ENTRY_DISPLAY_WIDGET_H
#include "../../../../cards/card_info_picture_with_text_overlay_widget.h"
#include "../api_response/deck_listings/archidekt_api_response_deck_listing_container.h"
#include "archidekt_deck_preview_image_display_widget.h"
#include <QLabel>
#include <QNetworkAccessManager>
#include <QResizeEvent>
#include <QVBoxLayout>
#include <QWidget>
class BackgroundPlateWidget;
/**
* @class ArchidektApiResponseDeckEntryDisplayWidget
* @brief Displays a single Archidekt deck listing as a preview card with metadata.
*
* This widget renders a deck entry received from an Archidekt API response. It includes:
* - A scaled deck preview image loaded asynchronously via QNetworkAccessManager.
* - Elided deck name in the top-left corner.
* - Deck size, EDH bracket, and view count labels.
* - A color distribution bar summarizing deck colors.
* - Metadata labels including owner, creation date, and last update date.
*
* The widget dynamically scales the preview image and labels according to a linked
* CardSizeWidget slider. Hovering over the widget highlights the background plate,
* and clicking emits a `requestNavigation` signal pointing to the deck URL.
*
* ### Features
* - Asynchronous image loading with fallback to a default placeholder image.
* - Maintains a fixed aspect ratio for the preview image (150:267).
* - Updates text elision dynamically when resized or scaled.
* - Integrates with FlowWidget containers for scrollable deck galleries.
*
* ### Signals
* - `requestNavigation(QString url)` emitted when the widget is clicked to request
* navigation to the deck's Archidekt page.
*
* ### Slots
* - `actRequestNavigationToDeck()` triggers navigation.
* - `setScaleFactor(int scale)` adjusts preview image scaling.
*/
class ArchidektApiResponseDeckEntryDisplayWidget : public QWidget
{
Q_OBJECT
signals:
/**
* @brief Emitted when the user requests navigation.
* @param url Full URL to the Archidekt page.
*/
void requestNavigation(QString url);
public:
/**
* @brief Constructs a deck entry display widget.
* @param parent Parent widget.
* @param response API container holding deck listing data.
* @param imageNetworkManager Shared network manager for fetching preview images.
*/
explicit ArchidektApiResponseDeckEntryDisplayWidget(QWidget *parent,
ArchidektApiResponseDeckListingContainer response,
QNetworkAccessManager *imageNetworkManager);
/**
* @brief Handles finished network replies for preview images.
* @param reply QNetworkReply containing image data.
*
* Validates that the reply corresponds to this widget and updates the preview image.
*/
void onPreviewImageLoadFinished(QNetworkReply *reply);
/**
* @brief Updates the scaled preview image and adjusts layout accordingly.
*/
void updateScaledPreview();
/**
* @brief Ensures layout responds correctly on resize events.
* @param event Resize event.
*/
void resizeEvent(QResizeEvent *event) override;
public slots:
/**
* @brief Emits `requestNavigation` for the deck's URL.
*/
void actRequestNavigationToDeck();
/**
* @brief Sets a scaling factor (percentage) for the preview image.
* @param scale Scale percentage (100 = normal size).
*/
void setScaleFactor(int scale);
protected:
void mousePressEvent(QMouseEvent *event) override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent *event) override; ///< Qt6 hover enter
#else
void enterEvent(QEvent *event) override; ///< Qt5 hover enter
#endif
void leaveEvent(QEvent *event) override;
private:
QVBoxLayout *layout; ///< Main vertical layout
ArchidektApiResponseDeckListingContainer response; ///< Deck data
QUrl imageUrl; ///< URL of the deck's preview image
QNetworkAccessManager *imageNetworkManager; ///< Shared network manager
ArchidektDeckPreviewImageDisplayWidget *previewWidget; ///< Widget showing the deck preview
QLabel *picture; ///< QLabel displaying the scaled pixmap
QPixmap originalPixmap; ///< Original image for scaling (avoids degradation)
int scaleFactor = 100; ///< Current scaling percentage
BackgroundPlateWidget *backgroundPlateWidget; ///< Plate for metadata labels
static constexpr float DESIGN_RATIO = 150.0f / 267.0f; ///< Design aspect ratio
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_ENTRY_DISPLAY_WIDGET_H

View file

@ -1,58 +0,0 @@
#include "archidekt_api_response_deck_listings_display_widget.h"
#include "../../../../cards/card_info_picture_with_text_overlay_widget.h"
#include "archidekt_api_response_deck_entry_display_widget.h"
ArchidektApiResponseDeckListingsDisplayWidget::ArchidektApiResponseDeckListingsDisplayWidget(
QWidget *parent,
ArchidektDeckListingApiResponse response,
CardSizeWidget *_cardSizeSlider)
: QWidget(parent), cardSizeSlider(_cardSizeSlider)
{
layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
imageNetworkManager = new QNetworkAccessManager(this);
imageNetworkManager->setTransferTimeout(); // Use Qt's default timeout
imageNetworkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
// Add widgets for deck listings
auto deckListings = response.results;
for (const auto &deckListing : deckListings) {
auto cardListDisplayWidget =
new ArchidektApiResponseDeckEntryDisplayWidget(this, deckListing, imageNetworkManager);
cardListDisplayWidget->setScaleFactor(cardSizeSlider->getSlider()->value());
connect(cardListDisplayWidget, &ArchidektApiResponseDeckEntryDisplayWidget::requestNavigation, this,
&ArchidektApiResponseDeckListingsDisplayWidget::requestNavigation);
connect(cardSizeSlider->getSlider(), &QSlider::valueChanged, cardListDisplayWidget,
&ArchidektApiResponseDeckEntryDisplayWidget::setScaleFactor);
flowWidget->addWidget(cardListDisplayWidget);
}
layout->addWidget(flowWidget);
}
void ArchidektApiResponseDeckListingsDisplayWidget::append(const ArchidektDeckListingApiResponse &data)
{
for (const auto &deckListing : data.results) {
auto cardListDisplayWidget =
new ArchidektApiResponseDeckEntryDisplayWidget(this, deckListing, imageNetworkManager);
cardListDisplayWidget->setScaleFactor(cardSizeSlider->getSlider()->value());
connect(cardListDisplayWidget, &ArchidektApiResponseDeckEntryDisplayWidget::requestNavigation, this,
&ArchidektApiResponseDeckListingsDisplayWidget::requestNavigation);
connect(cardSizeSlider->getSlider(), &QSlider::valueChanged, cardListDisplayWidget,
&ArchidektApiResponseDeckEntryDisplayWidget::setScaleFactor);
flowWidget->addWidget(cardListDisplayWidget);
}
}
void ArchidektApiResponseDeckListingsDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
layout->invalidate();
layout->activate();
layout->update();
}

View file

@ -1,98 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_LISTINGS_DISPLAY_WIDGET_H
#define COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_LISTINGS_DISPLAY_WIDGET_H
#include "../../../../cards/card_size_widget.h"
#include "../../../../general/layout_containers/flow_widget.h"
#include "../api_response/archidekt_deck_listing_api_response.h"
#include <QNetworkAccessManager>
#include <QResizeEvent>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
/**
* @class ArchidektApiResponseDeckListingsDisplayWidget
* @brief Displays a scrollable horizontal list of Archidekt deck listings with dynamic card sizing.
*
* This widget serves as a container for multiple
* ArchidektApiResponseDeckEntryDisplayWidget instances, each representing one deck listing
* returned from an Archidekt API call.
*
* ### Responsibilities
* - Creates a **FlowWidget** that arranges deck entries horizontally with wrapping.
* - Creates a shared **QNetworkAccessManager** for entry widgets to fetch card thumbnails.
* - Connects a **CardSizeWidget** slider to all deck entries to dynamically rescale their preview cards.
* - Propagates deck-navigation requests (`requestNavigation`) from children to the parent.
*
* ### Layout
* The widget uses a single `QHBoxLayout` containing the `FlowWidget`.
* The FlowWidget automatically manages child flow and scrollbar behavior (horizontal = off,
* vertical = auto), providing an efficient scrollable gallery of deck entries.
*
* ### API Integration
* The constructor consumes an `ArchidektDeckListingApiResponse`, iterates through its
* `results`, and instantiates a child entry widget for each deck.
*
* ### Signals
* - `requestNavigation(QString url)` emitted whenever a child entry widget requests
* navigation to a deck or related Archidekt page.
*
* ### Performance Notes
* - All entry widgets share a single QNetworkAccessManager instance to reuse connections
* and avoid redundant session creation.
* - `resizeEvent()` forces layout invalidation to ensure the flow layout responds properly
* to container resizing.
*/
class ArchidektApiResponseDeckListingsDisplayWidget : public QWidget
{
Q_OBJECT
signals:
/**
* @brief Emitted when a child deck entry requests that the UI navigate to a particular URL.
* @param url The destination URL (typically an Archidekt deck page).
*/
void requestNavigation(QString url);
public:
/**
* @brief Constructs a widget that displays multiple deck listing previews.
*
* @param parent Parent widget.
* @param response The Archidekt API response containing deck listings.
* @param cardSizeSlider A slider widget used to dynamically resize card previews.
*
* Each deck in `response.results` becomes its own
* ArchidektApiResponseDeckEntryDisplayWidget, added to the FlowWidget.
*/
explicit ArchidektApiResponseDeckListingsDisplayWidget(QWidget *parent,
ArchidektDeckListingApiResponse response,
CardSizeWidget *cardSizeSlider);
void append(const ArchidektDeckListingApiResponse &data);
/**
* @brief Ensures FlowWidget layout properly recomputes on resize.
*
* Forces the layout to invalidate and activate itself so that the
* FlowWidget recalculates wrapping and child placement.
*
* @param event Resize event.
*/
void resizeEvent(QResizeEvent *event) override;
private:
/// Slider controlling the scale of card thumbnails in all deck entry widgets.
CardSizeWidget *cardSizeSlider;
/// Main horizontal layout containing the FlowWidget.
QHBoxLayout *layout;
/// Container providing scrollable multi-row flow layout of deck entries.
FlowWidget *flowWidget;
/// Shared network manager used to download card images for all child entry widgets.
QNetworkAccessManager *imageNetworkManager;
};
#endif // COCKATRICE_ARCHIDEKT_API_RESPONSE_DECK_LISTINGS_DISPLAY_WIDGET_H

View file

@ -1,80 +0,0 @@
#include "archidekt_deck_preview_image_display_widget.h"
#include <QFontMetrics>
#include <QPainter>
ArchidektDeckPreviewImageDisplayWidget::ArchidektDeckPreviewImageDisplayWidget(QWidget *parent) : QWidget(parent)
{
imageLabel = new QLabel(this);
imageLabel->setAlignment(Qt::AlignCenter);
topLeftLabel = new ShadowBackgroundLabel(this, "");
topRightLabel = new ShadowBackgroundLabel(this, "");
bottomLeftLabel = new ShadowBackgroundLabel(this, "");
bottomRightLabel = new ShadowBackgroundLabel(this, "");
QFont f;
f.setBold(true);
f.setPointSize(16);
topLeftLabel->setFont(f);
topRightLabel->setFont(f);
bottomLeftLabel->setFont(f);
bottomRightLabel->setFont(f);
// Raise so labels appear above image
topLeftLabel->raise();
topRightLabel->raise();
bottomLeftLabel->raise();
bottomRightLabel->raise();
}
void ArchidektDeckPreviewImageDisplayWidget::setAspectRatio(float ratio)
{
aspectRatio = ratio;
}
void ArchidektDeckPreviewImageDisplayWidget::setPreviewWidth(int width)
{
int height = int(width * aspectRatio);
setFixedSize(width, height);
updateGeometry();
update();
}
void ArchidektDeckPreviewImageDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
// Full size for the image
imageLabel->setGeometry(rect());
topLeftLabel->adjustSize();
topRightLabel->adjustSize();
bottomLeftLabel->adjustSize();
bottomRightLabel->adjustSize();
// Padding settings
const int horizontalPadding = 8;
const int topPadding = 6;
const int bottomPadding = 6;
// Left-aligned, top-aligned (Deck Name)
topLeftLabel->move(horizontalPadding, topPadding);
// Right-aligned, top-aligned (Card Count)
topRightLabel->move(width() - topRightLabel->width() - horizontalPadding, topPadding);
// Bottom-left, bottom-aligned (EDH bracket)
bottomLeftLabel->move(horizontalPadding, height() - bottomLeftLabel->height() - bottomPadding);
// Bottom-right, bottom-aligned (views)
bottomRightLabel->move(width() - bottomRightLabel->width() - horizontalPadding,
height() - bottomRightLabel->height() - bottomPadding);
// Ensure labels stay above image
topLeftLabel->raise();
topRightLabel->raise();
bottomLeftLabel->raise();
bottomRightLabel->raise();
}

View file

@ -1,66 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_DECK_PREVIEW_IMAGE_DISPLAY_WIDGET_H
#define COCKATRICE_ARCHIDEKT_DECK_PREVIEW_IMAGE_DISPLAY_WIDGET_H
#include "../../../../general/display/shadow_background_label.h"
#include <QLabel>
#include <QWidget>
/**
* @class ArchidektDeckPreviewImageDisplayWidget
* @brief Widget for displaying a deck preview image with overlaid metadata labels.
*
* This widget shows a deck's preview image along with several overlay labels:
* - Top-left: Deck name.
* - Top-right: Card count.
* - Bottom-left: EDH bracket (if applicable).
* - Bottom-right: View count.
*
* Labels automatically scale and position themselves relative to the widget's size.
* The image can be scaled while maintaining a specified aspect ratio.
*
* ### Features
* - Adjustable preview width and aspect ratio.
* - Labels automatically repositioned on resize.
* - Supports overlaying multiple pieces of metadata with shadowed labels for readability.
*/
class ArchidektDeckPreviewImageDisplayWidget : public QWidget
{
Q_OBJECT
public:
/**
* @brief Constructs the deck preview display widget.
* @param parent Optional parent widget.
*/
explicit ArchidektDeckPreviewImageDisplayWidget(QWidget *parent = nullptr);
/**
* @brief Sets the aspect ratio for the preview image (height / width).
* @param ratio Aspect ratio to maintain.
*/
void setAspectRatio(float ratio);
/**
* @brief Sets the width of the preview image; height is adjusted according to the aspect ratio.
* @param width Desired width in pixels.
*/
void setPreviewWidth(int width);
QLabel *imageLabel; ///< QLabel to display the deck image
ShadowBackgroundLabel *topLeftLabel; ///< Overlay label at top-left (deck name)
ShadowBackgroundLabel *topRightLabel; ///< Overlay label at top-right (card count)
ShadowBackgroundLabel *bottomLeftLabel; ///< Overlay label at bottom-left (EDH bracket)
ShadowBackgroundLabel *bottomRightLabel; ///< Overlay label at bottom-right (views)
protected:
/**
* @brief Handles resize events to reposition the image and overlay labels.
* @param event Resize event.
*/
void resizeEvent(QResizeEvent *event) override;
private:
float aspectRatio = 1.0f; ///< Aspect ratio to maintain for the preview image
};
#endif // COCKATRICE_ARCHIDEKT_DECK_PREVIEW_IMAGE_DISPLAY_WIDGET_H

View file

@ -1,669 +0,0 @@
#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 <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QPushButton>
#include <QRegularExpression>
#include <QResizeEvent>
#include <QScrollArea>
#include <QScrollBar>
#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>
#include <version_string.h>
TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor)
: Tab(_tabSupervisor), currentPage(1), isLoadingMore(false), isListMode(true)
{
// Initialize network
networkManager = new QNetworkAccessManager(this);
networkManager->setTransferTimeout();
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
connect(networkManager, &QNetworkAccessManager::finished, this, &TabArchidekt::processApiJson);
// Initialize debounce timer
searchDebounceTimer = new QTimer(this);
searchDebounceTimer->setSingleShot(true);
searchDebounceTimer->setInterval(300);
connect(searchDebounceTimer, &QTimer::timeout, this, &TabArchidekt::doSearchImmediate);
initializeUi();
setupFilterWidgets();
connectSignals();
retranslateUi();
getTopDecks();
}
void TabArchidekt::initializeUi()
{
// Main container
container = new QWidget(this);
mainLayout = new QVBoxLayout(container);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
// Primary toolbar (most important filters)
primaryToolbar = new QWidget(container);
primaryToolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
primaryToolbarLayout = new QHBoxLayout(primaryToolbar);
primaryToolbarLayout->setContentsMargins(6, 6, 6, 6);
primaryToolbarLayout->setSpacing(6);
// Sort controls
sortByLabel = new QLabel(primaryToolbar);
orderByCombo = new QComboBox(primaryToolbar);
orderByCombo->addItems({"name", "updatedAt", "createdAt", "viewCount", "size", "edhBracket"});
orderByCombo->setCurrentText("updatedAt");
orderByCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
orderDirButton = new QPushButton(tr("Desc."), primaryToolbar);
orderDirButton->setCheckable(true);
orderDirButton->setChecked(true);
orderDirButton->setFixedWidth(60);
// Color filter (inline)
QWidget *colorWidget = new QWidget(primaryToolbar);
QHBoxLayout *colorLayout = new QHBoxLayout(colorWidget);
colorLayout->setContentsMargins(0, 0, 0, 0);
colorLayout->setSpacing(2);
QString colorIdentity = "WUBRG";
for (const QChar &color : colorIdentity) {
auto *manaSymbol = new ManaSymbolWidget(colorWidget, color, false, true);
manaSymbol->setFixedSize(28, 28);
colorSymbols.append(manaSymbol);
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(tr("AND"), primaryToolbar);
logicalAndCheck->setToolTip(tr("Require ALL selected colors"));
// Common search fields
nameField = new QLineEdit(primaryToolbar);
nameField->setPlaceholderText(tr("Deck name..."));
nameField->setMinimumWidth(150);
ownerField = new QLineEdit(primaryToolbar);
ownerField->setPlaceholderText(tr("Owner..."));
ownerField->setMinimumWidth(120);
// Filter by label
filterByLabel = new QLabel(primaryToolbar);
// Package toggle
packagesCheck = new QCheckBox(tr("Packages"), primaryToolbar);
// Search button
searchButton = new QPushButton(tr("Search"), primaryToolbar);
searchButton->setDefault(true);
// Advanced filters toggle button
advancedFiltersButton = new QPushButton(tr("Advanced Filters"), primaryToolbar);
advancedFiltersButton->setCheckable(true);
advancedFiltersButton->setChecked(false);
// Settings
settingsButton = new SettingsButtonWidget(primaryToolbar);
cardSizeSlider = new CardSizeWidget(primaryToolbar, nullptr, SettingsCache::instance().getArchidektPreviewSize());
settingsButton->addSettingsWidget(cardSizeSlider);
// Assemble primary toolbar
primaryToolbarLayout->addWidget(sortByLabel);
primaryToolbarLayout->addWidget(orderByCombo);
primaryToolbarLayout->addWidget(orderDirButton);
// Add separator/spacing
primaryToolbarLayout->addSpacing(12);
primaryToolbarLayout->addWidget(filterByLabel);
primaryToolbarLayout->addWidget(colorWidget);
primaryToolbarLayout->addWidget(logicalAndCheck);
primaryToolbarLayout->addWidget(nameField, 1);
primaryToolbarLayout->addWidget(ownerField, 1);
primaryToolbarLayout->addWidget(packagesCheck);
primaryToolbarLayout->addWidget(searchButton, 1);
primaryToolbarLayout->addWidget(advancedFiltersButton);
primaryToolbarLayout->addWidget(settingsButton);
// Secondary toolbar (advanced filters - initially hidden)
secondaryToolbar = new QWidget(container);
secondaryToolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
secondaryToolbar->setVisible(false); // Start hidden
secondaryToolbarLayout = new QHBoxLayout(secondaryToolbar);
secondaryToolbarLayout->setContentsMargins(6, 3, 6, 6);
secondaryToolbarLayout->setSpacing(6);
// Scrollable results area
scrollArea = new QScrollArea(container);
scrollArea->setWidgetResizable(true);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
resultsContainer = new QWidget();
resultsLayout = new QVBoxLayout(resultsContainer);
resultsLayout->setContentsMargins(0, 0, 0, 0);
resultsLayout->setSpacing(0);
scrollArea->setWidget(resultsContainer);
scrollArea->viewport()->installEventFilter(this);
mainLayout->addWidget(primaryToolbar);
mainLayout->addWidget(secondaryToolbar);
mainLayout->addWidget(scrollArea);
setCentralWidget(container);
}
bool TabArchidekt::eventFilter(QObject *obj, QEvent *event)
{
if (obj == scrollArea->viewport() && event->type() == QEvent::Wheel) {
auto *wheelEvent = static_cast<QWheelEvent *>(event);
if (wheelEvent->angleDelta().y() < 0 && !isLoadingMore && isListMode) {
loadNextPage();
wheelEvent->accept();
return false; // allow scrolling
}
}
// Always pass the event to the parent to handle normal scrolling
return QWidget::eventFilter(obj, event);
}
void TabArchidekt::setupFilterWidgets()
{
// Advanced filters (in secondary toolbar)
// EDH Bracket
auto *bracketLabel = new QLabel(tr("Bracket:"), secondaryToolbar);
edhBracketCombo = new QComboBox(secondaryToolbar);
edhBracketCombo->addItem(tr("Any"));
edhBracketCombo->addItems({"1", "2", "3", "4", "5"});
edhBracketCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
// Format filter (collapsible)
formatButton = new SettingsButtonWidget(secondaryToolbar);
formatButton->setButtonText(tr("Formats"));
formatButton->setButtonIcon(QPixmap("theme:icons/scale_balanced"));
QWidget *formatContainer = new QWidget(secondaryToolbar);
QGridLayout *formatLayout = new QGridLayout(formatContainer);
formatLayout->setContentsMargins(4, 4, 4, 4);
QStringList formatNames = {"Standard", "Modern", "Commander", "Legacy", "Vintage",
"Pauper", "Custom", "Frontier", "Future Std", "Penny Dreadful",
"1v1 Commander", "Dual Commander", "Brawl"};
int row = 0, col = 0;
for (const QString &formatName : formatNames) {
auto *formatCheckBox = new QCheckBox(formatName, formatContainer);
formatChecks << formatCheckBox;
formatLayout->addWidget(formatCheckBox, row, col);
connect(formatCheckBox, &QCheckBox::clicked, this, &TabArchidekt::doSearch);
col++;
if (col >= 3) {
col = 0;
row++;
}
}
formatButton->addSettingsWidget(formatContainer);
cardsField = new QLineEdit(secondaryToolbar);
cardsField->setPlaceholderText(tr("Contains card..."));
cardsField->setMinimumWidth(140);
commandersField = new QLineEdit(secondaryToolbar);
commandersField->setPlaceholderText(tr("Commander..."));
commandersField->setMinimumWidth(140);
deckTagNameField = new QLineEdit(secondaryToolbar);
deckTagNameField->setPlaceholderText(tr("Tag..."));
deckTagNameField->setMinimumWidth(100);
// Deck size filter (collapsible)
deckSizeButton = new SettingsButtonWidget(secondaryToolbar);
deckSizeButton->setButtonText(tr("Deck Size"));
QWidget *sizeContainer = new QWidget(secondaryToolbar);
QHBoxLayout *sizeLayout = new QHBoxLayout(sizeContainer);
sizeLayout->setContentsMargins(4, 4, 4, 4);
minDeckSizeSpin = new QSpinBox(sizeContainer);
minDeckSizeSpin->setSpecialValueText(tr("Any"));
minDeckSizeSpin->setRange(0, 200);
minDeckSizeSpin->setValue(0);
minDeckSizeLogicCombo = new QComboBox(sizeContainer);
minDeckSizeLogicCombo->addItems({"Exact", "", ""});
minDeckSizeLogicCombo->setCurrentIndex(1);
sizeLayout->addWidget(new QLabel(tr("Cards:"), sizeContainer));
sizeLayout->addWidget(minDeckSizeSpin);
sizeLayout->addWidget(minDeckSizeLogicCombo);
deckSizeButton->addSettingsWidget(sizeContainer);
// Setup card name autocomplete
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);
// Keep autocomplete working for both fields
connect(cardsField, &QLineEdit::textChanged, this, [=](const QString &text) {
searchModel->updateSearchResults(text);
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
if (!text.isEmpty())
completer->complete();
});
connect(commandersField, &QLineEdit::textChanged, this, [=](const QString &text) {
searchModel->updateSearchResults(text);
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
if (!text.isEmpty())
completer->complete();
});
// Assemble secondary toolbar
secondaryToolbarLayout->addWidget(bracketLabel);
secondaryToolbarLayout->addWidget(edhBracketCombo);
secondaryToolbarLayout->addWidget(formatButton);
secondaryToolbarLayout->addWidget(cardsField);
secondaryToolbarLayout->addWidget(commandersField);
secondaryToolbarLayout->addWidget(deckTagNameField);
secondaryToolbarLayout->addWidget(deckSizeButton);
secondaryToolbarLayout->addStretch();
}
void TabArchidekt::connectSignals()
{
// Advanced filters toggle
connect(advancedFiltersButton, &QPushButton::clicked, this,
[this](bool checked) { secondaryToolbar->setVisible(checked); });
// These trigger immediate search (no debounce needed)
connect(orderByCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
connect(orderDirButton, &QPushButton::clicked, [this](bool checked) {
orderDirButton->setText(checked ? tr("Desc.") : tr("Asc."));
doSearch();
});
connect(cardSizeSlider, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance(),
&SettingsCache::setArchidektPreviewCardSize);
// Search button triggers immediate search
connect(searchButton, &QPushButton::clicked, this, &TabArchidekt::doSearchImmediate);
// These trigger search (but not text fields)
connect(logicalAndCheck, &QCheckBox::clicked, this, &TabArchidekt::doSearch);
connect(packagesCheck, &QCheckBox::clicked, [this]() {
updatePackageModeState(packagesCheck->isChecked());
doSearch();
});
// Format filters trigger search
connect(edhBracketCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
connect(minDeckSizeSpin, qOverload<int>(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
connect(minDeckSizeLogicCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
// Allow Enter key in text fields to trigger search
connect(nameField, &QLineEdit::returnPressed, this, &TabArchidekt::doSearchImmediate);
connect(ownerField, &QLineEdit::returnPressed, this, &TabArchidekt::doSearchImmediate);
connect(cardsField, &QLineEdit::returnPressed, this, &TabArchidekt::doSearchImmediate);
connect(commandersField, &QLineEdit::returnPressed, this, &TabArchidekt::doSearchImmediate);
connect(deckTagNameField, &QLineEdit::returnPressed, this, &TabArchidekt::doSearchImmediate);
// Format checkboxes trigger search
for (auto *formatCheck : formatChecks) {
connect(formatCheck, &QCheckBox::clicked, this, &TabArchidekt::doSearch);
}
}
void TabArchidekt::updatePackageModeState(bool isPackageMode)
{
// Disable format-specific and commander-specific filters in package mode
for (auto *cb : formatChecks) {
cb->setEnabled(!isPackageMode);
}
edhBracketCombo->setEnabled(!isPackageMode);
if (isPackageMode) {
edhBracketCombo->setCurrentIndex(0);
}
commandersField->setEnabled(!isPackageMode);
deckTagNameField->setEnabled(!isPackageMode);
}
void TabArchidekt::retranslateUi()
{
sortByLabel->setText(tr("Sort by:"));
orderDirButton->setText(orderDirButton->isChecked() ? tr("Desc.") : tr("Asc."));
filterByLabel->setText(tr("Filter by:"));
logicalAndCheck->setText(tr("AND"));
logicalAndCheck->setToolTip(tr("Require ALL selected colors"));
nameField->setPlaceholderText(tr("Deck name..."));
ownerField->setPlaceholderText(tr("Owner..."));
packagesCheck->setText(tr("Packages"));
advancedFiltersButton->setText(tr("Advanced Filters"));
cardsField->setPlaceholderText(tr("Contains card..."));
commandersField->setPlaceholderText(tr("Commander..."));
deckTagNameField->setPlaceholderText(tr("Tag..."));
formatButton->setButtonText(tr("Formats"));
deckSizeButton->setButtonText(tr("Deck Size"));
searchButton->setText(tr("Search"));
settingsButton->setToolTip(tr("Display Settings"));
}
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 (disabled in package mode)
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 (edhBracketCombo->currentIndex() > 0) {
query.addQueryItem("edhBracket", edhBracketCombo->currentText());
}
}
// Package mode
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("cards", cardsField->text());
}
// Commander (disabled in package mode)
if (!packagesCheck->isChecked() && !commandersField->text().isEmpty()) {
query.addQueryItem("commanderName", commandersField->text());
}
// Deck tag (disabled in package mode)
if (!packagesCheck->isChecked() && !deckTagNameField->text().isEmpty()) {
query.addQueryItem("deckTagName", deckTagNameField->text());
}
// Page number (for infinite scroll)
query.addQueryItem("page", QString::number(currentPage));
// Min deck size
if (minDeckSizeSpin->value() != 0) {
query.addQueryItem("size", QString::number(minDeckSizeSpin->value()));
QString logic = "GTE";
QString selected = minDeckSizeLogicCombo->currentText();
if (selected == "")
logic = "GTE";
else if (selected == "")
logic = "LTE";
else
logic = "";
if (!logic.isEmpty()) {
query.addQueryItem("sizeLogic", logic);
}
}
QUrl url("https://archidekt.com/api/decks/v3/");
url.setQuery(query);
return url.toString();
}
void TabArchidekt::doSearch()
{
// Reset to first page on new search
currentPage = 1;
// We're searching, so we'll be in list mode
isListMode = true;
// Don't debounce - only called by explicit user actions now
doSearchImmediate();
}
void TabArchidekt::doSearchImmediate()
{
QString url = buildSearchUrl();
QNetworkRequest req{QUrl(url)};
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(req);
}
void TabArchidekt::loadNextPage()
{
if (isLoadingMore) {
return;
}
isLoadingMore = true;
currentPage++;
QString url = buildSearchUrl();
QNetworkRequest req{QUrl(url)};
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(req);
}
void TabArchidekt::actNavigatePage(QString url)
{
QNetworkRequest request{QUrl(url)};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
void TabArchidekt::getTopDecks()
{
currentPage = 1;
QNetworkRequest request{QUrl(buildSearchUrl())};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
void TabArchidekt::processApiJson(QNetworkReply *reply)
{
if (reply->error() != QNetworkReply::NoError) {
isLoadingMore = false;
reply->deleteLater();
return;
}
QByteArray responseData = reply->readAll();
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
if (!jsonDoc.isObject()) {
isLoadingMore = false;
reply->deleteLater();
return;
}
QJsonObject jsonObj = jsonDoc.object();
QString responseUrl = reply->url().toString();
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);
}
isLoadingMore = false;
reply->deleteLater();
}
void TabArchidekt::processTopDecksResponse(QJsonObject reply)
{
ArchidektDeckListingApiResponse deckData;
deckData.fromJson(reply);
// New search → clear everything
if (currentPage == 1) {
QLayoutItem *item;
while ((item = resultsLayout->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
listingsWidget = new ArchidektApiResponseDeckListingsDisplayWidget(resultsContainer, deckData, cardSizeSlider);
connect(listingsWidget, &ArchidektApiResponseDeckListingsDisplayWidget::requestNavigation, this,
&TabArchidekt::actNavigatePage);
resultsLayout->addWidget(listingsWidget);
return;
}
// Infinite scroll → append
if (listingsWidget) {
listingsWidget->append(deckData);
}
}
void TabArchidekt::processDeckResponse(QJsonObject reply)
{
ArchidektApiResponseDeck deckData;
deckData.fromJson(reply);
// We're in single deck mode - disable infinite scroll
isListMode = false;
// Clear existing results for single deck view
QLayoutItem *item;
while ((item = resultsLayout->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
auto display = new ArchidektApiResponseDeckDisplayWidget(resultsContainer, deckData, cardSizeSlider);
connect(display, &ArchidektApiResponseDeckDisplayWidget::requestNavigation, this, &TabArchidekt::actNavigatePage);
connect(display, &ArchidektApiResponseDeckDisplayWidget::requestSearch, this, &TabArchidekt::doSearchImmediate);
connect(display, &ArchidektApiResponseDeckDisplayWidget::openInDeckEditor, tabSupervisor,
&TabSupervisor::openDeckInNewTab);
resultsLayout->addWidget(display);
}
void TabArchidekt::prettyPrintJson(const QJsonValue &value, int indentLevel)
{
const QString indent(indentLevel * 2, ' ');
if (value.isObject()) {
QJsonObject obj = value.toObject();
for (auto it = obj.begin(); it != obj.end(); ++it) {
qInfo().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) {
qInfo().noquote() << indent + QString("[%1]:").arg(i);
prettyPrintJson(array[i], indentLevel + 1);
}
} else if (value.isString()) {
qInfo().noquote() << indent + "\"" + value.toString() + "\"";
} else if (value.isDouble()) {
qInfo().noquote() << indent + QString::number(value.toDouble());
} else if (value.isBool()) {
qInfo().noquote() << indent + (value.toBool() ? "true" : "false");
} else if (value.isNull()) {
qInfo().noquote() << indent + "null";
}
}

View file

@ -1,294 +0,0 @@
#ifndef COCKATRICE_TAB_ARCHIDEKT_H
#define COCKATRICE_TAB_ARCHIDEKT_H
#include "../../interface/widgets/cards/card_size_widget.h"
#include "../../interface/widgets/quick_settings/settings_button_widget.h"
#include "../../tab.h"
#include "display/archidekt_api_response_deck_listings_display_widget.h"
#include <QCheckBox>
#include <QComboBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QNetworkAccessManager>
#include <QPushButton>
#include <QScrollArea>
#include <QSet>
#include <QSpinBox>
#include <QString>
#include <QTimer>
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/card/database/card_database.h>
/** Base API link for Archidekt deck search */
inline QString archidektApiLink = "https://archidekt.com/api/decks/v3/?name=";
class ManaSymbolWidget;
/**
* @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 supports infinite scroll pagination for seamless browsing.
*/
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
* - Minimum amount of cards in the deck
* - Current page (for infinite scroll)
*/
QString buildSearchUrl();
/**
* @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;
}
public slots:
/**
* @brief Trigger a debounced search using the current filters
*
* Resets to page 1 and starts the debounce timer. The actual search will execute
* after 300ms of inactivity.
*/
void doSearch();
/**
* @brief Immediately trigger a search using the current filters
*
* Sends a network request to the Archidekt API using the URL generated by buildSearchUrl().
* Updates the results display asynchronously.
*/
void doSearchImmediate();
/**
* @brief Load the next page of results for infinite scroll
*
* Increments the current page and fetches additional results, which are appended
* to the existing results display.
*/
void loadNextPage();
/**
* @brief Process a network reply containing JSON data
* @param reply QNetworkReply object with the API response
*
* Determines whether the response corresponds to a deck listing 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 deck listings
*
* If this is page 1, clears previous results. Appends new results to the display.
*/
void processTopDecksResponse(QJsonObject reply);
/**
* @brief Handle a JSON response for a single deck
* @param reply QJsonObject containing deck data
*
* Clears the results area and displays the single 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 deck card is clicked in the listing.
*/
void actNavigatePage(QString url);
/**
* @brief Fetch top decks from the Archidekt API
*
* Called on initialization to populate the initial results display.
*/
void getTopDecks();
protected:
/**
* @brief Event filter to catch wheel events for infinite scroll
* @param obj The object that received the event
* @param event The event to filter
* @return bool Whether the event was handled
*/
bool eventFilter(QObject *obj, QEvent *event) override;
private:
/**
* @brief Initialize the main UI layout and toolbars
*
* Creates the container, main layout, primary toolbar (sort, colors, name, owner, packages),
* secondary toolbar (advanced filters), and scrollable results area.
*/
void initializeUi();
/**
* @brief Set up all filter widgets
*
* Creates filter widgets for:
* - Card search with autocomplete
* - Commander search with autocomplete
* - Deck tags
* - Format selection (collapsible)
* - Deck size filter (collapsible)
*/
void setupFilterWidgets();
/**
* @brief Connect all signals and slots for UI interactions
*
* Links all widget signals to their appropriate handlers, including
* search triggers, filter changes, package mode toggling, and infinite scroll.
*/
void connectSignals();
/**
* @brief Update UI state when package mode is toggled
* @param isPackageMode Whether package mode is currently enabled
*
* Disables format-specific and commander-specific filters when searching
* for card packages instead of full decks.
*/
void updatePackageModeState(bool isPackageMode);
// ---------------------------------------------------------------------
// Network & Timing
// ---------------------------------------------------------------------
QNetworkAccessManager *networkManager; ///< Network manager for handling API requests
QTimer *searchDebounceTimer; ///< Timer to debounce search requests
int currentPage; ///< Current page number for infinite scroll
bool isLoadingMore; ///< Flag to prevent multiple simultaneous page loads
bool isListMode;
ArchidektApiResponseDeckListingsDisplayWidget *listingsWidget = nullptr;
// ---------------------------------------------------------------------
// Layout Containers
// ---------------------------------------------------------------------
QWidget *container; ///< Root container for the entire tab
QVBoxLayout *mainLayout; ///< Outer vertical layout containing toolbars and results
QWidget *primaryToolbar; ///< Primary toolbar with most important filters
QHBoxLayout *primaryToolbarLayout; ///< Layout for primary toolbar
QWidget *secondaryToolbar; ///< Secondary toolbar with advanced filters
QHBoxLayout *secondaryToolbarLayout; ///< Layout for secondary toolbar
QScrollArea *scrollArea; ///< Scrollable area for results (enables infinite scroll)
QWidget *resultsContainer; ///< Container widget inside scroll area
QVBoxLayout *resultsLayout; ///< Layout for results (decks appended here)
// ---------------------------------------------------------------------
// Primary Toolbar Controls (Most Important)
// ---------------------------------------------------------------------
QLabel *sortByLabel; ///< Label for sort controls
QComboBox *orderByCombo; ///< Dropdown for selecting the sort field
QPushButton *orderDirButton; ///< Toggle button for ascending/descending sort
QLabel *filterByLabel; ///< Label for filter controls
QList<ManaSymbolWidget *> colorSymbols; ///< Mana symbol toggle buttons
QSet<QChar> activeColors; ///< Set of currently active mana colors
QCheckBox *logicalAndCheck; ///< Require ALL selected colors instead of ANY
QLineEdit *nameField; ///< Input for deck name filter
QLineEdit *ownerField; ///< Input for owner name filter
QCheckBox *packagesCheck; ///< Toggle for searching card packages instead of full decks
QPushButton *searchButton; ///< Button to trigger search
QPushButton *advancedFiltersButton; ///< Button to show/hide advanced filters
SettingsButtonWidget *settingsButton; ///< Container for card size settings
CardSizeWidget *cardSizeSlider; ///< Slider to adjust card size in results
// ---------------------------------------------------------------------
// Secondary Toolbar Controls (Advanced Filters)
// ---------------------------------------------------------------------
QLineEdit *cardsField; ///< Input for cards included in the deck
QLineEdit *commandersField; ///< Input for commander cards
QLineEdit *deckTagNameField; ///< Input for deck tag filtering
SettingsButtonWidget *formatButton; ///< Collapsible button for format filters
QVector<QCheckBox *> formatChecks; ///< Individual checkboxes for each format
QComboBox *edhBracketCombo; ///< Dropdown for EDH bracket selection
SettingsButtonWidget *deckSizeButton; ///< Collapsible button for deck size filter
QSpinBox *minDeckSizeSpin; ///< Spinner to select minimum deck size
QComboBox *minDeckSizeLogicCombo; ///< Combo box for size comparison logic
// ---------------------------------------------------------------------
// Optional Context
// ---------------------------------------------------------------------
CardInfoPtr cardToQuery; ///< Optional pre-selected card for initial filtering
};
#endif // COCKATRICE_TAB_ARCHIDEKT_H

View file

@ -1,42 +0,0 @@
#include "edhrec_api_response_archidekt_links.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
void EdhrecApiResponseArchidektLink::fromJson(const QJsonObject &json)
{
c = json.value("c").toString();
f = json.value("f").toInt(0);
q = json.value("q").toInt(0);
u = json.value("u").toString();
}
void EdhrecApiResponseArchidektLink::debugPrint() const
{
qDebug() << " C:" << c;
qDebug() << " F:" << f;
qDebug() << " Q:" << q;
qDebug() << " U:" << u;
}
void EdhrecCommanderApiResponseArchidektLinks::fromJson(const QJsonArray &json)
{
entries.clear();
for (const QJsonValue &value : json) {
if (value.isObject()) {
QJsonObject entryJson = value.toObject();
EdhrecApiResponseArchidektLink entry;
entry.fromJson(entryJson);
entries.append(entry);
}
}
}
void EdhrecCommanderApiResponseArchidektLinks::debugPrint() const
{
qDebug() << "Archidekt Entries:";
for (const auto &entry : entries) {
entry.debugPrint();
}
}

View file

@ -1,39 +0,0 @@
#ifndef ARCHIDEKTENTRY_H
#define ARCHIDEKTENTRY_H
#include <QJsonObject>
#include <QString>
#include <QVector>
/**
* @class EdhrecApiResponseArchidektLink
* @ingroup ApiResponses
* @brief Represents a single Archidekt entry
*/
class EdhrecApiResponseArchidektLink
{
public:
QString c;
int f = 0;
int q = 0;
QString u;
void fromJson(const QJsonObject &json);
void debugPrint() const;
};
/**
* @class EdhrecCommanderApiResponseArchidektLinks
* @ingroup ApiResponses
* @brief Represents the Archidekt section as a list of entries
*/
class EdhrecCommanderApiResponseArchidektLinks
{
public:
QVector<EdhrecApiResponseArchidektLink> entries;
void fromJson(const QJsonArray &json);
void debugPrint() const;
};
#endif // ARCHIDEKTENTRY_H

View file

@ -1,47 +0,0 @@
#include "edhrec_average_deck_api_response.h"
#include <QDebug>
#include <QJsonArray>
void EdhrecAverageDeckApiResponse::fromJson(const QJsonObject &json)
{
// Parse the collapsed DeckStatistics
deckStats.fromJson(json);
// Parse Archidekt section
QJsonArray archidektJson = json.value("archidekt").toArray();
archidekt.fromJson(archidektJson);
// Parse other fields
similar = json.value("similar").toObject();
header = json.value("header").toString();
panels = json.value("panels").toObject();
description = json.value("description").toString();
QJsonObject containerJson = json.value("container").toObject();
container.fromJson(containerJson);
QJsonArray cardsJson = json.value("deck").toArray();
deck.fromJson(cardsJson);
}
void EdhrecAverageDeckApiResponse::debugPrint() const
{
qDebug() << "Deck Statistics:";
qDebug() << " Creature:" << deckStats.creature;
qDebug() << " Instant:" << deckStats.instant;
qDebug() << " Sorcery:" << deckStats.sorcery;
qDebug() << " Artifact:" << deckStats.artifact;
qDebug() << " Enchantment:" << deckStats.enchantment;
qDebug() << " Battle:" << deckStats.battle;
qDebug() << " Planeswalker:" << deckStats.planeswalker;
qDebug() << " Land:" << deckStats.land;
qDebug() << " Basic:" << deckStats.basic;
qDebug() << " Nonbasic:" << deckStats.nonbasic;
archidekt.debugPrint();
qDebug() << "Similar:" << similar;
qDebug() << "Header:" << header;
qDebug() << "Panels:" << panels;
qDebug() << "Description:" << description;
container.debugPrint();
}

View file

@ -1,33 +0,0 @@
#ifndef EDHREC_AVERAGE_DECK_API_RESPONSE_H
#define EDHREC_AVERAGE_DECK_API_RESPONSE_H
#include "../archidekt_links/edhrec_api_response_archidekt_links.h"
#include "../cards/edhrec_api_response_card_container.h"
#include "../commander/edhrec_commander_api_response_average_deck_statistics.h"
#include "edhrec_deck_api_response.h"
#include <QJsonObject>
#include <QString>
/**
* @class EdhrecAverageDeckApiResponse
* @ingroup ApiResponses
* @brief Represents the main structure of the JSON
*/
class EdhrecAverageDeckApiResponse
{
public:
EdhrecCommanderApiResponseAverageDeckStatistics deckStats;
EdhrecCommanderApiResponseArchidektLinks archidekt;
QJsonObject similar;
QString header;
QJsonObject panels;
QString description;
EdhrecApiResponseCardContainer container;
EdhrecDeckApiResponse deck;
void fromJson(const QJsonObject &json);
void debugPrint() const;
};
#endif // EDHREC_AVERAGE_DECK_API_RESPONSE_H

View file

@ -1,23 +0,0 @@
#include "edhrec_deck_api_response.h"
#include <QApplication>
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <libcockatrice/card/import/card_name_normalizer.h>
void EdhrecDeckApiResponse::fromJson(const QJsonArray &json)
{
QString deckList;
for (const QJsonValue &cardlistValue : json) {
deckList += cardlistValue.toString() + "\n";
}
QTextStream stream(&deckList);
deck.loadFromStream_Plain(stream, true, CardNameNormalizer());
}
void EdhrecDeckApiResponse::debugPrint() const
{
qDebug() << "Breadcrumb:";
}

View file

@ -1,27 +0,0 @@
/**
* @file edhrec_deck_api_response.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef EDHREC_DECK_API_RESPONSE_H
#define EDHREC_DECK_API_RESPONSE_H
#include "../../../../../../deck_loader/deck_loader.h"
class EdhrecDeckApiResponse
{
public:
// Constructor
EdhrecDeckApiResponse() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonArray &json);
// Debug method for logging
void debugPrint() const;
DeckList deck;
};
#endif // EDHREC_DECK_API_RESPONSE_H

View file

@ -1,31 +0,0 @@
#include "edhrec_api_response_card_prices.h"
#include <QDebug>
void CardPrices::fromJson(const QJsonObject &json)
{
// Parse prices from various sources
cardhoarder = json.value("cardhoarder").toObject();
cardkingdom = json.value("cardkingdom").toObject();
cardmarket = json.value("cardmarket").toObject();
face2face = json.value("face2face").toObject();
manapool = json.value("manapool").toObject();
mtgstocks = json.value("mtgstocks").toObject();
scg = json.value("scg").toObject();
tcgl = json.value("tcgl").toObject();
tcgplayer = json.value("tcgplayer").toObject();
}
void CardPrices::debugPrint() const
{
qInfo() << "Card Prices:";
qInfo() << "Cardhoarder:" << cardhoarder;
qInfo() << "Cardkingdom:" << cardkingdom;
qInfo() << "Cardmarket:" << cardmarket;
qInfo() << "Face2Face:" << face2face;
qInfo() << "Manapool:" << manapool;
qInfo() << "Mtgstocks:" << mtgstocks;
qInfo() << "SCG:" << scg;
qInfo() << "TCGL:" << tcgl;
qInfo() << "Tcgplayer:" << tcgplayer;
}

View file

@ -1,72 +0,0 @@
/**
* @file edhrec_api_response_card_prices.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H
#define EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H
#include <QJsonObject>
class CardPrices
{
public:
// Constructor
CardPrices() = default;
// Parse prices from JSON
void fromJson(const QJsonObject &json);
void debugPrint() const;
// Getter methods for card prices
[[nodiscard]] const QJsonObject &getCardhoarder() const
{
return cardhoarder;
}
[[nodiscard]] const QJsonObject &getCardkingdom() const
{
return cardkingdom;
}
[[nodiscard]] const QJsonObject &getCardmarket() const
{
return cardmarket;
}
[[nodiscard]] const QJsonObject &getFace2face() const
{
return face2face;
}
[[nodiscard]] const QJsonObject &getManapool() const
{
return manapool;
}
[[nodiscard]] const QJsonObject &getMtgstocks() const
{
return mtgstocks;
}
[[nodiscard]] const QJsonObject &getScg() const
{
return scg;
}
[[nodiscard]] const QJsonObject &getTcgl() const
{
return tcgl;
}
[[nodiscard]] const QJsonObject &getTcgplayer() const
{
return tcgplayer;
}
private:
QJsonObject cardhoarder;
QJsonObject cardkingdom;
QJsonObject cardmarket;
QJsonObject face2face;
QJsonObject manapool;
QJsonObject mtgstocks;
QJsonObject scg;
QJsonObject tcgl;
QJsonObject tcgplayer;
};
#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H

View file

@ -1,49 +0,0 @@
#include "edhrec_api_response_card_container.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
void EdhrecApiResponseCardContainer::fromJson(const QJsonObject &json)
{
// Parse breadcrumb
QJsonArray breadcrumbArray = json.value("breadcrumb").toArray();
for (const QJsonValue &breadcrumbValue : breadcrumbArray) {
breadcrumb.push_back(breadcrumbValue.toObject());
}
description = json.value("description").toString();
QJsonObject jsonDict = json.value("json_dict").toObject();
card.fromJson(jsonDict.value("card").toObject());
QJsonArray cardlistsArray = jsonDict.value("cardlists").toArray();
for (const QJsonValue &cardlistValue : cardlistsArray) {
QJsonObject cardlistObj = cardlistValue.toObject();
QJsonArray cardviewsArray = cardlistObj.value("cardviews").toArray();
EdhrecApiResponseCardList cardView;
cardView.fromJson(cardlistValue.toObject());
cardlists.push_back(cardView);
}
keywords = json.value("keywords").toString();
title = json.value("title").toString();
}
void EdhrecApiResponseCardContainer::debugPrint() const
{
qDebug() << "Breadcrumb:";
for (const auto &breadcrumbEntry : breadcrumb) {
qDebug() << breadcrumbEntry;
}
qDebug() << "Description:" << description;
card.debugPrint();
qDebug() << "Cardlists:";
for (const auto &cardlist : cardlists) {
cardlist.debugPrint();
}
qDebug() << "Keywords:" << keywords;
qDebug() << "Title:" << title;
}

View file

@ -1,64 +0,0 @@
/**
* @file edhrec_api_response_card_container.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef CONTAINER_ENTRY_H
#define CONTAINER_ENTRY_H
#include "edhrec_api_response_card_list.h"
#include "edhrec_commander_api_response_commander_details.h"
#include <QJsonObject>
#include <QString>
#include <QVector>
class EdhrecApiResponseCardContainer
{
public:
// Constructor
EdhrecApiResponseCardContainer() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
// Getter methods for deck container
[[nodiscard]] const QString &getDescription() const
{
return description;
}
[[nodiscard]] const QVector<QJsonObject> &getBreadcrumb() const
{
return breadcrumb;
}
[[nodiscard]] const EdhrecCommanderApiResponseCommanderDetails &getCommanderDetails() const
{
return card;
}
[[nodiscard]] const QVector<EdhrecApiResponseCardList> &getCardlists() const
{
return cardlists;
}
[[nodiscard]] const QString &getKeywords() const
{
return keywords;
}
[[nodiscard]] const QString &getTitle() const
{
return title;
}
private:
QString description;
QVector<QJsonObject> breadcrumb;
EdhrecCommanderApiResponseCommanderDetails card;
QVector<EdhrecApiResponseCardList> cardlists;
QString keywords;
QString title;
};
#endif // CONTAINER_ENTRY_H

View file

@ -1,36 +0,0 @@
#include "edhrec_api_response_card_details.h"
#include <QDebug>
EdhrecApiResponseCardDetails::EdhrecApiResponseCardDetails()
: synergy(0.0), inclusion(0), numDecks(0), potentialDecks(0)
{
}
void EdhrecApiResponseCardDetails::fromJson(const QJsonObject &json)
{
// Parse the fields from the JSON object
name = json.value("name").toString();
sanitized = json.value("sanitized").toString();
sanitizedWo = json.value("sanitized_wo").toString();
url = json.value("url").toString();
synergy = json.value("synergy").toDouble(0.0);
inclusion = json.value("inclusion").toInt(0);
label = json.value("label").toString();
numDecks = json.value("num_decks").toInt(0);
potentialDecks = json.value("potential_decks").toInt(0);
}
void EdhrecApiResponseCardDetails::debugPrint() const
{
// Print out all the fields for debugging
qDebug() << "Name:" << name;
qDebug() << "Sanitized:" << sanitized;
qDebug() << "Sanitized Wo:" << sanitizedWo;
qDebug() << "URL:" << url;
qDebug() << "Synergy:" << synergy;
qDebug() << "Inclusion:" << inclusion;
qDebug() << "Label:" << label;
qDebug() << "Num Decks:" << numDecks;
qDebug() << "Potential Decks:" << potentialDecks;
}

View file

@ -1,35 +0,0 @@
/**
* @file edhrec_api_response_card_details.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef CARD_VIEW_H
#define CARD_VIEW_H
#include <QJsonObject>
#include <QString>
class EdhrecApiResponseCardDetails
{
public:
QString name;
QString sanitized;
QString sanitizedWo;
QString url;
double synergy;
int inclusion;
QString label;
int numDecks;
int potentialDecks;
EdhrecApiResponseCardDetails();
// Method to populate the object from a JSON object
void fromJson(const QJsonObject &json);
// Debug method to print out the data
void debugPrint() const;
};
#endif // CARD_VIEW_H

View file

@ -1,33 +0,0 @@
#include "edhrec_api_response_card_list.h"
#include <QDebug>
EdhrecApiResponseCardList::EdhrecApiResponseCardList()
{
}
void EdhrecApiResponseCardList::fromJson(const QJsonObject &json)
{
// Parse the header from the JSON object
header = json.value("header").toString();
// Parse the cardviews array and populate cardViews
QJsonArray cardviewsArray = json.value("cardviews").toArray();
for (const QJsonValue &value : cardviewsArray) {
QJsonObject cardviewObj = value.toObject();
EdhrecApiResponseCardDetails cardView;
cardView.fromJson(cardviewObj);
cardViews.append(cardView);
}
}
void EdhrecApiResponseCardList::debugPrint() const
{
// Print out the header
qDebug() << "Header:" << header;
// Print out all the CardView objects
for (const EdhrecApiResponseCardDetails &cardView : cardViews) {
cardView.debugPrint();
}
}

View file

@ -1,32 +0,0 @@
/**
* @file edhrec_api_response_card_list.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef CARD_LIST_H
#define CARD_LIST_H
#include "edhrec_api_response_card_details.h"
#include <QJsonArray>
#include <QList>
#include <QString>
class EdhrecApiResponseCardList
{
public:
QString header;
QList<EdhrecApiResponseCardDetails> cardViews;
// Default constructor
EdhrecApiResponseCardList();
// Method to populate the object from a JSON object
void fromJson(const QJsonObject &json);
// Debug method to print out the data
void debugPrint() const;
};
#endif // CARD_LIST_H

View file

@ -1,90 +0,0 @@
#include "edhrec_commander_api_response_commander_details.h"
#include <QDebug>
void EdhrecCommanderApiResponseCommanderDetails::fromJson(const QJsonObject &json)
{
// Parse card-related data
aetherhubUri = json.value("aetherhub_uri").toString();
archidektUri = json.value("archidekt_uri").toString();
cmc = json.value("cmc").toInt(0);
colorIdentity = json.value("color_identity").toArray();
combos = json.value("combos").toBool(false);
deckstatsUri = json.value("deckstats_uri").toString();
// Parse image URIs
QJsonArray imageUrisArray = json.value("image_uris").toArray();
for (const QJsonValue &imageValue : imageUrisArray) {
QJsonObject imageObject = imageValue.toObject();
imageUris.push_back(imageObject.value("normal").toString());
imageUris.push_back(imageObject.value("art_crop").toString());
}
inclusion = json.value("inclusion").toInt(0);
isCommander = json.value("is_commander").toBool(false);
label = json.value("label").toString();
layout = json.value("layout").toString();
legalCommander = json.value("legal_commander").toBool(false);
moxfieldUri = json.value("moxfield_uri").toString();
mtggoldfishUri = json.value("mtggoldfish_uri").toString();
name = json.value("name").toString();
names = json.value("names").toArray();
numDecks = json.value("num_decks").toInt(0);
potentialDecks = json.value("potential_decks").toInt(0);
precon = json.value("precon").toString();
// Parse prices
prices.fromJson(json.value("prices").toObject());
primaryType = json.value("primary_type").toString();
rarity = json.value("rarity").toString();
salt = json.value("salt").toDouble(0.0);
sanitized = json.value("sanitized").toString();
sanitizedWo = json.value("sanitized_wo").toString();
scryfallUri = json.value("scryfall_uri").toString();
spellbookUri = json.value("spellbook_uri").toString();
type = json.value("type").toString();
url = json.value("url").toString();
}
void EdhrecCommanderApiResponseCommanderDetails::debugPrint() const
{
qDebug() << "Card Data:";
qDebug() << "Aetherhub URI:" << aetherhubUri;
qDebug() << "Archidekt URI:" << archidektUri;
qDebug() << "CMC:" << cmc;
qDebug() << "Color Identity:" << colorIdentity;
qDebug() << "Combos:" << combos;
qDebug() << "Deckstats URI:" << deckstatsUri;
qDebug() << "Image URIs:";
for (const auto &uri : imageUris) {
qDebug() << uri;
}
qDebug() << "Inclusion:" << inclusion;
qDebug() << "Is Commander:" << isCommander;
qDebug() << "Label:" << label;
qDebug() << "Layout:" << layout;
qDebug() << "Legal Commander:" << legalCommander;
qDebug() << "Moxfield URI:" << moxfieldUri;
qDebug() << "MTGGoldfish URI:" << mtggoldfishUri;
qDebug() << "Name:" << name;
qDebug() << "Names:" << names;
qDebug() << "Number of Decks:" << numDecks;
qDebug() << "Potential Decks:" << potentialDecks;
qDebug() << "Precon:" << precon;
// Print the prices using the debugPrint method from CardPrices
prices.debugPrint();
qDebug() << "Primary Type:" << primaryType;
qDebug() << "Rarity:" << rarity;
qDebug() << "Salt:" << salt;
qDebug() << "Sanitized:" << sanitized;
qDebug() << "Sanitized WO:" << sanitizedWo;
qDebug() << "Scryfall URI:" << scryfallUri;
qDebug() << "Spellbook URI:" << spellbookUri;
qDebug() << "Type:" << type;
qDebug() << "URL:" << url;
}

View file

@ -1,178 +0,0 @@
/**
* @file edhrec_commander_api_response_commander_details.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H
#define EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H
#include "../card_prices/edhrec_api_response_card_prices.h"
#include <QJsonArray>
#include <QString>
#include <QVector>
class EdhrecCommanderApiResponseCommanderDetails
{
public:
// Constructor
EdhrecCommanderApiResponseCommanderDetails() = default;
// Parse card-related data from JSON
void fromJson(const QJsonObject &json);
// Debug method for logging
void debugPrint() const;
// Getters for the card data
[[nodiscard]] const QString &getAetherhubUri() const
{
return aetherhubUri;
}
[[nodiscard]] const QString &getArchidektUri() const
{
return archidektUri;
}
[[nodiscard]] int getCmc() const
{
return cmc;
}
[[nodiscard]] const QJsonArray &getColorIdentity() const
{
return colorIdentity;
}
[[nodiscard]] bool isCombos() const
{
return combos;
}
[[nodiscard]] const QString &getDeckstatsUri() const
{
return deckstatsUri;
}
[[nodiscard]] const QVector<QString> &getImageUris() const
{
return imageUris;
}
[[nodiscard]] int getInclusion() const
{
return inclusion;
}
[[nodiscard]] bool getIsCommander() const
{
return isCommander;
}
[[nodiscard]] const QString &getLabel() const
{
return label;
}
[[nodiscard]] const QString &getLayout() const
{
return layout;
}
[[nodiscard]] bool getLegalCommander() const
{
return legalCommander;
}
[[nodiscard]] const QString &getMoxfieldUri() const
{
return moxfieldUri;
}
[[nodiscard]] const QString &getMtggoldfishUri() const
{
return mtggoldfishUri;
}
[[nodiscard]] const QString &getName() const
{
return name;
}
[[nodiscard]] const QJsonArray &getNames() const
{
return names;
}
[[nodiscard]] int getNumDecks() const
{
return numDecks;
}
[[nodiscard]] int getPotentialDecks() const
{
return potentialDecks;
}
[[nodiscard]] const QString &getPrecon() const
{
return precon;
}
[[nodiscard]] const CardPrices &getPrices() const
{
return prices;
}
[[nodiscard]] const QString &getPrimaryType() const
{
return primaryType;
}
[[nodiscard]] const QString &getRarity() const
{
return rarity;
}
[[nodiscard]] double getSalt() const
{
return salt;
}
[[nodiscard]] const QString &getSanitized() const
{
return sanitized;
}
[[nodiscard]] const QString &getSanitizedWo() const
{
return sanitizedWo;
}
[[nodiscard]] const QString &getScryfallUri() const
{
return scryfallUri;
}
[[nodiscard]] const QString &getSpellbookUri() const
{
return spellbookUri;
}
[[nodiscard]] const QString &getType() const
{
return type;
}
[[nodiscard]] const QString &getUrl() const
{
return url;
}
private:
QString aetherhubUri;
QString archidektUri;
int cmc = 0;
QJsonArray colorIdentity;
bool combos = false;
QString deckstatsUri;
QVector<QString> imageUris;
int inclusion = 0;
bool isCommander = false;
QString label;
QString layout;
bool legalCommander = false;
QString moxfieldUri;
QString mtggoldfishUri;
QString name;
QJsonArray names;
int numDecks = 0;
int potentialDecks = 0;
QString precon;
CardPrices prices;
QString primaryType;
QString rarity;
double salt = 0.0;
QString sanitized;
QString sanitizedWo;
QString scryfallUri;
QString spellbookUri;
QString type;
QString url;
};
#endif // EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H

View file

@ -1,45 +0,0 @@
#include "edhrec_commander_api_response.h"
#include <QDebug>
#include <QJsonArray>
void EdhrecCommanderApiResponse::fromJson(const QJsonObject &json)
{
// Parse the collapsed DeckStatistics
deckStats.fromJson(json);
// Parse Archidekt section
QJsonArray archidektJson = json.value("archidekt").toArray();
archidekt.fromJson(archidektJson);
// Parse other fields
similar = json.value("similar").toObject();
header = json.value("header").toString();
panels = json.value("panels").toObject();
description = json.value("description").toString();
QJsonObject containerJson = json.value("container").toObject();
container.fromJson(containerJson);
}
void EdhrecCommanderApiResponse::debugPrint() const
{
qDebug() << "Deck Statistics:";
qDebug() << " Creature:" << deckStats.creature;
qDebug() << " Instant:" << deckStats.instant;
qDebug() << " Sorcery:" << deckStats.sorcery;
qDebug() << " Artifact:" << deckStats.artifact;
qDebug() << " Enchantment:" << deckStats.enchantment;
qDebug() << " Battle:" << deckStats.battle;
qDebug() << " Planeswalker:" << deckStats.planeswalker;
qDebug() << " Land:" << deckStats.land;
qDebug() << " Basic:" << deckStats.basic;
qDebug() << " Nonbasic:" << deckStats.nonbasic;
archidekt.debugPrint();
qDebug() << "Similar:" << similar;
qDebug() << "Header:" << header;
qDebug() << "Panels:" << panels;
qDebug() << "Description:" << description;
container.debugPrint();
}

View file

@ -1,31 +0,0 @@
#ifndef DECKDATA_H
#define DECKDATA_H
#include "../archidekt_links/edhrec_api_response_archidekt_links.h"
#include "../cards/edhrec_api_response_card_container.h"
#include "edhrec_commander_api_response_average_deck_statistics.h"
#include <QJsonObject>
#include <QString>
/**
* @class EdhrecCommanderApiResponse
* @ingroup ApiResponses
* @brief Represents the main structure of the JSON
*/
class EdhrecCommanderApiResponse
{
public:
EdhrecCommanderApiResponseAverageDeckStatistics deckStats;
EdhrecCommanderApiResponseArchidektLinks archidekt;
QJsonObject similar;
QString header;
QJsonObject panels;
QString description;
EdhrecApiResponseCardContainer container;
void fromJson(const QJsonObject &json);
void debugPrint() const;
};
#endif // DECKDATA_H

View file

@ -1,15 +0,0 @@
#include "edhrec_commander_api_response_average_deck_statistics.h"
void EdhrecCommanderApiResponseAverageDeckStatistics::fromJson(const QJsonObject &json)
{
creature = json.value("creature").toInt(0);
instant = json.value("instant").toInt(0);
sorcery = json.value("sorcery").toInt(0);
artifact = json.value("artifact").toInt(0);
enchantment = json.value("enchantment").toInt(0);
battle = json.value("battle").toInt(0);
planeswalker = json.value("planeswalker").toInt(0);
land = json.value("land").toInt(0);
basic = json.value("basic").toInt(0);
nonbasic = json.value("nonbasic").toInt(0);
}

View file

@ -1,26 +0,0 @@
#ifndef AVERAGE_DECK_STATISTICS_H
#define AVERAGE_DECK_STATISTICS_H
#include <QJsonObject>
/**
* @struct EdhrecCommanderApiResponseAverageDeckStatistics
* @ingroup ApiResponses
* @brief Represents the typical deck statistics (collapsed section)
*/
struct EdhrecCommanderApiResponseAverageDeckStatistics
{
int creature = 0;
int instant = 0;
int sorcery = 0;
int artifact = 0;
int enchantment = 0;
int battle = 0;
int planeswalker = 0;
int land = 0;
int basic = 0;
int nonbasic = 0;
void fromJson(const QJsonObject &json);
};
#endif // AVERAGE_DECK_STATISTICS_H

View file

@ -1,19 +0,0 @@
#include "edhrec_top_cards_api_response.h"
#include <QDebug>
#include <QJsonArray>
void EdhrecTopCardsApiResponse::fromJson(const QJsonObject &json)
{
header = json.value("header").toString();
description = json.value("description").toString();
QJsonObject containerJson = json.value("container").toObject();
container.fromJson(containerJson);
}
void EdhrecTopCardsApiResponse::debugPrint() const
{
qDebug() << "Header:" << header;
qDebug() << "Description:" << description;
container.debugPrint();
}

View file

@ -1,25 +0,0 @@
/**
* @file edhrec_top_cards_api_response.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef EDHREC_TOP_CARDS_API_RESPONSE_H
#define EDHREC_TOP_CARDS_API_RESPONSE_H
#include "../cards/edhrec_api_response_card_container.h"
#include <QString>
class EdhrecTopCardsApiResponse
{
public:
QString header;
QString description;
EdhrecApiResponseCardContainer container;
void fromJson(const QJsonObject &json);
void debugPrint() const;
};
#endif // EDHREC_TOP_CARDS_API_RESPONSE_H

View file

@ -1,19 +0,0 @@
#include "edhrec_top_commanders_api_response.h"
#include <QDebug>
#include <QJsonArray>
void EdhrecTopCommandersApiResponse::fromJson(const QJsonObject &json)
{
header = json.value("header").toString();
description = json.value("description").toString();
QJsonObject containerJson = json.value("container").toObject();
container.fromJson(containerJson);
}
void EdhrecTopCommandersApiResponse::debugPrint() const
{
qDebug() << "Header:" << header;
qDebug() << "Description:" << description;
container.debugPrint();
}

View file

@ -1,25 +0,0 @@
/**
* @file edhrec_top_commanders_api_response.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef EDHREC_TOP_COMMANDERS_API_RESPONSE_H
#define EDHREC_TOP_COMMANDERS_API_RESPONSE_H
#include "../cards/edhrec_api_response_card_container.h"
#include <QString>
class EdhrecTopCommandersApiResponse
{
public:
QString header;
QString description;
EdhrecApiResponseCardContainer container;
void fromJson(const QJsonObject &json);
void debugPrint() const;
};
#endif // EDHREC_TOP_COMMANDERS_API_RESPONSE_H

View file

@ -1,19 +0,0 @@
#include "edhrec_top_tags_api_response.h"
#include <QDebug>
#include <QJsonArray>
void EdhrecTopTagsApiResponse::fromJson(const QJsonObject &json)
{
header = json.value("header").toString();
description = json.value("description").toString();
QJsonObject containerJson = json.value("container").toObject();
container.fromJson(containerJson);
}
void EdhrecTopTagsApiResponse::debugPrint() const
{
qDebug() << "Header:" << header;
qDebug() << "Description:" << description;
container.debugPrint();
}

View file

@ -1,25 +0,0 @@
/**
* @file edhrec_top_tags_api_response.h
* @ingroup ApiResponses
* @brief TODO: Document this.
*/
#ifndef EDHREC_TOP_TAGS_API_RESPONSE_H
#define EDHREC_TOP_TAGS_API_RESPONSE_H
#include "../cards/edhrec_api_response_card_container.h"
#include <QString>
class EdhrecTopTagsApiResponse
{
public:
QString header;
QString description;
EdhrecApiResponseCardContainer container;
void fromJson(const QJsonObject &json);
void debugPrint() const;
};
#endif // EDHREC_TOP_TAGS_API_RESPONSE_H

View file

@ -1,66 +0,0 @@
#include "edhrec_api_response_card_prices_display_widget.h"
EdhrecApiResponseCardPricesDisplayWidget::EdhrecApiResponseCardPricesDisplayWidget(QWidget *parent,
const CardPrices &_cardPrices)
: QWidget(parent), cardPrices(_cardPrices)
{
layout = new QGridLayout(this);
setLayout(layout);
cardHoarderLabel = new QLabel(this);
cardHoarderPrice = new QLabel(QString::number(cardPrices.getCardhoarder().value("price").toDouble()), this);
cardKingdomLabel = new QLabel(this);
cardKingdomPrice = new QLabel(QString::number(cardPrices.getCardkingdom().value("price").toDouble()), this);
cardMarketLabel = new QLabel(this);
cardMarketPrice = new QLabel(QString::number(cardPrices.getCardmarket().value("price").toDouble()), this);
face2faceLabel = new QLabel(this);
face2facePrice = new QLabel(QString::number(cardPrices.getFace2face().value("price").toDouble()), this);
manaPoolLabel = new QLabel(this);
manaPoolPrice = new QLabel(QString::number(cardPrices.getManapool().value("price").toDouble()), this);
mtgStocksLabel = new QLabel(this);
mtgStocksPrice = new QLabel(QString::number(cardPrices.getMtgstocks().value("price").toDouble()), this);
scgLabel = new QLabel(this);
scgPrice = new QLabel(QString::number(cardPrices.getScg().value("price").toDouble()), this);
tcglLabel = new QLabel(this);
tcglPrice = new QLabel(QString::number(cardPrices.getTcgl().value("price").toDouble()), this);
tcgplayerLabel = new QLabel(this);
tcgplayerPrice = new QLabel(QString::number(cardPrices.getTcgplayer().value("price").toDouble()), this);
layout->addWidget(cardHoarderLabel, 0, 0);
layout->addWidget(cardHoarderPrice, 0, 1);
layout->addWidget(cardKingdomLabel, 0, 2);
layout->addWidget(cardKingdomPrice, 0, 3);
layout->addWidget(cardMarketLabel, 1, 0);
layout->addWidget(cardMarketPrice, 1, 1);
layout->addWidget(face2faceLabel, 1, 2);
layout->addWidget(face2facePrice, 1, 3);
layout->addWidget(manaPoolLabel, 2, 0);
layout->addWidget(manaPoolPrice, 2, 1);
layout->addWidget(mtgStocksLabel, 2, 2);
layout->addWidget(mtgStocksPrice, 2, 3);
layout->addWidget(scgLabel, 3, 0);
layout->addWidget(scgPrice, 3, 1);
layout->addWidget(tcglLabel, 3, 2);
layout->addWidget(tcglPrice, 3, 3);
layout->addWidget(tcgplayerLabel, 4, 0);
layout->addWidget(tcgplayerPrice, 4, 1);
retranslateUi();
}
void EdhrecApiResponseCardPricesDisplayWidget::retranslateUi()
{
cardHoarderLabel->setText(tr("Card Hoarder"));
cardKingdomLabel->setText(tr("Card Kingdom"));
cardMarketLabel->setText(tr("Card Market"));
face2faceLabel->setText(tr("Face 2-Face"));
manaPoolLabel->setText(tr("Mana Pool"));
mtgStocksLabel->setText(tr("MTG Stocks"));
scgLabel->setText(tr("Scg"));
tcglLabel->setText(tr("Tcgl"));
tcgplayerLabel->setText(tr("Tcgplayer"));
}

View file

@ -1,48 +0,0 @@
/**
* @file edhrec_api_response_card_prices_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_API_RESPONSE_CARD_PRICES_DISPLAY_WIDGET_H
#define EDHREC_API_RESPONSE_CARD_PRICES_DISPLAY_WIDGET_H
#include "../../api_response/card_prices/edhrec_api_response_card_prices.h"
#include <QGridLayout>
#include <QLabel>
#include <QWidget>
class EdhrecApiResponseCardPricesDisplayWidget : public QWidget
{
Q_OBJECT
public:
EdhrecApiResponseCardPricesDisplayWidget(QWidget *parent, const CardPrices &cardPrices);
public slots:
void retranslateUi();
private:
CardPrices cardPrices;
QGridLayout *layout;
QLabel *cardHoarderLabel;
QLabel *cardHoarderPrice;
QLabel *cardKingdomLabel;
QLabel *cardKingdomPrice;
QLabel *cardMarketLabel;
QLabel *cardMarketPrice;
QLabel *face2faceLabel;
QLabel *face2facePrice;
QLabel *manaPoolLabel;
QLabel *manaPoolPrice;
QLabel *mtgStocksLabel;
QLabel *mtgStocksPrice;
QLabel *scgLabel;
QLabel *scgPrice;
QLabel *tcglLabel;
QLabel *tcglPrice;
QLabel *tcgplayerLabel;
QLabel *tcgplayerPrice;
};
#endif // EDHREC_API_RESPONSE_CARD_PRICES_DISPLAY_WIDGET_H

View file

@ -1,87 +0,0 @@
#include "edhrec_api_response_card_details_display_widget.h"
#include "../../../../../general/display/background_plate_widget.h"
#include "../../tab_edhrec_main.h"
#include <QMouseEvent>
#include <libcockatrice/card/database/card_database_manager.h>
EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWidget(
QWidget *parent,
const EdhrecApiResponseCardDetails &_toDisplay)
: QWidget(parent), toDisplay(_toDisplay)
{
layout = new QVBoxLayout(this);
setLayout(layout);
cardPictureWidget = new CardInfoPictureWidget(this);
cardPictureWidget->setCard(CardDatabaseManager::query()->guessCard({toDisplay.sanitized}));
nameLabel = new QLabel(this);
nameLabel->setText(toDisplay.name);
nameLabel->setAlignment(Qt::AlignHCenter);
nameLabel->setStyleSheet("font-size: 20px; font-weight: bold");
inclusionDisplayWidget = new EdhrecApiResponseCardInclusionDisplayWidget(this, toDisplay);
synergyDisplayWidget = new EdhrecApiResponseCardSynergyDisplayWidget(this, toDisplay);
backgroundPlateWidget = new BackgroundPlateWidget(this);
auto plateLayout = new QVBoxLayout(backgroundPlateWidget);
plateLayout->addWidget(nameLabel);
plateLayout->addWidget(cardPictureWidget);
plateLayout->addWidget(inclusionDisplayWidget);
plateLayout->addWidget(synergyDisplayWidget);
layout->addWidget(backgroundPlateWidget);
QWidget *currentParent = parentWidget();
TabEdhRecMain *parentTab = nullptr;
while (currentParent) {
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
break;
}
currentParent = currentParent->parentWidget();
}
if (parentTab) {
cardPictureWidget->setScaleFactor(parentTab->getCardSizeSlider()->getSlider()->value());
connect(cardPictureWidget, &CardInfoPictureWidget::cardClicked, this,
&EdhrecApiResponseCardDetailsDisplayWidget::mousePressEvent);
connect(parentTab->getCardSizeSlider()->getSlider(), &QSlider::valueChanged, cardPictureWidget,
&CardInfoPictureWidget::setScaleFactor);
connect(this, &EdhrecApiResponseCardDetailsDisplayWidget::requestUrl, parentTab,
&TabEdhRecMain::actNavigatePage);
}
}
void EdhrecApiResponseCardDetailsDisplayWidget::mousePressEvent(QMouseEvent *event)
{
QWidget::mousePressEvent(event);
if (event->button() == Qt::LeftButton) {
actRequestPageNavigation();
}
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void EdhrecApiResponseCardDetailsDisplayWidget::enterEvent(QEnterEvent *event)
#else
void EdhrecApiResponseCardDetailsDisplayWidget::enterEvent(QEvent *event)
#endif
{
QWidget::enterEvent(event);
backgroundPlateWidget->setFocused(true);
}
void EdhrecApiResponseCardDetailsDisplayWidget::leaveEvent(QEvent *event)
{
QWidget::leaveEvent(event);
backgroundPlateWidget->setFocused(false);
}
void EdhrecApiResponseCardDetailsDisplayWidget::actRequestPageNavigation()
{
emit requestUrl(toDisplay.url);
}

View file

@ -1,50 +0,0 @@
/**
* @file edhrec_api_response_card_details_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H
#define EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H
#include "../../../../../cards/card_info_picture_widget.h"
#include "../../../../../general/display/background_plate_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_details.h"
#include "edhrec_api_response_card_inclusion_display_widget.h"
#include "edhrec_api_response_card_synergy_display_widget.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QWidget>
class EdhrecApiResponseCardDetailsDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecApiResponseCardDetailsDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
public slots:
void actRequestPageNavigation();
signals:
void requestUrl(QString url);
private:
EdhrecApiResponseCardDetails toDisplay;
QVBoxLayout *layout;
CardInfoPictureWidget *cardPictureWidget;
BackgroundPlateWidget *backgroundPlateWidget; ///< Plate for metadata labels
QLabel *nameLabel;
EdhrecApiResponseCardInclusionDisplayWidget *inclusionDisplayWidget;
EdhrecApiResponseCardSynergyDisplayWidget *synergyDisplayWidget;
protected slots:
void mousePressEvent(QMouseEvent *event) override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent *event) override; ///< Qt6 hover enter
#else
void enterEvent(QEvent *event) override; ///< Qt5 hover enter
#endif
void leaveEvent(QEvent *event) override;
};
#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H

View file

@ -1,41 +0,0 @@
#include "edhrec_api_response_card_inclusion_display_widget.h"
EdhrecApiResponseCardInclusionDisplayWidget::EdhrecApiResponseCardInclusionDisplayWidget(
QWidget *parent,
const EdhrecApiResponseCardDetails &_toDisplay)
: QWidget(parent), toDisplay(_toDisplay)
{
layout = new QVBoxLayout(this);
setLayout(layout);
commanderLabel = new QLabel(this);
commanderLabel->setAlignment(Qt::AlignCenter);
amountLabel = new QLabel(this);
amountLabel->setAlignment(Qt::AlignCenter);
inclusionLabel = new QLabel(this);
inclusionLabel->setAlignment(Qt::AlignCenter);
percentBarWidget = new PercentBarWidget(this, toDisplay.inclusion / (toDisplay.potentialDecks / 100.0));
if (toDisplay.inclusion != 0 && toDisplay.potentialDecks != 0) {
layout->addWidget(amountLabel);
layout->addWidget(inclusionLabel);
layout->addWidget(percentBarWidget);
commanderLabel->hide();
} else {
amountLabel->hide();
inclusionLabel->hide();
percentBarWidget->hide();
layout->addWidget(commanderLabel);
}
retranslateUi();
}
void EdhrecApiResponseCardInclusionDisplayWidget::retranslateUi()
{
commanderLabel->setText(toDisplay.label);
amountLabel->setText(tr("In %1 decks").arg(QString::number(toDisplay.inclusion)));
inclusionLabel->setText(tr("%1% of %2 decks")
.arg(QString::number(toDisplay.inclusion / (toDisplay.potentialDecks / 100.0), 'f', 2),
QString::number(toDisplay.potentialDecks)));
}

View file

@ -1,33 +0,0 @@
/**
* @file edhrec_api_response_card_inclusion_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H
#define EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H
#include "../../../../../general/display/charts/bars/percent_bar_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_details.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecApiResponseCardInclusionDisplayWidget : public QWidget
{
Q_OBJECT
public:
EdhrecApiResponseCardInclusionDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
void retranslateUi();
private:
QVBoxLayout *layout;
EdhrecApiResponseCardDetails toDisplay;
QLabel *commanderLabel;
QLabel *amountLabel;
QLabel *inclusionLabel;
PercentBarWidget *percentBarWidget;
};
#endif // EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H

View file

@ -1,33 +0,0 @@
#include "edhrec_api_response_card_list_display_widget.h"
#include "../../../../../general/display/banner_widget.h"
#include "edhrec_api_response_card_details_display_widget.h"
#include <QLabel>
EdhrecApiResponseCardListDisplayWidget::EdhrecApiResponseCardListDisplayWidget(QWidget *parent,
EdhrecApiResponseCardList toDisplay)
: QWidget(parent)
{
layout = new QVBoxLayout(this);
setLayout(layout);
header = new BannerWidget(this, toDisplay.header);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
header->setBuddy(flowWidget);
foreach (EdhrecApiResponseCardDetails card_detail, toDisplay.cardViews) {
auto widget = new EdhrecApiResponseCardDetailsDisplayWidget(flowWidget, card_detail);
flowWidget->addWidget(widget);
}
layout->addWidget(header);
layout->addWidget(flowWidget);
}
void EdhrecApiResponseCardListDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
qDebug() << event->size();
}

View file

@ -1,35 +0,0 @@
/**
* @file edhrec_api_response_card_list_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H
#define EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H
#include "../../../../../general/display/banner_widget.h"
#include "../../../../../general/layout_containers/flow_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_list.h"
#include <QResizeEvent>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecApiResponseCardListDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecApiResponseCardListDisplayWidget(QWidget *parent, EdhrecApiResponseCardList toDisplay);
void resizeEvent(QResizeEvent *event) override;
[[nodiscard]] QString getBannerText() const
{
return header->getText();
}
private:
QVBoxLayout *layout;
BannerWidget *header;
FlowWidget *flowWidget;
};
#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H

View file

@ -1,28 +0,0 @@
#include "edhrec_api_response_card_synergy_display_widget.h"
EdhrecApiResponseCardSynergyDisplayWidget::EdhrecApiResponseCardSynergyDisplayWidget(
QWidget *parent,
const EdhrecApiResponseCardDetails &_toDisplay)
: QWidget(parent), toDisplay(_toDisplay)
{
layout = new QVBoxLayout(this);
setLayout(layout);
label = new QLabel(this);
label->setAlignment(Qt::AlignCenter);
percentBarWidget = new PercentBarWidget(this, toDisplay.synergy * 100.0);
if (toDisplay.synergy != 0) {
layout->addWidget(label);
layout->addWidget(percentBarWidget);
} else {
hide();
}
retranslateUi();
}
void EdhrecApiResponseCardSynergyDisplayWidget::retranslateUi()
{
label->setText(tr("%1% Synergy").arg(QString::number(toDisplay.synergy * 100.0, 'f', 1)));
}

View file

@ -1,31 +0,0 @@
/**
* @file edhrec_api_response_card_synergy_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H
#define EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H
#include "../../../../../general/display/charts/bars/percent_bar_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_details.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecApiResponseCardSynergyDisplayWidget : public QWidget
{
Q_OBJECT
public:
EdhrecApiResponseCardSynergyDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
void retranslateUi();
private:
QVBoxLayout *layout;
EdhrecApiResponseCardDetails toDisplay;
QLabel *label;
PercentBarWidget *percentBarWidget;
};
#endif // EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H

View file

@ -1,82 +0,0 @@
#include "edhrec_api_response_commander_details_display_widget.h"
#include "../../../../../cards/card_info_picture_widget.h"
#include "../../tab_edhrec_main.h"
#include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
#include "edhrec_commander_api_response_bracket_navigation_widget.h"
#include <libcockatrice/card/database/card_database_manager.h>
EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
QWidget *parent,
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
QString baseUrl)
: QWidget(parent), commanderDetails(_commanderDetails)
{
layout = new QHBoxLayout(this);
setLayout(layout);
commanderLayout = new QHBoxLayout();
commanderDetailsLayout = new QVBoxLayout();
commanderDetailsLayout->setAlignment(Qt::AlignCenter);
navigationAndPricesLayout = new QVBoxLayout();
commanderPicture = new CardInfoPictureWidget(this);
commanderPicture->setCard(CardDatabaseManager::query()->getCard({commanderDetails.getName()}));
QWidget *currentParent = parentWidget();
TabEdhRecMain *parentTab = nullptr;
while (currentParent) {
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
break;
}
currentParent = currentParent->parentWidget();
}
if (parentTab) {
connect(parentTab->getCardSizeSlider()->getSlider(), &QSlider::valueChanged, commanderPicture,
&CardInfoPictureWidget::setScaleFactor);
commanderPicture->setScaleFactor(parentTab->getCardSizeSlider()->getSlider()->value());
}
commanderDetails.debugPrint();
commanderName = new QLabel(this);
commanderName->setText(commanderDetails.getName());
commanderName->setAlignment(Qt::AlignCenter);
commanderName->setStyleSheet("font-size: 28px; font-weight: bold");
label = new QLabel(this);
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("font-size: 16px");
salt = new QLabel(this);
salt->setAlignment(Qt::AlignCenter);
salt->setStyleSheet("font-size: 16px");
cardPricesDisplayWidget = new EdhrecApiResponseCardPricesDisplayWidget(this, commanderDetails.getPrices());
navigationWidget = new EdhrecCommanderApiResponseNavigationWidget(this, commanderDetails, baseUrl);
commanderLayout->addWidget(commanderPicture);
commanderDetailsLayout->addWidget(commanderName);
commanderDetailsLayout->addSpacing(1);
commanderDetailsLayout->addWidget(label);
commanderDetailsLayout->addWidget(salt);
commanderDetailsLayout->addWidget(cardPricesDisplayWidget);
commanderLayout->addLayout(commanderDetailsLayout);
navigationAndPricesLayout->addWidget(navigationWidget);
// navigationAndPricesLayout->addWidget(cardPricesDisplayWidget);
layout->addLayout(commanderLayout);
layout->addLayout(navigationAndPricesLayout);
retranslateUi();
}
void EdhrecCommanderResponseCommanderDetailsDisplayWidget::retranslateUi()
{
label->setText(commanderDetails.getLabel());
salt->setText(tr("Salt: ") + QString::number(commanderDetails.getSalt()));
}

View file

@ -1,44 +0,0 @@
/**
* @file edhrec_api_response_commander_details_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H
#define EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H
#include "../../../../../cards/card_info_picture_widget.h"
#include "../../api_response/cards/edhrec_commander_api_response_commander_details.h"
#include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
#include "edhrec_commander_api_response_navigation_widget.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecCommanderApiResponseNavigationWidget;
class EdhrecCommanderResponseCommanderDetailsDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecCommanderResponseCommanderDetailsDisplayWidget(
QWidget *parent,
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
QString baseUrl);
void retranslateUi();
private:
EdhrecCommanderApiResponseCommanderDetails commanderDetails;
QHBoxLayout *layout;
QHBoxLayout *commanderLayout;
QVBoxLayout *commanderDetailsLayout;
QVBoxLayout *navigationAndPricesLayout;
CardInfoPictureWidget *commanderPicture;
QLabel *commanderName;
QLabel *label;
QLabel *salt;
EdhrecApiResponseCardPricesDisplayWidget *cardPricesDisplayWidget;
EdhrecCommanderApiResponseNavigationWidget *navigationWidget;
};
#endif // EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H

View file

@ -1,95 +0,0 @@
#include "edhrec_commander_api_response_bracket_navigation_widget.h"
#include <QSet>
EdhrecCommanderApiResponseBracketNavigationWidget::EdhrecCommanderApiResponseBracketNavigationWidget(
QWidget *parent,
const QString &baseUrl)
: QWidget(parent)
{
layout = new QGridLayout(this);
setLayout(layout);
gameChangerLabel = new QLabel(this);
layout->addWidget(gameChangerLabel, 1, 0, 1, 2);
for (int i = 0; i < gameChangerOptions.length(); i++) {
QString option = gameChangerOptions.at(i);
QString label = option.isEmpty() ? "All" : option.at(0).toUpper() + option.mid(1);
QPushButton *optionButton = new QPushButton(label, this);
optionButton->setMinimumHeight(84);
optionButton->setStyleSheet("font-size: 24px");
gameChangerButtons[option] = optionButton;
layout->addWidget(optionButton, 2, i);
connect(optionButton, &QPushButton::clicked, this, [=, this]() {
selectedGameChanger = option;
updateOptionButtonSelection(gameChangerButtons, option);
emit requestNavigation();
});
}
updateOptionButtonSelection(gameChangerButtons, "");
retranslateUi();
applyOptionsFromUrl(baseUrl);
}
void EdhrecCommanderApiResponseBracketNavigationWidget::retranslateUi()
{
gameChangerLabel->setText(tr("Game Changers"));
}
void EdhrecCommanderApiResponseBracketNavigationWidget::applyOptionsFromUrl(const QString &url)
{
QString cleanedUrl = url;
// Remove base and file extension
if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) {
cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length());
}
if (cleanedUrl.endsWith(".json")) {
cleanedUrl.chop(5);
}
// Expecting something like: "commanders/the-ur-dragon/core/expensive"
QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts);
if (parts.size() < 2) {
return;
}
QString commanderName = parts[1];
QString gameChangerOpt;
// Define valid sets
QSet<QString> validGameChangers = {"exhibition", "core", "upgraded", "optimized", "cedh"};
// Check remaining parts after commander
for (int i = 2; i < parts.size(); ++i) {
QString part = parts[i].toLower();
if (validGameChangers.contains(part)) {
gameChangerOpt = part;
}
}
// Validate and apply
if (!gameChangerButtons.contains(gameChangerOpt)) {
gameChangerOpt.clear();
}
selectedGameChanger = gameChangerOpt;
updateOptionButtonSelection(gameChangerButtons, selectedGameChanger);
}
void EdhrecCommanderApiResponseBracketNavigationWidget::updateOptionButtonSelection(
QMap<QString, QPushButton *> &buttons,
const QString &selectedKey)
{
for (auto it = buttons.begin(); it != buttons.end(); ++it) {
it.value()->setStyleSheet(it.key() == selectedKey
? "background-color: lightblue; font-weight: bold; font-size: 24px;"
: "font-size: 24px");
}
}

View file

@ -1,37 +0,0 @@
#ifndef COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H
#define COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H
#include <QGridLayout>
#include <QLabel>
#include <QMap>
#include <QPushButton>
#include <QWidget>
class EdhrecCommanderApiResponseBracketNavigationWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecCommanderApiResponseBracketNavigationWidget(QWidget *parent, const QString &baseUrl);
void retranslateUi();
void applyOptionsFromUrl(const QString &url);
QString getSelectedGameChanger() const
{
return selectedGameChanger;
}
signals:
void requestNavigation();
private:
QGridLayout *layout;
QLabel *gameChangerLabel;
QStringList gameChangerOptions = {"", "exhibition", "core", "upgraded", "optimized", "cedh"};
QString selectedGameChanger;
QMap<QString, QPushButton *> gameChangerButtons;
void updateOptionButtonSelection(QMap<QString, QPushButton *> &buttons, const QString &selectedKey);
};
#endif // COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H

View file

@ -1,97 +0,0 @@
#include "edhrec_commander_api_response_budget_navigation_widget.h"
#include <QSet>
EdhrecCommanderApiResponseBudgetNavigationWidget::EdhrecCommanderApiResponseBudgetNavigationWidget(
QWidget *parent,
const QString &baseUrl)
: QWidget(parent)
{
layout = new QGridLayout(this);
setLayout(layout);
budgetLabel = new QLabel(this);
layout->addWidget(budgetLabel, 3, 0, 1, 2);
for (int i = 0; i < budgetOptions.length(); i++) {
QString option = budgetOptions.at(i);
QString label = option.isEmpty() ? "Any" : option.at(0).toUpper() + option.mid(1);
QPushButton *btn = new QPushButton(label, this);
btn->setMinimumHeight(84);
btn->setStyleSheet("font-size: 24px");
budgetButtons[option] = btn;
layout->addWidget(btn, 4, i);
connect(btn, &QPushButton::clicked, this, [=, this]() {
selectedBudget = option;
updateOptionButtonSelection(budgetButtons, option);
emit requestNavigation();
});
}
updateOptionButtonSelection(budgetButtons, "");
retranslateUi();
applyOptionsFromUrl(baseUrl);
}
void EdhrecCommanderApiResponseBudgetNavigationWidget::retranslateUi()
{
budgetLabel->setText(tr("Budget"));
}
void EdhrecCommanderApiResponseBudgetNavigationWidget::applyOptionsFromUrl(const QString &url)
{
QString cleanedUrl = url;
// Remove base and file extension
if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) {
cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length());
}
if (cleanedUrl.endsWith(".json")) {
cleanedUrl.chop(5);
}
// Expecting something like: "commanders/the-ur-dragon/core/expensive"
QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts);
if (parts.size() < 2) {
return;
}
QString commanderName = parts[1];
QString gameChangerOpt, budgetOpt;
// Define valid sets
QSet<QString> validGameChangers = {"exhibition", "core", "upgraded", "optimized", "cedh"};
QSet<QString> validBudgets = {"budget", "expensive"};
// Check remaining parts after commander
for (int i = 2; i < parts.size(); ++i) {
QString part = parts[i].toLower();
if (validGameChangers.contains(part)) {
gameChangerOpt = part;
} else if (validBudgets.contains(part)) {
budgetOpt = part;
}
}
if (!budgetButtons.contains(budgetOpt)) {
budgetOpt.clear();
}
selectedBudget = budgetOpt;
updateOptionButtonSelection(budgetButtons, selectedBudget);
}
void EdhrecCommanderApiResponseBudgetNavigationWidget::updateOptionButtonSelection(
QMap<QString, QPushButton *> &buttons,
const QString &selectedKey)
{
for (auto it = buttons.begin(); it != buttons.end(); ++it) {
it.value()->setStyleSheet(it.key() == selectedKey
? "background-color: lightblue; font-weight: bold; font-size: 24px;"
: "font-size: 24px");
}
}

View file

@ -1,37 +0,0 @@
#ifndef COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H
#define COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H
#include <QGridLayout>
#include <QLabel>
#include <QMap>
#include <QPushButton>
#include <QWidget>
class EdhrecCommanderApiResponseBudgetNavigationWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecCommanderApiResponseBudgetNavigationWidget(QWidget *parent, const QString &baseUrl);
void retranslateUi();
void applyOptionsFromUrl(const QString &url);
QString getSelectedBudget() const
{
return selectedBudget;
}
signals:
void requestNavigation();
private:
QGridLayout *layout;
QLabel *budgetLabel;
QStringList budgetOptions = {"", "budget", "expensive"};
QString selectedBudget;
QMap<QString, QPushButton *> budgetButtons;
void updateOptionButtonSelection(QMap<QString, QPushButton *> &buttons, const QString &selectedKey);
};
#endif // COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H

View file

@ -1,103 +0,0 @@
#include "edhrec_commander_api_response_display_widget.h"
#include "../../../../../cards/card_info_picture_widget.h"
#include "../../api_response/commander/edhrec_commander_api_response.h"
#include "../cards/edhrec_api_response_card_list_display_widget.h"
#include "edhrec_api_response_commander_details_display_widget.h"
#include <QListView>
#include <QResizeEvent>
#include <QScrollArea>
#include <QSplitter>
#include <QStringListModel>
EdhrecCommanderApiResponseDisplayWidget::EdhrecCommanderApiResponseDisplayWidget(QWidget *parent,
EdhrecCommanderApiResponse response,
QString baseUrl)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
setLayout(layout);
cardDisplayLayout = new QVBoxLayout(this);
// Create a QSplitter to hold the ListView and ScrollArea holding CardListdisplayWidgets side by side
auto splitter = new QSplitter(this);
splitter->setOrientation(Qt::Horizontal);
auto listView = new QListView(splitter);
listView->setMinimumWidth(50);
listView->setMaximumWidth(150);
auto listModel = new QStringListModel(this);
QStringList widgetNames;
// Add commander details
auto commanderPicture = new EdhrecCommanderResponseCommanderDetailsDisplayWidget(
this, response.container.getCommanderDetails(), baseUrl);
cardDisplayLayout->addWidget(commanderPicture);
widgetNames.append("Commander Details");
// Add card list widgets
auto edhrec_commander_api_response_card_lists = response.container.getCardlists();
for (const EdhrecApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
auto cardListDisplayWidget = new EdhrecApiResponseCardListDisplayWidget(this, card_list);
cardDisplayLayout->addWidget(cardListDisplayWidget);
widgetNames.append(cardListDisplayWidget->getBannerText());
}
// Create a QScrollArea to hold the card display widgets
scrollArea = new QScrollArea(splitter);
scrollArea->setWidgetResizable(true);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// Set the cardDisplayLayout inside the scroll area
auto scrollWidget = new QWidget(scrollArea);
scrollWidget->setLayout(cardDisplayLayout);
connect(splitter, &QSplitter::splitterMoved, this, &EdhrecCommanderApiResponseDisplayWidget::onSplitterChange);
scrollArea->setWidget(scrollWidget);
// Configure the list view
listModel->setStringList(widgetNames);
listView->setModel(listModel);
listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
// Connect the list view to ensure the corresponding widget is visible
connect(listView, &QListView::clicked, this, [this](const QModelIndex &index) {
int widgetIndex = index.row();
qDebug() << "clicked: " << widgetIndex;
auto targetWidget = cardDisplayLayout->itemAt(widgetIndex)->widget();
if (targetWidget) {
qDebug() << "Found targetWidget" << targetWidget;
// Attempt to cast the parent to QScrollArea
auto scrollArea = qobject_cast<QScrollArea *>(this->scrollArea); // Use the scroll area instance
if (scrollArea) {
qDebug() << "ScrollArea" << scrollArea;
scrollArea->ensureWidgetVisible(targetWidget);
}
}
});
// Add splitter to the main layout
splitter->addWidget(listView);
splitter->addWidget(scrollArea);
layout->addWidget(splitter);
}
void EdhrecCommanderApiResponseDisplayWidget::onSplitterChange()
{
scrollArea->widget()->resize(scrollArea->size());
}
void EdhrecCommanderApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
qDebug() << event->size();
layout->invalidate();
layout->activate();
layout->update();
if (scrollArea && scrollArea->widget()) {
scrollArea->widget()->resize(event->size());
}
}

View file

@ -1,35 +0,0 @@
/**
* @file edhrec_commander_api_response_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H
#define EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H
#include "../../api_response/commander/edhrec_commander_api_response.h"
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecCommanderApiResponseDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecCommanderApiResponseDisplayWidget(QWidget *parent,
EdhrecCommanderApiResponse response,
QString baseUrl);
void resizeEvent(QResizeEvent *event) override;
public slots:
void onSplitterChange();
private:
QHBoxLayout *layout;
QVBoxLayout *cardDisplayLayout;
QScrollArea *scrollArea;
};
#endif // EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H

View file

@ -1,89 +0,0 @@
#include "edhrec_commander_api_response_navigation_widget.h"
#include "../../tab_edhrec_main.h"
EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigationWidget(
QWidget *parent,
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
QString baseUrl)
: QWidget(parent), commanderDetails(_commanderDetails)
{
layout = new QGridLayout(this);
setLayout(layout);
bracketNavigationWidget = new EdhrecCommanderApiResponseBracketNavigationWidget(this, baseUrl);
connect(bracketNavigationWidget, &EdhrecCommanderApiResponseBracketNavigationWidget::requestNavigation, this,
&EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation);
budgetNavigationWidget = new EdhrecCommanderApiResponseBudgetNavigationWidget(this, baseUrl);
connect(budgetNavigationWidget, &EdhrecCommanderApiResponseBudgetNavigationWidget::requestNavigation, this,
&EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation);
comboPushButton = new QPushButton(this);
comboPushButton->setMinimumHeight(84);
comboPushButton->setStyleSheet("font-size: 24px");
averageDeckPushButton = new QPushButton(this);
averageDeckPushButton->setMinimumHeight(84);
averageDeckPushButton->setStyleSheet("font-size: 24px");
layout->addWidget(comboPushButton, 0, 0, 1, 1);
layout->addWidget(averageDeckPushButton, 0, 1, 1, 1);
layout->addWidget(bracketNavigationWidget, 1, 0, 1, 2);
layout->addWidget(budgetNavigationWidget, 2, 0, 1, 2);
QWidget *currentParent = parentWidget();
TabEdhRecMain *parentTab = nullptr;
while (currentParent) {
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
break;
}
currentParent = currentParent->parentWidget();
}
if (parentTab) {
connect(comboPushButton, &QPushButton::clicked, this,
&EdhrecCommanderApiResponseNavigationWidget::actRequestComboNavigation);
connect(averageDeckPushButton, &QPushButton::clicked, this,
&EdhrecCommanderApiResponseNavigationWidget::actRequestAverageDeckNavigation);
connect(this, &EdhrecCommanderApiResponseNavigationWidget::requestUrl, parentTab,
&TabEdhRecMain::actNavigatePage);
}
retranslateUi();
}
void EdhrecCommanderApiResponseNavigationWidget::retranslateUi()
{
comboPushButton->setText(tr("Combos"));
averageDeckPushButton->setText(tr("Average Deck"));
}
QString EdhrecCommanderApiResponseNavigationWidget::addNavigationOptionsToUrl(QString baseUrl)
{
if (!bracketNavigationWidget->getSelectedGameChanger().isEmpty()) {
baseUrl += "/" + bracketNavigationWidget->getSelectedGameChanger();
}
if (!budgetNavigationWidget->getSelectedBudget().isEmpty()) {
baseUrl += "/" + budgetNavigationWidget->getSelectedBudget();
}
return baseUrl;
}
void EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation()
{
emit requestUrl(addNavigationOptionsToUrl("/commanders/" + commanderDetails.getSanitized()));
}
void EdhrecCommanderApiResponseNavigationWidget::actRequestComboNavigation()
{
emit requestUrl("/combos/" + commanderDetails.getSanitized());
}
void EdhrecCommanderApiResponseNavigationWidget::actRequestAverageDeckNavigation()
{
emit requestUrl(addNavigationOptionsToUrl("/average-decks/" + commanderDetails.getSanitized()));
}

View file

@ -1,51 +0,0 @@
/**
* @file edhrec_commander_api_response_navigation_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H
#define EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H
#include "edhrec_api_response_commander_details_display_widget.h"
#include "edhrec_commander_api_response_bracket_navigation_widget.h"
#include "edhrec_commander_api_response_budget_navigation_widget.h"
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QWidget>
class EdhrecCommanderApiResponseNavigationWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecCommanderApiResponseNavigationWidget(
QWidget *parent,
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
QString baseUrl);
void retranslateUi();
public slots:
void actRequestCommanderNavigation();
void actRequestComboNavigation();
void actRequestAverageDeckNavigation();
signals:
void requestUrl(QString url);
private:
QGridLayout *layout;
EdhrecCommanderApiResponseBracketNavigationWidget *bracketNavigationWidget;
EdhrecCommanderApiResponseBudgetNavigationWidget *budgetNavigationWidget;
QPushButton *comboPushButton;
QPushButton *averageDeckPushButton;
EdhrecCommanderApiResponseCommanderDetails commanderDetails;
QString addNavigationOptionsToUrl(QString baseUrl);
QString buildComboUrl() const;
};
#endif // EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H

View file

@ -1,45 +0,0 @@
#include "edhrec_top_cards_api_response_display_widget.h"
#include "../../api_response/top_cards/edhrec_top_cards_api_response.h"
#include "../cards/edhrec_api_response_card_list_display_widget.h"
EdhrecTopCardsApiResponseDisplayWidget::EdhrecTopCardsApiResponseDisplayWidget(QWidget *parent,
EdhrecTopCardsApiResponse response)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
setLayout(layout);
cardDisplayLayout = new QVBoxLayout(this);
// Add card list widgets
auto edhrec_commander_api_response_card_lists = response.container.getCardlists();
for (const EdhrecApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
auto cardListDisplayWidget = new EdhrecApiResponseCardListDisplayWidget(this, card_list);
cardDisplayLayout->addWidget(cardListDisplayWidget);
}
// Create a QScrollArea to hold the card display widgets
scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// Set the cardDisplayLayout inside the scroll area
auto scrollWidget = new QWidget(scrollArea);
scrollWidget->setLayout(cardDisplayLayout);
scrollArea->setWidget(scrollWidget);
layout->addWidget(scrollArea);
}
void EdhrecTopCardsApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
layout->invalidate();
layout->activate();
layout->update();
if (scrollArea && scrollArea->widget()) {
scrollArea->widget()->resize(event->size());
}
}

View file

@ -1,31 +0,0 @@
/**
* @file edhrec_top_cards_api_response_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_TOP_CARDS_API_RESPONSE_DISPLAY_WIDGET_H
#define EDHREC_TOP_CARDS_API_RESPONSE_DISPLAY_WIDGET_H
#include "../../api_response/top_cards/edhrec_top_cards_api_response.h"
#include <QResizeEvent>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecTopCardsApiResponseDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecTopCardsApiResponseDisplayWidget(QWidget *parent, EdhrecTopCardsApiResponse response);
void resizeEvent(QResizeEvent *event) override;
private:
QHBoxLayout *layout;
QVBoxLayout *cardDisplayLayout;
QScrollArea *scrollArea;
};
#endif // EDHREC_TOP_CARDS_API_RESPONSE_DISPLAY_WIDGET_H

View file

@ -1,47 +0,0 @@
#include "edhrec_top_commanders_api_response_display_widget.h"
#include "../../api_response/top_commanders/edhrec_top_commanders_api_response.h"
#include "../cards/edhrec_api_response_card_list_display_widget.h"
EdhrecTopCommandersApiResponseDisplayWidget::EdhrecTopCommandersApiResponseDisplayWidget(
QWidget *parent,
EdhrecTopCommandersApiResponse response)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
setLayout(layout);
cardDisplayLayout = new QVBoxLayout(this);
// Add card list widgets
auto edhrec_commander_api_response_card_lists = response.container.getCardlists();
for (const EdhrecApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
auto cardListDisplayWidget = new EdhrecApiResponseCardListDisplayWidget(this, card_list);
cardDisplayLayout->addWidget(cardListDisplayWidget);
}
// Create a QScrollArea to hold the card display widgets
scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// Set the cardDisplayLayout inside the scroll area
auto scrollWidget = new QWidget(scrollArea);
scrollWidget->setLayout(cardDisplayLayout);
scrollArea->setWidget(scrollWidget);
layout->addWidget(scrollArea);
}
void EdhrecTopCommandersApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
qDebug() << event->size();
layout->invalidate();
layout->activate();
layout->update();
if (scrollArea && scrollArea->widget()) {
scrollArea->widget()->resize(event->size());
}
}

View file

@ -1,31 +0,0 @@
/**
* @file edhrec_top_commanders_api_response_display_widget.h
* @ingroup ApiResponseDisplayWidgets
* @brief TODO: Document this.
*/
#ifndef EDHREC_TOP_COMMANDERS_API_RESPONSE_DISPLAY_WIDGET_H
#define EDHREC_TOP_COMMANDERS_API_RESPONSE_DISPLAY_WIDGET_H
#include "../../api_response/top_commanders/edhrec_top_commanders_api_response.h"
#include <QResizeEvent>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecTopCommandersApiResponseDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecTopCommandersApiResponseDisplayWidget(QWidget *parent, EdhrecTopCommandersApiResponse response);
void resizeEvent(QResizeEvent *event) override;
private:
QHBoxLayout *layout;
QVBoxLayout *cardDisplayLayout;
QScrollArea *scrollArea;
};
#endif // EDHREC_TOP_COMMANDERS_API_RESPONSE_DISPLAY_WIDGET_H

Some files were not shown because too many files have changed in this diff Show more