From 4d791f4d7aa48f491919e8d6e87190c637d7bade Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sat, 25 Jan 2025 23:29:27 +0100 Subject: [PATCH] Edhrec tab (#5512) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --------- Co-authored-by: Lukas BrĂ¼bach Co-authored-by: Zach H --- cockatrice/CMakeLists.txt | 14 ++ .../edhrec_commander_api_response.cpp | 45 +++++ .../edhrec_commander_api_response.h | 28 +++ ...commander_api_response_archidekt_links.cpp | 43 +++++ ...c_commander_api_response_archidekt_links.h | 32 ++++ ...r_api_response_average_deck_statistics.cpp | 15 ++ ...der_api_response_average_deck_statistics.h | 22 +++ ..._commander_api_response_card_container.cpp | 49 +++++ ...ec_commander_api_response_card_container.h | 60 ++++++ ...ec_commander_api_response_card_details.cpp | 36 ++++ ...hrec_commander_api_response_card_details.h | 29 +++ ...dhrec_commander_api_response_card_list.cpp | 33 ++++ .../edhrec_commander_api_response_card_list.h | 27 +++ ...rec_commander_api_response_card_prices.cpp | 31 ++++ ...dhrec_commander_api_response_card_prices.h | 66 +++++++ ...mmander_api_response_commander_details.cpp | 90 +++++++++ ...commander_api_response_commander_details.h | 173 ++++++++++++++++++ ...i_response_card_details_display_widget.cpp | 41 +++++ ...api_response_card_details_display_widget.h | 26 +++ ..._api_response_card_list_display_widget.cpp | 34 ++++ ...er_api_response_card_list_display_widget.h | 30 +++ ...ponse_commander_details_display_widget.cpp | 36 ++++ ...esponse_commander_details_display_widget.h | 28 +++ ..._commander_api_response_display_widget.cpp | 102 +++++++++++ ...ec_commander_api_response_display_widget.h | 27 +++ .../src/client/tabs/api/edhrec/tab_edhrec.cpp | 112 ++++++++++++ .../src/client/tabs/api/edhrec/tab_edhrec.h | 37 ++++ .../src/client/tabs/tab_deck_editor.cpp | 69 ++++--- cockatrice/src/client/tabs/tab_deck_editor.h | 2 +- cockatrice/src/client/tabs/tab_supervisor.cpp | 12 ++ cockatrice/src/client/tabs/tab_supervisor.h | 6 + .../widgets/cards/card_info_frame_widget.cpp | 3 + .../cards/card_info_picture_widget.cpp | 27 +++ .../widgets/general/display/banner_widget.cpp | 63 +++++++ .../widgets/general/display/banner_widget.h | 40 ++++ .../general/layout_containers/flow_widget.cpp | 3 + cockatrice/src/client/ui/window_main.h | 5 + 37 files changed, 1468 insertions(+), 28 deletions(-) create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.h create mode 100644 cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp create mode 100644 cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h create mode 100644 cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/general/display/banner_widget.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 7881d330b..04a7c52c4 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -81,6 +81,7 @@ set(cockatrice_SOURCES src/utility/logger.cpp src/client/ui/widgets/cards/card_info_picture_enlarged_widget.cpp src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp + src/client/ui/widgets/general/display/banner_widget.cpp src/client/ui/widgets/general/display/labeled_input.cpp src/client/ui/widgets/general/display/dynamic_font_size_label.cpp src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp @@ -149,6 +150,19 @@ set(cockatrice_SOURCES src/client/tabs/tab_room.cpp src/client/tabs/tab_server.cpp src/client/tabs/tab_supervisor.cpp + src/client/tabs/api/edhrec/tab_edhrec.cpp + src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.cpp + src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp + src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp + src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.cpp + src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.cpp src/game/zones/table_zone.cpp src/client/tapped_out_interface.cpp src/client/ui/theme_manager.cpp diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.cpp new file mode 100644 index 000000000..da2a9c052 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.cpp @@ -0,0 +1,45 @@ +#include "edhrec_commander_api_response.h" + +#include +#include + +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(); +} diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.h new file mode 100644 index 000000000..91cf3afbc --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.h @@ -0,0 +1,28 @@ +#ifndef DECKDATA_H +#define DECKDATA_H + +#include "edhrec_commander_api_response_archidekt_links.h" +#include "edhrec_commander_api_response_average_deck_statistics.h" +#include "edhrec_commander_api_response_card_container.h" + +#include +#include +#include + +// Represents the main structure of the JSON +class EdhrecCommanderApiResponse +{ +public: + EdhrecCommanderApiResponseAverageDeckStatistics deckStats; + EdhrecCommanderApiResponseArchidektLinks archidekt; + QJsonObject similar; + QString header; + QJsonObject panels; + QString description; + EdhrecCommanderApiResponseCardContainer container; + + void fromJson(const QJsonObject &json); + void debugPrint() const; +}; + +#endif // DECKDATA_H diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.cpp new file mode 100644 index 000000000..aac540f90 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.cpp @@ -0,0 +1,43 @@ +#include "edhrec_commander_api_response_archidekt_links.h" + +#include +#include +#include +#include + +void EdhrecCommanderApiResponseArchidektLink::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 EdhrecCommanderApiResponseArchidektLink::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(); + EdhrecCommanderApiResponseArchidektLink entry; + entry.fromJson(entryJson); + entries.append(entry); + } + } +} + +void EdhrecCommanderApiResponseArchidektLinks::debugPrint() const +{ + qDebug() << "Archidekt Entries:"; + for (const auto &entry : entries) { + entry.debugPrint(); + } +} diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.h new file mode 100644 index 000000000..537dbb1fc --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.h @@ -0,0 +1,32 @@ +#ifndef ARCHIDEKTENTRY_H +#define ARCHIDEKTENTRY_H + +#include +#include +#include +#include + +// Represents a single Archidekt entry +class EdhrecCommanderApiResponseArchidektLink +{ +public: + QString c; + int f = 0; + int q = 0; + QString u; + + void fromJson(const QJsonObject &json); + void debugPrint() const; +}; + +// Represents the Archidekt section as a list of entries +class EdhrecCommanderApiResponseArchidektLinks +{ +public: + QVector entries; + + void fromJson(const QJsonArray &json); + void debugPrint() const; +}; + +#endif // ARCHIDEKTENTRY_H diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.cpp new file mode 100644 index 000000000..74cce5322 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.cpp @@ -0,0 +1,15 @@ +#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); +} \ No newline at end of file diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.h new file mode 100644 index 000000000..1dd01abe2 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.h @@ -0,0 +1,22 @@ +#ifndef AVERAGE_DECK_STATISTICS_H +#define AVERAGE_DECK_STATISTICS_H + +#include + +// 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 diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.cpp new file mode 100644 index 000000000..f8ffab787 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.cpp @@ -0,0 +1,49 @@ +#include "edhrec_commander_api_response_card_container.h" + +#include +#include +#include + +void EdhrecCommanderApiResponseCardContainer::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(); + EdhrecCommanderApiResponseCardList cardView; + cardView.fromJson(cardlistValue.toObject()); + cardlists.push_back(cardView); + } + + keywords = json.value("keywords").toString(); + title = json.value("title").toString(); +} + +void EdhrecCommanderApiResponseCardContainer::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; +} diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.h new file mode 100644 index 000000000..d2ea05874 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.h @@ -0,0 +1,60 @@ +#ifndef CONTAINER_ENTRY_H +#define CONTAINER_ENTRY_H + +#include "edhrec_commander_api_response_card_list.h" +#include "edhrec_commander_api_response_commander_details.h" + +#include +#include +#include +#include +#include + +class EdhrecCommanderApiResponseCardContainer +{ +public: + // Constructor + EdhrecCommanderApiResponseCardContainer() = default; + + // Parse deck-related data from JSON + void fromJson(const QJsonObject &json); + + // Debug method for logging + void debugPrint() const; + + // Getter methods for deck container + const QString &getDescription() const + { + return description; + } + const QVector &getBreadcrumb() const + { + return breadcrumb; + } + const EdhrecCommanderApiResponseCommanderDetails &getCommanderDetails() const + { + return card; + } + const QVector &getCardlists() const + { + return cardlists; + } + const QString &getKeywords() const + { + return keywords; + } + const QString &getTitle() const + { + return title; + } + +private: + QString description; + QVector breadcrumb; + EdhrecCommanderApiResponseCommanderDetails card; + QVector cardlists; + QString keywords; + QString title; +}; + +#endif // CONTAINER_ENTRY_H diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.cpp new file mode 100644 index 000000000..2f2791de1 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.cpp @@ -0,0 +1,36 @@ +#include "edhrec_commander_api_response_card_details.h" + +#include + +EdhrecCommanderApiResponseCardDetails::EdhrecCommanderApiResponseCardDetails() + : synergy(0.0), inclusion(0), numDecks(0), potentialDecks(0) +{ +} + +void EdhrecCommanderApiResponseCardDetails::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 EdhrecCommanderApiResponseCardDetails::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; +} diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.h new file mode 100644 index 000000000..82d85824b --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.h @@ -0,0 +1,29 @@ +#ifndef CARD_VIEW_H +#define CARD_VIEW_H + +#include +#include + +class EdhrecCommanderApiResponseCardDetails +{ +public: + QString name; + QString sanitized; + QString sanitizedWo; + QString url; + double synergy; + int inclusion; + QString label; + int numDecks; + int potentialDecks; + + EdhrecCommanderApiResponseCardDetails(); + + // 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 diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.cpp new file mode 100644 index 000000000..5d3eebedd --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.cpp @@ -0,0 +1,33 @@ +#include "edhrec_commander_api_response_card_list.h" + +#include + +EdhrecCommanderApiResponseCardList::EdhrecCommanderApiResponseCardList() +{ +} + +void EdhrecCommanderApiResponseCardList::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(); + EdhrecCommanderApiResponseCardDetails cardView; + cardView.fromJson(cardviewObj); + cardViews.append(cardView); + } +} + +void EdhrecCommanderApiResponseCardList::debugPrint() const +{ + // Print out the header + qDebug() << "Header:" << header; + + // Print out all the CardView objects + for (const EdhrecCommanderApiResponseCardDetails &cardView : cardViews) { + cardView.debugPrint(); + } +} diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.h new file mode 100644 index 000000000..e0ddc3466 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.h @@ -0,0 +1,27 @@ +#ifndef CARD_LIST_H +#define CARD_LIST_H + +#include "edhrec_commander_api_response_card_details.h" + +#include +#include +#include +#include + +class EdhrecCommanderApiResponseCardList +{ +public: + QString header; + QList cardViews; + + // Default constructor + EdhrecCommanderApiResponseCardList(); + + // 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 diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.cpp new file mode 100644 index 000000000..6454e5500 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.cpp @@ -0,0 +1,31 @@ +#include "edhrec_commander_api_response_card_prices.h" + +#include + +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 +{ + qDebug() << "Card Prices:"; + qDebug() << "Cardhoarder:" << cardhoarder; + qDebug() << "Cardkingdom:" << cardkingdom; + qDebug() << "Cardmarket:" << cardmarket; + qDebug() << "Face2Face:" << face2face; + qDebug() << "Manapool:" << manapool; + qDebug() << "Mtgstocks:" << mtgstocks; + qDebug() << "SCG:" << scg; + qDebug() << "TCGL:" << tcgl; + qDebug() << "Tcgplayer:" << tcgplayer; +} diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.h new file mode 100644 index 000000000..32e017a92 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.h @@ -0,0 +1,66 @@ +#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H +#define EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H + +#include + +class CardPrices +{ +public: + // Constructor + CardPrices() = default; + + // Parse prices from JSON + void fromJson(const QJsonObject &json); + void debugPrint() const; + + // Getter methods for card prices + const QJsonObject &getCardhoarder() const + { + return cardhoarder; + } + const QJsonObject &getCardkingdom() const + { + return cardkingdom; + } + const QJsonObject &getCardmarket() const + { + return cardmarket; + } + const QJsonObject &getFace2face() const + { + return face2face; + } + const QJsonObject &getManapool() const + { + return manapool; + } + const QJsonObject &getMtgstocks() const + { + return mtgstocks; + } + const QJsonObject &getScg() const + { + return scg; + } + const QJsonObject &getTcgl() const + { + return tcgl; + } + 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 diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.cpp new file mode 100644 index 000000000..7b220603e --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.cpp @@ -0,0 +1,90 @@ +#include "edhrec_commander_api_response_commander_details.h" + +#include + +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; +} \ No newline at end of file diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.h b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.h new file mode 100644 index 000000000..78ccfc23a --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.h @@ -0,0 +1,173 @@ +#ifndef EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H +#define EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H + +#include "edhrec_commander_api_response_card_prices.h" + +#include +#include +#include +#include + +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 + const QString &getAetherhubUri() const + { + return aetherhubUri; + } + const QString &getArchidektUri() const + { + return archidektUri; + } + int getCmc() const + { + return cmc; + } + const QJsonArray &getColorIdentity() const + { + return colorIdentity; + } + bool isCombos() const + { + return combos; + } + const QString &getDeckstatsUri() const + { + return deckstatsUri; + } + const QVector &getImageUris() const + { + return imageUris; + } + int getInclusion() const + { + return inclusion; + } + bool getIsCommander() const + { + return isCommander; + } + const QString &getLabel() const + { + return label; + } + const QString &getLayout() const + { + return layout; + } + bool getLegalCommander() const + { + return legalCommander; + } + const QString &getMoxfieldUri() const + { + return moxfieldUri; + } + const QString &getMtggoldfishUri() const + { + return mtggoldfishUri; + } + const QString &getName() const + { + return name; + } + const QJsonArray &getNames() const + { + return names; + } + int getNumDecks() const + { + return numDecks; + } + int getPotentialDecks() const + { + return potentialDecks; + } + const QString &getPrecon() const + { + return precon; + } + const CardPrices &getPrices() const + { + return prices; + } + const QString &getPrimaryType() const + { + return primaryType; + } + const QString &getRarity() const + { + return rarity; + } + double getSalt() const + { + return salt; + } + const QString &getSanitized() const + { + return sanitized; + } + const QString &getSanitizedWo() const + { + return sanitizedWo; + } + const QString &getScryfallUri() const + { + return scryfallUri; + } + const QString &getSpellbookUri() const + { + return spellbookUri; + } + const QString &getType() const + { + return type; + } + const QString &getUrl() const + { + return url; + } + +private: + QString aetherhubUri; + QString archidektUri; + int cmc = 0; + QJsonArray colorIdentity; + bool combos = false; + QString deckstatsUri; + QVector 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 diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp new file mode 100644 index 000000000..87853b9aa --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp @@ -0,0 +1,41 @@ +#include "edhrec_commander_api_response_card_details_display_widget.h" + +#include "../../../../game/cards/card_database_manager.h" + +EdhrecCommanderApiResponseCardDetailsDisplayWidget::EdhrecCommanderApiResponseCardDetailsDisplayWidget( + QWidget *parent, + const EdhrecCommanderApiResponseCardDetails &_toDisplay) + : QWidget(parent), toDisplay(_toDisplay) +{ + layout = new QVBoxLayout(this); + setLayout(layout); + + cardPictureWidget = new CardInfoPictureWidget(this); + cardPictureWidget->setCard(CardDatabaseManager::getInstance()->getCard(toDisplay.name)); + + label = new QLabel(this); + label->setText(toDisplay.name + "\n" + toDisplay.label); + label->setAlignment(Qt::AlignHCenter); + + // Set label color based on inclusion rate + int inclusionRate = (toDisplay.numDecks * 100) / toDisplay.potentialDecks; + + QColor labelColor; + if (inclusionRate <= 30) { + labelColor = QColor(255, 0, 0); // Red + } else if (inclusionRate <= 60) { + int red = 255 - ((inclusionRate - 30) * 2); + int green = (inclusionRate - 30) * 4; // Adjust green to make the transition smoother + labelColor = QColor(red, green, 0); // purple-ish + } else if (inclusionRate <= 90) { + int green = (inclusionRate - 60) * 5; // Increase green + labelColor = QColor(100, green, 100); // Green shades + } else { + labelColor = QColor(100, 200, 100); // Dark Green + } + + label->setStyleSheet(QString("color: %1").arg(labelColor.name())); + + layout->addWidget(cardPictureWidget); + layout->addWidget(label); +} diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.h new file mode 100644 index 000000000..1cd76989c --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.h @@ -0,0 +1,26 @@ +#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H +#define EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H + +#include "../../../ui/widgets/cards/card_info_picture_widget.h" +#include "api_response/edhrec_commander_api_response_card_details.h" + +#include +#include +#include + +class EdhrecCommanderApiResponseCardDetailsDisplayWidget : public QWidget +{ + Q_OBJECT +public: + explicit EdhrecCommanderApiResponseCardDetailsDisplayWidget( + QWidget *parent, + const EdhrecCommanderApiResponseCardDetails &_toDisplay); + +private: + EdhrecCommanderApiResponseCardDetails toDisplay; + QVBoxLayout *layout; + CardInfoPictureWidget *cardPictureWidget; + QLabel *label; +}; + +#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp new file mode 100644 index 000000000..0d1f1e8f3 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp @@ -0,0 +1,34 @@ +#include "edhrec_commander_api_response_card_list_display_widget.h" + +#include "../../../ui/widgets/general/display/banner_widget.h" +#include "edhrec_commander_api_response_card_details_display_widget.h" + +#include + +EdhrecCommanderApiResponseCardListDisplayWidget::EdhrecCommanderApiResponseCardListDisplayWidget( + QWidget *parent, + EdhrecCommanderApiResponseCardList 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 (EdhrecCommanderApiResponseCardDetails card_detail, toDisplay.cardViews) { + auto widget = new EdhrecCommanderApiResponseCardDetailsDisplayWidget(flowWidget, card_detail); + flowWidget->addWidget(widget); + } + + layout->addWidget(header); + layout->addWidget(flowWidget); +} + +void EdhrecCommanderApiResponseCardListDisplayWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + qDebug() << event->size(); +} diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.h new file mode 100644 index 000000000..9df667929 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.h @@ -0,0 +1,30 @@ +#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H +#define EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H + +#include "../../../ui/widgets/general/display/banner_widget.h" +#include "../../../ui/widgets/general/layout_containers/flow_widget.h" +#include "api_response/edhrec_commander_api_response_card_list.h" + +#include +#include +#include + +class EdhrecCommanderApiResponseCardListDisplayWidget : public QWidget +{ + Q_OBJECT +public: + explicit EdhrecCommanderApiResponseCardListDisplayWidget(QWidget *parent, + EdhrecCommanderApiResponseCardList 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 diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp new file mode 100644 index 000000000..58b77df73 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp @@ -0,0 +1,36 @@ +#include "edhrec_commander_api_response_commander_details_display_widget.h" + +#include "../../../../game/cards/card_database_manager.h" +#include "../../../ui/widgets/cards/card_info_picture_widget.h" + +#include + +EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget( + QWidget *parent, + const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails) + : QWidget(parent), commanderDetails(_commanderDetails) +{ + layout = new QVBoxLayout(this); + setLayout(layout); + + commanderPicture = new CardInfoPictureWidget(this); + commanderPicture->setCard(CardDatabaseManager::getInstance()->getCard(commanderDetails.getName())); + + commanderDetails.debugPrint(); + + label = new QLabel(this); + label->setAlignment(Qt::AlignCenter); + salt = new QLabel(this); + salt->setAlignment(Qt::AlignCenter); + + layout->addWidget(commanderPicture); + layout->addWidget(label); + layout->addWidget(salt); + retranslateUi(); +} + +void EdhrecCommanderResponseCommanderDetailsDisplayWidget::retranslateUi() +{ + label->setText(commanderDetails.getLabel()); + salt->setText(tr("Salt: ") + QString::number(commanderDetails.getSalt())); +} \ No newline at end of file diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.h new file mode 100644 index 000000000..85970d503 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.h @@ -0,0 +1,28 @@ +#ifndef EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H +#define EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H + +#include "../../../ui/widgets/cards/card_info_picture_widget.h" +#include "api_response/edhrec_commander_api_response_commander_details.h" + +#include +#include +#include + +class EdhrecCommanderResponseCommanderDetailsDisplayWidget : public QWidget +{ + Q_OBJECT +public: + explicit EdhrecCommanderResponseCommanderDetailsDisplayWidget( + QWidget *parent, + const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails); + void retranslateUi(); + +private: + QLabel *label; + QLabel *salt; + QVBoxLayout *layout; + CardInfoPictureWidget *commanderPicture; + EdhrecCommanderApiResponseCommanderDetails commanderDetails; +}; + +#endif // EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.cpp new file mode 100644 index 000000000..3cab65bf8 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.cpp @@ -0,0 +1,102 @@ +#include "edhrec_commander_api_response_display_widget.h" + +#include "../../../ui/widgets/cards/card_info_picture_widget.h" +#include "api_response/edhrec_commander_api_response.h" +#include "edhrec_commander_api_response_card_list_display_widget.h" +#include "edhrec_commander_api_response_commander_details_display_widget.h" + +#include +#include +#include +#include +#include + +EdhrecCommanderApiResponseDisplayWidget::EdhrecCommanderApiResponseDisplayWidget(QWidget *parent, + EdhrecCommanderApiResponse response) + : 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()); + cardDisplayLayout->addWidget(commanderPicture); + widgetNames.append("Commander Details"); + + // Add card list widgets + auto edhrec_commander_api_response_card_lists = response.container.getCardlists(); + for (const EdhrecCommanderApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) { + auto cardListDisplayWidget = new EdhrecCommanderApiResponseCardListDisplayWidget(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(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()); + } +} diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.h new file mode 100644 index 000000000..d1e95e3d6 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.h @@ -0,0 +1,27 @@ +#ifndef EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H +#define EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H + +#include "api_response/edhrec_commander_api_response.h" + +#include +#include +#include + +class EdhrecCommanderApiResponseDisplayWidget : public QWidget +{ + Q_OBJECT + +public: + explicit EdhrecCommanderApiResponseDisplayWidget(QWidget *parent, EdhrecCommanderApiResponse response); + void resizeEvent(QResizeEvent *event) override; + +public slots: + void onSplitterChange(); + +private: + QHBoxLayout *layout; + QVBoxLayout *cardDisplayLayout; + QScrollArea *scrollArea; +}; + +#endif // EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H diff --git a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp new file mode 100644 index 000000000..18662aaab --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp @@ -0,0 +1,112 @@ +#include "tab_edhrec.h" + +#include "api_response/edhrec_commander_api_response.h" +#include "edhrec_commander_api_response_display_widget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TabEdhRec::TabEdhRec(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) +{ + networkManager = new QNetworkAccessManager(this); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + networkManager->setTransferTimeout(); // Use Qt's default timeout +#endif + + networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); + connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *))); +} + +void TabEdhRec::retranslateUi() +{ +} + +void TabEdhRec::setCard(CardInfoPtr _cardToQuery, bool isCommander) +{ + cardToQuery = _cardToQuery; + + if (!cardToQuery) { + qDebug() << "Invalid card information provided."; + return; + } + + QString cardName = cardToQuery->getName(); + QString formattedName = cardName.toLower().replace(" ", "-").remove(QRegularExpression("[^a-z0-9\\-]")); + + QString url; + if (isCommander) { + url = QString("https://json.edhrec.com/pages/commanders/%1.json").arg(formattedName); + } else { + url = QString("https://json.edhrec.com/pages/cards/%1.json").arg(formattedName); + } + + QNetworkRequest request{QUrl(url)}; + + networkManager->get(request); +} + +void TabEdhRec::processApiJson(QNetworkReply *reply) +{ + if (reply->error() != QNetworkReply::NoError) { + qDebug() << "Network error occurred:" << reply->errorString(); + reply->deleteLater(); + return; + } + + QByteArray responseData = reply->readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData); + + if (!jsonDoc.isObject()) { + qDebug() << "Invalid JSON response received."; + reply->deleteLater(); + return; + } + + QJsonObject jsonObj = jsonDoc.object(); + + // qDebug() << jsonObj; + + EdhrecCommanderApiResponse deckData; + deckData.fromJson(jsonObj); + + displayWidget = new EdhrecCommanderApiResponseDisplayWidget(this, deckData); + // flowWidget->addWidget(displayWidget); + setCentralWidget(displayWidget); + + reply->deleteLater(); + update(); +} + +void TabEdhRec::prettyPrintJson(const QJsonValue &value, int indentLevel) +{ + const QString indent(indentLevel * 2, ' '); // Adjust spacing as needed for pretty printing + + if (value.isObject()) { + QJsonObject obj = value.toObject(); + for (auto it = obj.begin(); it != obj.end(); ++it) { + qDebug().noquote() << indent + it.key() + ":"; + prettyPrintJson(it.value(), indentLevel + 1); + } + } else if (value.isArray()) { + QJsonArray array = value.toArray(); + for (int i = 0; i < array.size(); ++i) { + qDebug().noquote() << indent + QString("[%1]:").arg(i); + prettyPrintJson(array[i], indentLevel + 1); + } + } else if (value.isString()) { + qDebug().noquote() << indent + "\"" + value.toString() + "\""; + } else if (value.isDouble()) { + qDebug().noquote() << indent + QString::number(value.toDouble()); + } else if (value.isBool()) { + qDebug().noquote() << indent + (value.toBool() ? "true" : "false"); + } else if (value.isNull()) { + qDebug().noquote() << indent + "null"; + } +} diff --git a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h new file mode 100644 index 000000000..c15f22b26 --- /dev/null +++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h @@ -0,0 +1,37 @@ +#ifndef TAB_EDHREC_H +#define TAB_EDHREC_H + +#include "../../../../game/cards/card_database.h" +#include "../../../ui/widgets/general/layout_containers/flow_widget.h" +#include "../../tab.h" +#include "edhrec_commander_api_response_display_widget.h" + +#include +#include + +class TabEdhRec : public Tab +{ + Q_OBJECT +public: + explicit TabEdhRec(TabSupervisor *_tabSupervisor); + + void retranslateUi() override; + QString getTabText() const override + { + auto cardName = cardToQuery.isNull() ? QString() : cardToQuery->getName(); + return tr("EDHREC: ") + cardName; + } + + QNetworkAccessManager *networkManager; + +public slots: + void processApiJson(QNetworkReply *reply); + void prettyPrintJson(const QJsonValue &value, int indentLevel); + void setCard(CardInfoPtr _cardToQuery, bool isCommander = false); + +private: + CardInfoPtr cardToQuery; + EdhrecCommanderApiResponseDisplayWidget *displayWidget; +}; + +#endif // TAB_EDHREC_H diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp index 0152566ab..8eee22cf5 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp @@ -51,6 +51,13 @@ #include #include +static bool canBeCommander(const CardInfoPtr &cardInfo) +{ + return ((cardInfo->getCardType().contains("Legendary", Qt::CaseInsensitive) && + cardInfo->getCardType().contains("Creature", Qt::CaseInsensitive))) || + cardInfo->getText().contains("can be your commander", Qt::CaseInsensitive); +} + void TabDeckEditor::createDeckDock() { deckModel = new DeckListModel(this); @@ -540,31 +547,40 @@ void TabDeckEditor::databaseCustomMenu(QPoint point) QMenu menu; const CardInfoPtr info = currentCardInfo(); - // add to deck and sideboard options - QAction *addToDeck, *addToSideboard, *selectPrinting; - addToDeck = menu.addAction(tr("Add to Deck")); - addToSideboard = menu.addAction(tr("Add to Sideboard")); - selectPrinting = menu.addAction(tr("Select Printing")); - - connect(addToDeck, SIGNAL(triggered()), this, SLOT(actAddCard())); - connect(addToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); - connect(selectPrinting, &QAction::triggered, this, [this, info] { this->showPrintingSelector(); }); - - // filling out the related cards submenu - auto *relatedMenu = new QMenu(tr("Show Related cards")); - menu.addMenu(relatedMenu); - auto relatedCards = info->getAllRelatedCards(); - if (relatedCards.isEmpty()) { - relatedMenu->setDisabled(true); - } else { - for (const CardRelation *rel : relatedCards) { - const QString &relatedCardName = rel->getName(); - QAction *relatedCard = relatedMenu->addAction(relatedCardName); - connect(relatedCard, &QAction::triggered, cardInfo, - [this, relatedCardName] { cardInfo->setCard(relatedCardName); }); + if (info) { + // add to deck and sideboard options + QAction *addToDeck, *addToSideboard, *selectPrinting, *edhRecCommander, *edhRecCard; + addToDeck = menu.addAction(tr("Add to Deck")); + addToSideboard = menu.addAction(tr("Add to Sideboard")); + selectPrinting = menu.addAction(tr("Select Printing")); + if (canBeCommander(info)) { + edhRecCommander = menu.addAction(tr("Show on EDHREC (Commander)")); + connect(edhRecCommander, &QAction::triggered, this, + [this, info] { this->tabSupervisor->addEdhrecTab(info, true); }); } + edhRecCard = menu.addAction(tr("Show on EDHREC (Card)")); + + connect(addToDeck, SIGNAL(triggered()), this, SLOT(actAddCard())); + connect(addToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); + connect(selectPrinting, &QAction::triggered, this, [this, info] { this->showPrintingSelector(); }); + connect(edhRecCard, &QAction::triggered, this, [this, info] { this->tabSupervisor->addEdhrecTab(info); }); + + // filling out the related cards submenu + auto *relatedMenu = new QMenu(tr("Show Related cards")); + menu.addMenu(relatedMenu); + auto relatedCards = info->getAllRelatedCards(); + if (relatedCards.isEmpty()) { + relatedMenu->setDisabled(true); + } else { + for (const CardRelation *rel : relatedCards) { + const QString &relatedCardName = rel->getName(); + QAction *relatedCard = relatedMenu->addAction(relatedCardName); + connect(relatedCard, &QAction::triggered, cardInfo, + [this, relatedCardName] { cardInfo->setCard(relatedCardName); }); + } + } + menu.exec(databaseView->mapToGlobal(point)); } - menu.exec(databaseView->mapToGlobal(point)); } void TabDeckEditor::decklistCustomMenu(QPoint point) @@ -1383,9 +1399,8 @@ QModelIndexList TabDeckEditor::getSelectedCardNodes() const return selectedRows; } -void TabDeckEditor::addCardHelper(QString zoneName) +void TabDeckEditor::addCardHelper(const CardInfoPtr info, QString zoneName) { - const CardInfoPtr info = currentCardInfo(); if (!info) return; if (info->getIsToken()) @@ -1461,13 +1476,13 @@ void TabDeckEditor::actAddCard() if (QApplication::keyboardModifiers() & Qt::ControlModifier) actAddCardToSideboard(); else - addCardHelper(DECK_ZONE_MAIN); + addCardHelper(currentCardInfo(), DECK_ZONE_MAIN); setSaveStatus(true); } void TabDeckEditor::actAddCardToSideboard() { - addCardHelper(DECK_ZONE_SIDE); + addCardHelper(currentCardInfo(), DECK_ZONE_SIDE); setSaveStatus(true); } diff --git a/cockatrice/src/client/tabs/tab_deck_editor.h b/cockatrice/src/client/tabs/tab_deck_editor.h index 6a5d320a4..ab0c0883e 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.h +++ b/cockatrice/src/client/tabs/tab_deck_editor.h @@ -110,7 +110,6 @@ private: bool isBlankNewDeck() const; CardInfoPtr currentCardInfo() const; - void addCardHelper(QString zoneName); void offsetCountAtIndex(const QModelIndex &idx, int offset); void decrementCardHelper(QString zoneName); bool swapCard(const QModelIndex &idx); @@ -179,6 +178,7 @@ public: void createMenus(); void createCentralFrame(); void updateCardInfo(CardInfoPtr _card); + void addCardHelper(CardInfoPtr info, QString zoneName); public slots: void closeRequest(bool forced = false) override; diff --git a/cockatrice/src/client/tabs/tab_supervisor.cpp b/cockatrice/src/client/tabs/tab_supervisor.cpp index 92190de5e..a665d8df2 100644 --- a/cockatrice/src/client/tabs/tab_supervisor.cpp +++ b/cockatrice/src/client/tabs/tab_supervisor.cpp @@ -707,6 +707,18 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(const DeckLoader *deckToOpen) return tab; } +TabEdhRec *TabSupervisor::addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander) +{ + auto *tab = new TabEdhRec(this); + if (cardToQuery) { + tab->setCard(cardToQuery, isCommander); + } + + myAddTab(tab); + setCurrentWidget(tab); + return tab; +} + void TabSupervisor::deckEditorClosed(TabDeckEditor *tab) { if (tab == currentWidget()) diff --git a/cockatrice/src/client/tabs/tab_supervisor.h b/cockatrice/src/client/tabs/tab_supervisor.h index 3925807fb..db941aacc 100644 --- a/cockatrice/src/client/tabs/tab_supervisor.h +++ b/cockatrice/src/client/tabs/tab_supervisor.h @@ -3,6 +3,7 @@ #include "../../deck/deck_loader.h" #include "../../server/user/user_list_proxy.h" +#include "api/edhrec/tab_edhrec.h" #include "visual_deck_storage/tab_deck_storage_visual.h" #include @@ -131,6 +132,10 @@ public: { return roomTabs; } + QList getDeckEditorTabs() const + { + return deckEditorTabs; + } bool getAdminLocked() const; bool closeRequest(); bool switchToGameTabIfAlreadyExists(const int gameId); @@ -143,6 +148,7 @@ signals: public slots: TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen); + TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false); void openReplay(GameReplay *replay); void maximizeMainWindow(); private slots: diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp index 714ad20ca..eabdbbe40 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp +++ b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp @@ -151,6 +151,9 @@ void CardInfoFrameWidget::setCard(CardInfoPtr card) disconnect(info.data(), nullptr, this, nullptr); } + if (viewTransformationButton) { + viewTransformationButton->setVisible(false); + } info = std::move(card); if (info) { diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp index d2806121a..51caf9bc1 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp +++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp @@ -3,7 +3,10 @@ #include "../../../../game/cards/card_database_manager.h" #include "../../../../game/cards/card_item.h" #include "../../../../settings/cache_settings.h" +#include "../../../tabs/tab_deck_editor.h" +#include "../../../tabs/tab_supervisor.h" #include "../../picture_loader/picture_loader.h" +#include "../../window_main.h" #include #include @@ -254,7 +257,31 @@ QMenu *CardInfoPictureWidget::createRightClickMenu() } auto viewRelatedCards = new QMenu(tr("View related cards")); + auto addToOpenDeckMenu = new QMenu(tr("Add card to deck")); cardMenu->addMenu(viewRelatedCards); + cardMenu->addMenu(addToOpenDeckMenu); + + auto *mainWindow = qobject_cast(window()); + QList deckEditorTabs = mainWindow->getTabSupervisor()->getDeckEditorTabs(); + + for (auto &deckEditorTab : deckEditorTabs) { + auto *addCardMenu = new QMenu(tr("Add card to") + " " + deckEditorTab->getTabText()); + QAction *addCard = new QAction(this); + addCard->setText(tr("Mainboard")); + connect(addCard, &QAction::triggered, this, [this, deckEditorTab] { + deckEditorTab->updateCardInfo(info); + deckEditorTab->addCardHelper(info, DECK_ZONE_MAIN); + }); + QAction *addCardSideboard = new QAction(this); + addCardSideboard->setText(tr("Sideboard")); + connect(addCardSideboard, &QAction::triggered, this, [this, deckEditorTab] { + deckEditorTab->updateCardInfo(info); + deckEditorTab->addCardHelper(info, DECK_ZONE_SIDE); + }); + addCardMenu->addAction(addCard); + addCardMenu->addAction(addCardSideboard); + addToOpenDeckMenu->addMenu(addCardMenu); + } bool atLeastOneGoodRelationFound = false; QList relatedCards = info->getAllRelatedCards(); diff --git a/cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp b/cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp new file mode 100644 index 000000000..cf1959d7a --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp @@ -0,0 +1,63 @@ +#include "banner_widget.h" + +#include +#include +#include +#include + +BannerWidget::BannerWidget(QWidget *parent, const QString &text, Qt::Orientation orientation, int transparency) + : QWidget(parent), gradientOrientation(orientation), transparency(qBound(0, transparency, 100)) +{ + // Create the banner label and set properties + bannerLabel = new QLabel(text, this); + bannerLabel->setAlignment(Qt::AlignCenter); + bannerLabel->setStyleSheet("font-size: 24px; font-weight: bold; color: white;"); + + // Layout to center the banner label + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(bannerLabel); + layout->setContentsMargins(0, 20, 0, 20); // Space for the gradient + setLayout(layout); + + // Set minimum height for the widget + setMinimumHeight(100); + connect(this, &BannerWidget::buddyVisibilityChanged, this, &BannerWidget::toggleBuddyVisibility); +} + +void BannerWidget::mousePressEvent(QMouseEvent *event) +{ + QWidget::mousePressEvent(event); + emit buddyVisibilityChanged(); +} + +void BannerWidget::toggleBuddyVisibility() const +{ + if (buddy) { + buddy->setVisible(!buddy->isVisible()); + } +} + +void BannerWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter painter(this); + + // Calculate alpha based on transparency percentage + int alpha = (255 * transparency) / 100; + + // Determine gradient direction + QLinearGradient gradient; + if (gradientOrientation == Qt::Vertical) { + gradient = QLinearGradient(rect().topLeft(), rect().bottomLeft()); + } else { + gradient = QLinearGradient(rect().topLeft(), rect().topRight()); + } + + // Set neutral gradient colors with calculated transparency + gradient.setColorAt(0, QColor(200, 200, 200, alpha)); // Light grey with alpha + gradient.setColorAt(1, QColor(100, 100, 100, alpha / 1.5)); // Darker grey, slightly more transparent + + // Fill the widget background with the gradient + painter.fillRect(rect(), gradient); +} diff --git a/cockatrice/src/client/ui/widgets/general/display/banner_widget.h b/cockatrice/src/client/ui/widgets/general/display/banner_widget.h new file mode 100644 index 000000000..4947ffd58 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/general/display/banner_widget.h @@ -0,0 +1,40 @@ +#ifndef BANNER_WIDGET_H +#define BANNER_WIDGET_H + +#include +#include + +class BannerWidget : public QWidget +{ + Q_OBJECT + +public: + explicit BannerWidget(QWidget *parent, + const QString &text, + Qt::Orientation orientation = Qt::Vertical, + int transparency = 80); + void mousePressEvent(QMouseEvent *event) override; + void setBuddy(QWidget *_buddy) + { + buddy = _buddy; + } + QString getText() const + { + return bannerLabel->text(); + } + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + QLabel *bannerLabel; + Qt::Orientation gradientOrientation; + int transparency; // Transparency percentage for the gradient + QWidget *buddy; +signals: + void buddyVisibilityChanged(); +private slots: + void toggleBuddyVisibility() const; +}; + +#endif // BANNER_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp b/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp index a413d2927..13e1f15ea 100644 --- a/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp +++ b/cockatrice/src/client/ui/widgets/general/layout_containers/flow_widget.cpp @@ -123,6 +123,8 @@ void FlowWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); + qCDebug(FlowWidgetSizeLog) << event->size(); + // Trigger the layout to recalculate if (flowLayout != nullptr) { flowLayout->invalidate(); // Marks the layout as dirty and requires recalculation @@ -131,6 +133,7 @@ void FlowWidget::resizeEvent(QResizeEvent *event) // Ensure the scroll area and its content adjust correctly if (scrollArea != nullptr && scrollArea->widget() != nullptr) { + qCDebug(FlowWidgetSizeLog) << "Got a scrollarea: " << scrollArea->widget()->size(); scrollArea->widget()->adjustSize(); } else { container->adjustSize(); diff --git a/cockatrice/src/client/ui/window_main.h b/cockatrice/src/client/ui/window_main.h index 8ba3ea440..9910cf7fb 100644 --- a/cockatrice/src/client/ui/window_main.h +++ b/cockatrice/src/client/ui/window_main.h @@ -163,6 +163,11 @@ public: } ~MainWindow() override; + TabSupervisor *getTabSupervisor() const + { + return tabSupervisor; + } + protected: void closeEvent(QCloseEvent *event) override; void changeEvent(QEvent *event) override;