diff --git a/.ci/release_template.md b/.ci/release_template.md
index 6e53a3ee1..c23e285c3 100644
--- a/.ci/release_template.md
+++ b/.ci/release_template.md
@@ -41,7 +41,7 @@ If you ever encounter a bug, have a suggestion or idea, or feel a need for a dev
For basic information related to the app and getting started, please take a look at our official site: **https://cockatrice.github.io**
-If you'd like to help and contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-).
+If you'd like to help and contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#contribute).
We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 3ab3b7730..4744d611a 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -2,7 +2,8 @@
name: "🐛 Bug Report"
about: Report an issue encountered while using Cockatrice
title: ''
-labels: 'Bug'
+type: 'Bug'
+labels: ''
assignees: ''
---
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 91f2edea6..d366d4ca8 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,8 +1,9 @@
---
name: "💡 Feature Request"
-about: Request a new feature
+about: Request a new feature for Cockatrice
title: ''
-labels: 'Feature Request'
+type: 'Feature'
+labels: ''
assignees: ''
---
diff --git a/README.md b/README.md
index e2433e695..48cacff78 100644
--- a/README.md
+++ b/README.md
@@ -5,159 +5,154 @@
Cockatrice |
Download |
- Get Involved |
- Community |
- Translations |
+ Related |
+ Community |
+ Contribute |
Build |
- Run |
- License
+ Run
---
-
-To get started, ⇢ [view our webpage](https://cockatrice.github.io/)
-To get support or suggest changes ⇢ [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
-To help with development, see how to [get involved](#get-involved-)
-
+
+To get started with Cockatrice ⇢ [view our webpage](https://cockatrice.github.io/)
+To get support, or suggest changes to the app ⇢ [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
+To help with development ⇢ learn [how to contribute](#contribute-)
+
# Cockatrice
-Cockatrice is an open-source, multiplatform program for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline. This project uses C++ and the Qt5 libraries.
+Cockatrice is an open-source, multiplatform application for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline.
+This project uses C++ and the Qt libraries.
+First work on a webclient with Typescript was started as well.
-# Download [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
+# Download [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0)
-Downloads are available for full releases and the current beta version in development. There is no strict release schedule for either of them.
+Downloads are available for **stable releases** and the current **beta version** in development.
+There is no strict release schedule for either of them.
+
+Latest stable release:
+[](https://github.com/cockatrice/cockatrice/releases/latest)  [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0)
+
+Latest beta version:
+[](https://github.com/cockatrice/cockatrice/releases)  [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0) [](https://github.com/Cockatrice/Cockatrice/pulls?q=is%3Apr+is%3Aclosed)
+While incorporating the latest fixes and features, beta builds may not be stable and/or contain new bugs!
+Please report any findings when testing them!
+
-- Latest `stable` release: [](https://github.com/cockatrice/cockatrice/releases/latest) [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
- - Stable versions are checkpoints featuring major feature and UI enhancements.
- - **Recommended for most users!**
+# Related Repositories
-- Latest `beta` release: [](https://github.com/cockatrice/cockatrice/releases) [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
- - Beta versions include the most recently added features and bugfixes, but can be unstable.
- - To be a Cockatrice Beta Tester, use this version. Find more information [here](https://github.com/Cockatrice/Cockatrice/wiki/Release-Channels)!
-
-
-# Get Involved [](https://discord.gg/3Z9yzmA)
-
-Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with the project, contributors or fellow users of the app. Come here to talk about the application, features, or just to hang out.
-For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.
-
-To contribute code to the project, please review [the guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md).
-We maintain two tags for contributors to find issues to work on:
-- [Good first issue](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Good%20first%20issue%22%20): issues tagged in this way provide a simple way to get started. They don't require much experience to be worked on.
-- [Help wanted](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Help%20Wanted%22%20): This tag is used for issues that we are looking for a contributor to work on. Often this is for feature suggestions we are willing to accept, but don't have the time to work on ourselves.
-
-For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code review for changes they submit.
-
-We try to be responsive to new issues. We'll provide advice on how best to implement a feature; alternately, we can show you where the codebase is doing something similar before you get too far along.
-
-Cockatrice uses the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
+- [Magic-Token](https://github.com/Cockatrice/Magic-Token): MtG token data to use in Cockatrice
+- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Script to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) to use in Cockatrice
+- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official webpage of the Cockatrice project
-# Community Resources
+# Community Resources [](https://discord.gg/3Z9yzmA)
-- [Cockatrice Official Site](https://cockatrice.github.io)
-- [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
-- [Cockatrice Official Discord](https://discord.gg/3Z9yzmA)
+Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other projet contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out.
+- [Official Website](https://cockatrice.github.io)
+- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
+- [Official Discord](https://discord.gg/3Z9yzmA)
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
+>[!IMPORTANT]
+>For support regarding specific servers, please contact that server's admin/mods and use their dedicated communication channels rather than contacting the team building the software.
-# Translations [](https://transifex.com/cockatrice/cockatrice/)
-Cockatrice uses Transifex for translations. You can help us bring Cockatrice, Oracle and Webatrice to your language or just adjust single wordings right from within your browser by visiting our [Transifex project page](https://transifex.com/cockatrice/cockatrice/).
+# Contribute
-Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about contributing!
+### Code
+
+To contribute code to the project, please review our [guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md) first.
+We maintain two tags for contributors to easier find issues to potentially work on:
+- [](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Good%20first%20issue%22%20)
+Issues tagged in this way provide a simple way to get started. They don't require much experience to be worked on.
+- [](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Help%20Wanted%22%20)
+This tag is used for issues that we are looking for somebody to pick up. Often this is for feature suggestions we support, but don't have the time to work on ourselves.
+
+For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code reviews for submitted changes.
+We'll happily advice on how best to implement a feature, or we can show you where the codebase is doing something similar before you get too far along - put a note on an issue you want to discuss more on!
+
+Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
+
+
+Kudos to our amazing contributors ❤️
+
+
+
+ Made with contrib.rocks.
+
+
+### Translations [](https://transifex.com/cockatrice/cockatrice/)
+
+Cockatrice uses Transifex to manage translations. You can help us bring Cockatrice, Oracle and Webatrice to your language and just adjust single wordings right from within your browser by visiting our [Transifex project page](https://transifex.com/cockatrice/cockatrice/).
+
+Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting invovled, and join a group of hundreds of others!
# Build [](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush)
-**Detailed compiling instructions can be found on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)**
-
-Dependencies: *(for minimum requirements search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))*
+Dependencies: *(for minimum versions search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))*
- [Qt](https://www.qt.io/developers/)
- [protobuf](https://github.com/protocolbuffers/protobuf)
- [CMake](https://www.cmake.org/)
-Oracle can optionally use zlib and xz to load compressed files:
+Oracle can *optionally* use some packages to load compressed card files:
- [xz](https://tukaani.org/xz/)
- [zlib](https://www.zlib.net/)
-To compile:
+
- mkdir build
- cd build
- cmake ..
- make
-
-You can then run
+**Basic compilation steps:**
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
+You can then
+ - Create a Cockatrice installation inside the `release` folder:
+ ```bash
make install
-
-to get a cockatrice installation inside the `release` folder, or:
-
+ ```
+ - Or make an installation package specific to your system:
+ ```bash
make package
+ ```
-to create a system-specific installation package.
+>[!NOTE]
+>Detailed compiling instructions can be found in the Cockatrice wiki at [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)
-The following flags can be passed to `cmake`:
+
-- `-DWITH_SERVER=1` Whether to build the server (default 0 = no).
-- `-DWITH_CLIENT=0` Whether to build the client (default 1 = yes).
-- `-DWITH_ORACLE=0` Whether to build oracle (default 1 = yes).
-- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings (default `Release`).
-- `-DWARNING_AS_ERROR=0` Whether to treat compilation warnings as errors in debug mode (default 1 = yes).
-- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files (default 0 = no).
-- `-DTEST=1` Enable regression tests (default 0 = no). Note: needs googletest, will be downloaded on the fly if unavailable. To run tests: ```make test```.
-- `-DFORCE_USE_QT5=1` Skip looking for Qt6 before trying to find Qt5
+The following flags (with their non-default values) can be passed to `cmake`:
+| Flag | Description |
+| --- | --- |
+| `-DWITH_SERVER=1` | Build Servatrice server |
+| `-DWITH_CLIENT=0` | Don't build Cockatrice client |
+| `-DWITH_ORACLE=0` | Don't build Oracle card database tool |
+| `-DCMAKE_BUILD_TYPE=Debug` | Compile in debug mode
Enables extra logging output, debug symbols, and much more verbose compiler warnings |
+| `-DWARNING_AS_ERROR=0` | Don't treat compilation warnings as errors in debug mode |
+| `-DUPDATE_TRANSLATIONS=1` | Configure `make` to update the translation .ts files for new strings in the source code
**Note:** `make clean` will remove the .ts files |
+| `-DTEST=1` | Enable regression tests
**Note:** `make test` to run tests, *googletest* will be downloaded if not available |
+| `-DFORCE_USE_QT5=1` | Skip looking for Qt6 before trying to find Qt5 |
# Run
-`Cockatrice` is the game client
-`Oracle` fetches card data
-`Servatrice` is the server
+Cockatrice is the game client
+Oracle fetches card data
+Servatrice is the server
-**Servatrice Docker container**
+#### Docker
-You can run an instance of Servatrice (the Cockatrice server) using [Docker](https://www.docker.com/what-docker) and the Cockatrice Dockerfile.
+You can run an instance of Servatrice (the Cockatrice server) using [Docker](https://www.docker.com/resources/what-container/) and our Dockerfile.
-First, create an image from the Dockerfile
-`cd /path/to/Cockatrice-Repo/`
-`docker build -t servatrice .`
-And then run it
-`docker run -i -p 4748:4748 -t servatrice:latest`
-
-For versions after 2.10.2, there is also an official image available on the GitHub container registry. You can pull it using
-`docker pull ghcr.io/cockatrice/servatrice:latest`
-And then run it
-`docker run -i -p 4748:4748 -t ghcr.io/cockatrice/servatrice:latest`
-
->Note: Running this command exposes the port 4748 of the docker container
-to permit connections to the server.
-
-Find more information on how to use Servatrice with Docker in our [wiki](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).
-
-**Docker compose**
-
-There is also a docker-compose file available which will configure and run both a MySQL server and Servatrice. The docker-compose setup scripts can be found in the `servatrice/docker` folder and vary only slightly from the default sql and server .ini files. The setup scripts can either be modified in place, or docker-compose can mount alternative files into the images, as you prefer.
-
-To run Servatrice via docker-compose, first install docker-compose following the [install instructions](https://docs.docker.com/compose/install/). Once installed, run the following from the root of the repository:
-```bash
-docker-compose build # Build the Servatrice image using the same Dockerfile as above.
-docker-compose up # Setup and run both the MySQL server and Servatrice.
-```
-
->Note: Similar to the above Docker setup, this will expose port 4748.
-
->Note: The first time running the docker-compose setup, the MySQL server will take a little time to run the initial setup scripts. Due to this, the Servatrice instance may fail the first few attempts to connect to the database. Servatrice is set to `restart: always` in the docker-compose.yml, which will allow it to continue attempting to start up. Once the MySQL scripts have completed, Servatrice should then connect automatically on the next attempt.
-
-**Docker compose in Windows**
-A out of box working docker-compose file has been added to help setup in Windows.
-
-Docker in Windows requires additional steps in form of using Docker Desktop to allow resource sharing from the drive the volumes are mapped from, as well as potential workarounds needed to get file sharing working in Windows. This [StackOverflow discussion sheds some light on it](https://stackoverflow.com/questions/42203488/settings-to-windows-firewall-to-allow-docker-for-windows-to-share-drive)
+For more information, have a look in our wiki section on [Setting up Servatrice](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).
+There, you'll also find more hints on our **docker-compose** file which will configure and run both a MySQL server and Servatrice.
# License [](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE)
diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt
index 9b50c9469..cbd0ed1d7 100644
--- a/cockatrice/CMakeLists.txt
+++ b/cockatrice/CMakeLists.txt
@@ -4,9 +4,245 @@
project(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
-file(GLOB_RECURSE cockatrice_CPP_FILES CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/cockatrice/src/*.cpp)
-
-set(cockatrice_SOURCES ${cockatrice_CPP_FILES} ${VERSION_STRING_CPP})
+set(cockatrice_SOURCES
+ ${VERSION_STRING_CPP}
+ # sort by alphabetical order, so that there is no debate about where to add new sources to the list
+ src/client/game_logic/abstract_client.cpp
+ src/client/game_logic/key_signals.cpp
+ src/client/get_text_with_max.cpp
+ src/client/menus/deck_editor/deck_editor_menu.cpp
+ src/client/network/client_update_checker.cpp
+ src/client/network/release_channel.cpp
+ src/client/network/replay_timeline_widget.cpp
+ src/client/network/sets_model.cpp
+ src/client/network/spoiler_background_updater.cpp
+ src/client/sound_engine.cpp
+ src/client/tabs/abstract_tab_deck_editor.cpp
+ src/client/tabs/api/edhrec/tab_edhrec.cpp
+ src/client/tabs/api/edhrec/tab_edhrec_main.cpp
+ src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
+ src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
+ src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
+ src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
+ src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
+ src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
+ src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
+ src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
+ src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
+ src/client/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
+ src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
+ src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
+ src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
+ src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
+ src/client/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
+ src/client/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
+ src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
+ src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
+ src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
+ src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
+ src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
+ src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
+ src/client/tabs/tab.cpp
+ src/client/tabs/tab_account.cpp
+ src/client/tabs/tab_admin.cpp
+ src/client/tabs/tab_deck_editor.cpp
+ src/client/tabs/tab_deck_storage.cpp
+ src/client/tabs/tab_game.cpp
+ src/client/tabs/tab_logs.cpp
+ src/client/tabs/tab_message.cpp
+ src/client/tabs/tab_replays.cpp
+ src/client/tabs/tab_room.cpp
+ src/client/tabs/tab_server.cpp
+ src/client/tabs/tab_supervisor.cpp
+ src/client/tabs/tab_visual_database_display.cpp
+ src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
+ src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
+ src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
+ src/client/tapped_out_interface.cpp
+ src/client/translate_counter_name.cpp
+ src/client/ui/layouts/flow_layout.cpp
+ src/client/ui/layouts/overlap_layout.cpp
+ src/client/ui/line_edit_completer.cpp
+ src/client/ui/phases_toolbar.cpp
+ src/client/ui/picture_loader/picture_loader.cpp
+ src/client/ui/picture_loader/picture_loader_worker.cpp
+ src/client/ui/picture_loader/picture_to_load.cpp
+ src/client/ui/pixel_map_generator.cpp
+ src/client/ui/theme_manager.cpp
+ src/client/ui/tip_of_the_day.cpp
+ src/client/ui/widgets/cards/additional_info/color_identity_widget.cpp
+ src/client/ui/widgets/cards/additional_info/mana_cost_widget.cpp
+ src/client/ui/widgets/cards/additional_info/mana_symbol_widget.cpp
+ src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
+ src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
+ src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
+ src/client/ui/widgets/cards/card_info_display_widget.cpp
+ src/client/ui/widgets/cards/card_info_frame_widget.cpp
+ src/client/ui/widgets/cards/card_info_picture_enlarged_widget.cpp
+ src/client/ui/widgets/cards/card_info_picture_widget.cpp
+ src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
+ src/client/ui/widgets/cards/card_info_text_widget.cpp
+ src/client/ui/widgets/cards/card_size_widget.cpp
+ src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp
+ src/client/ui/widgets/cards/deck_preview_card_picture_widget.cpp
+ src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp
+ src/client/ui/widgets/deck_analytics/mana_base_widget.cpp
+ src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp
+ src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp
+ src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
+ src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.cpp
+ src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
+ src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
+ src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
+ src/client/ui/widgets/general/display/banner_widget.cpp
+ src/client/ui/widgets/general/display/bar_widget.cpp
+ src/client/ui/widgets/general/display/dynamic_font_size_label.cpp
+ src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp
+ src/client/ui/widgets/general/display/labeled_input.cpp
+ src/client/ui/widgets/general/display/shadow_background_label.cpp
+ src/client/ui/widgets/general/layout_containers/flow_widget.cpp
+ src/client/ui/widgets/general/layout_containers/overlap_control_widget.cpp
+ src/client/ui/widgets/general/layout_containers/overlap_widget.cpp
+ src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp
+ src/client/ui/widgets/printing_selector/card_amount_widget.cpp
+ src/client/ui/widgets/printing_selector/printing_selector.cpp
+ src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp
+ src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
+ src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp
+ src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp
+ src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
+ src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
+ src/client/ui/widgets/quick_settings/settings_button_widget.cpp
+ src/client/ui/widgets/quick_settings/settings_popup_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
+ src/client/ui/widgets/visual_database_display/visual_database_filter_display_widget.cpp
+ src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
+ src/client/ui/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp
+ src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_item_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
+ src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
+ src/client/ui/window_main.cpp
+ src/client/update_downloader.cpp
+ src/deck/custom_line_edit.cpp
+ src/deck/deck_list_model.cpp
+ src/deck/deck_loader.cpp
+ src/deck/deck_stats_interface.cpp
+ src/dialogs/dlg_connect.cpp
+ src/dialogs/dlg_convert_deck_to_cod_format.cpp
+ src/dialogs/dlg_create_game.cpp
+ src/dialogs/dlg_create_token.cpp
+ src/dialogs/dlg_edit_avatar.cpp
+ src/dialogs/dlg_edit_password.cpp
+ src/dialogs/dlg_edit_tokens.cpp
+ src/dialogs/dlg_edit_user.cpp
+ src/dialogs/dlg_filter_games.cpp
+ src/dialogs/dlg_forgot_password_challenge.cpp
+ src/dialogs/dlg_forgot_password_request.cpp
+ src/dialogs/dlg_forgot_password_reset.cpp
+ src/dialogs/dlg_load_deck.cpp
+ src/dialogs/dlg_load_deck_from_clipboard.cpp
+ src/dialogs/dlg_load_remote_deck.cpp
+ src/dialogs/dlg_manage_sets.cpp
+ src/dialogs/dlg_move_top_cards_until.cpp
+ src/dialogs/dlg_register.cpp
+ src/dialogs/dlg_roll_dice.cpp
+ src/dialogs/dlg_settings.cpp
+ src/dialogs/dlg_tip_of_the_day.cpp
+ src/dialogs/dlg_update.cpp
+ src/dialogs/dlg_view_log.cpp
+ src/game/board/abstract_counter.cpp
+ src/game/board/abstract_graphics_item.cpp
+ src/game/board/arrow_item.cpp
+ src/game/board/arrow_target.cpp
+ src/game/board/counter_general.cpp
+ src/game/cards/abstract_card_drag_item.cpp
+ src/game/cards/abstract_card_item.cpp
+ src/game/cards/card_completer_proxy_model.cpp
+ src/game/cards/card_database.cpp
+ src/game/cards/card_database_manager.cpp
+ src/game/cards/card_database_model.cpp
+ src/game/cards/card_database_parser/card_database_parser.cpp
+ src/game/cards/card_database_parser/cockatrice_xml_3.cpp
+ src/game/cards/card_database_parser/cockatrice_xml_4.cpp
+ src/game/cards/card_drag_item.cpp
+ src/game/cards/card_info.cpp
+ src/game/cards/card_item.cpp
+ src/game/cards/card_list.cpp
+ src/game/cards/card_search_model.cpp
+ src/game/deckview/deck_view.cpp
+ src/game/deckview/deck_view_container.cpp
+ src/game/filters/filter_builder.cpp
+ src/game/filters/filter_card.cpp
+ src/game/filters/filter_string.cpp
+ src/game/filters/filter_tree.cpp
+ src/game/filters/filter_tree_model.cpp
+ src/game/filters/syntax_help.cpp
+ src/game/game_scene.cpp
+ src/game/game_selector.cpp
+ src/game/game_view.cpp
+ src/game/games_model.cpp
+ src/game/hand_counter.cpp
+ src/game/phase.cpp
+ src/game/player/player.cpp
+ src/game/player/player_list_widget.cpp
+ src/game/player/player_target.cpp
+ src/game/zones/card_zone.cpp
+ src/game/zones/hand_zone.cpp
+ src/game/zones/pile_zone.cpp
+ src/game/zones/select_zone.cpp
+ src/game/zones/stack_zone.cpp
+ src/game/zones/table_zone.cpp
+ src/game/zones/view_zone.cpp
+ src/game/zones/view_zone_widget.cpp
+ src/main.cpp
+ src/server/chat_view/chat_view.cpp
+ src/server/handle_public_servers.cpp
+ src/server/local_client.cpp
+ src/server/local_server.cpp
+ src/server/local_server_interface.cpp
+ src/server/message_log_widget.cpp
+ src/server/pending_command.cpp
+ src/server/remote/remote_client.cpp
+ src/server/remote/remote_decklist_tree_widget.cpp
+ src/server/remote/remote_replay_list_tree_widget.cpp
+ src/server/user/user_context_menu.cpp
+ src/server/user/user_info_box.cpp
+ src/server/user/user_info_connection.cpp
+ src/server/user/user_list_manager.cpp
+ src/server/user/user_list_widget.cpp
+ src/settings/cache_settings.cpp
+ src/settings/card_database_settings.cpp
+ src/settings/card_override_settings.cpp
+ src/settings/debug_settings.cpp
+ src/settings/download_settings.cpp
+ src/settings/game_filters_settings.cpp
+ src/settings/layouts_settings.cpp
+ src/settings/message_settings.cpp
+ src/settings/recents_settings.cpp
+ src/settings/servers_settings.cpp
+ src/settings/settings_manager.cpp
+ src/settings/shortcut_treeview.cpp
+ src/settings/shortcuts_settings.cpp
+ src/utility/card_info_comparator.cpp
+ src/utility/levenshtein.cpp
+ src/utility/logger.cpp
+ src/utility/sequence_edit.cpp
+)
add_subdirectory(sounds)
add_subdirectory(themes)
diff --git a/cockatrice/resources/config/qtlogging.ini b/cockatrice/resources/config/qtlogging.ini
index 5404c96fb..eeae47b7f 100644
--- a/cockatrice/resources/config/qtlogging.ini
+++ b/cockatrice/resources/config/qtlogging.ini
@@ -27,6 +27,7 @@
# servers_settings = false
# shortcuts_settings = false
+# local_client = false
# remote_client = false
# player = false
diff --git a/cockatrice/src/client/menus/deck_editor/deck_editor_menu.cpp b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.cpp
index 2d8c613a5..a01ccc3cf 100644
--- a/cockatrice/src/client/menus/deck_editor/deck_editor_menu.cpp
+++ b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.cpp
@@ -3,14 +3,13 @@
#include "../../../settings/cache_settings.h"
#include "../../../settings/shortcuts_settings.h"
-DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEditor)
- : QMenu(parent), deckEditor(_deckEditor)
+DeckEditorMenu::DeckEditorMenu(AbstractTabDeckEditor *parent) : QMenu(parent), deckEditor(parent)
{
aNewDeck = new QAction(QString(), this);
- connect(aNewDeck, SIGNAL(triggered()), deckEditor, SLOT(actNewDeck()));
+ connect(aNewDeck, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actNewDeck);
aLoadDeck = new QAction(QString(), this);
- connect(aLoadDeck, SIGNAL(triggered()), deckEditor, SLOT(actLoadDeck()));
+ connect(aLoadDeck, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actLoadDeck);
loadRecentDeckMenu = new QMenu(this);
connect(&SettingsCache::instance().recents(), &RecentsSettings::recentlyOpenedDeckPathsChanged, this,
@@ -22,47 +21,50 @@ DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEdit
updateRecentlyOpened();
aSaveDeck = new QAction(QString(), this);
- connect(aSaveDeck, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeck()));
+ connect(aSaveDeck, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actSaveDeck);
aSaveDeckAs = new QAction(QString(), this);
- connect(aSaveDeckAs, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckAs()));
+ connect(aSaveDeckAs, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actSaveDeckAs);
aLoadDeckFromClipboard = new QAction(QString(), this);
- connect(aLoadDeckFromClipboard, SIGNAL(triggered()), deckEditor, SLOT(actLoadDeckFromClipboard()));
+ connect(aLoadDeckFromClipboard, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actLoadDeckFromClipboard);
aEditDeckInClipboard = new QAction(QString(), this);
- connect(aEditDeckInClipboard, SIGNAL(triggered()), deckEditor, SLOT(actEditDeckInClipboard()));
+ connect(aEditDeckInClipboard, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actEditDeckInClipboard);
aEditDeckInClipboardRaw = new QAction(QString(), this);
- connect(aEditDeckInClipboardRaw, SIGNAL(triggered()), deckEditor, SLOT(actEditDeckInClipboardRaw()));
+ connect(aEditDeckInClipboardRaw, &QAction::triggered, deckEditor,
+ &AbstractTabDeckEditor::actEditDeckInClipboardRaw);
aSaveDeckToClipboard = new QAction(QString(), this);
- connect(aSaveDeckToClipboard, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckToClipboard()));
+ connect(aSaveDeckToClipboard, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actSaveDeckToClipboard);
aSaveDeckToClipboardNoSetInfo = new QAction(QString(), this);
- connect(aSaveDeckToClipboardNoSetInfo, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckToClipboardNoSetInfo()));
+ connect(aSaveDeckToClipboardNoSetInfo, &QAction::triggered, deckEditor,
+ &AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo);
aSaveDeckToClipboardRaw = new QAction(QString(), this);
- connect(aSaveDeckToClipboardRaw, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckToClipboardRaw()));
+ connect(aSaveDeckToClipboardRaw, &QAction::triggered, deckEditor,
+ &AbstractTabDeckEditor::actSaveDeckToClipboardRaw);
aSaveDeckToClipboardRawNoSetInfo = new QAction(QString(), this);
- connect(aSaveDeckToClipboardRawNoSetInfo, SIGNAL(triggered()), deckEditor,
- SLOT(actSaveDeckToClipboardRawNoSetInfo()));
+ connect(aSaveDeckToClipboardRawNoSetInfo, &QAction::triggered, deckEditor,
+ &AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo);
aPrintDeck = new QAction(QString(), this);
- connect(aPrintDeck, SIGNAL(triggered()), deckEditor, SLOT(actPrintDeck()));
+ connect(aPrintDeck, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actPrintDeck);
aExportDeckDecklist = new QAction(QString(), this);
- connect(aExportDeckDecklist, SIGNAL(triggered()), deckEditor, SLOT(actExportDeckDecklist()));
+ connect(aExportDeckDecklist, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actExportDeckDecklist);
aExportDeckDecklistXyz = new QAction(QString(), this);
- connect(aExportDeckDecklistXyz, SIGNAL(triggered()), deckEditor, SLOT(actExportDeckDecklistXyz()));
+ connect(aExportDeckDecklistXyz, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actExportDeckDecklistXyz);
aAnalyzeDeckDeckstats = new QAction(QString(), this);
- connect(aAnalyzeDeckDeckstats, SIGNAL(triggered()), deckEditor, SLOT(actAnalyzeDeckDeckstats()));
+ connect(aAnalyzeDeckDeckstats, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actAnalyzeDeckDeckstats);
aAnalyzeDeckTappedout = new QAction(QString(), this);
- connect(aAnalyzeDeckTappedout, SIGNAL(triggered()), deckEditor, SLOT(actAnalyzeDeckTappedout()));
+ connect(aAnalyzeDeckTappedout, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::actAnalyzeDeckTappedout);
analyzeDeckMenu = new QMenu(this);
analyzeDeckMenu->addAction(aExportDeckDecklist);
@@ -103,7 +105,8 @@ DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEdit
addAction(aClose);
retranslateUi();
- connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
+ connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
+ &DeckEditorMenu::refreshShortcuts);
refreshShortcuts();
}
diff --git a/cockatrice/src/client/menus/deck_editor/deck_editor_menu.h b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.h
index a713b009f..7bfc58c4b 100644
--- a/cockatrice/src/client/menus/deck_editor/deck_editor_menu.h
+++ b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.h
@@ -10,7 +10,7 @@ class DeckEditorMenu : public QMenu
{
Q_OBJECT
public:
- explicit DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *deckEditor);
+ explicit DeckEditorMenu(AbstractTabDeckEditor *parent);
AbstractTabDeckEditor *deckEditor;
diff --git a/cockatrice/src/client/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/client/tabs/abstract_tab_deck_editor.cpp
index fe2148293..1c599034b 100644
--- a/cockatrice/src/client/tabs/abstract_tab_deck_editor.cpp
+++ b/cockatrice/src/client/tabs/abstract_tab_deck_editor.cpp
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -65,7 +66,8 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
connect(filterDockWidget, &DeckEditorFilterDockWidget::clearAllDatabaseFilters, databaseDisplayDockWidget,
&DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters);
- connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
+ connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
+ &AbstractTabDeckEditor::refreshShortcuts);
}
void AbstractTabDeckEditor::updateCard(CardInfoPtr _card)
@@ -124,8 +126,13 @@ void AbstractTabDeckEditor::actSwapCard(CardInfoPtr info, QString zoneName)
{
QString providerId = CardDatabaseManager::getInstance()->getSetInfoForCard(info).getProperty("uuid");
QString collectorNumber = CardDatabaseManager::getInstance()->getSetInfoForCard(info).getProperty("num");
- deckDockWidget->swapCard(
- deckDockWidget->deckModel->findCard(info->getName(), zoneName, providerId, collectorNumber));
+
+ QModelIndex foundCard = deckDockWidget->deckModel->findCard(info->getName(), zoneName, providerId, collectorNumber);
+ if (!foundCard.isValid()) {
+ foundCard = deckDockWidget->deckModel->findCard(info->getName(), zoneName);
+ }
+
+ deckDockWidget->swapCard(foundCard);
}
/**
@@ -332,8 +339,7 @@ bool AbstractTabDeckEditor::actSaveDeck()
cmd.set_deck_list(deckString.toStdString());
PendingCommand *pend = AbstractClient::prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(saveDeckRemoteFinished(Response)));
+ connect(pend, &PendingCommand::finished, this, &AbstractTabDeckEditor::saveDeckRemoteFinished);
tabSupervisor->getClient()->sendCommand(pend);
return true;
@@ -357,6 +363,7 @@ bool AbstractTabDeckEditor::actSaveDeckAs()
dialog.setDefaultSuffix("cod");
dialog.setNameFilters(DeckLoader::FILE_NAME_FILTERS);
dialog.selectFile(getDeckList()->getName().trimmed());
+
if (!dialog.exec())
return false;
@@ -451,7 +458,7 @@ void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo()
void AbstractTabDeckEditor::actPrintDeck()
{
auto *dlg = new QPrintPreviewDialog(this);
- connect(dlg, SIGNAL(paintRequested(QPrinter *)), deckDockWidget->deckModel, SLOT(printDeckList(QPrinter *)));
+ connect(dlg, &QPrintPreviewDialog::paintRequested, deckDockWidget->deckModel, &DeckListModel::printDeckList);
dlg->exec();
}
diff --git a/cockatrice/src/client/tabs/abstract_tab_deck_editor.h b/cockatrice/src/client/tabs/abstract_tab_deck_editor.h
index 0a5f97f56..796213d27 100644
--- a/cockatrice/src/client/tabs/abstract_tab_deck_editor.h
+++ b/cockatrice/src/client/tabs/abstract_tab_deck_editor.h
@@ -1,7 +1,7 @@
#ifndef TAB_GENERIC_DECK_EDITOR_H
#define TAB_GENERIC_DECK_EDITOR_H
-#include "../../game/cards/card_database.h"
+#include "../../game/cards/card_info.h"
#include "../menus/deck_editor/deck_editor_menu.h"
#include "../ui/widgets/deck_editor/deck_editor_card_info_dock_widget.h"
#include "../ui/widgets/deck_editor/deck_editor_database_display_widget.h"
@@ -44,6 +44,8 @@ class AbstractTabDeckEditor : public Tab
{
Q_OBJECT
+ friend class DeckEditorMenu;
+
public:
explicit AbstractTabDeckEditor(TabSupervisor *_tabSupervisor);
@@ -67,7 +69,7 @@ public:
DeckEditorPrintingSelectorDockWidget *printingSelectorDockWidget;
public slots:
- void onDeckChanged();
+ virtual void onDeckChanged();
void updateCard(CardInfoPtr _card);
void actAddCard(CardInfoPtr info);
void actAddCardToSideboard(CardInfoPtr info);
@@ -90,7 +92,7 @@ protected slots:
void cleanDeckAndResetModified();
virtual void actLoadDeck();
bool actSaveDeck();
- bool actSaveDeckAs();
+ virtual bool actSaveDeckAs();
virtual void actLoadDeckFromClipboard();
void actEditDeckInClipboard();
void actEditDeckInClipboardRaw();
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/archidekt_links/edhrec_api_response_archidekt_links.cpp
similarity index 76%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
index aac540f90..f071949f2 100644
--- 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/archidekt_links/edhrec_api_response_archidekt_links.cpp
@@ -1,11 +1,11 @@
-#include "edhrec_commander_api_response_archidekt_links.h"
+#include "edhrec_api_response_archidekt_links.h"
#include
#include
#include
#include
-void EdhrecCommanderApiResponseArchidektLink::fromJson(const QJsonObject &json)
+void EdhrecApiResponseArchidektLink::fromJson(const QJsonObject &json)
{
c = json.value("c").toString();
f = json.value("f").toInt(0);
@@ -13,7 +13,7 @@ void EdhrecCommanderApiResponseArchidektLink::fromJson(const QJsonObject &json)
u = json.value("u").toString();
}
-void EdhrecCommanderApiResponseArchidektLink::debugPrint() const
+void EdhrecApiResponseArchidektLink::debugPrint() const
{
qDebug() << " C:" << c;
qDebug() << " F:" << f;
@@ -27,7 +27,7 @@ void EdhrecCommanderApiResponseArchidektLinks::fromJson(const QJsonArray &json)
for (const QJsonValue &value : json) {
if (value.isObject()) {
QJsonObject entryJson = value.toObject();
- EdhrecCommanderApiResponseArchidektLink entry;
+ EdhrecApiResponseArchidektLink entry;
entry.fromJson(entryJson);
entries.append(entry);
}
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/archidekt_links/edhrec_api_response_archidekt_links.h
similarity index 83%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_archidekt_links.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.h
index 537dbb1fc..b47101e78 100644
--- 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/archidekt_links/edhrec_api_response_archidekt_links.h
@@ -7,7 +7,7 @@
#include
// Represents a single Archidekt entry
-class EdhrecCommanderApiResponseArchidektLink
+class EdhrecApiResponseArchidektLink
{
public:
QString c;
@@ -23,7 +23,7 @@ public:
class EdhrecCommanderApiResponseArchidektLinks
{
public:
- QVector entries;
+ QVector entries;
void fromJson(const QJsonArray &json);
void debugPrint() const;
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
new file mode 100644
index 000000000..1297fc157
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
@@ -0,0 +1,47 @@
+#include "edhrec_average_deck_api_response.h"
+
+#include
+#include
+
+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();
+}
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.h b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.h
new file mode 100644
index 000000000..2d330e3e8
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.h
@@ -0,0 +1,30 @@
+#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
+#include
+#include
+
+// 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
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
new file mode 100644
index 000000000..a2a015f72
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
@@ -0,0 +1,27 @@
+#include "edhrec_deck_api_response.h"
+
+#include "../../../../../../deck/deck_loader.h"
+
+#include
+#include
+#include
+#include
+#include
+
+void EdhrecDeckApiResponse::fromJson(const QJsonArray &json)
+{
+ QString deckList;
+ for (const QJsonValue &cardlistValue : json) {
+ deckList += cardlistValue.toString() + "\n";
+ }
+
+ deckLoader = new DeckLoader();
+
+ QTextStream stream(&deckList);
+ deckLoader->loadFromStream_Plain(stream, true);
+}
+
+void EdhrecDeckApiResponse::debugPrint() const
+{
+ qDebug() << "Breadcrumb:";
+}
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h
new file mode 100644
index 000000000..b888cb134
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h
@@ -0,0 +1,27 @@
+#ifndef EDHREC_DECK_API_RESPONSE_H
+#define EDHREC_DECK_API_RESPONSE_H
+
+#include "../../../../../../deck/deck_loader.h"
+
+#include
+#include
+#include
+#include
+#include
+
+class EdhrecDeckApiResponse
+{
+public:
+ // Constructor
+ EdhrecDeckApiResponse() = default;
+
+ // Parse deck-related data from JSON
+ void fromJson(const QJsonArray &json);
+
+ // Debug method for logging
+ void debugPrint() const;
+
+ DeckLoader *deckLoader;
+};
+
+#endif // EDHREC_DECK_API_RESPONSE_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/card_prices/edhrec_api_response_card_prices.cpp
similarity index 57%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
index 6454e5500..1231d147a 100644
--- 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/card_prices/edhrec_api_response_card_prices.cpp
@@ -1,4 +1,4 @@
-#include "edhrec_commander_api_response_card_prices.h"
+#include "edhrec_api_response_card_prices.h"
#include
@@ -18,14 +18,14 @@ void CardPrices::fromJson(const QJsonObject &json)
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;
+ 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;
}
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/card_prices/edhrec_api_response_card_prices.h
similarity index 100%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.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/cards/edhrec_api_response_card_container.cpp
similarity index 83%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
index f8ffab787..3fef8be34 100644
--- 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/cards/edhrec_api_response_card_container.cpp
@@ -1,10 +1,10 @@
-#include "edhrec_commander_api_response_card_container.h"
+#include "edhrec_api_response_card_container.h"
#include
#include
#include
-void EdhrecCommanderApiResponseCardContainer::fromJson(const QJsonObject &json)
+void EdhrecApiResponseCardContainer::fromJson(const QJsonObject &json)
{
// Parse breadcrumb
QJsonArray breadcrumbArray = json.value("breadcrumb").toArray();
@@ -20,7 +20,7 @@ void EdhrecCommanderApiResponseCardContainer::fromJson(const QJsonObject &json)
for (const QJsonValue &cardlistValue : cardlistsArray) {
QJsonObject cardlistObj = cardlistValue.toObject();
QJsonArray cardviewsArray = cardlistObj.value("cardviews").toArray();
- EdhrecCommanderApiResponseCardList cardView;
+ EdhrecApiResponseCardList cardView;
cardView.fromJson(cardlistValue.toObject());
cardlists.push_back(cardView);
}
@@ -29,7 +29,7 @@ void EdhrecCommanderApiResponseCardContainer::fromJson(const QJsonObject &json)
title = json.value("title").toString();
}
-void EdhrecCommanderApiResponseCardContainer::debugPrint() const
+void EdhrecApiResponseCardContainer::debugPrint() const
{
qDebug() << "Breadcrumb:";
for (const auto &breadcrumbEntry : breadcrumb) {
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/cards/edhrec_api_response_card_container.h
similarity index 79%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_container.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.h
index d2ea05874..94650785f 100644
--- 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/cards/edhrec_api_response_card_container.h
@@ -1,7 +1,7 @@
#ifndef CONTAINER_ENTRY_H
#define CONTAINER_ENTRY_H
-#include "edhrec_commander_api_response_card_list.h"
+#include "edhrec_api_response_card_list.h"
#include "edhrec_commander_api_response_commander_details.h"
#include
@@ -10,11 +10,11 @@
#include
#include
-class EdhrecCommanderApiResponseCardContainer
+class EdhrecApiResponseCardContainer
{
public:
// Constructor
- EdhrecCommanderApiResponseCardContainer() = default;
+ EdhrecApiResponseCardContainer() = default;
// Parse deck-related data from JSON
void fromJson(const QJsonObject &json);
@@ -35,7 +35,7 @@ public:
{
return card;
}
- const QVector &getCardlists() const
+ const QVector &getCardlists() const
{
return cardlists;
}
@@ -52,7 +52,7 @@ private:
QString description;
QVector breadcrumb;
EdhrecCommanderApiResponseCommanderDetails card;
- QVector cardlists;
+ QVector cardlists;
QString keywords;
QString title;
};
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/cards/edhrec_api_response_card_details.cpp
similarity index 78%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
index 2f2791de1..a4a746c61 100644
--- 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/cards/edhrec_api_response_card_details.cpp
@@ -1,13 +1,13 @@
-#include "edhrec_commander_api_response_card_details.h"
+#include "edhrec_api_response_card_details.h"
#include
-EdhrecCommanderApiResponseCardDetails::EdhrecCommanderApiResponseCardDetails()
+EdhrecApiResponseCardDetails::EdhrecApiResponseCardDetails()
: synergy(0.0), inclusion(0), numDecks(0), potentialDecks(0)
{
}
-void EdhrecCommanderApiResponseCardDetails::fromJson(const QJsonObject &json)
+void EdhrecApiResponseCardDetails::fromJson(const QJsonObject &json)
{
// Parse the fields from the JSON object
name = json.value("name").toString();
@@ -21,7 +21,7 @@ void EdhrecCommanderApiResponseCardDetails::fromJson(const QJsonObject &json)
potentialDecks = json.value("potential_decks").toInt(0);
}
-void EdhrecCommanderApiResponseCardDetails::debugPrint() const
+void EdhrecApiResponseCardDetails::debugPrint() const
{
// Print out all the fields for debugging
qDebug() << "Name:" << name;
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/cards/edhrec_api_response_card_details.h
similarity index 84%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.h
index 82d85824b..9f75aac72 100644
--- 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/cards/edhrec_api_response_card_details.h
@@ -4,7 +4,7 @@
#include
#include
-class EdhrecCommanderApiResponseCardDetails
+class EdhrecApiResponseCardDetails
{
public:
QString name;
@@ -17,7 +17,7 @@ public:
int numDecks;
int potentialDecks;
- EdhrecCommanderApiResponseCardDetails();
+ EdhrecApiResponseCardDetails();
// Method to populate the object from a JSON object
void fromJson(const QJsonObject &json);
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/cards/edhrec_api_response_card_list.cpp
similarity index 59%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
index 5d3eebedd..72d6fe2cc 100644
--- 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/cards/edhrec_api_response_card_list.cpp
@@ -1,12 +1,12 @@
-#include "edhrec_commander_api_response_card_list.h"
+#include "edhrec_api_response_card_list.h"
#include
-EdhrecCommanderApiResponseCardList::EdhrecCommanderApiResponseCardList()
+EdhrecApiResponseCardList::EdhrecApiResponseCardList()
{
}
-void EdhrecCommanderApiResponseCardList::fromJson(const QJsonObject &json)
+void EdhrecApiResponseCardList::fromJson(const QJsonObject &json)
{
// Parse the header from the JSON object
header = json.value("header").toString();
@@ -15,19 +15,19 @@ void EdhrecCommanderApiResponseCardList::fromJson(const QJsonObject &json)
QJsonArray cardviewsArray = json.value("cardviews").toArray();
for (const QJsonValue &value : cardviewsArray) {
QJsonObject cardviewObj = value.toObject();
- EdhrecCommanderApiResponseCardDetails cardView;
+ EdhrecApiResponseCardDetails cardView;
cardView.fromJson(cardviewObj);
cardViews.append(cardView);
}
}
-void EdhrecCommanderApiResponseCardList::debugPrint() const
+void EdhrecApiResponseCardList::debugPrint() const
{
// Print out the header
qDebug() << "Header:" << header;
// Print out all the CardView objects
- for (const EdhrecCommanderApiResponseCardDetails &cardView : cardViews) {
+ for (const EdhrecApiResponseCardDetails &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/cards/edhrec_api_response_card_list.h
similarity index 65%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.h
index e0ddc3466..9fcacfffe 100644
--- 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/cards/edhrec_api_response_card_list.h
@@ -1,21 +1,21 @@
#ifndef CARD_LIST_H
#define CARD_LIST_H
-#include "edhrec_commander_api_response_card_details.h"
+#include "edhrec_api_response_card_details.h"
#include
#include
#include
#include
-class EdhrecCommanderApiResponseCardList
+class EdhrecApiResponseCardList
{
public:
QString header;
- QList cardViews;
+ QList cardViews;
// Default constructor
- EdhrecCommanderApiResponseCardList();
+ EdhrecApiResponseCardList();
// Method to populate the object from a JSON object
void fromJson(const QJsonObject &json);
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/cards/edhrec_commander_api_response_commander_details.cpp
similarity index 100%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
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/cards/edhrec_commander_api_response_commander_details.h
similarity index 98%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.h
index 78ccfc23a..7b754c9b2 100644
--- 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/cards/edhrec_commander_api_response_commander_details.h
@@ -1,7 +1,7 @@
#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 "../card_prices/edhrec_api_response_card_prices.h"
#include
#include
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/commander/edhrec_commander_api_response.cpp
similarity index 100%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
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/commander/edhrec_commander_api_response.h
similarity index 76%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.h
index 91cf3afbc..f1141a113 100644
--- a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response.h
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.h
@@ -1,9 +1,9 @@
#ifndef DECKDATA_H
#define DECKDATA_H
-#include "edhrec_commander_api_response_archidekt_links.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 "edhrec_commander_api_response_card_container.h"
#include
#include
@@ -19,7 +19,7 @@ public:
QString header;
QJsonObject panels;
QString description;
- EdhrecCommanderApiResponseCardContainer container;
+ EdhrecApiResponseCardContainer container;
void fromJson(const QJsonObject &json);
void debugPrint() const;
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/commander/edhrec_commander_api_response_average_deck_statistics.cpp
similarity index 100%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.cpp
rename to cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
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/commander/edhrec_commander_api_response_average_deck_statistics.h
similarity index 100%
rename from cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.h
rename to cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.h
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
new file mode 100644
index 000000000..26122ce89
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
@@ -0,0 +1,19 @@
+#include "edhrec_top_cards_api_response.h"
+
+#include
+#include
+
+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();
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.h b/cockatrice/src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.h
new file mode 100644
index 000000000..569c31894
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.h
@@ -0,0 +1,20 @@
+#ifndef EDHREC_TOP_CARDS_API_RESPONSE_H
+#define EDHREC_TOP_CARDS_API_RESPONSE_H
+
+#include "../cards/edhrec_api_response_card_container.h"
+
+#include
+#include
+
+class EdhrecTopCardsApiResponse
+{
+public:
+ QString header;
+ QString description;
+ EdhrecApiResponseCardContainer container;
+
+ void fromJson(const QJsonObject &json);
+ void debugPrint() const;
+};
+
+#endif // EDHREC_TOP_CARDS_API_RESPONSE_H
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
new file mode 100644
index 000000000..83b10c9e5
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
@@ -0,0 +1,19 @@
+#include "edhrec_top_commanders_api_response.h"
+
+#include
+#include
+
+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();
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.h b/cockatrice/src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.h
new file mode 100644
index 000000000..526611eb7
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.h
@@ -0,0 +1,20 @@
+#ifndef EDHREC_TOP_COMMANDERS_API_RESPONSE_H
+#define EDHREC_TOP_COMMANDERS_API_RESPONSE_H
+
+#include "../cards/edhrec_api_response_card_container.h"
+
+#include
+#include
+
+class EdhrecTopCommandersApiResponse
+{
+public:
+ QString header;
+ QString description;
+ EdhrecApiResponseCardContainer container;
+
+ void fromJson(const QJsonObject &json);
+ void debugPrint() const;
+};
+
+#endif // EDHREC_TOP_COMMANDERS_API_RESPONSE_H
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
new file mode 100644
index 000000000..56eb4fa4f
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
@@ -0,0 +1,19 @@
+#include "edhrec_top_tags_api_response.h"
+
+#include
+#include
+
+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();
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.h b/cockatrice/src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.h
new file mode 100644
index 000000000..f31d03a8e
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.h
@@ -0,0 +1,20 @@
+#ifndef EDHREC_TOP_TAGS_API_RESPONSE_H
+#define EDHREC_TOP_TAGS_API_RESPONSE_H
+
+#include "../cards/edhrec_api_response_card_container.h"
+
+#include
+#include
+
+class EdhrecTopTagsApiResponse
+{
+public:
+ QString header;
+ QString description;
+ EdhrecApiResponseCardContainer container;
+
+ void fromJson(const QJsonObject &json);
+ void debugPrint() const;
+};
+
+#endif // EDHREC_TOP_TAGS_API_RESPONSE_H
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
new file mode 100644
index 000000000..75902daad
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
@@ -0,0 +1,66 @@
+#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"));
+}
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.h
new file mode 100644
index 000000000..8dae375fe
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.h
@@ -0,0 +1,42 @@
+#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
+#include
+#include
+
+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
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
new file mode 100644
index 000000000..f6e0cf4fa
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
@@ -0,0 +1,67 @@
+#include "edhrec_api_response_card_details_display_widget.h"
+
+#include "../../../../../../game/cards/card_database_manager.h"
+#include "../../tab_edhrec_main.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::getInstance()->guessCard(toDisplay.sanitized));
+
+ label = new QLabel(this);
+ label->setText(toDisplay.name + "\n" + toDisplay.label);
+ label->setAlignment(Qt::AlignHCenter);
+
+ int inclusionRate = 0;
+ // Set label color based on inclusion rate
+ if (toDisplay.potentialDecks != 0) {
+ 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);
+
+ QWidget *currentParent = parentWidget();
+ TabEdhRecMain *parentTab = nullptr;
+
+ while (currentParent) {
+ if ((parentTab = qobject_cast(currentParent))) {
+ break;
+ }
+ currentParent = currentParent->parentWidget();
+ }
+
+ if (parentTab) {
+ connect(cardPictureWidget, &CardInfoPictureWidget::cardClicked, this,
+ &EdhrecApiResponseCardDetailsDisplayWidget::actRequestPageNavigation);
+ connect(this, &EdhrecApiResponseCardDetailsDisplayWidget::requestUrl, parentTab,
+ &TabEdhRecMain::actNavigatePage);
+ }
+}
+
+void EdhrecApiResponseCardDetailsDisplayWidget::actRequestPageNavigation()
+{
+ emit requestUrl(toDisplay.url);
+}
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.h
new file mode 100644
index 000000000..7492659e9
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.h
@@ -0,0 +1,28 @@
+#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/cards/edhrec_api_response_card_details.h"
+
+#include
+#include
+#include
+
+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;
+ QLabel *label;
+};
+
+#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
new file mode 100644
index 000000000..068003fac
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
@@ -0,0 +1,33 @@
+#include "edhrec_api_response_card_list_display_widget.h"
+
+#include "../../../../../ui/widgets/general/display/banner_widget.h"
+#include "edhrec_api_response_card_details_display_widget.h"
+
+#include
+
+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();
+}
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/display/cards/edhrec_api_response_card_list_display_widget.h
similarity index 53%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.h
rename to cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.h
index 9df667929..291150cde 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.h
+++ b/cockatrice/src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.h
@@ -1,20 +1,19 @@
#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 "../../../../../ui/widgets/general/display/banner_widget.h"
+#include "../../../../../ui/widgets/general/layout_containers/flow_widget.h"
+#include "../../api_response/cards/edhrec_api_response_card_list.h"
#include
#include
#include
-class EdhrecCommanderApiResponseCardListDisplayWidget : public QWidget
+class EdhrecApiResponseCardListDisplayWidget : public QWidget
{
Q_OBJECT
public:
- explicit EdhrecCommanderApiResponseCardListDisplayWidget(QWidget *parent,
- EdhrecCommanderApiResponseCardList toDisplay);
+ explicit EdhrecApiResponseCardListDisplayWidget(QWidget *parent, EdhrecApiResponseCardList toDisplay);
void resizeEvent(QResizeEvent *event) override;
[[nodiscard]] QString getBannerText() const
{
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/display/commander/edhrec_api_response_commander_details_display_widget.cpp
similarity index 61%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp
rename to cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
index 58b77df73..e04325a35 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
@@ -1,13 +1,13 @@
-#include "edhrec_commander_api_response_commander_details_display_widget.h"
+#include "edhrec_api_response_commander_details_display_widget.h"
-#include "../../../../game/cards/card_database_manager.h"
-#include "../../../ui/widgets/cards/card_info_picture_widget.h"
-
-#include
+#include "../../../../../../game/cards/card_database_manager.h"
+#include "../../../../../ui/widgets/cards/card_info_picture_widget.h"
+#include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
QWidget *parent,
- const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails)
+ const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
+ QString baseUrl)
: QWidget(parent), commanderDetails(_commanderDetails)
{
layout = new QVBoxLayout(this);
@@ -23,9 +23,16 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom
salt = new QLabel(this);
salt->setAlignment(Qt::AlignCenter);
+ cardPricesDisplayWidget = new EdhrecApiResponseCardPricesDisplayWidget(this, commanderDetails.getPrices());
+
+ navigationWidget = new EdhrecCommanderApiResponseNavigationWidget(this, commanderDetails, baseUrl);
+
layout->addWidget(commanderPicture);
layout->addWidget(label);
layout->addWidget(salt);
+ layout->addWidget(cardPricesDisplayWidget);
+ layout->addWidget(navigationWidget);
+
retranslateUi();
}
@@ -33,4 +40,4 @@ 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/display/commander/edhrec_api_response_commander_details_display_widget.h
similarity index 59%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.h
rename to cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h
index 85970d503..ba6a895b1 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.h
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h
@@ -1,28 +1,34 @@
#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 "../../../../../ui/widgets/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
#include
#include
+class EdhrecCommanderApiResponseNavigationWidget;
class EdhrecCommanderResponseCommanderDetailsDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit EdhrecCommanderResponseCommanderDetailsDisplayWidget(
QWidget *parent,
- const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails);
+ const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
+ QString baseUrl);
void retranslateUi();
private:
- QLabel *label;
- QLabel *salt;
+ EdhrecCommanderApiResponseCommanderDetails commanderDetails;
QVBoxLayout *layout;
CardInfoPictureWidget *commanderPicture;
- EdhrecCommanderApiResponseCommanderDetails commanderDetails;
+ QLabel *label;
+ QLabel *salt;
+ EdhrecApiResponseCardPricesDisplayWidget *cardPricesDisplayWidget;
+ EdhrecCommanderApiResponseNavigationWidget *navigationWidget;
};
#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/display/commander/edhrec_commander_api_response_display_widget.cpp
similarity index 81%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.cpp
rename to cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
index 3cab65bf8..9ea071546 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.cpp
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
@@ -1,9 +1,9 @@
#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 "../../../../../ui/widgets/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
#include
@@ -12,7 +12,8 @@
#include
EdhrecCommanderApiResponseDisplayWidget::EdhrecCommanderApiResponseDisplayWidget(QWidget *parent,
- EdhrecCommanderApiResponse response)
+ EdhrecCommanderApiResponse response,
+ QString baseUrl)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
@@ -31,15 +32,15 @@ EdhrecCommanderApiResponseDisplayWidget::EdhrecCommanderApiResponseDisplayWidget
QStringList widgetNames;
// Add commander details
- auto commanderPicture =
- new EdhrecCommanderResponseCommanderDetailsDisplayWidget(this, response.container.getCommanderDetails());
+ 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 EdhrecCommanderApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
- auto cardListDisplayWidget = new EdhrecCommanderApiResponseCardListDisplayWidget(this, card_list);
+ 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());
}
diff --git a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.h
similarity index 71%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.h
rename to cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.h
index d1e95e3d6..172b64e32 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_display_widget.h
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.h
@@ -1,7 +1,7 @@
#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 "../../api_response/commander/edhrec_commander_api_response.h"
#include
#include
@@ -12,7 +12,9 @@ class EdhrecCommanderApiResponseDisplayWidget : public QWidget
Q_OBJECT
public:
- explicit EdhrecCommanderApiResponseDisplayWidget(QWidget *parent, EdhrecCommanderApiResponse response);
+ explicit EdhrecCommanderApiResponseDisplayWidget(QWidget *parent,
+ EdhrecCommanderApiResponse response,
+ QString baseUrl);
void resizeEvent(QResizeEvent *event) override;
public slots:
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
new file mode 100644
index 000000000..3f3b9f1ba
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
@@ -0,0 +1,174 @@
+#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);
+
+ gameChangerLabel = new QLabel(this);
+ budgetLabel = new QLabel(this);
+
+ comboPushButton = new QPushButton(this);
+ averageDeckPushButton = new QPushButton(this);
+
+ layout->addWidget(comboPushButton, 0, 0, 1, 1);
+ layout->addWidget(averageDeckPushButton, 0, 1, 1, 1);
+
+ 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);
+ gameChangerButtons[option] = optionButton;
+ layout->addWidget(optionButton, 2, i);
+ connect(optionButton, &QPushButton::clicked, this, [=, this]() {
+ selectedGameChanger = option;
+ updateOptionButtonSelection(gameChangerButtons, option);
+ actRequestCommanderNavigation();
+ });
+ }
+
+ 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);
+ budgetButtons[option] = btn;
+ layout->addWidget(btn, 4, i);
+ connect(btn, &QPushButton::clicked, this, [=, this]() {
+ selectedBudget = option;
+ updateOptionButtonSelection(budgetButtons, option);
+ actRequestCommanderNavigation();
+ });
+ }
+
+ updateOptionButtonSelection(gameChangerButtons, "");
+ updateOptionButtonSelection(budgetButtons, "");
+
+ QWidget *currentParent = parentWidget();
+ TabEdhRecMain *parentTab = nullptr;
+
+ while (currentParent) {
+ if ((parentTab = qobject_cast(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();
+ applyOptionsFromUrl(baseUrl);
+}
+
+void EdhrecCommanderApiResponseNavigationWidget::retranslateUi()
+{
+ comboPushButton->setText(tr("Combos"));
+ averageDeckPushButton->setText(tr("Average Deck"));
+ gameChangerLabel->setText(tr("Game Changers"));
+ budgetLabel->setText(tr("Budget"));
+}
+
+void EdhrecCommanderApiResponseNavigationWidget::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"
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts);
+#else
+ QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts);
+#endif
+
+ if (parts.size() < 2) {
+ return;
+ }
+
+ QString commanderName = parts[1];
+ QString gameChangerOpt, budgetOpt;
+
+ // Define valid sets
+ QSet validGameChangers = {"core", "upgraded", "optimized"};
+ QSet 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;
+ }
+ }
+
+ // Validate and apply
+ if (!gameChangerButtons.contains(gameChangerOpt)) {
+ gameChangerOpt.clear();
+ }
+ if (!budgetButtons.contains(budgetOpt)) {
+ budgetOpt.clear();
+ }
+
+ selectedGameChanger = gameChangerOpt;
+ selectedBudget = budgetOpt;
+
+ updateOptionButtonSelection(gameChangerButtons, selectedGameChanger);
+ updateOptionButtonSelection(budgetButtons, selectedBudget);
+}
+
+void EdhrecCommanderApiResponseNavigationWidget::updateOptionButtonSelection(QMap &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;" : "");
+ }
+}
+
+QString EdhrecCommanderApiResponseNavigationWidget::addNavigationOptionsToUrl(QString baseUrl)
+{
+ if (!selectedGameChanger.isEmpty()) {
+ baseUrl += "/" + selectedGameChanger;
+ }
+ if (!selectedBudget.isEmpty()) {
+ baseUrl += "/" + selectedBudget;
+ }
+ 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()));
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h
new file mode 100644
index 000000000..16f601b9b
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h
@@ -0,0 +1,54 @@
+#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
+#include
+#include
+#include
+
+class EdhrecCommanderApiResponseNavigationWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit EdhrecCommanderApiResponseNavigationWidget(
+ QWidget *parent,
+ const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
+ QString baseUrl);
+ void retranslateUi();
+ void applyOptionsFromUrl(const QString &url);
+
+public slots:
+ void actRequestCommanderNavigation();
+ void actRequestComboNavigation();
+ void actRequestAverageDeckNavigation();
+signals:
+ void requestUrl(QString url);
+
+private:
+ QGridLayout *layout;
+
+ QLabel *gameChangerLabel;
+ QLabel *budgetLabel;
+
+ QStringList gameChangerOptions = {"", "core", "upgraded", "optimized"};
+ QStringList budgetOptions = {"", "budget", "expensive"};
+
+ QString selectedGameChanger;
+ QString selectedBudget;
+
+ QMap gameChangerButtons;
+ QMap budgetButtons;
+
+ QPushButton *comboPushButton;
+ QPushButton *averageDeckPushButton;
+
+ EdhrecCommanderApiResponseCommanderDetails commanderDetails;
+
+ void updateOptionButtonSelection(QMap &buttons, const QString &selectedKey);
+ QString addNavigationOptionsToUrl(QString baseUrl);
+ QString buildComboUrl() const;
+};
+
+#endif // EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
new file mode 100644
index 000000000..ae07c9dc8
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
@@ -0,0 +1,45 @@
+#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());
+ }
+}
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.h
new file mode 100644
index 000000000..4b7a34d6a
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.h
@@ -0,0 +1,25 @@
+#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
+#include
+#include
+#include
+
+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
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
new file mode 100644
index 000000000..13f54a979
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
@@ -0,0 +1,47 @@
+#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());
+ }
+}
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.h
new file mode 100644
index 000000000..e8774d38b
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.h
@@ -0,0 +1,25 @@
+#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
+#include
+#include
+#include
+
+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
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
new file mode 100644
index 000000000..91a1ce3fa
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
@@ -0,0 +1,45 @@
+#include "edhrec_top_tags_api_response_display_widget.h"
+
+#include "../../api_response/top_tags/edhrec_top_tags_api_response.h"
+#include "../cards/edhrec_api_response_card_list_display_widget.h"
+
+EdhrecTopTagsApiResponseDisplayWidget::EdhrecTopTagsApiResponseDisplayWidget(QWidget *parent,
+ EdhrecTopTagsApiResponse 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 EdhrecTopTagsApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ layout->invalidate();
+ layout->activate();
+ layout->update();
+ if (scrollArea && scrollArea->widget()) {
+ scrollArea->widget()->resize(event->size());
+ }
+}
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.h b/cockatrice/src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.h
new file mode 100644
index 000000000..d67875058
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.h
@@ -0,0 +1,25 @@
+#ifndef EDHREC_TOP_TAGS_API_RESPONSE_DISPLAY_WIDGET_H
+#define EDHREC_TOP_TAGS_API_RESPONSE_DISPLAY_WIDGET_H
+
+#include "../../api_response/top_tags/edhrec_top_tags_api_response.h"
+
+#include
+#include
+#include
+#include
+
+class EdhrecTopTagsApiResponseDisplayWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit EdhrecTopTagsApiResponseDisplayWidget(QWidget *parent, EdhrecTopTagsApiResponse response);
+ void resizeEvent(QResizeEvent *event) override;
+
+private:
+ QHBoxLayout *layout;
+ QVBoxLayout *cardDisplayLayout;
+ QScrollArea *scrollArea;
+};
+
+#endif // EDHREC_TOP_TAGS_API_RESPONSE_DISPLAY_WIDGET_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
deleted file mode 100644
index 87853b9aa..000000000
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#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
deleted file mode 100644
index 1cd76989c..000000000
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#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
deleted file mode 100644
index 0d1f1e8f3..000000000
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#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/tab_edhrec.cpp b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp
index 18662aaab..a2755b9e1 100644
--- a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp
+++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp
@@ -1,7 +1,7 @@
#include "tab_edhrec.h"
-#include "api_response/edhrec_commander_api_response.h"
-#include "edhrec_commander_api_response_display_widget.h"
+#include "api_response/commander/edhrec_commander_api_response.h"
+#include "display/commander/edhrec_commander_api_response_display_widget.h"
#include
#include
@@ -21,7 +21,7 @@ TabEdhRec::TabEdhRec(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
#endif
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
- connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *)));
+ connect(networkManager, &QNetworkAccessManager::finished, this, &TabEdhRec::processApiJson);
}
void TabEdhRec::retranslateUi()
@@ -76,7 +76,7 @@ void TabEdhRec::processApiJson(QNetworkReply *reply)
EdhrecCommanderApiResponse deckData;
deckData.fromJson(jsonObj);
- displayWidget = new EdhrecCommanderApiResponseDisplayWidget(this, deckData);
+ displayWidget = new EdhrecCommanderApiResponseDisplayWidget(this, deckData, reply->url().toString());
// flowWidget->addWidget(displayWidget);
setCentralWidget(displayWidget);
diff --git a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h
index c15f22b26..ff299fadc 100644
--- a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h
+++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.h
@@ -1,12 +1,11 @@
#ifndef TAB_EDHREC_H
#define TAB_EDHREC_H
-#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_info.h"
#include "../../../ui/widgets/general/layout_containers/flow_widget.h"
#include "../../tab.h"
-#include "edhrec_commander_api_response_display_widget.h"
+#include "display/commander/edhrec_commander_api_response_display_widget.h"
-#include
#include
class TabEdhRec : public Tab
diff --git a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp
new file mode 100644
index 000000000..ac30cef4d
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp
@@ -0,0 +1,380 @@
+#include "tab_edhrec_main.h"
+
+#include "../../../../game/cards/card_completer_proxy_model.h"
+#include "../../../../game/cards/card_database_manager.h"
+#include "../../../../game/cards/card_search_model.h"
+#include "../../tab_supervisor.h"
+#include "api_response/average_deck/edhrec_average_deck_api_response.h"
+#include "api_response/commander/edhrec_commander_api_response.h"
+#include "api_response/top_cards/edhrec_top_cards_api_response.h"
+#include "api_response/top_commanders/edhrec_top_commanders_api_response.h"
+#include "api_response/top_tags/edhrec_top_tags_api_response.h"
+#include "display/commander/edhrec_commander_api_response_display_widget.h"
+#include "display/top_cards/edhrec_top_cards_api_response_display_widget.h"
+#include "display/top_commander/edhrec_top_commanders_api_response_display_widget.h"
+#include "display/top_tags/edhrec_top_tags_api_response_display_widget.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#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);
+}
+
+TabEdhRecMain::TabEdhRecMain(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 *)));
+
+ container = new QWidget(this);
+ mainLayout = new QVBoxLayout(container);
+ container->setLayout(mainLayout);
+
+ navigationContainer = new QWidget(container);
+ navigationContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ navigationLayout = new QHBoxLayout(navigationContainer);
+ navigationLayout->setSpacing(5);
+ navigationContainer->setLayout(navigationLayout);
+
+ cardsPushButton = new QPushButton(navigationContainer);
+ connect(cardsPushButton, &QPushButton::clicked, this, &TabEdhRecMain::getTopCards);
+ topCommandersPushButton = new QPushButton(navigationContainer);
+ connect(topCommandersPushButton, &QPushButton::clicked, this, &TabEdhRecMain::getTopCommanders);
+ tagsPushButton = new QPushButton(navigationContainer);
+ connect(tagsPushButton, &QPushButton::clicked, this, &TabEdhRecMain::getTopTags);
+
+ searchBar = new QLineEdit(this);
+ auto cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
+ auto displayModel = new CardDatabaseDisplayModel(this);
+ displayModel->setSourceModel(cardDatabaseModel);
+ CardSearchModel *searchModel = new CardSearchModel(displayModel, this);
+
+ CardCompleterProxyModel *proxyModel = new CardCompleterProxyModel(this);
+ proxyModel->setSourceModel(searchModel);
+ proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ proxyModel->setFilterRole(Qt::DisplayRole);
+
+ QCompleter *completer = new QCompleter(proxyModel, this);
+ completer->setCompletionRole(Qt::DisplayRole);
+ completer->setCompletionMode(QCompleter::PopupCompletion);
+ completer->setCaseSensitivity(Qt::CaseInsensitive);
+ completer->setFilterMode(Qt::MatchContains);
+ completer->setMaxVisibleItems(10);
+ searchBar->setCompleter(completer);
+
+ // Update suggestions dynamically
+ connect(searchBar, &QLineEdit::textChanged, searchModel, &CardSearchModel::updateSearchResults);
+ connect(searchBar, &QLineEdit::textChanged, this, [=](const QString &text) {
+ // Ensure substring matching
+ QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
+ proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
+
+ if (!text.isEmpty()) {
+ completer->complete(); // Force the dropdown to appear
+ }
+ });
+
+ searchPushButton = new QPushButton(navigationContainer);
+ connect(searchPushButton, &QPushButton::clicked, this, [=, this]() { doSearch(); });
+
+ navigationLayout->addWidget(cardsPushButton);
+ navigationLayout->addWidget(topCommandersPushButton);
+ navigationLayout->addWidget(tagsPushButton);
+ navigationLayout->addWidget(searchBar);
+ navigationLayout->addWidget(searchPushButton);
+
+ currentPageDisplay = new QWidget(container);
+ currentPageLayout = new QVBoxLayout(currentPageDisplay);
+ currentPageDisplay->setLayout(currentPageLayout);
+
+ mainLayout->addWidget(navigationContainer);
+ mainLayout->addWidget(currentPageDisplay);
+
+ // Ensure navigation stays at the top and currentPageDisplay takes remaining space
+ mainLayout->setStretch(0, 0); // navigationContainer gets minimum space
+ mainLayout->setStretch(1, 1); // currentPageDisplay expands as much as possible
+
+ setCentralWidget(container);
+
+ TabEdhRecMain::retranslateUi();
+
+ getTopCards();
+}
+
+void TabEdhRecMain::retranslateUi()
+{
+ cardsPushButton->setText(tr("&Cards"));
+ topCommandersPushButton->setText(tr("Top Commanders"));
+ tagsPushButton->setText(tr("Tags"));
+ searchBar->setPlaceholderText(tr("Search for a card ..."));
+ searchPushButton->setText(tr("Search"));
+}
+
+void TabEdhRecMain::doSearch()
+{
+ CardInfoPtr searchedCard = CardDatabaseManager::getInstance()->getCard(searchBar->text());
+ if (!searchedCard) {
+ return;
+ }
+
+ setCard(searchedCard, canBeCommander(searchedCard));
+}
+
+void TabEdhRecMain::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 TabEdhRecMain::actNavigatePage(QString url)
+{
+ QNetworkRequest request{QUrl("https://json.edhrec.com/pages" + url + ".json")};
+
+ networkManager->get(request);
+}
+
+void TabEdhRecMain::getTopCards()
+{
+ QNetworkRequest request{QUrl("https://json.edhrec.com/pages/top/year.json")};
+
+ networkManager->get(request);
+}
+
+void TabEdhRecMain::getTopCommanders()
+{
+ QNetworkRequest request{QUrl("https://json.edhrec.com/pages/commanders/year.json")};
+
+ networkManager->get(request);
+}
+
+void TabEdhRecMain::getTopTags()
+{
+ QNetworkRequest request{QUrl("https://json.edhrec.com/pages/tags.json")};
+
+ networkManager->get(request);
+}
+
+void TabEdhRecMain::processApiJson(QNetworkReply *reply)
+{
+ if (reply->error() != QNetworkReply::NoError) {
+ qDebug() << "Network error occurred:" << reply->errorString();
+ reply->deleteLater();
+ return;
+ }
+
+ QByteArray responseData = reply->readAll();
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
+
+ if (!jsonDoc.isObject()) {
+ qDebug() << "Invalid JSON response received.";
+ reply->deleteLater();
+ return;
+ }
+
+ QJsonObject jsonObj = jsonDoc.object();
+
+ // Get the actual URL from the reply
+ QString responseUrl = reply->url().toString();
+
+ // Check if the response URL matches a commander request
+ if (responseUrl.startsWith("https://json.edhrec.com/pages/commanders/year.json")) {
+ processTopCommandersResponse(jsonObj);
+ } else if (responseUrl.startsWith("https://json.edhrec.com/pages/commanders/")) {
+ qInfo() << "Received top kek";
+ processCommanderResponse(jsonObj, responseUrl);
+ } else if (responseUrl.startsWith("https://json.edhrec.com/pages/cards/")) {
+ processCommanderResponse(jsonObj);
+ } else if (responseUrl.startsWith("https://json.edhrec.com/pages/tags/")) {
+ processCommanderResponse(jsonObj);
+ } else if (responseUrl.startsWith("https://json.edhrec.com/pages/tags.json")) {
+ processTopTagsResponse(jsonObj);
+ } else if (responseUrl.startsWith("https://json.edhrec.com/pages/top/year.json")) {
+ processTopCardsResponse(jsonObj);
+ } else if (responseUrl.startsWith("https://json.edhrec.com/pages/combos/")) {
+ qInfo() << "Received combos";
+ processCommanderResponse(jsonObj);
+ } else if (responseUrl.startsWith("https://json.edhrec.com/pages/average-decks/")) {
+ processAverageDeckResponse(jsonObj);
+ } else {
+ prettyPrintJson(jsonObj, 4);
+ }
+
+ reply->deleteLater();
+}
+
+void TabEdhRecMain::processTopCardsResponse(QJsonObject reply)
+{
+ EdhrecTopCardsApiResponse deckData;
+ deckData.fromJson(reply);
+
+ // **Remove previous page display to prevent stacking**
+ if (currentPageDisplay) {
+ mainLayout->removeWidget(currentPageDisplay);
+ delete currentPageDisplay;
+ currentPageDisplay = nullptr;
+ }
+
+ // **Create new currentPageDisplay**
+ currentPageDisplay = new QWidget(container);
+ currentPageLayout = new QVBoxLayout(currentPageDisplay);
+ currentPageDisplay->setLayout(currentPageLayout);
+
+ auto display = new EdhrecTopCardsApiResponseDisplayWidget(currentPageDisplay, deckData);
+ currentPageLayout->addWidget(display);
+
+ mainLayout->addWidget(currentPageDisplay);
+
+ // **Ensure layout stays correct**
+ mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
+ mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
+}
+
+void TabEdhRecMain::processTopTagsResponse(QJsonObject reply)
+{
+ EdhrecTopTagsApiResponse deckData;
+ deckData.fromJson(reply);
+
+ // **Remove previous page display to prevent stacking**
+ if (currentPageDisplay) {
+ mainLayout->removeWidget(currentPageDisplay);
+ delete currentPageDisplay;
+ currentPageDisplay = nullptr;
+ }
+
+ // **Create new currentPageDisplay**
+ currentPageDisplay = new QWidget(container);
+ currentPageLayout = new QVBoxLayout(currentPageDisplay);
+ currentPageDisplay->setLayout(currentPageLayout);
+
+ auto display = new EdhrecTopTagsApiResponseDisplayWidget(currentPageDisplay, deckData);
+ currentPageLayout->addWidget(display);
+
+ mainLayout->addWidget(currentPageDisplay);
+
+ // **Ensure layout stays correct**
+ mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
+ mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
+}
+
+void TabEdhRecMain::processTopCommandersResponse(QJsonObject reply)
+{
+ EdhrecTopCommandersApiResponse deckData;
+ deckData.fromJson(reply);
+
+ // **Remove previous page display to prevent stacking**
+ if (currentPageDisplay) {
+ mainLayout->removeWidget(currentPageDisplay);
+ delete currentPageDisplay;
+ currentPageDisplay = nullptr;
+ }
+
+ // **Create new currentPageDisplay**
+ currentPageDisplay = new QWidget(container);
+ currentPageLayout = new QVBoxLayout(currentPageDisplay);
+ currentPageDisplay->setLayout(currentPageLayout);
+
+ auto display = new EdhrecTopCommandersApiResponseDisplayWidget(currentPageDisplay, deckData);
+ currentPageLayout->addWidget(display);
+
+ mainLayout->addWidget(currentPageDisplay);
+
+ // **Ensure layout stays correct**
+ mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
+ mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
+}
+
+void TabEdhRecMain::processCommanderResponse(QJsonObject reply, QString responseUrl)
+{
+ EdhrecCommanderApiResponse deckData;
+ deckData.fromJson(reply);
+
+ // **Remove previous page display to prevent stacking**
+ if (currentPageDisplay) {
+ mainLayout->removeWidget(currentPageDisplay);
+ delete currentPageDisplay;
+ currentPageDisplay = nullptr;
+ }
+
+ // **Create new currentPageDisplay**
+ currentPageDisplay = new QWidget(container);
+ currentPageLayout = new QVBoxLayout(currentPageDisplay);
+ currentPageDisplay->setLayout(currentPageLayout);
+
+ auto display = new EdhrecCommanderApiResponseDisplayWidget(currentPageDisplay, deckData, responseUrl);
+ currentPageLayout->addWidget(display);
+
+ mainLayout->addWidget(currentPageDisplay);
+
+ // **Ensure layout stays correct**
+ mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
+ mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
+}
+
+void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply)
+{
+ EdhrecAverageDeckApiResponse deckData;
+ deckData.fromJson(reply);
+ tabSupervisor->addVisualDeckEditorTab(deckData.deck.deckLoader);
+}
+
+void TabEdhRecMain::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_main.h b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.h
new file mode 100644
index 000000000..1c6c67e5c
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.h
@@ -0,0 +1,60 @@
+#ifndef TAB_EDHREC_MAIN_H
+#define TAB_EDHREC_MAIN_H
+
+#include "../../../../game/cards/card_database.h"
+#include "../../../ui/widgets/general/layout_containers/flow_widget.h"
+#include "../../tab.h"
+#include "display/commander/edhrec_commander_api_response_display_widget.h"
+
+#include
+#include
+#include
+#include
+
+class TabEdhRecMain : public Tab
+{
+ Q_OBJECT
+public:
+ explicit TabEdhRecMain(TabSupervisor *_tabSupervisor);
+
+ void retranslateUi() override;
+ void doSearch();
+ QString getTabText() const override
+ {
+ auto cardName = cardToQuery.isNull() ? QString() : cardToQuery->getName();
+ return tr("EDHREC: ") + cardName;
+ }
+
+ QNetworkAccessManager *networkManager;
+
+public slots:
+ void processApiJson(QNetworkReply *reply);
+ void processCommanderResponse(QJsonObject reply, QString responseUrl = "");
+ void processTopCardsResponse(QJsonObject reply);
+ void processTopTagsResponse(QJsonObject reply);
+ void processTopCommandersResponse(QJsonObject reply);
+ void processAverageDeckResponse(QJsonObject reply);
+ void prettyPrintJson(const QJsonValue &value, int indentLevel);
+ void setCard(CardInfoPtr _cardToQuery, bool isCommander = false);
+ void actNavigatePage(QString url);
+ void getTopCards();
+ void getTopCommanders();
+ void getTopTags();
+
+private:
+ QWidget *container;
+ QWidget *navigationContainer;
+ QWidget *currentPageDisplay;
+ QVBoxLayout *mainLayout;
+ QHBoxLayout *navigationLayout;
+ QVBoxLayout *currentPageLayout;
+ QPushButton *cardsPushButton;
+ QPushButton *topCommandersPushButton;
+ QPushButton *tagsPushButton;
+ QLineEdit *searchBar;
+ QPushButton *searchPushButton;
+ CardInfoPtr cardToQuery;
+ EdhrecCommanderApiResponseDisplayWidget *displayWidget;
+};
+
+#endif // TAB_EDHREC_MAIN_H
diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp
index dbe38a215..1179c5a92 100644
--- a/cockatrice/src/client/tabs/tab_deck_editor.cpp
+++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp
@@ -55,7 +55,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor) : AbstractTabDeckEdi
void TabDeckEditor::createMenus()
{
- deckMenu = new DeckEditorMenu(this, this);
+ deckMenu = new DeckEditorMenu(this);
addTabMenu(deckMenu);
viewMenu = new QMenu(this);
@@ -67,36 +67,36 @@ void TabDeckEditor::createMenus()
aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
aCardInfoDockVisible->setCheckable(true);
- connect(aCardInfoDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aCardInfoDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
aCardInfoDockFloating = cardInfoDockMenu->addAction(QString());
aCardInfoDockFloating->setCheckable(true);
- connect(aCardInfoDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aCardInfoDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
aDeckDockVisible = deckDockMenu->addAction(QString());
aDeckDockVisible->setCheckable(true);
- connect(aDeckDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aDeckDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
aDeckDockFloating = deckDockMenu->addAction(QString());
aDeckDockFloating->setCheckable(true);
- connect(aDeckDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aDeckDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
aFilterDockVisible = filterDockMenu->addAction(QString());
aFilterDockVisible->setCheckable(true);
- connect(aFilterDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aFilterDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
aFilterDockFloating = filterDockMenu->addAction(QString());
aFilterDockFloating->setCheckable(true);
- connect(aFilterDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aFilterDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
aPrintingSelectorDockVisible = printingSelectorDockMenu->addAction(QString());
aPrintingSelectorDockVisible->setCheckable(true);
- connect(aPrintingSelectorDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aPrintingSelectorDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
aPrintingSelectorDockFloating = printingSelectorDockMenu->addAction(QString());
aPrintingSelectorDockFloating->setCheckable(true);
- connect(aPrintingSelectorDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aPrintingSelectorDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
viewMenu->addSeparator();
aResetLayout = viewMenu->addAction(QString());
- connect(aResetLayout, SIGNAL(triggered()), this, SLOT(restartLayout()));
+ connect(aResetLayout, &QAction::triggered, this, &TabDeckEditor::restartLayout);
viewMenu->addAction(aResetLayout);
deckMenu->setSaveStatus(false);
@@ -200,7 +200,7 @@ void TabDeckEditor::loadLayout()
databaseDisplayDockWidget->setMinimumSize(100, 100);
databaseDisplayDockWidget->setMaximumSize(1400, 5000);
- QTimer::singleShot(100, this, SLOT(freeDocksSize()));
+ QTimer::singleShot(100, this, &TabDeckEditor::freeDocksSize);
}
void TabDeckEditor::restartLayout()
@@ -236,7 +236,7 @@ void TabDeckEditor::restartLayout()
splitDockWidget(cardInfoDockWidget, printingSelectorDockWidget, Qt::Horizontal);
splitDockWidget(cardInfoDockWidget, filterDockWidget, Qt::Vertical);
- QTimer::singleShot(100, this, SLOT(freeDocksSize()));
+ QTimer::singleShot(100, this, &TabDeckEditor::freeDocksSize);
}
void TabDeckEditor::freeDocksSize()
diff --git a/cockatrice/src/client/tabs/tab_deck_editor.h b/cockatrice/src/client/tabs/tab_deck_editor.h
index d9b10de7d..710689b8e 100644
--- a/cockatrice/src/client/tabs/tab_deck_editor.h
+++ b/cockatrice/src/client/tabs/tab_deck_editor.h
@@ -1,7 +1,7 @@
#ifndef WINDOW_DECKEDITOR_H
#define WINDOW_DECKEDITOR_H
-#include "../../game/cards/card_database.h"
+#include "../../game/cards/card_info.h"
#include "../game_logic/key_signals.h"
#include "../ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include "abstract_tab_deck_editor.h"
diff --git a/cockatrice/src/client/tabs/tab_deck_storage.cpp b/cockatrice/src/client/tabs/tab_deck_storage.cpp
index f696f607c..426431c68 100644
--- a/cockatrice/src/client/tabs/tab_deck_storage.cpp
+++ b/cockatrice/src/client/tabs/tab_deck_storage.cpp
@@ -105,19 +105,19 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
// Left side actions
aOpenLocalDeck = new QAction(this);
aOpenLocalDeck->setIcon(QPixmap("theme:icons/pencil"));
- connect(aOpenLocalDeck, SIGNAL(triggered()), this, SLOT(actOpenLocalDeck()));
+ connect(aOpenLocalDeck, &QAction::triggered, this, &TabDeckStorage::actOpenLocalDeck);
aRenameLocal = new QAction(this);
aRenameLocal->setIcon(QPixmap("theme:icons/rename"));
connect(aRenameLocal, &QAction::triggered, this, &TabDeckStorage::actRenameLocal);
aUpload = new QAction(this);
aUpload->setIcon(QPixmap("theme:icons/arrow_right_green"));
- connect(aUpload, SIGNAL(triggered()), this, SLOT(actUpload()));
+ connect(aUpload, &QAction::triggered, this, &TabDeckStorage::actUpload);
aNewLocalFolder = new QAction(this);
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
connect(aNewLocalFolder, &QAction::triggered, this, &TabDeckStorage::actNewLocalFolder);
aDeleteLocalDeck = new QAction(this);
aDeleteLocalDeck->setIcon(QPixmap("theme:icons/remove_row"));
- connect(aDeleteLocalDeck, SIGNAL(triggered()), this, SLOT(actDeleteLocalDeck()));
+ connect(aDeleteLocalDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteLocalDeck);
aOpenDecksFolder = new QAction(this);
aOpenDecksFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_DirOpenIcon));
@@ -126,16 +126,16 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
// Right side actions
aOpenRemoteDeck = new QAction(this);
aOpenRemoteDeck->setIcon(QPixmap("theme:icons/pencil"));
- connect(aOpenRemoteDeck, SIGNAL(triggered()), this, SLOT(actOpenRemoteDeck()));
+ connect(aOpenRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actOpenRemoteDeck);
aDownload = new QAction(this);
aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
- connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload()));
+ connect(aDownload, &QAction::triggered, this, &TabDeckStorage::actDownload);
aNewFolder = new QAction(this);
aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
- connect(aNewFolder, SIGNAL(triggered()), this, SLOT(actNewFolder()));
+ connect(aNewFolder, &QAction::triggered, this, &TabDeckStorage::actNewFolder);
aDeleteRemoteDeck = new QAction(this);
aDeleteRemoteDeck->setIcon(QPixmap("theme:icons/remove_row"));
- connect(aDeleteRemoteDeck, SIGNAL(triggered()), this, SLOT(actDeleteRemoteDeck()));
+ connect(aDeleteRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteRemoteDeck);
// Add actions to toolbars
leftToolBar->addAction(aOpenLocalDeck);
@@ -339,8 +339,7 @@ void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPa
cmd.set_deck_list(deckString.toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(uploadFinished(Response, CommandContainer)));
+ connect(pend, &PendingCommand::finished, this, &TabDeckStorage::uploadFinished);
client->sendCommand(pend);
}
@@ -421,8 +420,7 @@ void TabDeckStorage::actOpenRemoteDeck()
cmd.set_deck_id(node->getId());
PendingCommand *pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(openRemoteDeckFinished(Response, CommandContainer)));
+ connect(pend, &PendingCommand::finished, this, &TabDeckStorage::openRemoteDeckFinished);
client->sendCommand(pend);
}
}
@@ -479,8 +477,7 @@ void TabDeckStorage::downloadNodeAtIndex(const QModelIndex &curLeft, const QMode
PendingCommand *pend = client->prepareSessionCommand(cmd);
pend->setExtraData(filePath);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(downloadFinished(Response, CommandContainer, QVariant)));
+ connect(pend, &PendingCommand::finished, this, &TabDeckStorage::downloadFinished);
client->sendCommand(pend);
}
// node at index is invalid
@@ -522,8 +519,7 @@ void TabDeckStorage::actNewFolder()
cmd.set_dir_name(folder);
PendingCommand *pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(newFolderFinished(Response, CommandContainer)));
+ connect(pend, &PendingCommand::finished, this, &TabDeckStorage::newFolderFinished);
client->sendCommand(pend);
}
@@ -573,15 +569,13 @@ void TabDeckStorage::deleteRemoteDeck(const RemoteDeckList_TreeModel::Node *curR
Command_DeckDelDir cmd;
cmd.set_path(targetPath.toStdString());
pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(deleteFolderFinished(Response, CommandContainer)));
+ connect(pend, &PendingCommand::finished, this, &TabDeckStorage::deleteFolderFinished);
} else {
const auto *deckNode = dynamic_cast(curRight);
Command_DeckDel cmd;
cmd.set_deck_id(deckNode->getId());
pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(deleteDeckFinished(Response, CommandContainer)));
+ connect(pend, &PendingCommand::finished, this, &TabDeckStorage::deleteDeckFinished);
}
client->sendCommand(pend);
diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp
index 8c018f644..e01153d00 100644
--- a/cockatrice/src/client/tabs/tab_game.cpp
+++ b/cockatrice/src/client/tabs/tab_game.cpp
@@ -115,11 +115,12 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay)
createReplayMenuItems();
createViewMenuItems();
retranslateUi();
- connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
+ connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
+ &TabGame::refreshShortcuts);
refreshShortcuts();
messageLog->logReplayStarted(gameInfo.game_id());
- QTimer::singleShot(0, this, SLOT(loadLayout()));
+ QTimer::singleShot(0, this, &TabGame::loadLayout);
}
TabGame::TabGame(TabSupervisor *_tabSupervisor,
@@ -156,14 +157,15 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor,
createMenuItems();
createViewMenuItems();
retranslateUi();
- connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
+ connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
+ &TabGame::refreshShortcuts);
refreshShortcuts();
// append game to rooms game list for others to see
for (int i = gameInfo.game_types_size() - 1; i >= 0; i--)
gameTypes.append(roomGameTypes.find(gameInfo.game_types(i)).value());
- QTimer::singleShot(0, this, SLOT(loadLayout()));
+ QTimer::singleShot(0, this, &TabGame::loadLayout);
}
void TabGame::addMentionTag(const QString &value)
@@ -628,7 +630,7 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info)
}
scene->addPlayer(newPlayer);
- connect(newPlayer, SIGNAL(newCardAdded(AbstractCardItem *)), this, SLOT(newCardAdded(AbstractCardItem *)));
+ connect(newPlayer, &Player::newCardAdded, this, &TabGame::newCardAdded);
messageLog->connectToPlayer(newPlayer);
if (local && !spectator) {
@@ -636,7 +638,7 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info)
newPlayer->setShortcutsActive();
auto *deckView = new DeckViewContainer(playerId, this);
- connect(deckView, SIGNAL(newCardAdded(AbstractCardItem *)), this, SLOT(newCardAdded(AbstractCardItem *)));
+ connect(deckView, &DeckViewContainer::newCardAdded, this, &TabGame::newCardAdded);
deckViewContainers.insert(playerId, deckView);
deckViewContainerLayout->addWidget(deckView);
@@ -784,8 +786,7 @@ void TabGame::sendGameCommand(PendingCommand *pend, int playerId)
if (!client)
return;
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(commandFinished(const Response &)));
+ connect(pend, &PendingCommand::finished, this, &TabGame::commandFinished);
client->sendCommand(pend);
}
@@ -796,8 +797,7 @@ void TabGame::sendGameCommand(const google::protobuf::Message &command, int play
return;
PendingCommand *pend = prepareGameCommand(command);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(commandFinished(const Response &)));
+ connect(pend, &PendingCommand::finished, this, &TabGame::commandFinished);
client->sendCommand(pend);
}
@@ -1198,10 +1198,11 @@ void TabGame::eventSetActivePhase(const Event_SetActivePhase &event,
void TabGame::newCardAdded(AbstractCardItem *card)
{
- connect(card, SIGNAL(hovered(AbstractCardItem *)), cardInfoFrameWidget, SLOT(setCard(AbstractCardItem *)));
+ connect(card, &AbstractCardItem::hovered, cardInfoFrameWidget,
+ qOverload(&CardInfoFrameWidget::setCard));
connect(card, &AbstractCardItem::showCardInfoPopup, this, &TabGame::showCardInfoPopup);
connect(card, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
- connect(card, SIGNAL(cardShiftClicked(QString)), this, SLOT(linkCardToChat(QString)));
+ connect(card, &AbstractCardItem::cardShiftClicked, this, &TabGame::linkCardToChat);
}
CardItem *TabGame::getCard(int playerId, const QString &zoneName, int cardId) const
@@ -1289,34 +1290,34 @@ void TabGame::updateCardMenu(AbstractCardItem *card)
void TabGame::createMenuItems()
{
aNextPhase = new QAction(this);
- connect(aNextPhase, SIGNAL(triggered()), this, SLOT(actNextPhase()));
+ connect(aNextPhase, &QAction::triggered, this, &TabGame::actNextPhase);
aNextPhaseAction = new QAction(this);
- connect(aNextPhaseAction, SIGNAL(triggered()), this, SLOT(actNextPhaseAction()));
+ connect(aNextPhaseAction, &QAction::triggered, this, &TabGame::actNextPhaseAction);
aNextTurn = new QAction(this);
- connect(aNextTurn, SIGNAL(triggered()), this, SLOT(actNextTurn()));
+ connect(aNextTurn, &QAction::triggered, this, &TabGame::actNextTurn);
aReverseTurn = new QAction(this);
- connect(aReverseTurn, SIGNAL(triggered()), this, SLOT(actReverseTurn()));
+ connect(aReverseTurn, &QAction::triggered, this, &TabGame::actReverseTurn);
aRemoveLocalArrows = new QAction(this);
- connect(aRemoveLocalArrows, SIGNAL(triggered()), this, SLOT(actRemoveLocalArrows()));
+ connect(aRemoveLocalArrows, &QAction::triggered, this, &TabGame::actRemoveLocalArrows);
aRotateViewCW = new QAction(this);
- connect(aRotateViewCW, SIGNAL(triggered()), this, SLOT(actRotateViewCW()));
+ connect(aRotateViewCW, &QAction::triggered, this, &TabGame::actRotateViewCW);
aRotateViewCCW = new QAction(this);
- connect(aRotateViewCCW, SIGNAL(triggered()), this, SLOT(actRotateViewCCW()));
+ connect(aRotateViewCCW, &QAction::triggered, this, &TabGame::actRotateViewCCW);
aGameInfo = new QAction(this);
- connect(aGameInfo, SIGNAL(triggered()), this, SLOT(actGameInfo()));
+ connect(aGameInfo, &QAction::triggered, this, &TabGame::actGameInfo);
aConcede = new QAction(this);
- connect(aConcede, SIGNAL(triggered()), this, SLOT(actConcede()));
+ connect(aConcede, &QAction::triggered, this, &TabGame::actConcede);
aLeaveGame = new QAction(this);
connect(aLeaveGame, &QAction::triggered, this, [this] { closeRequest(); });
aFocusChat = new QAction(this);
- connect(aFocusChat, SIGNAL(triggered()), sayEdit, SLOT(setFocus()));
+ connect(aFocusChat, &QAction::triggered, sayEdit, qOverload<>(&LineEditCompleter::setFocus));
aCloseReplay = nullptr;
phasesMenu = new TearOffMenu(this);
for (int i = 0; i < phasesToolbar->phaseCount(); ++i) {
QAction *temp = new QAction(QString(), this);
- connect(temp, SIGNAL(triggered()), this, SLOT(actPhaseAction()));
+ connect(temp, &QAction::triggered, this, &TabGame::actPhaseAction);
phasesMenu->addAction(temp);
phaseActions.append(temp);
}
@@ -1375,40 +1376,40 @@ void TabGame::createViewMenuItems()
aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
aCardInfoDockVisible->setCheckable(true);
- connect(aCardInfoDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aCardInfoDockVisible, &QAction::triggered, this, &TabGame::dockVisibleTriggered);
aCardInfoDockFloating = cardInfoDockMenu->addAction(QString());
aCardInfoDockFloating->setCheckable(true);
- connect(aCardInfoDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aCardInfoDockFloating, &QAction::triggered, this, &TabGame::dockFloatingTriggered);
aMessageLayoutDockVisible = messageLayoutDockMenu->addAction(QString());
aMessageLayoutDockVisible->setCheckable(true);
- connect(aMessageLayoutDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aMessageLayoutDockVisible, &QAction::triggered, this, &TabGame::dockVisibleTriggered);
aMessageLayoutDockFloating = messageLayoutDockMenu->addAction(QString());
aMessageLayoutDockFloating->setCheckable(true);
- connect(aMessageLayoutDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aMessageLayoutDockFloating, &QAction::triggered, this, &TabGame::dockFloatingTriggered);
aPlayerListDockVisible = playerListDockMenu->addAction(QString());
aPlayerListDockVisible->setCheckable(true);
- connect(aPlayerListDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aPlayerListDockVisible, &QAction::triggered, this, &TabGame::dockVisibleTriggered);
aPlayerListDockFloating = playerListDockMenu->addAction(QString());
aPlayerListDockFloating->setCheckable(true);
- connect(aPlayerListDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aPlayerListDockFloating, &QAction::triggered, this, &TabGame::dockFloatingTriggered);
if (replayDock) {
replayDockMenu = viewMenu->addMenu(QString());
aReplayDockVisible = replayDockMenu->addAction(QString());
aReplayDockVisible->setCheckable(true);
- connect(aReplayDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ connect(aReplayDockVisible, &QAction::triggered, this, &TabGame::dockVisibleTriggered);
aReplayDockFloating = replayDockMenu->addAction(QString());
aReplayDockFloating->setCheckable(true);
- connect(aReplayDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+ connect(aReplayDockFloating, &QAction::triggered, this, &TabGame::dockFloatingTriggered);
}
viewMenu->addSeparator();
aResetLayout = viewMenu->addAction(QString());
- connect(aResetLayout, SIGNAL(triggered()), this, SLOT(actResetLayout()));
+ connect(aResetLayout, &QAction::triggered, this, &TabGame::actResetLayout);
viewMenu->addAction(aResetLayout);
addTabMenu(viewMenu);
@@ -1459,7 +1460,7 @@ void TabGame::loadLayout()
aReplayDockFloating->setChecked(replayDock->isFloating());
}
- QTimer::singleShot(100, this, SLOT(freeDocksSize()));
+ QTimer::singleShot(100, this, &TabGame::freeDocksSize);
}
void TabGame::freeDocksSize()
@@ -1525,15 +1526,15 @@ void TabGame::actResetLayout()
playerListDock->setMaximumSize(250, 50);
}
- QTimer::singleShot(100, this, SLOT(freeDocksSize()));
+ QTimer::singleShot(100, this, &TabGame::freeDocksSize);
}
void TabGame::createPlayAreaWidget(bool bReplay)
{
phasesToolbar = new PhasesToolbar;
if (!bReplay)
- connect(phasesToolbar, SIGNAL(sendGameCommand(const ::google::protobuf::Message &, int)), this,
- SLOT(sendGameCommand(const ::google::protobuf::Message &, int)));
+ connect(phasesToolbar, &PhasesToolbar::sendGameCommand, this,
+ qOverload(&TabGame::sendGameCommand));
scene = new GameScene(phasesToolbar, this);
gameView = new GameView(scene);
@@ -1551,9 +1552,8 @@ void TabGame::createReplayDock()
// timeline widget
timelineWidget = new ReplayTimelineWidget;
timelineWidget->setTimeline(replayTimeline);
- connect(timelineWidget, SIGNAL(processNextEvent(Player::EventProcessingOptions)), this,
- SLOT(replayNextEvent(Player::EventProcessingOptions)));
- connect(timelineWidget, SIGNAL(replayFinished()), this, SLOT(replayFinished()));
+ connect(timelineWidget, &ReplayTimelineWidget::processNextEvent, this, &TabGame::replayNextEvent);
+ connect(timelineWidget, &ReplayTimelineWidget::replayFinished, this, &TabGame::replayFinished);
connect(timelineWidget, &ReplayTimelineWidget::rewound, this, &TabGame::replayRewind);
// timeline skip shortcuts
@@ -1585,13 +1585,13 @@ void TabGame::createReplayDock()
playButtonIcon.addPixmap(QPixmap("theme:replay/pause"), QIcon::Normal, QIcon::On);
replayPlayButton->setIcon(playButtonIcon);
replayPlayButton->setCheckable(true);
- connect(replayPlayButton, SIGNAL(toggled(bool)), this, SLOT(replayPlayButtonToggled(bool)));
+ connect(replayPlayButton, &QToolButton::toggled, this, &TabGame::replayPlayButtonToggled);
replayFastForwardButton = new QToolButton;
replayFastForwardButton->setIconSize(QSize(32, 32));
replayFastForwardButton->setIcon(QPixmap("theme:replay/fastforward"));
replayFastForwardButton->setCheckable(true);
- connect(replayFastForwardButton, SIGNAL(toggled(bool)), this, SLOT(replayFastForwardButtonToggled(bool)));
+ connect(replayFastForwardButton, &QToolButton::toggled, this, &TabGame::replayFastForwardButtonToggled);
// putting everything together
replayControlLayout = new QHBoxLayout;
@@ -1611,7 +1611,7 @@ void TabGame::createReplayDock()
replayDock->setFloating(false);
replayDock->installEventFilter(this);
- connect(replayDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
+ connect(replayDock, &QDockWidget::topLevelChanged, this, &TabGame::dockTopLevelChanged);
}
void TabGame::createDeckViewContainerWidget(bool bReplay)
@@ -1652,7 +1652,7 @@ void TabGame::createCardInfoDock(bool bReplay)
cardInfoDock->setFloating(false);
cardInfoDock->installEventFilter(this);
- connect(cardInfoDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
+ connect(cardInfoDock, &QDockWidget::topLevelChanged, this, &TabGame::dockTopLevelChanged);
}
void TabGame::createPlayerListDock(bool bReplay)
@@ -1674,26 +1674,28 @@ void TabGame::createPlayerListDock(bool bReplay)
playerListDock->setFloating(false);
playerListDock->installEventFilter(this);
- connect(playerListDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
+ connect(playerListDock, &QDockWidget::topLevelChanged, this, &TabGame::dockTopLevelChanged);
}
void TabGame::createMessageDock(bool bReplay)
{
messageLog = new MessageLogWidget(tabSupervisor, this);
- connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfoFrameWidget, SLOT(setCard(QString)));
+ connect(messageLog, &MessageLogWidget::cardNameHovered, cardInfoFrameWidget,
+ qOverload(&CardInfoFrameWidget::setCard));
connect(messageLog, &MessageLogWidget::showCardInfoPopup, this, &TabGame::showCardInfoPopup);
- connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
+ connect(messageLog, &MessageLogWidget::deleteCardInfoPopup, this, &TabGame::deleteCardInfoPopup);
if (!bReplay) {
- connect(messageLog, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
- connect(messageLog, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
- connect(&SettingsCache::instance(), SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged()));
+ connect(messageLog, &MessageLogWidget::openMessageDialog, this, &TabGame::openMessageDialog);
+ connect(messageLog, &MessageLogWidget::addMentionTag, this, &TabGame::addMentionTag);
+ connect(&SettingsCache::instance(), &SettingsCache::chatMentionCompleterChanged, this,
+ &TabGame::actCompleterChanged);
timeElapsedLabel = new QLabel;
timeElapsedLabel->setAlignment(Qt::AlignCenter);
gameTimer = new QTimer(this);
gameTimer->setInterval(1000);
- connect(gameTimer, SIGNAL(timeout()), this, SLOT(incrementGameTime()));
+ connect(gameTimer, &QTimer::timeout, this, &TabGame::incrementGameTime);
gameTimer->start();
sayLabel = new QLabel;
@@ -1721,8 +1723,8 @@ void TabGame::createMessageDock(bool bReplay)
}
}
- connect(tabSupervisor, SIGNAL(adminLockChanged(bool)), this, SLOT(adminLockChanged(bool)));
- connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(actSay()));
+ connect(tabSupervisor, &TabSupervisor::adminLockChanged, this, &TabGame::adminLockChanged);
+ connect(sayEdit, &LineEditCompleter::returnPressed, this, &TabGame::actSay);
sayHLayout = new QHBoxLayout;
sayHLayout->addWidget(sayLabel);
@@ -1748,7 +1750,7 @@ void TabGame::createMessageDock(bool bReplay)
messageLayoutDock->setFloating(false);
messageLayoutDock->installEventFilter(this);
- connect(messageLayoutDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
+ connect(messageLayoutDock, &QDockWidget::topLevelChanged, this, &TabGame::dockTopLevelChanged);
}
void TabGame::hideEvent(QHideEvent *event)
diff --git a/cockatrice/src/client/tabs/tab_logs.cpp b/cockatrice/src/client/tabs/tab_logs.cpp
index e43be2215..0f75b4c74 100644
--- a/cockatrice/src/client/tabs/tab_logs.cpp
+++ b/cockatrice/src/client/tabs/tab_logs.cpp
@@ -116,8 +116,7 @@ void TabLog::getClicked()
cmd.set_date_range(dateRange);
cmd.set_maximum_results(maximumResults->value());
PendingCommand *pend = client->prepareModeratorCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(viewLogHistory_processResponse(Response)));
+ connect(pend, &PendingCommand::finished, this, &TabLog::viewLogHistory_processResponse);
client->sendCommand(pend);
}
@@ -186,11 +185,11 @@ void TabLog::createDock()
getButton = new QPushButton(tr("Get User Logs"));
getButton->setAutoDefault(true);
- connect(getButton, SIGNAL(clicked()), this, SLOT(getClicked()));
+ connect(getButton, &QPushButton::clicked, this, &TabLog::getClicked);
clearButton = new QPushButton(tr("Clear Filters"));
clearButton->setAutoDefault(true);
- connect(clearButton, SIGNAL(clicked()), this, SLOT(clearClicked()));
+ connect(clearButton, &QPushButton::clicked, this, &TabLog::clearClicked);
criteriaGrid = new QGridLayout;
criteriaGrid->addWidget(labelFindUserName, 0, 0);
diff --git a/cockatrice/src/client/tabs/tab_message.cpp b/cockatrice/src/client/tabs/tab_message.cpp
index 78950e5be..9dc53156d 100644
--- a/cockatrice/src/client/tabs/tab_message.cpp
+++ b/cockatrice/src/client/tabs/tab_message.cpp
@@ -28,11 +28,11 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor,
{
chatView = new ChatView(tabSupervisor, 0, true);
connect(chatView, &ChatView::showCardInfoPopup, this, &TabMessage::showCardInfoPopup);
- connect(chatView, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
- connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
+ connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabMessage::deleteCardInfoPopup);
+ connect(chatView, &ChatView::addMentionTag, this, &TabMessage::addMentionTag);
sayEdit = new LineEditUnfocusable;
sayEdit->setMaxLength(MAX_TEXT_LENGTH);
- connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage()));
+ connect(sayEdit, &LineEditUnfocusable::returnPressed, this, &TabMessage::sendMessage);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(chatView);
@@ -102,7 +102,7 @@ void TabMessage::sendMessage()
cmd.set_message(sayEdit->text().toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(messageSent(const Response &)));
+ connect(pend, &PendingCommand::finished, this, &TabMessage::messageSent);
client->sendCommand(pend);
sayEdit->clear();
@@ -140,10 +140,10 @@ bool TabMessage::shouldShowSystemPopup(const Event_UserMessage &event)
void TabMessage::showSystemPopup(const Event_UserMessage &event)
{
if (trayIcon) {
- disconnect(trayIcon, SIGNAL(messageClicked()), 0, 0);
+ disconnect(trayIcon, &QSystemTrayIcon::messageClicked, 0, 0);
trayIcon->showMessage(tr("Private message from") + " " + otherUserInfo->name().c_str(),
event.message().c_str());
- connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked()));
+ connect(trayIcon, &QSystemTrayIcon::messageClicked, this, &TabMessage::messageClicked);
} else {
qCWarning(TabMessageLog) << "Error: trayIcon is NULL. TabMessage::showSystemPopup failed";
}
diff --git a/cockatrice/src/client/tabs/tab_replays.cpp b/cockatrice/src/client/tabs/tab_replays.cpp
index 8081883cd..123c3d3fc 100644
--- a/cockatrice/src/client/tabs/tab_replays.cpp
+++ b/cockatrice/src/client/tabs/tab_replays.cpp
@@ -96,8 +96,8 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, c
// Left side actions
aOpenLocalReplay = new QAction(this);
aOpenLocalReplay->setIcon(QPixmap("theme:icons/view"));
- connect(aOpenLocalReplay, SIGNAL(triggered()), this, SLOT(actOpenLocalReplay()));
- connect(localDirView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actOpenLocalReplay()));
+ connect(aOpenLocalReplay, &QAction::triggered, this, &TabReplays::actOpenLocalReplay);
+ connect(localDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenLocalReplay);
aRenameLocal = new QAction(this);
aRenameLocal->setIcon(QPixmap("theme:icons/rename"));
connect(aRenameLocal, &QAction::triggered, this, &TabReplays::actRenameLocal);
@@ -106,7 +106,7 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, c
connect(aNewLocalFolder, &QAction::triggered, this, &TabReplays::actNewLocalFolder);
aDeleteLocalReplay = new QAction(this);
aDeleteLocalReplay->setIcon(QPixmap("theme:icons/remove_row"));
- connect(aDeleteLocalReplay, SIGNAL(triggered()), this, SLOT(actDeleteLocalReplay()));
+ connect(aDeleteLocalReplay, &QAction::triggered, this, &TabReplays::actDeleteLocalReplay);
aOpenReplaysFolder = new QAction(this);
aOpenReplaysFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_DirOpenIcon));
@@ -115,17 +115,17 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, c
// Right side actions
aOpenRemoteReplay = new QAction(this);
aOpenRemoteReplay->setIcon(QPixmap("theme:icons/view"));
- connect(aOpenRemoteReplay, SIGNAL(triggered()), this, SLOT(actOpenRemoteReplay()));
- connect(serverDirView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actOpenRemoteReplay()));
+ connect(aOpenRemoteReplay, &QAction::triggered, this, &TabReplays::actOpenRemoteReplay);
+ connect(serverDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenRemoteReplay);
aDownload = new QAction(this);
aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
- connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload()));
+ connect(aDownload, &QAction::triggered, this, &TabReplays::actDownload);
aKeep = new QAction(this);
aKeep->setIcon(QPixmap("theme:icons/lock"));
- connect(aKeep, SIGNAL(triggered()), this, SLOT(actKeepRemoteReplay()));
+ connect(aKeep, &QAction::triggered, this, &TabReplays::actKeepRemoteReplay);
aDeleteRemoteReplay = new QAction(this);
aDeleteRemoteReplay->setIcon(QPixmap("theme:icons/remove_row"));
- connect(aDeleteRemoteReplay, SIGNAL(triggered()), this, SLOT(actDeleteRemoteReplay()));
+ connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay);
// Add actions to toolbars
leftToolBar->addAction(aOpenLocalReplay);
@@ -146,8 +146,7 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, c
mainWidget->setLayout(hbox);
setCentralWidget(mainWidget);
- connect(client, SIGNAL(replayAddedEventReceived(const Event_ReplayAdded &)), this,
- SLOT(replayAddedEventReceived(const Event_ReplayAdded &)));
+ connect(client, &AbstractClient::replayAddedEventReceived, this, &TabReplays::replayAddedEventReceived);
connect(client, &AbstractClient::userInfoChanged, this, &TabReplays::handleConnected);
connect(client, &AbstractClient::statusChanged, this, &TabReplays::handleConnectionChanged);
@@ -323,8 +322,7 @@ void TabReplays::actOpenRemoteReplay()
cmd.set_replay_id(curRight->replay_id());
PendingCommand *pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(openRemoteReplayFinished(const Response &)));
+ connect(pend, &PendingCommand::finished, this, &TabReplays::openRemoteReplayFinished);
client->sendCommand(pend);
}
}
@@ -378,8 +376,7 @@ void TabReplays::downloadNodeAtIndex(const QModelIndex &curLeft, const QModelInd
PendingCommand *pend = client->prepareSessionCommand(cmd);
pend->setExtraData(filePath);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(downloadFinished(Response, CommandContainer, QVariant)));
+ connect(pend, &PendingCommand::finished, this, &TabReplays::downloadFinished);
client->sendCommand(pend);
}
// node at index was invalid
@@ -416,8 +413,7 @@ void TabReplays::actKeepRemoteReplay()
cmd.set_do_not_hide(!curRight->do_not_hide());
PendingCommand *pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(keepRemoteReplayFinished(Response, CommandContainer)));
+ connect(pend, &PendingCommand::finished, this, &TabReplays::keepRemoteReplayFinished);
client->sendCommand(pend);
}
}
@@ -455,8 +451,7 @@ void TabReplays::actDeleteRemoteReplay()
cmd.set_game_id(curRight->game_id());
PendingCommand *pend = client->prepareSessionCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(deleteRemoteReplayFinished(Response, CommandContainer)));
+ connect(pend, &PendingCommand::finished, this, &TabReplays::deleteRemoteReplayFinished);
client->sendCommand(pend);
}
}
diff --git a/cockatrice/src/client/tabs/tab_room.cpp b/cockatrice/src/client/tabs/tab_room.cpp
index 02243cd19..1eb648f48 100644
--- a/cockatrice/src/client/tabs/tab_room.cpp
+++ b/cockatrice/src/client/tabs/tab_room.cpp
@@ -53,28 +53,29 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
SIGNAL(openMessageDialog(const QString &, bool)));
chatView = new ChatView(tabSupervisor, nullptr, true, this);
- connect(chatView, SIGNAL(showMentionPopup(const QString &)), this, SLOT(actShowMentionPopup(const QString &)));
- connect(chatView, SIGNAL(messageClickedSignal()), this, SLOT(focusTab()));
- connect(chatView, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
+ connect(chatView, &ChatView::showMentionPopup, this, &TabRoom::actShowMentionPopup);
+ connect(chatView, &ChatView::messageClickedSignal, this, &TabRoom::focusTab);
+ connect(chatView, &ChatView::openMessageDialog, this, &TabRoom::openMessageDialog);
connect(chatView, &ChatView::showCardInfoPopup, this, &TabRoom::showCardInfoPopup);
- connect(chatView, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
- connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
- connect(&SettingsCache::instance(), SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged()));
+ connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabRoom::deleteCardInfoPopup);
+ connect(chatView, &ChatView::addMentionTag, this, &TabRoom::addMentionTag);
+ connect(&SettingsCache::instance(), &SettingsCache::chatMentionCompleterChanged, this,
+ &TabRoom::actCompleterChanged);
sayLabel = new QLabel;
sayEdit = new LineEditCompleter;
sayEdit->setMaxLength(MAX_TEXT_LENGTH);
sayLabel->setBuddy(sayEdit);
- connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage()));
+ connect(sayEdit, &LineEditCompleter::returnPressed, this, &TabRoom::sendMessage);
QMenu *chatSettingsMenu = new QMenu(this);
aClearChat = chatSettingsMenu->addAction(QString());
- connect(aClearChat, SIGNAL(triggered()), this, SLOT(actClearChat()));
+ connect(aClearChat, &QAction::triggered, this, &TabRoom::actClearChat);
chatSettingsMenu->addSeparator();
aOpenChatSettings = chatSettingsMenu->addAction(QString());
- connect(aOpenChatSettings, SIGNAL(triggered()), this, SLOT(actOpenChatSettings()));
+ connect(aOpenChatSettings, &QAction::triggered, this, &TabRoom::actOpenChatSettings);
QToolButton *chatSettingsButton = new QToolButton;
chatSettingsButton->setIcon(QPixmap("theme:icons/settings"));
@@ -126,7 +127,8 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
sayEdit->setCompleter(completer);
actCompleterChanged();
- connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
+ connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
+ &TabRoom::refreshShortcuts);
refreshShortcuts();
retranslateUi();
@@ -165,9 +167,9 @@ void TabRoom::actShowPopup(const QString &message)
{
if (trayIcon && (tabSupervisor->currentIndex() != tabSupervisor->indexOf(this) ||
QApplication::activeWindow() == nullptr || QApplication::focusWidget() == nullptr)) {
- disconnect(trayIcon, SIGNAL(messageClicked()), nullptr, nullptr);
+ disconnect(trayIcon, &QSystemTrayIcon::messageClicked, nullptr, nullptr);
trayIcon->showMessage(message, tr("Click to view"));
- connect(trayIcon, SIGNAL(messageClicked()), chatView, SLOT(actMessageClicked()));
+ connect(trayIcon, &QSystemTrayIcon::messageClicked, chatView, &ChatView::messageClickedSignal);
}
}
@@ -201,8 +203,7 @@ void TabRoom::sendMessage()
cmd.set_message(sayEdit->text().toStdString());
PendingCommand *pend = prepareRoomCommand(cmd);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(sayFinished(const Response &)));
+ connect(pend, &PendingCommand::finished, this, &TabRoom::sayFinished);
sendRoomCommand(pend);
sayEdit->clear();
}
diff --git a/cockatrice/src/client/tabs/tab_server.cpp b/cockatrice/src/client/tabs/tab_server.cpp
index 40fa401d2..24c3e7839 100644
--- a/cockatrice/src/client/tabs/tab_server.cpp
+++ b/cockatrice/src/client/tabs/tab_server.cpp
@@ -33,7 +33,7 @@ RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) : QGroupBox
roomList->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
joinButton = new QPushButton;
- connect(joinButton, SIGNAL(clicked()), this, SLOT(joinClicked()));
+ connect(joinButton, &QPushButton::clicked, this, &RoomSelector::joinClicked);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
buttonLayout->addWidget(joinButton);
@@ -44,9 +44,8 @@ RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) : QGroupBox
retranslateUi();
setLayout(vbox);
- connect(client, SIGNAL(listRoomsEventReceived(const Event_ListRooms &)), this,
- SLOT(processListRoomsEvent(const Event_ListRooms &)));
- connect(roomList, SIGNAL(activated(const QModelIndex &)), this, SLOT(joinClicked()));
+ connect(client, &AbstractClient::listRoomsEventReceived, this, &RoomSelector::processListRoomsEvent);
+ connect(roomList, &QTreeWidget::activated, this, &RoomSelector::joinClicked);
client->sendCommand(client->prepareSessionCommand(Command_ListRooms()));
}
@@ -144,10 +143,9 @@ TabServer::TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client) : T
serverInfoBox = new QTextBrowser;
serverInfoBox->setOpenExternalLinks(true);
- connect(roomSelector, SIGNAL(joinRoomRequest(int, bool)), this, SLOT(joinRoom(int, bool)));
+ connect(roomSelector, &RoomSelector::joinRoomRequest, this, &TabServer::joinRoom);
- connect(client, SIGNAL(serverMessageEventReceived(const Event_ServerMessage &)), this,
- SLOT(processServerMessageEvent(const Event_ServerMessage &)));
+ connect(client, &AbstractClient::serverMessageEventReceived, this, &TabServer::processServerMessageEvent);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(roomSelector);
@@ -185,8 +183,7 @@ void TabServer::joinRoom(int id, bool setCurrent)
PendingCommand *pend = client->prepareSessionCommand(cmd);
pend->setExtraData(setCurrent);
- connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
- SLOT(joinRoomFinished(Response, CommandContainer, QVariant)));
+ connect(pend, &PendingCommand::finished, this, &TabServer::joinRoomFinished);
client->sendCommand(pend);
diff --git a/cockatrice/src/client/tabs/tab_supervisor.cpp b/cockatrice/src/client/tabs/tab_supervisor.cpp
index e7be2a1ff..5d9600d11 100644
--- a/cockatrice/src/client/tabs/tab_supervisor.cpp
+++ b/cockatrice/src/client/tabs/tab_supervisor.cpp
@@ -6,6 +6,7 @@
#include "../../server/user/user_list_widget.h"
#include "../../settings/cache_settings.h"
#include "../ui/pixel_map_generator.h"
+#include "api/edhrec/tab_edhrec_main.h"
#include "pb/event_game_joined.pb.h"
#include "pb/event_notify_user.pb.h"
#include "pb/event_user_message.pb.h"
@@ -25,6 +26,9 @@
#include "tab_replays.h"
#include "tab_room.h"
#include "tab_server.h"
+#include "tab_visual_database_display.h"
+#include "visual_deck_editor/tab_deck_editor_visual.h"
+#include "visual_deck_editor/tab_deck_editor_visual_tab_widget.h"
#include "visual_deck_storage/tab_deck_storage_visual.h"
#include
@@ -130,10 +134,19 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
aTabDeckEditor = new QAction(this);
connect(aTabDeckEditor, &QAction::triggered, this, [this] { addDeckEditorTab(nullptr); });
+ aTabVisualDeckEditor = new QAction(this);
+ connect(aTabVisualDeckEditor, &QAction::triggered, this, [this] { addVisualDeckEditorTab(nullptr); });
+
+ aTabEdhRec = new QAction(this);
+ connect(aTabEdhRec, &QAction::triggered, this, [this] { addEdhrecMainTab(); });
+
aTabVisualDeckStorage = new QAction(this);
aTabVisualDeckStorage->setCheckable(true);
connect(aTabVisualDeckStorage, &QAction::triggered, this, &TabSupervisor::actTabVisualDeckStorage);
+ aTabVisualDatabaseDisplay = new QAction(this);
+ connect(aTabVisualDatabaseDisplay, &QAction::triggered, this, [this] { addVisualDatabaseDisplayTab(); });
+
aTabServer = new QAction(this);
aTabServer->setCheckable(true);
connect(aTabServer, &QAction::triggered, this, &TabSupervisor::actTabServer);
@@ -176,7 +189,10 @@ void TabSupervisor::retranslateUi()
{
// tab menu actions
aTabDeckEditor->setText(tr("Deck Editor"));
+ aTabVisualDeckEditor->setText(tr("Visual Deck Editor"));
+ aTabEdhRec->setText(tr("EDHRec"));
aTabVisualDeckStorage->setText(tr("&Visual Deck Storage"));
+ aTabVisualDatabaseDisplay->setText(tr("Visual Database Display"));
aTabServer->setText(tr("Server"));
aTabAccount->setText(tr("Account"));
aTabDeckStorage->setText(tr("Deck Storage"));
@@ -223,6 +239,7 @@ void TabSupervisor::refreshShortcuts()
{
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
aTabDeckEditor->setShortcuts(shortcuts.getShortcut("Tabs/aTabDeckEditor"));
+ aTabVisualDeckEditor->setShortcuts(shortcuts.getShortcut("Tabs/aTabVisualDeckEditor"));
aTabVisualDeckStorage->setShortcuts(shortcuts.getShortcut("Tabs/aTabVisualDeckStorage"));
aTabServer->setShortcuts(shortcuts.getShortcut("Tabs/aTabServer"));
aTabAccount->setShortcuts(shortcuts.getShortcut("Tabs/aTabAccount"));
@@ -370,8 +387,11 @@ void TabSupervisor::resetTabsMenu()
{
tabsMenu->clear();
tabsMenu->addAction(aTabDeckEditor);
+ tabsMenu->addAction(aTabVisualDeckEditor);
+ tabsMenu->addAction(aTabEdhRec);
tabsMenu->addSeparator();
tabsMenu->addAction(aTabVisualDeckStorage);
+ tabsMenu->addAction(aTabVisualDatabaseDisplay);
tabsMenu->addAction(aTabDeckStorage);
tabsMenu->addAction(aTabReplays);
}
@@ -804,6 +824,36 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(const DeckLoader *deckToOpen)
return tab;
}
+TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab(const DeckLoader *deckToOpen)
+{
+ auto *tab = new TabDeckEditorVisual(this);
+ if (deckToOpen)
+ tab->openDeck(new DeckLoader(*deckToOpen));
+ connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed);
+ connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addVisualDeckEditorTab);
+ myAddTab(tab);
+ deckEditorTabs.append(tab);
+ setCurrentWidget(tab);
+ return tab;
+}
+
+TabEdhRecMain *TabSupervisor::addEdhrecMainTab()
+{
+ auto *tab = new TabEdhRecMain(this);
+
+ myAddTab(tab);
+ setCurrentWidget(tab);
+ return tab;
+}
+
+TabVisualDatabaseDisplay *TabSupervisor::addVisualDatabaseDisplayTab()
+{
+ auto *tab = new TabVisualDatabaseDisplay(this);
+ myAddTab(tab);
+ setCurrentWidget(tab);
+ return tab;
+}
+
TabEdhRec *TabSupervisor::addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander)
{
auto *tab = new TabEdhRec(this);
diff --git a/cockatrice/src/client/tabs/tab_supervisor.h b/cockatrice/src/client/tabs/tab_supervisor.h
index c8ae109d8..0ca13ac41 100644
--- a/cockatrice/src/client/tabs/tab_supervisor.h
+++ b/cockatrice/src/client/tabs/tab_supervisor.h
@@ -5,6 +5,10 @@
#include "../../server/user/user_list_proxy.h"
#include "abstract_tab_deck_editor.h"
#include "api/edhrec/tab_edhrec.h"
+#include "api/edhrec/tab_edhrec_main.h"
+#include "tab_visual_database_display.h"
+#include "visual_deck_editor/tab_deck_editor_visual.h"
+#include "visual_deck_editor/tab_deck_editor_visual_tab_widget.h"
#include "visual_deck_storage/tab_deck_storage_visual.h"
#include
@@ -91,8 +95,8 @@ private:
QList deckEditorTabs;
bool isLocalGame;
- QAction *aTabDeckEditor, *aTabVisualDeckStorage, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays,
- *aTabAdmin, *aTabLog;
+ QAction *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabVisualDeckStorage, *aTabVisualDatabaseDisplay,
+ *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin, *aTabLog;
int myAddTab(Tab *tab, QAction *manager = nullptr);
void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager);
@@ -149,6 +153,9 @@ signals:
public slots:
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
+ TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen);
+ TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();
+ TabEdhRecMain *addEdhrecMainTab();
TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false);
void openReplay(GameReplay *replay);
void maximizeMainWindow();
diff --git a/cockatrice/src/client/tabs/tab_visual_database_display.cpp b/cockatrice/src/client/tabs/tab_visual_database_display.cpp
new file mode 100644
index 000000000..d45cdded0
--- /dev/null
+++ b/cockatrice/src/client/tabs/tab_visual_database_display.cpp
@@ -0,0 +1,20 @@
+#include "tab_visual_database_display.h"
+
+#include "tab_deck_editor.h"
+
+TabVisualDatabaseDisplay::TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
+{
+ deckEditor = new TabDeckEditor(_tabSupervisor);
+ deckEditor->setHidden(true);
+ visualDatabaseDisplayWidget =
+ new VisualDatabaseDisplayWidget(this, deckEditor, deckEditor->databaseDisplayDockWidget->databaseModel,
+ deckEditor->databaseDisplayDockWidget->databaseDisplayModel);
+
+ setCentralWidget(visualDatabaseDisplayWidget);
+
+ TabVisualDatabaseDisplay::retranslateUi();
+}
+
+void TabVisualDatabaseDisplay::retranslateUi()
+{
+}
diff --git a/cockatrice/src/client/tabs/tab_visual_database_display.h b/cockatrice/src/client/tabs/tab_visual_database_display.h
new file mode 100644
index 000000000..55f895e5b
--- /dev/null
+++ b/cockatrice/src/client/tabs/tab_visual_database_display.h
@@ -0,0 +1,26 @@
+#ifndef TAB_VISUAL_DATABASE_DISPLAY_H
+#define TAB_VISUAL_DATABASE_DISPLAY_H
+
+#include "../ui/widgets/visual_database_display/visual_database_display_widget.h"
+#include "tab.h"
+
+#include
+
+class TabVisualDatabaseDisplay : public Tab
+{
+ Q_OBJECT
+
+private:
+ TabDeckEditor *deckEditor;
+ VisualDatabaseDisplayWidget *visualDatabaseDisplayWidget;
+
+public:
+ TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor);
+ void retranslateUi() override;
+ QString getTabText() const override
+ {
+ return tr("Visual Database Display");
+ }
+};
+
+#endif // TAB_VISUAL_DATABASE_DISPLAY_H
diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
new file mode 100644
index 000000000..224c4db15
--- /dev/null
+++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
@@ -0,0 +1,470 @@
+#include "tab_deck_editor_visual.h"
+
+#include "../../../deck/deck_list_model.h"
+#include "../../../deck/deck_stats_interface.h"
+#include "../../../game/cards/card_database_model.h"
+#include "../../../game/filters/filter_builder.h"
+#include "../../../server/pending_command.h"
+#include "../../../settings/cache_settings.h"
+#include "../../ui/pixel_map_generator.h"
+#include "../../ui/widgets/cards/card_info_frame_widget.h"
+#include "../../ui/widgets/deck_analytics/deck_analytics_widget.h"
+#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_widget.h"
+#include "../tab_deck_editor.h"
+#include "../tab_supervisor.h"
+#include "pb/command_deck_upload.pb.h"
+#include "tab_deck_editor_visual_tab_widget.h"
+#include "trice_limits.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : AbstractTabDeckEditor(_tabSupervisor)
+{
+ setObjectName("TabDeckEditorVisual");
+
+ createCentralFrame();
+
+ TabDeckEditorVisual::createMenus();
+
+ installEventFilter(this);
+
+ TabDeckEditorVisual::retranslateUi();
+ connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
+ TabDeckEditorVisual::refreshShortcuts();
+
+ TabDeckEditorVisual::loadLayout();
+ databaseDisplayDockWidget->setHidden(true);
+}
+
+void TabDeckEditorVisual::createCentralFrame()
+{
+ centralWidget = new QWidget(this);
+ centralWidget->setObjectName("centralWidget");
+
+ centralFrame = new QVBoxLayout;
+ centralWidget->setLayout(centralFrame);
+
+ tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckDockWidget->deckModel,
+ databaseDisplayDockWidget->databaseModel,
+ databaseDisplayDockWidget->databaseDisplayModel);
+ connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChanged, this,
+ &TabDeckEditorVisual::changeModelIndexAndCardInfo);
+ connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChangedDatabaseDisplay, this,
+ &AbstractTabDeckEditor::updateCard);
+ connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClicked, this,
+ &TabDeckEditorVisual::processMainboardCardClick);
+
+ connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClickedDatabaseDisplay, this,
+ &TabDeckEditorVisual::processCardClickDatabaseDisplay);
+ centralFrame->addWidget(tabContainer);
+
+ setCentralWidget(centralWidget);
+ setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
+}
+
+void TabDeckEditorVisual::onDeckChanged()
+{
+ AbstractTabDeckEditor::onDeckChanged();
+ tabContainer->visualDeckView->decklistDataChanged(QModelIndex(), QModelIndex());
+ tabContainer->deckAnalytics->refreshDisplays(deckDockWidget->deckModel);
+ tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel);
+}
+
+void TabDeckEditorVisual::createMenus()
+{
+ deckMenu = new DeckEditorMenu(this);
+ addTabMenu(deckMenu);
+
+ viewMenu = new QMenu(this);
+
+ cardInfoDockMenu = viewMenu->addMenu(QString());
+ deckDockMenu = viewMenu->addMenu(QString());
+ deckAnalyticsMenu = viewMenu->addMenu(QString());
+ filterDockMenu = viewMenu->addMenu(QString());
+ printingSelectorDockMenu = viewMenu->addMenu(QString());
+
+ aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
+ aCardInfoDockVisible->setCheckable(true);
+ connect(aCardInfoDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ aCardInfoDockFloating = cardInfoDockMenu->addAction(QString());
+ aCardInfoDockFloating->setCheckable(true);
+ connect(aCardInfoDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+
+ aDeckDockVisible = deckDockMenu->addAction(QString());
+ aDeckDockVisible->setCheckable(true);
+ connect(aDeckDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ aDeckDockFloating = deckDockMenu->addAction(QString());
+ aDeckDockFloating->setCheckable(true);
+ connect(aDeckDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+
+ aDeckAnalyticsDockVisible = deckAnalyticsMenu->addAction(QString());
+ aDeckAnalyticsDockVisible->setCheckable(true);
+ connect(aDeckAnalyticsDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ aDeckAnalyticsDockFloating = deckAnalyticsMenu->addAction(QString());
+ aDeckAnalyticsDockFloating->setCheckable(true);
+ connect(aDeckAnalyticsDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+
+ aFilterDockVisible = filterDockMenu->addAction(QString());
+ aFilterDockVisible->setCheckable(true);
+ connect(aFilterDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ aFilterDockFloating = filterDockMenu->addAction(QString());
+ aFilterDockFloating->setCheckable(true);
+ connect(aFilterDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+
+ aPrintingSelectorDockVisible = printingSelectorDockMenu->addAction(QString());
+ aPrintingSelectorDockVisible->setCheckable(true);
+ connect(aPrintingSelectorDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
+ aPrintingSelectorDockFloating = printingSelectorDockMenu->addAction(QString());
+ aPrintingSelectorDockFloating->setCheckable(true);
+ connect(aPrintingSelectorDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
+
+ viewMenu->addSeparator();
+
+ aResetLayout = viewMenu->addAction(QString());
+ connect(aResetLayout, SIGNAL(triggered()), this, SLOT(restartLayout()));
+ viewMenu->addAction(aResetLayout);
+
+ deckMenu->setSaveStatus(false);
+
+ addTabMenu(viewMenu);
+}
+
+QString TabDeckEditorVisual::getTabText() const
+{
+ QString result = tr("Visual Deck: %1").arg(deckDockWidget->getSimpleDeckName());
+ if (modified)
+ result.prepend("* ");
+ return result;
+}
+
+void TabDeckEditorVisual::changeModelIndexAndCardInfo(const CardInfoPtr &activeCard)
+{
+ updateCard(activeCard);
+ changeModelIndexToCard(activeCard);
+}
+
+void TabDeckEditorVisual::changeModelIndexToCard(const CardInfoPtr &activeCard)
+{
+ QString cardName = activeCard->getName();
+ QModelIndex index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_MAIN);
+ if (!index.isValid()) {
+ index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_SIDE);
+ }
+ deckDockWidget->deckView->setCurrentIndex(index);
+}
+
+void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
+ CardInfoPictureWithTextOverlayWidget *instance,
+ QString zoneName)
+{
+ if (event->button() == Qt::LeftButton) {
+ actSwapCard(instance->getInfo(), zoneName);
+ } else if (event->button() == Qt::RightButton) {
+ actDecrementCard(instance->getInfo());
+ } else if (event->button() == Qt::MiddleButton) {
+ deckDockWidget->actRemoveCard();
+ }
+}
+
+void TabDeckEditorVisual::processCardClickDatabaseDisplay(QMouseEvent *event,
+ CardInfoPictureWithTextOverlayWidget *instance)
+{
+ if (event->button() == Qt::LeftButton) {
+ actAddCard(instance->getInfo());
+ } else if (event->button() == Qt::RightButton) {
+ actDecrementCard(instance->getInfo());
+ } else if (event->button() == Qt::MiddleButton) {
+ deckDockWidget->actRemoveCard();
+ }
+}
+
+bool TabDeckEditorVisual::actSaveDeckAs()
+{
+ // We have to disable the quick-add search bar or else it'll steal focus after dialog creation.
+ tabContainer->visualDeckView->searchBar->setEnabled(false);
+ auto result = AbstractTabDeckEditor::actSaveDeckAs();
+ tabContainer->visualDeckView->searchBar->setEnabled(true);
+ return result;
+}
+
+void TabDeckEditorVisual::showPrintingSelector()
+{
+ printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getInfo(), DECK_ZONE_MAIN);
+ printingSelectorDockWidget->printingSelector->updateDisplay();
+ aPrintingSelectorDockVisible->setChecked(true);
+ printingSelectorDockWidget->setVisible(true);
+}
+
+void TabDeckEditorVisual::restartLayout()
+{
+ deckDockWidget->setVisible(true);
+ cardInfoDockWidget->setVisible(true);
+ filterDockWidget->setVisible(true);
+
+ deckDockWidget->setFloating(false);
+ cardInfoDockWidget->setFloating(false);
+ filterDockWidget->setFloating(false);
+
+ aCardInfoDockVisible->setChecked(true);
+ aDeckDockVisible->setChecked(true);
+ aFilterDockVisible->setChecked(true);
+
+ aCardInfoDockFloating->setChecked(false);
+ aDeckDockFloating->setChecked(false);
+ aFilterDockFloating->setChecked(false);
+
+ addDockWidget(static_cast(2), deckDockWidget);
+ addDockWidget(static_cast(2), cardInfoDockWidget);
+ addDockWidget(static_cast(1), deckAnalyticsDock);
+ addDockWidget(static_cast(2), filterDockWidget);
+
+ splitDockWidget(cardInfoDockWidget, deckDockWidget, Qt::Horizontal);
+ splitDockWidget(cardInfoDockWidget, filterDockWidget, Qt::Vertical);
+ splitDockWidget(searchAndDatabaseDock, deckAnalyticsDock, Qt::Vertical);
+
+ deckDockWidget->setMinimumWidth(360);
+ deckDockWidget->setMaximumWidth(360);
+
+ cardInfoDockWidget->setMinimumSize(250, 360);
+ cardInfoDockWidget->setMaximumSize(250, 360);
+ QTimer::singleShot(100, this, SLOT(freeDocksSize()));
+}
+
+void TabDeckEditorVisual::freeDocksSize()
+{
+ deckDockWidget->setMinimumSize(100, 100);
+ deckDockWidget->setMaximumSize(5000, 5000);
+
+ cardInfoDockWidget->setMinimumSize(100, 100);
+ cardInfoDockWidget->setMaximumSize(5000, 5000);
+
+ filterDockWidget->setMinimumSize(100, 100);
+ filterDockWidget->setMaximumSize(5000, 5000);
+
+ databaseDisplayDockWidget->setMinimumSize(100, 100);
+ databaseDisplayDockWidget->setMaximumSize(1400, 5000);
+}
+
+void TabDeckEditorVisual::refreshShortcuts()
+{
+ ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
+ aResetLayout->setShortcuts(shortcuts.getShortcut("TabDeckEditorVisual/aResetLayout"));
+}
+
+void TabDeckEditorVisual::loadLayout()
+{
+ LayoutsSettings &layouts = SettingsCache::instance().layouts();
+ auto &layoutState = layouts.getDeckEditorLayoutState();
+ if (layoutState.isNull()) {
+ restartLayout();
+ } else {
+ restoreState(layoutState);
+ restoreGeometry(layouts.getDeckEditorGeometry());
+ }
+
+ aCardInfoDockVisible->setChecked(!cardInfoDockWidget->isHidden());
+ aFilterDockVisible->setChecked(!filterDockWidget->isHidden());
+ aDeckDockVisible->setChecked(!deckDockWidget->isHidden());
+ aPrintingSelectorDockVisible->setChecked(!printingSelectorDockWidget->isHidden());
+
+ aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
+ aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
+ aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
+ aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
+
+ aCardInfoDockFloating->setChecked(cardInfoDockWidget->isFloating());
+ aFilterDockFloating->setChecked(filterDockWidget->isFloating());
+ aDeckDockFloating->setChecked(deckDockWidget->isFloating());
+ aPrintingSelectorDockFloating->setChecked(printingSelectorDockWidget->isFloating());
+
+ cardInfoDockWidget->setMinimumSize(layouts.getDeckEditorCardSize());
+ cardInfoDockWidget->setMaximumSize(layouts.getDeckEditorCardSize());
+
+ filterDockWidget->setMinimumSize(layouts.getDeckEditorFilterSize());
+ filterDockWidget->setMaximumSize(layouts.getDeckEditorFilterSize());
+
+ deckDockWidget->setMinimumSize(layouts.getDeckEditorDeckSize());
+ deckDockWidget->setMaximumSize(layouts.getDeckEditorDeckSize());
+
+ printingSelectorDockWidget->setMinimumSize(layouts.getDeckEditorPrintingSelectorSize());
+ printingSelectorDockWidget->setMaximumSize(layouts.getDeckEditorPrintingSelectorSize());
+
+ databaseDisplayDockWidget->setMinimumSize(100, 100);
+ databaseDisplayDockWidget->setMaximumSize(1400, 5000);
+
+ QTimer::singleShot(100, this, &TabDeckEditorVisual::freeDocksSize);
+}
+
+void TabDeckEditorVisual::retranslateUi()
+{
+ deckMenu->setTitle(tr("&Visual Deck Editor"));
+
+ cardInfoDockWidget->setWindowTitle(tr("Card Info"));
+ deckDockWidget->setWindowTitle(tr("Deck"));
+ filterDockWidget->setWindowTitle(tr("Filters"));
+
+ viewMenu->setTitle(tr("&View"));
+ cardInfoDockMenu->setTitle(tr("Card Info"));
+ deckDockMenu->setTitle(tr("Deck"));
+ deckAnalyticsMenu->setTitle(tr("Deck Analytics"));
+ filterDockMenu->setTitle(tr("Filters"));
+ printingSelectorDockMenu->setTitle(tr("Printing"));
+
+ aCardInfoDockVisible->setText(tr("Visible"));
+ aCardInfoDockFloating->setText(tr("Floating"));
+
+ aDeckDockVisible->setText(tr("Visible"));
+ aDeckDockFloating->setText(tr("Floating"));
+
+ aDeckAnalyticsDockVisible->setText(tr("Visible"));
+ aDeckAnalyticsDockFloating->setText(tr("Floating"));
+
+ aFilterDockVisible->setText(tr("Visible"));
+ aFilterDockFloating->setText(tr("Floating"));
+
+ aPrintingSelectorDockVisible->setText(tr("Visible"));
+ aPrintingSelectorDockFloating->setText(tr("Floating"));
+
+ aResetLayout->setText(tr("Reset layout"));
+}
+
+// Method uses to sync docks state with menu items state
+bool TabDeckEditorVisual::eventFilter(QObject *o, QEvent *e)
+{
+ if (e->type() == QEvent::Close) {
+ if (o == cardInfoDockWidget) {
+ aCardInfoDockVisible->setChecked(false);
+ aCardInfoDockFloating->setEnabled(false);
+ } else if (o == deckDockWidget) {
+ aDeckDockVisible->setChecked(false);
+ aDeckDockFloating->setEnabled(false);
+ } else if (o == deckAnalyticsDock) {
+ aDeckAnalyticsDockVisible->setChecked(false);
+ aDeckAnalyticsDockFloating->setEnabled(false);
+ } else if (o == filterDockWidget) {
+ aFilterDockVisible->setChecked(false);
+ aFilterDockFloating->setEnabled(false);
+ } else if (o == printingSelectorDockWidget) {
+ aPrintingSelectorDockVisible->setChecked(false);
+ aPrintingSelectorDockFloating->setEnabled(false);
+ }
+ }
+ if (o == this && e->type() == QEvent::Hide) {
+ LayoutsSettings &layouts = SettingsCache::instance().layouts();
+ layouts.setDeckEditorLayoutState(saveState());
+ layouts.setDeckEditorGeometry(saveGeometry());
+ layouts.setDeckEditorCardSize(cardInfoDockWidget->size());
+ layouts.setDeckEditorFilterSize(filterDockWidget->size());
+ layouts.setDeckEditorDeckSize(deckDockWidget->size());
+ layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size());
+ }
+ return false;
+}
+
+void TabDeckEditorVisual::dockVisibleTriggered()
+{
+ QObject *o = sender();
+ if (o == aCardInfoDockVisible) {
+ cardInfoDockWidget->setHidden(!aCardInfoDockVisible->isChecked());
+ aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
+ return;
+ }
+
+ if (o == aDeckDockVisible) {
+ deckDockWidget->setHidden(!aDeckDockVisible->isChecked());
+ aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
+ return;
+ }
+
+ if (o == aDeckAnalyticsDockVisible) {
+ deckAnalyticsDock->setHidden(!aDeckAnalyticsDockVisible->isChecked());
+ aDeckAnalyticsDockFloating->setEnabled(aDeckAnalyticsDockVisible->isChecked());
+ return;
+ }
+
+ if (o == aFilterDockVisible) {
+ filterDockWidget->setHidden(!aFilterDockVisible->isChecked());
+ aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
+ return;
+ }
+
+ if (o == aPrintingSelectorDockVisible) {
+ printingSelectorDockWidget->setHidden(!aPrintingSelectorDockVisible->isChecked());
+ aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
+ return;
+ }
+}
+
+void TabDeckEditorVisual::dockFloatingTriggered()
+{
+ QObject *o = sender();
+ if (o == aCardInfoDockFloating) {
+ cardInfoDockWidget->setFloating(aCardInfoDockFloating->isChecked());
+ return;
+ }
+
+ if (o == aDeckDockFloating) {
+ deckDockWidget->setFloating(aDeckDockFloating->isChecked());
+ return;
+ }
+
+ if (o == aDeckAnalyticsDockFloating) {
+ deckAnalyticsDock->setFloating(aDeckAnalyticsDockFloating->isChecked());
+ return;
+ }
+
+ if (o == aFilterDockFloating) {
+ filterDockWidget->setFloating(aFilterDockFloating->isChecked());
+ return;
+ }
+
+ if (o == aPrintingSelectorDockFloating) {
+ printingSelectorDockWidget->setFloating(aPrintingSelectorDockFloating->isChecked());
+ return;
+ }
+}
+
+void TabDeckEditorVisual::dockTopLevelChanged(bool topLevel)
+{
+ QObject *o = sender();
+ if (o == cardInfoDockWidget) {
+ aCardInfoDockFloating->setChecked(topLevel);
+ return;
+ }
+
+ if (o == deckDockWidget) {
+ aDeckDockFloating->setChecked(topLevel);
+ return;
+ }
+
+ if (o == filterDockWidget) {
+ aFilterDockFloating->setChecked(topLevel);
+ return;
+ }
+
+ if (o == deckAnalyticsDock) {
+ aDeckAnalyticsDockFloating->setChecked(topLevel);
+ return;
+ }
+
+ if (o == printingSelectorDockWidget) {
+ aPrintingSelectorDockFloating->setChecked(topLevel);
+ return;
+ }
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h
new file mode 100644
index 000000000..f4add316e
--- /dev/null
+++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h
@@ -0,0 +1,53 @@
+#ifndef WINDOW_DECKEDITORVISUAL_H
+#define WINDOW_DECKEDITORVISUAL_H
+
+#include "../tab.h"
+#include "tab_deck_editor_visual_tab_widget.h"
+
+class TabDeckEditorVisual : public AbstractTabDeckEditor
+{
+ Q_OBJECT
+protected slots:
+ void loadLayout() override;
+ void restartLayout() override;
+ void freeDocksSize() override;
+ void refreshShortcuts() override;
+
+ bool eventFilter(QObject *o, QEvent *e) override;
+ void dockVisibleTriggered() override;
+ void dockFloatingTriggered() override;
+ void dockTopLevelChanged(bool topLevel) override;
+
+protected:
+ TabDeckEditorVisualTabWidget *tabContainer;
+
+ QVBoxLayout *centralFrame;
+ QVBoxLayout *searchAndDatabaseFrame;
+ QHBoxLayout *searchLayout;
+ QDockWidget *searchAndDatabaseDock;
+ QDockWidget *deckAnalyticsDock;
+ QWidget *centralWidget;
+ QMenu *deckAnalyticsMenu;
+ QAction *aDeckAnalyticsDockVisible, *aDeckAnalyticsDockFloating;
+
+public:
+ explicit TabDeckEditorVisual(TabSupervisor *_tabSupervisor);
+ void retranslateUi() override;
+ QString getTabText() const override;
+ void changeModelIndexAndCardInfo(const CardInfoPtr &activeCard);
+ void changeModelIndexToCard(const CardInfoPtr &activeCard);
+ void createDeckAnalyticsDock();
+ void createMenus() override;
+ void createSearchAndDatabaseFrame();
+ void createCentralFrame();
+
+public slots:
+ void onDeckChanged() override;
+ void showPrintingSelector() override;
+ void
+ processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
+ void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
+ bool actSaveDeckAs() override;
+};
+
+#endif
diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
new file mode 100644
index 000000000..a34d7ad94
--- /dev/null
+++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
@@ -0,0 +1,113 @@
+#include "tab_deck_editor_visual_tab_widget.h"
+
+#include "../../ui/widgets/visual_database_display/visual_database_display_widget.h"
+#include "../abstract_tab_deck_editor.h"
+
+TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent,
+ AbstractTabDeckEditor *_deckEditor,
+ DeckListModel *_deckModel,
+ CardDatabaseModel *_cardDatabaseModel,
+ CardDatabaseDisplayModel *_cardDatabaseDisplayModel)
+ : QTabWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), cardDatabaseModel(_cardDatabaseModel),
+ cardDatabaseDisplayModel(_cardDatabaseDisplayModel)
+{
+ this->setTabsClosable(true); // Enable tab closing
+ connect(this, &QTabWidget::tabCloseRequested, this, &TabDeckEditorVisualTabWidget::handleTabClose);
+
+ // Set up the layout and add tab widget
+ layout = new QVBoxLayout(this);
+ setLayout(layout);
+
+ visualDeckView = new VisualDeckEditorWidget(this, deckModel);
+ visualDeckView->setObjectName("visualDeckView");
+ connect(visualDeckView, &VisualDeckEditorWidget::activeCardChanged, this,
+ &TabDeckEditorVisualTabWidget::onCardChanged);
+ connect(visualDeckView, &VisualDeckEditorWidget::cardClicked, this,
+ &TabDeckEditorVisualTabWidget::onCardClickedDeckEditor);
+ connect(visualDeckView, &VisualDeckEditorWidget::cardAdditionRequested, deckEditor,
+ &AbstractTabDeckEditor::actAddCard);
+
+ visualDatabaseDisplay =
+ new VisualDatabaseDisplayWidget(this, deckEditor, _cardDatabaseModel, _cardDatabaseDisplayModel);
+ visualDatabaseDisplay->setObjectName("visualDatabaseView");
+ connect(visualDatabaseDisplay, &VisualDatabaseDisplayWidget::cardHoveredDatabaseDisplay, this,
+ &TabDeckEditorVisualTabWidget::onCardChangedDatabaseDisplay);
+ connect(visualDatabaseDisplay, &VisualDatabaseDisplayWidget::cardClickedDatabaseDisplay, this,
+ &TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay);
+
+ deckAnalytics = new DeckAnalyticsWidget(this, deckModel);
+ deckAnalytics->setObjectName("deckAnalytics");
+
+ sampleHandWidget = new VisualDeckEditorSampleHandWidget(this, deckModel);
+
+ this->addNewTab(visualDeckView, tr("Visual Deck View"));
+ this->addNewTab(visualDatabaseDisplay, tr("Visual Database Display"));
+ this->addNewTab(deckAnalytics, tr("Deck Analytics"));
+ this->addNewTab(sampleHandWidget, tr("Sample Hand"));
+}
+
+void TabDeckEditorVisualTabWidget::onCardChanged(CardInfoPtr activeCard)
+{
+ emit cardChanged(activeCard);
+}
+
+void TabDeckEditorVisualTabWidget::onCardChangedDatabaseDisplay(CardInfoPtr activeCard)
+{
+ emit cardChangedDatabaseDisplay(activeCard);
+}
+
+void TabDeckEditorVisualTabWidget::onCardClickedDeckEditor(QMouseEvent *event,
+ CardInfoPictureWithTextOverlayWidget *instance,
+ QString zoneName)
+{
+ emit cardClicked(event, instance, zoneName);
+}
+
+void TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay(QMouseEvent *event,
+ CardInfoPictureWithTextOverlayWidget *instance)
+{
+ emit cardClickedDatabaseDisplay(event, instance);
+}
+
+void TabDeckEditorVisualTabWidget::addNewTab(QWidget *widget, const QString &title)
+{
+ // Add new tab to the tab widget
+ this->addTab(widget, title);
+}
+
+void TabDeckEditorVisualTabWidget::removeCurrentTab()
+{
+ // Remove the currently selected tab
+ int currentIndex = this->currentIndex();
+ if (currentIndex != -1) {
+ this->removeTab(currentIndex);
+ }
+}
+
+void TabDeckEditorVisualTabWidget::setTabTitle(int index, const QString &title)
+{
+ // Set the title of the tab at the given index
+ if (index >= 0 && index < this->count()) {
+ this->setTabText(index, title);
+ }
+}
+
+QWidget *TabDeckEditorVisualTabWidget::getCurrentTab() const
+{
+ // Return the currently selected tab widget
+ return this->currentWidget();
+}
+
+int TabDeckEditorVisualTabWidget::getTabCount() const
+{
+ // Return the number of tabs
+ return this->count();
+}
+
+void TabDeckEditorVisualTabWidget::handleTabClose(int index)
+{
+ // Handle closing of the tab at the given index
+ QWidget *tab = this->widget(index);
+ this->removeTab(index);
+ delete tab; // Delete the tab's widget to free memory
+}
diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h
new file mode 100644
index 000000000..c2454538a
--- /dev/null
+++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h
@@ -0,0 +1,62 @@
+#ifndef TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
+#define TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
+
+#include "../../ui/widgets/deck_analytics/deck_analytics_widget.h"
+#include "../../ui/widgets/printing_selector/printing_selector.h"
+#include "../../ui/widgets/visual_database_display/visual_database_display_widget.h"
+#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.h"
+#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_widget.h"
+#include "../abstract_tab_deck_editor.h"
+
+#include
+#include
+#include
+
+class TabDeckEditorVisualTabWidget : public QTabWidget
+{
+ Q_OBJECT
+
+public:
+ explicit TabDeckEditorVisualTabWidget(QWidget *parent,
+ AbstractTabDeckEditor *_deckEditor,
+ DeckListModel *_deckModel,
+ CardDatabaseModel *_cardDatabaseModel,
+ CardDatabaseDisplayModel *_cardDatabaseDisplayModel);
+
+ // Utility functions
+ void addNewTab(QWidget *widget, const QString &title);
+ void removeCurrentTab();
+ void setTabTitle(int index, const QString &title);
+ QWidget *getCurrentTab() const;
+ int getTabCount() const;
+
+ VisualDeckEditorWidget *visualDeckView;
+ DeckAnalyticsWidget *deckAnalytics;
+ VisualDatabaseDisplayWidget *visualDatabaseDisplay;
+ PrintingSelector *printingSelector;
+ VisualDeckEditorSampleHandWidget *sampleHandWidget;
+
+public slots:
+ void onCardChanged(CardInfoPtr activeCard);
+ void onCardChangedDatabaseDisplay(CardInfoPtr activeCard);
+ void onCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
+ void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
+
+signals:
+ void cardChanged(CardInfoPtr activeCard);
+ void cardChangedDatabaseDisplay(CardInfoPtr activeCard);
+ void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
+ void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
+
+private:
+ QVBoxLayout *layout; // Layout for the tab widget and other controls
+ AbstractTabDeckEditor *deckEditor;
+ DeckListModel *deckModel;
+ CardDatabaseModel *cardDatabaseModel;
+ CardDatabaseDisplayModel *cardDatabaseDisplayModel;
+
+private slots:
+ void handleTabClose(int index); // Slot for closing a tab
+};
+
+#endif // TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
diff --git a/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp b/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
index 332f5b779..38af03c59 100644
--- a/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
+++ b/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
@@ -12,7 +12,7 @@
TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
: Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this))
{
- connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::addDeckEditorTab);
+ connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::addVisualDeckEditorTab);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this,
&TabDeckStorageVisual::actOpenLocalDeck);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this,
diff --git a/cockatrice/src/client/ui/layouts/overlap_layout.cpp b/cockatrice/src/client/ui/layouts/overlap_layout.cpp
index da44399a7..234bc41f4 100644
--- a/cockatrice/src/client/ui/layouts/overlap_layout.cpp
+++ b/cockatrice/src/client/ui/layouts/overlap_layout.cpp
@@ -1,5 +1,6 @@
#include "overlap_layout.h"
+#include
#include
/**
@@ -32,9 +33,10 @@ OverlapLayout::OverlapLayout(QWidget *parent,
const int overlapPercentage,
const int maxColumns,
const int maxRows,
- const Qt::Orientation direction)
+ const Qt::Orientation _overlapDirection,
+ const Qt::Orientation _flowDirection)
: QLayout(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows),
- direction(direction)
+ overlapDirection(_overlapDirection), flowDirection(_flowDirection)
{
}
@@ -148,17 +150,21 @@ void OverlapLayout::setGeometry(const QRect &rect)
}
// Calculate the overlap offsets based on the layout direction and overlap percentage.
- const int overlapOffsetWidth = (direction == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0;
- const int overlapOffsetHeight = (direction == Qt::Vertical) ? (maxItemHeight * overlapPercentage / 100) : 0;
+ const int overlapOffsetWidth = (overlapDirection == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0;
+ const int overlapOffsetHeight = (overlapDirection == Qt::Vertical) ? (maxItemHeight * overlapPercentage / 100) : 0;
// Determine the number of columns based on layout constraints and available space.
int columns;
- if (direction == Qt::Horizontal) {
+ if (flowDirection == Qt::Horizontal) {
if (maxColumns > 0) {
// Calculate the maximum possible columns given the available width and overlap.
const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth);
// Use the smaller of maxColumns and availableColumns.
+ qCDebug(OverlapLayoutLog) << " Max Columns " << maxColumns << " available columns " << availableColumns;
columns = qMin(maxColumns, availableColumns);
+ if (columns < 1) {
+ columns = 1;
+ }
} else {
// If no maxColumns constraint, allow as many columns as possible.
columns = INT_MAX;
@@ -170,12 +176,16 @@ void OverlapLayout::setGeometry(const QRect &rect)
// Determine the number of rows based on layout constraints and available space.
int rows;
- if (direction == Qt::Vertical) {
+ if (flowDirection == Qt::Vertical) {
if (maxRows > 0) {
// Calculate the maximum possible rows given the available height and overlap.
const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight);
// Use the smaller of maxRows and availableRows.
+ qCDebug(OverlapLayoutLog) << " Max Rows " << maxRows << " available rows " << availableRows;
rows = qMin(maxRows, availableRows);
+ if (rows < 1) {
+ rows = 1;
+ }
} else {
// If no maxRows constraint, allow as many rows as possible.
rows = INT_MAX;
@@ -200,16 +210,17 @@ void OverlapLayout::setGeometry(const QRect &rect)
const int yPos = rect.top() + currentRow * (maxItemHeight - overlapOffsetHeight);
item->setGeometry(QRect(xPos, yPos, maxItemWidth, maxItemHeight));
+ // TODO: Figure this out properly or maybe adjust size hint to account for this?
// Update row and column indices based on the layout direction.
- if (direction == Qt::Horizontal) {
+ if (overlapDirection == Qt::Horizontal) {
currentColumn++;
- if (currentColumn >= columns) {
+ if (currentColumn > qCeil(itemList.size() / rows)) {
currentColumn = 0;
currentRow++;
}
} else {
currentRow++;
- if (currentRow >= rows) {
+ if (currentRow > qCeil(itemList.size() / columns)) {
currentRow = 0;
currentColumn++;
}
@@ -223,88 +234,86 @@ void OverlapLayout::setGeometry(const QRect &rect)
*/
QSize OverlapLayout::calculatePreferredSize() const
{
+ // Get the parent widget for size and margin calculations.
+ const QWidget *parentWidget = this->parentWidget();
+ if (!parentWidget) {
+ return QSize(0, 0);
+ }
- // Determine the maximum item dimensions.
+ if (itemList.isEmpty()) {
+ return QSize(0, 0);
+ }
+
+ int availableWidth = parentWidget->width();
+ int availableHeight = parentWidget->height();
+ const QMargins margins = parentWidget->contentsMargins();
+ availableWidth -= margins.left() + margins.right();
+ availableHeight -= margins.top() + margins.bottom();
+
+ // Determine the maximum item width and height among all layout items.
int maxItemWidth = 0;
int maxItemHeight = 0;
for (QLayoutItem *item : itemList) {
- if (item == nullptr) {
- continue;
+ if (item != nullptr && item->widget()) {
+ QSize itemSize = item->widget()->sizeHint();
+ maxItemWidth = qMax(maxItemWidth, itemSize.width());
+ maxItemHeight = qMax(maxItemHeight, itemSize.height());
}
- if (!item->widget()) {
- continue;
- }
-
- QSize itemSize = item->widget()->sizeHint();
- maxItemWidth = qMax(maxItemWidth, itemSize.width());
- maxItemHeight = qMax(maxItemHeight, itemSize.height());
}
- // Calculate the overlap offsets.
- const int extra_for_overlap = (100 - overlapPercentage);
- const int overlapOffsetWidth =
- (direction == Qt::Horizontal) ? qRound((maxItemWidth / 100.0) * extra_for_overlap) : 0;
- const int overlapOffsetHeight =
- (direction == Qt::Vertical) ? qRound((maxItemHeight / 100.0) * extra_for_overlap) : 0;
+ // Calculate the overlap offsets based on the layout direction and overlap percentage.
+ const int overlapOffsetWidth = (overlapDirection == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0;
+ const int overlapOffsetHeight = (overlapDirection == Qt::Vertical) ? (maxItemHeight * overlapPercentage / 100) : 0;
- // Variables to hold the total dimensions based on layout orientation.
- int totalWidth = 0;
- int totalHeight = 0;
-
- // Calculate the total size based on the layout direction and constraints.
- if (direction == Qt::Horizontal) {
- // Determine the number of columns:
- // - Use maxColumns if it is greater than 0 (constraint set by the user).
- // - Otherwise, set the number of columns to the total number of items in the layout.
- const int numColumns = (maxColumns > 0) ? static_cast(maxColumns) : static_cast(itemList.size());
-
- // Calculate the extra space required for overlaps between columns:
- // - Each overlap reduces the effective width of subsequent items.
- const int extra_space_for_overlaps = overlapOffsetWidth * (numColumns - 1);
-
- // Total width:
- // - The first item's width is fully included (maxItemWidth).
- // - Add the space for all overlaps between adjacent items.
- totalWidth = maxItemWidth + extra_space_for_overlaps;
-
- // Determine the number of rows:
- // - Use maxRows if maxColumns is set (to constrain the number of rows).
- // - Otherwise, assume a single row.
- const int numRows =
- (maxColumns > 0) ? qCeil(static_cast(itemList.size()) / static_cast(numColumns)) : 1;
-
- // Total height:
- // - Multiply the number of rows by the item height (maxItemHeight).
- // - Subtract the height overlap between rows to avoid double-counting.
- totalHeight = maxItemHeight * numRows - (numRows - 1) * overlapOffsetHeight;
- } else if (direction == Qt::Vertical) {
- // Determine the number of columns:
- // - Use maxRows to calculate how many columns are needed if a row constraint exists.
- // - Otherwise, assume a single column.
- const int numColumns =
- (maxRows != 0) ? qCeil(static_cast(itemList.size()) / static_cast(maxRows)) : 1;
-
- // Total width:
- // - Multiply the number of columns by the item width (maxItemWidth).
- // - Subtract the width overlap between columns to avoid double-counting.
- totalWidth = maxItemWidth * numColumns - (numColumns - 1) * overlapOffsetWidth;
-
- // Determine the number of rows:
- // - Use maxRows if it is greater than 0 (constraint set by the user).
- // - Otherwise, set the number of rows to the total number of items in the layout.
- const int numRows = (maxRows > 0) ? static_cast(maxRows) : static_cast(itemList.size());
-
- // Calculate the extra space required for overlaps between rows:
- // - Each overlap reduces the effective height of subsequent items.
- const int extraSpaceForOverlaps = overlapOffsetHeight * (numRows - 1);
-
- // Total height:
- // - The first item's height is fully included (maxItemHeight).
- // - Add the space for all overlaps between adjacent items.
- totalHeight = maxItemHeight + extraSpaceForOverlaps;
+ // Determine the number of columns based on layout constraints and available space.
+ int columns;
+ if (flowDirection == Qt::Horizontal) {
+ if (maxColumns > 0) {
+ // Calculate the maximum possible columns given the available width and overlap.
+ const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth);
+ // Use the smaller of maxColumns and availableColumns.
+ qCDebug(OverlapLayoutLog) << " Max Columns " << maxColumns << " available columns " << availableColumns;
+ columns = qMin(maxColumns, availableColumns);
+ if (columns < 1) {
+ columns = 1;
+ }
+ } else {
+ // If no maxColumns constraint, allow as many columns as possible.
+ columns = INT_MAX;
+ }
+ } else {
+ // If not a horizontal layout, column count is irrelevant.
+ columns = INT_MAX;
}
- return {totalWidth, totalHeight};
+ // Determine the number of rows based on layout constraints and available space.
+ int rows;
+ if (flowDirection == Qt::Vertical) {
+ if (maxRows > 0) {
+ // Calculate the maximum possible rows given the available height and overlap.
+ const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight);
+ // Use the smaller of maxRows and availableRows.
+ qCDebug(OverlapLayoutLog) << " Max Rows " << maxRows << " available rows " << availableRows;
+ rows = qMin(maxRows, availableRows);
+ if (rows < 1) {
+ rows = 1;
+ }
+ } else {
+ // If no maxRows constraint, allow as many rows as possible.
+ rows = INT_MAX;
+ }
+ } else {
+ // If not a vertical layout, row count is irrelevant.
+ rows = INT_MAX;
+ }
+
+ if (overlapDirection == Qt::Horizontal) {
+ return QSize(maxItemWidth + ((qCeil(itemList.size() / rows)) * (maxItemWidth - overlapOffsetWidth)),
+ rows * maxItemHeight);
+ } else {
+ return QSize(columns * maxItemWidth,
+ maxItemHeight + ((qCeil(itemList.size() / columns)) * (maxItemHeight - overlapOffsetHeight)));
+ }
}
/**
@@ -341,7 +350,7 @@ QSize OverlapLayout::minimumSize() const
*/
void OverlapLayout::setDirection(const Qt::Orientation _direction)
{
- direction = _direction;
+ overlapDirection = _direction;
}
/**
@@ -378,7 +387,7 @@ void OverlapLayout::setMaxRows(const int _maxRows)
*/
int OverlapLayout::calculateMaxColumns() const
{
- if (direction != Qt::Vertical || itemList.isEmpty()) {
+ if (overlapDirection != Qt::Vertical || itemList.isEmpty()) {
return 1; // Only relevant if the layout direction is vertical
}
@@ -410,7 +419,7 @@ int OverlapLayout::calculateMaxColumns() const
*/
int OverlapLayout::calculateRowsForColumns(const int columns) const
{
- if (direction != Qt::Vertical || itemList.isEmpty() || columns <= 0) {
+ if (overlapDirection != Qt::Vertical || itemList.isEmpty() || columns <= 0) {
return 1; // Only relevant if the layout direction is vertical and there are items
}
@@ -429,7 +438,7 @@ int OverlapLayout::calculateRowsForColumns(const int columns) const
*/
int OverlapLayout::calculateMaxRows() const
{
- if (direction != Qt::Horizontal || itemList.isEmpty()) {
+ if (overlapDirection != Qt::Horizontal || itemList.isEmpty()) {
return 1; // Only relevant if the layout direction is horizontal
}
@@ -464,7 +473,7 @@ int OverlapLayout::calculateMaxRows() const
*/
int OverlapLayout::calculateColumnsForRows(const int rows) const
{
- if (direction != Qt::Horizontal || itemList.isEmpty() || rows <= 0) {
+ if (overlapDirection != Qt::Horizontal || itemList.isEmpty() || rows <= 0) {
return 1; // Only relevant if the layout direction is horizontal and there are items
}
diff --git a/cockatrice/src/client/ui/layouts/overlap_layout.h b/cockatrice/src/client/ui/layouts/overlap_layout.h
index 1975cce89..d2cfca32a 100644
--- a/cockatrice/src/client/ui/layouts/overlap_layout.h
+++ b/cockatrice/src/client/ui/layouts/overlap_layout.h
@@ -3,8 +3,11 @@
#include
#include
+#include
#include
+inline Q_LOGGING_CATEGORY(OverlapLayoutLog, "overlap_layout");
+
class OverlapLayout : public QLayout
{
public:
@@ -12,7 +15,8 @@ public:
int overlapPercentage = 10,
int maxColumns = 2,
int maxRows = 2,
- Qt::Orientation direction = Qt::Horizontal);
+ Qt::Orientation overlapDirection = Qt::Vertical,
+ Qt::Orientation flowDirection = Qt::Horizontal);
~OverlapLayout();
void addItem(QLayoutItem *item) override;
@@ -35,7 +39,8 @@ private:
int overlapPercentage;
int maxColumns;
int maxRows;
- Qt::Orientation direction;
+ Qt::Orientation overlapDirection;
+ Qt::Orientation flowDirection;
// Calculate the preferred size of the layout
QSize calculatePreferredSize() const;
diff --git a/cockatrice/src/client/ui/line_edit_completer.cpp b/cockatrice/src/client/ui/line_edit_completer.cpp
index 31e82248d..7820ad91b 100644
--- a/cockatrice/src/client/ui/line_edit_completer.cpp
+++ b/cockatrice/src/client/ui/line_edit_completer.cpp
@@ -117,7 +117,7 @@ void LineEditCompleter::setCompleter(QCompleter *completer)
{
c = completer;
c->setWidget(this);
- connect(c, SIGNAL(activated(QString)), this, SLOT(insertCompletion(QString)));
+ connect(c, qOverload(&QCompleter::activated), this, &LineEditCompleter::insertCompletion);
}
void LineEditCompleter::setCompletionList(QStringList completionList)
diff --git a/cockatrice/src/client/ui/phases_toolbar.cpp b/cockatrice/src/client/ui/phases_toolbar.cpp
index 036a7c19e..7c3bede16 100644
--- a/cockatrice/src/client/ui/phases_toolbar.cpp
+++ b/cockatrice/src/client/ui/phases_toolbar.cpp
@@ -18,7 +18,7 @@ PhaseButton::PhaseButton(const QString &_name, QGraphicsItem *parent, QAction *_
{
if (highlightable) {
activeAnimationTimer = new QTimer(this);
- connect(activeAnimationTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
+ connect(activeAnimationTimer, &QTimer::timeout, this, &PhaseButton::updateAnimation);
activeAnimationTimer->setSingleShot(false);
} else
activeAnimationCounter = 9;
@@ -106,9 +106,9 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
: QGraphicsItem(parent), width(100), height(100), ySpacing(1), symbolSize(8)
{
auto *aUntapAll = new QAction(this);
- connect(aUntapAll, SIGNAL(triggered()), this, SLOT(actUntapAll()));
+ connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::actUntapAll);
auto *aDrawCard = new QAction(this);
- connect(aDrawCard, SIGNAL(triggered()), this, SLOT(actDrawCard()));
+ connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::actDrawCard);
PhaseButton *untapButton = new PhaseButton("untap", this, aUntapAll);
PhaseButton *upkeepButton = new PhaseButton("upkeep", this);
@@ -126,10 +126,10 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
<< combatBlockersButton << combatDamageButton << combatEndButton << main2Button << cleanupButton;
for (auto &i : buttonList)
- connect(i, SIGNAL(clicked()), this, SLOT(phaseButtonClicked()));
+ connect(i, &PhaseButton::clicked, this, &PhasesToolbar::phaseButtonClicked);
nextTurnButton = new PhaseButton("nextturn", this, nullptr, false);
- connect(nextTurnButton, SIGNAL(clicked()), this, SLOT(actNextTurn()));
+ connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::actNextTurn);
rearrangeButtons();
diff --git a/cockatrice/src/client/ui/picture_loader/picture_loader.cpp b/cockatrice/src/client/ui/picture_loader/picture_loader.cpp
index eafeb9467..61ea8d0ad 100644
--- a/cockatrice/src/client/ui/picture_loader/picture_loader.cpp
+++ b/cockatrice/src/client/ui/picture_loader/picture_loader.cpp
@@ -23,11 +23,10 @@
PictureLoader::PictureLoader() : QObject(nullptr)
{
worker = new PictureLoaderWorker;
- connect(&SettingsCache::instance(), SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
- connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
+ connect(&SettingsCache::instance(), &SettingsCache::picsPathChanged, this, &PictureLoader::picsPathChanged);
+ connect(&SettingsCache::instance(), &SettingsCache::picDownloadChanged, this, &PictureLoader::picDownloadChanged);
- connect(worker, SIGNAL(imageLoaded(CardInfoPtr, const QImage &)), this,
- SLOT(imageLoaded(CardInfoPtr, const QImage &)));
+ connect(worker, &PictureLoaderWorker::imageLoaded, this, &PictureLoader::imageLoaded);
}
PictureLoader::~PictureLoader()
diff --git a/cockatrice/src/client/ui/picture_loader/picture_loader.h b/cockatrice/src/client/ui/picture_loader/picture_loader.h
index 68a9424e4..e2769f567 100644
--- a/cockatrice/src/client/ui/picture_loader/picture_loader.h
+++ b/cockatrice/src/client/ui/picture_loader/picture_loader.h
@@ -1,7 +1,7 @@
#ifndef PICTURELOADER_H
#define PICTURELOADER_H
-#include "../../../game/cards/card_database.h"
+#include "../../../game/cards/card_info.h"
#include "picture_loader_worker.h"
#include
diff --git a/cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp b/cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp
index a1cfa53be..fce81fe6a 100644
--- a/cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp
+++ b/cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp
@@ -19,9 +19,11 @@ PictureLoaderWorker::PictureLoaderWorker()
picDownload(SettingsCache::instance().getPicDownload()), downloadRunning(false), loadQueueRunning(false),
overrideAllCardArtWithPersonalPreference(SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference())
{
- connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection);
- connect(&SettingsCache::instance(), SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
- connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
+ connect(this, &PictureLoaderWorker::startLoadQueue, this, &PictureLoaderWorker::processLoadQueue,
+ Qt::QueuedConnection);
+ connect(&SettingsCache::instance(), &SettingsCache::picsPathChanged, this, &PictureLoaderWorker::picsPathChanged);
+ connect(&SettingsCache::instance(), &SettingsCache::picDownloadChanged, this,
+ &PictureLoaderWorker::picDownloadChanged);
connect(&SettingsCache::instance(), &SettingsCache::overrideAllCardArtWithPersonalPreferenceChanged, this,
&PictureLoaderWorker::setOverrideAllCardArtWithPersonalPreference);
@@ -43,7 +45,7 @@ PictureLoaderWorker::PictureLoaderWorker()
// Use a ManualRedirectPolicy since we keep track of redirects in picDownloadFinished
// We can't use NoLessSafeRedirectPolicy because it is not applied with AlwaysCache
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
- connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *)));
+ connect(networkManager, &QNetworkAccessManager::finished, this, &PictureLoaderWorker::picDownloadFinished);
cacheFilePath = SettingsCache::instance().getRedirectCachePath() + REDIRECT_CACHE_FILENAME;
loadRedirectCache();
@@ -244,6 +246,23 @@ QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url)
QNetworkRequest req(url);
+ // QNetworkDiskCache leaks file descriptors when downloading compressed
+ // files, see https://bugreports.qt.io/browse/QTBUG-135641
+ //
+ // We can set the Accept-Encoding header manually to disable Qt's automatic
+ // response decompression, but then we would have to deal with decompression
+ // ourselves.
+ //
+ // Since we are dowloading images that are usually stored in a
+ // compressed format (e.g. jpeg or webp), it's not clear compression at the
+ // HTTP level helps; in fact, images are typically returned without
+ // compression. Redirects, on the other hand, are compressed and cause file
+ // descriptor leaks -- but since redirects have no payload, we don't really
+ // care either.
+ //
+ // In the end, just do the simple thing and disable HTTP compression.
+ req.setRawHeader("accept-encoding", "identity");
+
if (!picDownload) {
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
}
diff --git a/cockatrice/src/client/ui/picture_loader/picture_loader_worker.h b/cockatrice/src/client/ui/picture_loader/picture_loader_worker.h
index e85ede8f1..47d2cca5a 100644
--- a/cockatrice/src/client/ui/picture_loader/picture_loader_worker.h
+++ b/cockatrice/src/client/ui/picture_loader/picture_loader_worker.h
@@ -1,7 +1,7 @@
#ifndef PICTURE_LOADER_WORKER_H
#define PICTURE_LOADER_WORKER_H
-#include "../../../game/cards/card_database.h"
+#include "../../../game/cards/card_info.h"
#include "picture_to_load.h"
#include
diff --git a/cockatrice/src/client/ui/picture_loader/picture_to_load.h b/cockatrice/src/client/ui/picture_loader/picture_to_load.h
index 96a114cd4..9d1a784f0 100644
--- a/cockatrice/src/client/ui/picture_loader/picture_to_load.h
+++ b/cockatrice/src/client/ui/picture_loader/picture_to_load.h
@@ -1,7 +1,7 @@
#ifndef PICTURE_TO_LOAD_H
#define PICTURE_TO_LOAD_H
-#include "../../../game/cards/card_database.h"
+#include "../../../game/cards/card_info.h"
#include
diff --git a/cockatrice/src/client/ui/theme_manager.cpp b/cockatrice/src/client/ui/theme_manager.cpp
index 0cff534a2..78d1344a1 100644
--- a/cockatrice/src/client/ui/theme_manager.cpp
+++ b/cockatrice/src/client/ui/theme_manager.cpp
@@ -24,7 +24,7 @@ static const QStringList DEFAULT_RESOURCE_PATHS = {":/resources"};
ThemeManager::ThemeManager(QObject *parent) : QObject(parent)
{
ensureThemeDirectoryExists();
- connect(&SettingsCache::instance(), SIGNAL(themeChanged()), this, SLOT(themeChangedSlot()));
+ connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this, &ThemeManager::themeChangedSlot);
themeChangedSlot();
}
diff --git a/cockatrice/src/client/ui/widgets/cards/additional_info/color_identity_widget.h b/cockatrice/src/client/ui/widgets/cards/additional_info/color_identity_widget.h
index 8518aa1a5..9b8fef71c 100644
--- a/cockatrice/src/client/ui/widgets/cards/additional_info/color_identity_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/additional_info/color_identity_widget.h
@@ -1,7 +1,7 @@
#ifndef COLOR_IDENTITY_WIDGET_H
#define COLOR_IDENTITY_WIDGET_H
-#include "../../../../../game/cards/card_database.h"
+#include "../../../../../game/cards/card_info.h"
#include
#include
diff --git a/cockatrice/src/client/ui/widgets/cards/additional_info/mana_cost_widget.h b/cockatrice/src/client/ui/widgets/cards/additional_info/mana_cost_widget.h
index 0872183f3..c9ed221be 100644
--- a/cockatrice/src/client/ui/widgets/cards/additional_info/mana_cost_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/additional_info/mana_cost_widget.h
@@ -1,7 +1,7 @@
#ifndef MANA_COST_WIDGET_H
#define MANA_COST_WIDGET_H
-#include "../../../../../game/cards/card_database.h"
+#include "../../../../../game/cards/card_info.h"
#include
#include
diff --git a/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
new file mode 100644
index 000000000..71569e92a
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
@@ -0,0 +1,81 @@
+#include "card_group_display_widget.h"
+
+#include "../../../../../deck/deck_list_model.h"
+#include "../../../../../game/cards/card_database_manager.h"
+#include "../../../../../utility/card_info_comparator.h"
+#include "../card_info_picture_with_text_overlay_widget.h"
+
+#include
+
+CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
+ DeckListModel *_deckListModel,
+ QString _zoneName,
+ QString _cardGroupCategory,
+ QString _activeGroupCriteria,
+ QStringList _activeSortCriteria,
+ int bannerOpacity,
+ CardSizeWidget *_cardSizeWidget)
+ : QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), cardGroupCategory(_cardGroupCategory),
+ activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria),
+ cardSizeWidget(_cardSizeWidget)
+{
+ layout = new QVBoxLayout(this);
+ setLayout(layout);
+ setMinimumSize(QSize(0, 0));
+
+ banner = new BannerWidget(this, cardGroupCategory, Qt::Orientation::Vertical, bannerOpacity);
+
+ layout->addWidget(banner);
+ updateCardDisplays();
+}
+
+void CardGroupDisplayWidget::updateCardDisplays()
+{
+}
+
+QList CardGroupDisplayWidget::getCardsMatchingGroup(QList cardsToSort)
+{
+ cardsToSort = sortCardList(cardsToSort, activeSortCriteria, Qt::SortOrder::AscendingOrder);
+
+ QList activeList;
+ for (const CardInfoPtr &info : cardsToSort) {
+ if (info && info->getProperty(activeGroupCriteria) == cardGroupCategory) {
+ activeList.append(info);
+ }
+ }
+
+ return activeList;
+}
+
+QList CardGroupDisplayWidget::sortCardList(QList cardsToSort,
+ const QStringList properties,
+ Qt::SortOrder order = Qt::AscendingOrder)
+{
+ CardInfoComparator comparator(properties, order);
+ std::sort(cardsToSort.begin(), cardsToSort.end(), comparator);
+
+ return cardsToSort;
+}
+
+void CardGroupDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSortCriteria)
+{
+ if (activeSortCriteria != _activeSortCriteria) {
+ activeSortCriteria = _activeSortCriteria;
+ updateCardDisplays(); // Refresh display with new sorting
+ }
+}
+
+void CardGroupDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
+{
+ emit cardClicked(event, card);
+}
+
+void CardGroupDisplayWidget::onHover(CardInfoPtr card)
+{
+ emit cardHovered(card);
+}
+
+void CardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.h b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.h
new file mode 100644
index 000000000..079c5e3ea
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.h
@@ -0,0 +1,53 @@
+#ifndef CARD_GROUP_DISPLAY_WIDGET_H
+#define CARD_GROUP_DISPLAY_WIDGET_H
+
+#include "../../../../../deck/deck_list_model.h"
+#include "../../../../../game/cards/card_info.h"
+#include "../../general/display/banner_widget.h"
+#include "../card_info_picture_with_text_overlay_widget.h"
+#include "../card_size_widget.h"
+
+#include
+#include
+#include
+
+class CardGroupDisplayWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CardGroupDisplayWidget(QWidget *parent,
+ DeckListModel *deckListModel,
+ QString zoneName,
+ QString cardGroupCategory,
+ QString activeGroupCriteria,
+ QStringList activeSortCriteria,
+ int bannerOpacity,
+ CardSizeWidget *cardSizeWidget);
+
+ QList getCardsMatchingGroup(QList cardsToSort);
+ void resizeEvent(QResizeEvent *event) override;
+
+ DeckListModel *deckListModel;
+ QString zoneName;
+ QString cardGroupCategory;
+ QString activeGroupCriteria;
+ QStringList activeSortCriteria;
+ CardSizeWidget *cardSizeWidget;
+
+public slots:
+ QList sortCardList(QList cardsToSort, QStringList properties, Qt::SortOrder order);
+ void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
+ void onHover(CardInfoPtr card);
+ virtual void updateCardDisplays();
+ void onActiveSortCriteriaChanged(QStringList activeSortCriteria);
+
+signals:
+ void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
+ void cardHovered(CardInfoPtr card);
+
+protected:
+ QVBoxLayout *layout;
+ BannerWidget *banner;
+};
+#endif // CARD_GROUP_DISPLAY_WIDGET_H
diff --git a/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
new file mode 100644
index 000000000..62eeb0134
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
@@ -0,0 +1,107 @@
+#include "flat_card_group_display_widget.h"
+
+#include "../../../../../deck/deck_list_model.h"
+#include "../../../../../game/cards/card_database_manager.h"
+#include "../../../../../utility/card_info_comparator.h"
+#include "../card_info_picture_with_text_overlay_widget.h"
+
+#include
+
+FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,
+ DeckListModel *_deckListModel,
+ QString _zoneName,
+ QString _cardGroupCategory,
+ QString _activeGroupCriteria,
+ QStringList _activeSortCriteria,
+ int bannerOpacity,
+ CardSizeWidget *_cardSizeWidget)
+ : CardGroupDisplayWidget(parent,
+ _deckListModel,
+ _zoneName,
+ _cardGroupCategory,
+ _activeGroupCriteria,
+ _activeSortCriteria,
+ bannerOpacity,
+ _cardSizeWidget)
+{
+ flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
+ banner->setBuddy(flowWidget);
+
+ layout->addWidget(flowWidget);
+ FlatCardGroupDisplayWidget::updateCardDisplays();
+ connect(deckListModel, &DeckListModel::dataChanged, this, &FlatCardGroupDisplayWidget::updateCardDisplays);
+}
+
+void FlatCardGroupDisplayWidget::updateCardDisplays()
+{
+ // Retrieve and sort cards
+ QList cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName));
+
+ // Show or hide widget
+ bool shouldBeVisible = !cardsInZone.isEmpty();
+ if (shouldBeVisible != isVisible()) {
+ setVisible(shouldBeVisible);
+ }
+
+ // Retrieve existing widgets
+ QList existingWidgets =
+ flowWidget->findChildren();
+
+ QHash> widgetMap;
+ for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
+ widgetMap[widget->getInfo()->getName()].append(widget);
+ }
+
+ QList sortedWidgets;
+ QSet usedWidgets;
+
+ // Ensure widgets are ordered to match the sorted cards
+ for (const CardInfoPtr &card : cardsInZone) {
+ QString name = card->getName();
+ CardInfoPictureWithTextOverlayWidget *widget = nullptr;
+
+ if (!widgetMap[name].isEmpty()) {
+ // Reuse an existing widget
+ widget = widgetMap[name].takeFirst();
+ } else {
+ // Create a new widget if needed
+ widget = new CardInfoPictureWithTextOverlayWidget(flowWidget, true);
+ widget->setScaleFactor(cardSizeWidget->getSlider()->value());
+ widget->setCard(card);
+
+ connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
+ &FlatCardGroupDisplayWidget::onClick);
+ connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this,
+ &FlatCardGroupDisplayWidget::onHover);
+ connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget,
+ &CardInfoPictureWidget::setScaleFactor);
+
+ flowWidget->addWidget(widget);
+ }
+
+ // Store in sorted order
+ sortedWidgets.append(widget);
+ usedWidgets.insert(widget);
+ }
+
+ // Remove extra widgets
+ for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
+ if (!usedWidgets.contains(widget)) {
+ flowWidget->layout()->removeWidget(widget);
+ widget->deleteLater();
+ }
+ }
+
+ // **Reorder widgets in place**
+ for (int i = 0; i < sortedWidgets.size(); ++i) {
+ sortedWidgets[i]->setParent(nullptr); // Temporarily detach
+ }
+ for (int i = 0; i < sortedWidgets.size(); ++i) {
+ flowWidget->addWidget(sortedWidgets[i]); // Reattach in correct order
+ }
+}
+
+void FlatCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.h b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.h
new file mode 100644
index 000000000..45a75907e
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.h
@@ -0,0 +1,30 @@
+#ifndef FLAT_CARD_GROUP_DISPLAY_WIDGET_H
+#define FLAT_CARD_GROUP_DISPLAY_WIDGET_H
+
+#include "../../general/layout_containers/flow_widget.h"
+#include "card_group_display_widget.h"
+
+class FlatCardGroupDisplayWidget : public CardGroupDisplayWidget
+{
+ Q_OBJECT
+
+public:
+ FlatCardGroupDisplayWidget(QWidget *parent,
+ DeckListModel *deckListModel,
+ QString zoneName,
+ QString cardGroupCategory,
+ QString activeGroupCriteria,
+ QStringList activeSortCriteria,
+ int bannerOpacity,
+ CardSizeWidget *cardSizeWidget);
+
+ void resizeEvent(QResizeEvent *event) override;
+
+public slots:
+ void updateCardDisplays() override;
+
+private:
+ FlowWidget *flowWidget;
+};
+
+#endif // FLAT_CARD_GROUP_DISPLAY_WIDGET_H
diff --git a/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
new file mode 100644
index 000000000..0e4a88cf3
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
@@ -0,0 +1,122 @@
+#include "overlapped_card_group_display_widget.h"
+
+#include "../../../../../deck/deck_list_model.h"
+#include "../../../../../game/cards/card_database_manager.h"
+#include "../../../../../utility/card_info_comparator.h"
+#include "../card_info_picture_with_text_overlay_widget.h"
+
+#include
+
+OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent,
+ DeckListModel *_deckListModel,
+ QString _zoneName,
+ QString _cardGroupCategory,
+ QString _activeGroupCriteria,
+ QStringList _activeSortCriteria,
+ int bannerOpacity,
+ CardSizeWidget *_cardSizeWidget)
+ : CardGroupDisplayWidget(parent,
+ _deckListModel,
+ _zoneName,
+ _cardGroupCategory,
+ _activeGroupCriteria,
+ _activeSortCriteria,
+ bannerOpacity,
+ _cardSizeWidget)
+{
+ overlapWidget = new OverlapWidget(this, 80, 1, 1, Qt::Vertical, true);
+ banner->setBuddy(overlapWidget);
+
+ layout->addWidget(overlapWidget);
+ OverlappedCardGroupDisplayWidget::updateCardDisplays();
+ connect(deckListModel, &DeckListModel::dataChanged, this, &OverlappedCardGroupDisplayWidget::updateCardDisplays);
+ connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, this,
+ [this]() { overlapWidget->adjustMaxColumnsAndRows(); });
+}
+
+void OverlappedCardGroupDisplayWidget::updateCardDisplays()
+{
+ overlapWidget->setUpdatesEnabled(false);
+ // Retrieve and sort cards
+ QList cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName));
+
+ // Show or hide widget
+ bool shouldBeVisible = !cardsInZone.isEmpty();
+ if (shouldBeVisible != isVisible()) {
+ setVisible(shouldBeVisible);
+ }
+
+ // Retrieve existing widgets
+ QList existingWidgets =
+ overlapWidget->findChildren();
+
+ QHash> widgetMap;
+ for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
+ widgetMap[widget->getInfo()->getName()].append(widget);
+ }
+
+ QList sortedWidgets;
+ QSet usedWidgets;
+
+ // Ensure widgets are ordered to match the sorted cards
+ for (const CardInfoPtr &card : cardsInZone) {
+ QString name = card->getName();
+ CardInfoPictureWithTextOverlayWidget *widget = nullptr;
+
+ if (!widgetMap[name].isEmpty()) {
+ // Reuse an existing widget
+ widget = widgetMap[name].takeFirst();
+ } else {
+ // Create a new widget if needed
+ widget = new CardInfoPictureWithTextOverlayWidget(overlapWidget, true);
+ widget->setScaleFactor(cardSizeWidget->getSlider()->value());
+ widget->setCard(card);
+
+ connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
+ &OverlappedCardGroupDisplayWidget::onClick);
+ connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this,
+ &OverlappedCardGroupDisplayWidget::onHover);
+ connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget,
+ &CardInfoPictureWidget::setScaleFactor);
+
+ overlapWidget->addWidget(widget);
+ }
+
+ // Store in sorted order
+ sortedWidgets.append(widget);
+ usedWidgets.insert(widget);
+ }
+
+ // Remove extra widgets
+ for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
+ if (!usedWidgets.contains(widget)) {
+ overlapWidget->layout()->removeWidget(widget);
+ widget->deleteLater();
+ }
+ }
+
+ // **Reorder widgets in place**
+ for (int i = 0; i < sortedWidgets.size(); ++i) {
+ sortedWidgets[i]->setParent(nullptr); // Temporarily detach
+ }
+ for (int i = 0; i < sortedWidgets.size(); ++i) {
+ overlapWidget->addWidget(sortedWidgets[i]); // Reattach in correct order
+ }
+
+ // Ensure proper layering
+ for (CardInfoPictureWithTextOverlayWidget *widget : sortedWidgets) {
+ widget->raise();
+ }
+
+ overlapWidget->adjustMaxColumnsAndRows();
+ overlapWidget->setUpdatesEnabled(true);
+ overlapWidget->update();
+}
+
+void OverlappedCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+
+ overlapWidget->resize(event->size());
+ overlapWidget->adjustMaxColumnsAndRows();
+}
\ No newline at end of file
diff --git a/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.h b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.h
new file mode 100644
index 000000000..e492316cd
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.h
@@ -0,0 +1,30 @@
+#ifndef OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H
+#define OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H
+
+#include "../../general/layout_containers/overlap_widget.h"
+#include "card_group_display_widget.h"
+
+class OverlappedCardGroupDisplayWidget : public CardGroupDisplayWidget
+{
+ Q_OBJECT
+
+public:
+ OverlappedCardGroupDisplayWidget(QWidget *parent,
+ DeckListModel *deckListModel,
+ QString zoneName,
+ QString cardGroupCategory,
+ QString activeGroupCriteria,
+ QStringList activeSortCriteria,
+ int bannerOpacity,
+ CardSizeWidget *cardSizeWidget);
+
+ void resizeEvent(QResizeEvent *event) override;
+
+public slots:
+ void updateCardDisplays() override;
+
+private:
+ OverlapWidget *overlapWidget;
+};
+
+#endif // OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.cpp
index 780ef53fe..d967c8477 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.cpp
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.cpp
@@ -22,7 +22,7 @@ CardInfoDisplayWidget::CardInfoDisplayWidget(const QString &cardName,
pic->setObjectName("pic");
text = new CardInfoTextWidget();
text->setObjectName("text");
- connect(text, SIGNAL(linkActivated(const QString &)), this, SLOT(setCard(const QString &)));
+ connect(text, &CardInfoTextWidget::linkActivated, this, [this](const QString &card) { setCard(card); });
auto *layout = new QVBoxLayout();
layout->setObjectName("layout");
@@ -52,7 +52,7 @@ void CardInfoDisplayWidget::setCard(CardInfoPtr card)
disconnect(info.data(), nullptr, this, nullptr);
info = std::move(card);
if (info)
- connect(info.data(), SIGNAL(destroyed()), this, SLOT(clear()));
+ connect(info.data(), &QObject::destroyed, this, &CardInfoDisplayWidget::clear);
text->setCard(info);
pic->setCard(info);
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.h
index 46d42b2bc..29ad703e7 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_display_widget.h
@@ -1,7 +1,7 @@
#ifndef CARDINFOWIDGET_H
#define CARDINFOWIDGET_H
-#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_info.h"
#include
#include
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..4d85f38a0 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
@@ -21,7 +21,7 @@ CardInfoFrameWidget::CardInfoFrameWidget(const QString &cardName, QWidget *paren
text = new CardInfoTextWidget();
text->setObjectName("text");
- connect(text, SIGNAL(linkActivated(const QString &)), this, SLOT(setCard(const QString &)));
+ connect(text, &CardInfoTextWidget::linkActivated, this, qOverload(&CardInfoFrameWidget::setCard));
tab1 = new QWidget(this);
tab2 = new QWidget(this);
@@ -34,7 +34,7 @@ CardInfoFrameWidget::CardInfoFrameWidget(const QString &cardName, QWidget *paren
insertTab(ImageOnlyView, tab1, QString());
insertTab(TextOnlyView, tab2, QString());
insertTab(ImageAndTextView, tab3, QString());
- connect(this, SIGNAL(currentChanged(int)), this, SLOT(setViewMode(int)));
+ connect(this, &CardInfoFrameWidget::currentChanged, this, &CardInfoFrameWidget::setViewMode);
tab1Layout = new QVBoxLayout();
tab1Layout->setObjectName("tab1Layout");
@@ -61,6 +61,7 @@ CardInfoFrameWidget::CardInfoFrameWidget(const QString &cardName, QWidget *paren
setViewMode(SettingsCache::instance().getCardInfoViewMode());
+ // TODO: Change this to be by UUID
setCard(CardDatabaseManager::getInstance()->getCard(cardName));
}
@@ -154,7 +155,7 @@ void CardInfoFrameWidget::setCard(CardInfoPtr card)
info = std::move(card);
if (info) {
- connect(info.data(), SIGNAL(destroyed()), this, SLOT(clearCard()));
+ connect(info.data(), &QObject::destroyed, this, &CardInfoFrameWidget::clearCard);
}
setViewTransformationButtonVisibility(hasTransformation(info));
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h
index 2b6012f1f..40a3b454a 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h
@@ -1,7 +1,7 @@
#ifndef CARDFRAME_H
#define CARDFRAME_H
-#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_info.h"
#include
#include
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_enlarged_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_picture_enlarged_widget.h
index 73b0a9cc5..d65b0da69 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_enlarged_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_enlarged_widget.h
@@ -1,7 +1,7 @@
#ifndef CARD_PICTURE_ENLARGED_WIDGET_H
#define CARD_PICTURE_ENLARGED_WIDGET_H
-#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_info.h"
#include
#include
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 f4f699171..4d76989b1 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
@@ -9,6 +9,7 @@
#include
#include
+#include
#include
#include
#include
@@ -29,21 +30,33 @@
*
* Initializes the widget with a minimum height and sets the pixmap to a dirty state for initial loading.
*/
-CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool hoverToZoomEnabled)
- : QWidget(parent), info(nullptr), pixmapDirty(true), hoverToZoomEnabled(hoverToZoomEnabled)
+CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverToZoomEnabled, const bool _raiseOnEnter)
+ : QWidget(parent), info(nullptr), pixmapDirty(true), hoverToZoomEnabled(_hoverToZoomEnabled),
+ raiseOnEnter(_raiseOnEnter)
{
setMinimumHeight(baseHeight);
if (hoverToZoomEnabled) {
setMouseTracking(true);
}
- enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(this);
+ enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(this->window());
enlargedPixmapWidget->hide();
hoverTimer = new QTimer(this);
hoverTimer->setSingleShot(true);
connect(hoverTimer, &QTimer::timeout, this, &CardInfoPictureWidget::showEnlargedPixmap);
+ // Store the widget's original position
+ originalPos = this->pos();
+
+ // Create the animation
+ animation = new QPropertyAnimation(this, "pos");
+ animation->setDuration(200); // 200ms animation duration
+ animation->setEasingCurve(QEasingCurve::OutQuad);
+
+ animation->setStartValue(originalPos);
+ animation->setEndValue(originalPos - QPoint(0, animationOffset));
+
connect(&SettingsCache::instance(), &SettingsCache::roundCardCornersChanged, this, [this](bool _roundCardCorners) {
Q_UNUSED(_roundCardCorners);
@@ -67,7 +80,7 @@ void CardInfoPictureWidget::setCard(CardInfoPtr card)
info = std::move(card);
if (info) {
- connect(info.data(), SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap()));
+ connect(info.data(), &CardInfo::pixmapUpdated, this, &CardInfoPictureWidget::updatePixmap);
}
updatePixmap();
@@ -83,6 +96,11 @@ void CardInfoPictureWidget::setHoverToZoomEnabled(const bool enabled)
setMouseTracking(enabled);
}
+void CardInfoPictureWidget::setRaiseOnEnterEnabled(const bool enabled)
+{
+ raiseOnEnter = enabled;
+}
+
/**
* @brief Handles widget resizing by updating the pixmap size.
* @param event The resize event (unused).
@@ -92,6 +110,7 @@ void CardInfoPictureWidget::setHoverToZoomEnabled(const bool enabled)
void CardInfoPictureWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
+ originalPos = pos(); // Update the baseline position
updatePixmap();
}
@@ -210,7 +229,8 @@ void CardInfoPictureWidget::paintEvent(QPaintEvent *event)
*/
QSize CardInfoPictureWidget::sizeHint() const
{
- return {static_cast(baseWidth * scaleFactor), static_cast(baseHeight * scaleFactor)};
+ return {static_cast(baseWidth * scaleFactor / 100.0),
+ static_cast(baseWidth * scaleFactor / 100.0 * aspectRatio)};
}
/**
@@ -232,6 +252,18 @@ void CardInfoPictureWidget::enterEvent(QEvent *event)
// Emit signal indicating a card is being hovered on
emit hoveredOnCard(info);
+
+ if (raiseOnEnter) {
+ if (animation->state() == QAbstractAnimation::Running) {
+ animation->pause(); // Pause current animation
+ } else {
+ originalPos = this->pos(); // Update the baseline position
+ animation->setStartValue(originalPos);
+ animation->setEndValue(originalPos - QPoint(0, animationOffset));
+ }
+ animation->setDirection(QAbstractAnimation::Forward);
+ animation->start();
+ }
}
/**
@@ -241,10 +273,32 @@ void CardInfoPictureWidget::enterEvent(QEvent *event)
void CardInfoPictureWidget::leaveEvent(QEvent *event)
{
QWidget::leaveEvent(event);
+
if (hoverToZoomEnabled) {
hoverTimer->stop();
enlargedPixmapWidget->hide();
}
+
+ if (raiseOnEnter) {
+ if (animation->state() == QAbstractAnimation::Running) {
+ animation->pause(); // Pause current animation
+ }
+ animation->setDirection(QAbstractAnimation::Backward);
+ animation->start();
+ }
+}
+
+void CardInfoPictureWidget::moveEvent(QMoveEvent *event)
+{
+ QWidget::moveEvent(event);
+
+ hoverTimer->stop();
+ enlargedPixmapWidget->hide();
+
+ if (animation->state() == QAbstractAnimation::Running) {
+ return;
+ }
+ originalPos = this->pos(); // Update the baseline position
}
/**
@@ -254,10 +308,24 @@ void CardInfoPictureWidget::leaveEvent(QEvent *event)
void CardInfoPictureWidget::mouseMoveEvent(QMouseEvent *event)
{
QWidget::mouseMoveEvent(event);
+
if (hoverToZoomEnabled && enlargedPixmapWidget->isVisible()) {
- const QPointF cursorPos = QCursor::pos();
- enlargedPixmapWidget->move(QPoint(static_cast(cursorPos.x()) + enlargedPixmapOffset,
- static_cast(cursorPos.y()) + enlargedPixmapOffset));
+ const QPoint cursorPos = QCursor::pos();
+ const QRect screenGeometry = QGuiApplication::screenAt(cursorPos)->geometry();
+ const QSize widgetSize = enlargedPixmapWidget->size();
+
+ int newX = cursorPos.x() + enlargedPixmapOffset;
+ int newY = cursorPos.y() + enlargedPixmapOffset;
+
+ // Adjust if out of bounds
+ if (newX + widgetSize.width() > screenGeometry.right()) {
+ newX = cursorPos.x() - widgetSize.width() - enlargedPixmapOffset;
+ }
+ if (newY + widgetSize.height() > screenGeometry.bottom()) {
+ newY = cursorPos.y() - widgetSize.height() - enlargedPixmapOffset;
+ }
+
+ enlargedPixmapWidget->move(newX, newY);
}
}
@@ -266,7 +334,11 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
QWidget::mousePressEvent(event);
if (event->button() == Qt::RightButton) {
createRightClickMenu()->popup(QCursor::pos());
+ } else {
+ emit cardClicked();
}
+
+ emit cardClicked();
}
QMenu *CardInfoPictureWidget::createRightClickMenu()
@@ -370,13 +442,25 @@ void CardInfoPictureWidget::showEnlargedPixmap() const
return;
}
- const QSize enlargedSize(static_cast(size().width() * scaleFactor),
- static_cast(size().width() * aspectRatio * scaleFactor));
+ const QSize enlargedSize(static_cast(size().width() * 2), static_cast(size().width() * aspectRatio * 2));
enlargedPixmapWidget->setCardPixmap(info, enlargedSize);
- const QPointF cursorPos = QCursor::pos();
- enlargedPixmapWidget->move(static_cast(cursorPos.x()) + enlargedPixmapOffset,
- static_cast(cursorPos.y()) + enlargedPixmapOffset);
+ const QPoint cursorPos = QCursor::pos();
+ const QRect screenGeometry = QGuiApplication::screenAt(cursorPos)->geometry();
+ const QSize widgetSize = enlargedPixmapWidget->size();
+
+ int newX = cursorPos.x() + enlargedPixmapOffset;
+ int newY = cursorPos.y() + enlargedPixmapOffset;
+
+ // Adjust if out of bounds
+ if (newX + widgetSize.width() > screenGeometry.right()) {
+ newX = cursorPos.x() - widgetSize.width() - enlargedPixmapOffset;
+ }
+ if (newY + widgetSize.height() > screenGeometry.bottom()) {
+ newY = cursorPos.y() - widgetSize.height() - enlargedPixmapOffset;
+ }
+
+ enlargedPixmapWidget->move(newX, newY);
enlargedPixmapWidget->show();
}
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h
index a74028a7f..10cc422aa 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h
@@ -1,9 +1,10 @@
#ifndef CARD_INFO_PICTURE_H
#define CARD_INFO_PICTURE_H
-#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_info.h"
#include "card_info_picture_enlarged_widget.h"
+#include
#include
#include
@@ -17,23 +18,27 @@ class CardInfoPictureWidget : public QWidget
Q_OBJECT
public:
- explicit CardInfoPictureWidget(QWidget *parent = nullptr, bool hoverToZoomEnabled = false);
+ explicit CardInfoPictureWidget(QWidget *parent = nullptr,
+ bool hoverToZoomEnabled = false,
+ bool raiseOnEnter = false);
CardInfoPtr getInfo()
{
return info;
}
[[nodiscard]] QSize sizeHint() const override;
- void setHoverToZoomEnabled(bool enabled);
public slots:
void setCard(CardInfoPtr card);
void setScaleFactor(int scale); // New slot for scaling
+ void setHoverToZoomEnabled(bool enabled);
+ void setRaiseOnEnterEnabled(bool enabled);
void updatePixmap();
signals:
void hoveredOnCard(CardInfoPtr hoveredCard);
void cardScaleFactorChanged(int _scale);
void cardChanged(CardInfoPtr card);
+ void cardClicked();
protected:
void resizeEvent(QResizeEvent *event) override;
@@ -44,6 +49,7 @@ protected:
void enterEvent(QEvent *event) override; // Qt5 signature
#endif
void leaveEvent(QEvent *event) override;
+ void moveEvent(QMoveEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void loadPixmap();
@@ -60,14 +66,18 @@ private:
qreal aspectRatio = magicTheGatheringCardAspectRatio;
int baseWidth = 200;
int baseHeight = 200;
- double scaleFactor = 1.5;
+ double scaleFactor = 100;
QPixmap resizedPixmap;
bool pixmapDirty;
bool hoverToZoomEnabled;
+ bool raiseOnEnter;
int hoverActivateThresholdInMs = 500;
CardInfoPictureEnlargedWidget *enlargedPixmapWidget;
int enlargedPixmapOffset = 10;
QTimer *hoverTimer;
+ QPropertyAnimation *animation;
+ QPoint originalPos; // Store the original position
+ const int animationOffset = 10; // Adjust this for how much the widget moves up
QMenu *createRightClickMenu();
QMenu *createViewRelatedCardsMenu();
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
index 255ab4738..1545dc4f6 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
@@ -9,6 +9,7 @@
* @brief Constructs a CardPictureWithTextOverlay widget.
* @param parent The parent widget.
* @param hoverToZoomEnabled If this widget will spawn a larger widget when hovered over.
+ * @param raiseOnEnter If this widget will raise slightly when entered.
* @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.
@@ -18,11 +19,12 @@
*/
CardInfoPictureWithTextOverlayWidget::CardInfoPictureWithTextOverlayWidget(QWidget *parent,
const bool hoverToZoomEnabled,
+ const bool raiseOnEnter,
const QColor &textColor,
const QColor &outlineColor,
const int fontSize,
const Qt::Alignment alignment)
- : CardInfoPictureWidget(parent, hoverToZoomEnabled), textColor(textColor), outlineColor(outlineColor),
+ : CardInfoPictureWidget(parent, hoverToZoomEnabled, raiseOnEnter), textColor(textColor), outlineColor(outlineColor),
fontSize(fontSize), textAlignment(alignment)
{
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.h
index 44107362a..97cee1a8c 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.h
@@ -14,6 +14,7 @@ class CardInfoPictureWithTextOverlayWidget : public CardInfoPictureWidget
public:
explicit CardInfoPictureWithTextOverlayWidget(QWidget *parent = nullptr,
bool hoverToZoomEnabled = false,
+ bool raiseOnEnter = false,
const QColor &textColor = Qt::white,
const QColor &outlineColor = Qt::black,
int fontSize = 12,
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_text_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_text_widget.h
index 17985021d..e066fdb34 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_text_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_text_widget.h
@@ -1,7 +1,7 @@
#ifndef CARDINFOTEXT_H
#define CARDINFOTEXT_H
-#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_info.h"
#include
class QLabel;
diff --git a/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp
new file mode 100644
index 000000000..b074241fe
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp
@@ -0,0 +1,168 @@
+#include "deck_card_zone_display_widget.h"
+
+#include "../../../../deck/deck_list_model.h"
+#include "../../../../utility/card_info_comparator.h"
+#include "card_group_display_widgets/flat_card_group_display_widget.h"
+#include "card_group_display_widgets/overlapped_card_group_display_widget.h"
+
+#include
+
+DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
+ DeckListModel *_deckListModel,
+ QString _zoneName,
+ QString _activeGroupCriteria,
+ QStringList _activeSortCriteria,
+ int bannerOpacity,
+ int subBannerOpacity,
+ CardSizeWidget *_cardSizeWidget)
+ : QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), activeGroupCriteria(_activeGroupCriteria),
+ activeSortCriteria(_activeSortCriteria), bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity),
+ cardSizeWidget(_cardSizeWidget)
+{
+ 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, &DeckListModel::dataChanged, this, &DeckCardZoneDisplayWidget::displayCards);
+}
+void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ for (QObject *child : layout->children()) {
+ QWidget *widget = qobject_cast(child);
+ if (widget) {
+ widget->setMaximumWidth(width());
+ }
+ }
+}
+void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
+{
+ emit cardClicked(event, card, zoneName);
+}
+void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card)
+{
+ emit cardHovered(card);
+}
+
+void DeckCardZoneDisplayWidget::displayCards()
+{
+ addCardGroupIfItDoesNotExist();
+ deleteCardGroupIfItDoesNotExist();
+}
+
+void DeckCardZoneDisplayWidget::refreshDisplayType(const QString &_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;
+ }
+
+ // 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::addCardGroupIfItDoesNotExist()
+{
+ QList cardGroupsDisplayWidgets =
+ cardGroupContainer->findChildren();
+
+ QList cardGroups = getGroupCriteriaValueList();
+
+ for (QString cardGroup : cardGroups) {
+ bool found = false;
+ for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
+ if (cardGroupDisplayWidget->cardGroupCategory == cardGroup) {
+ found = true;
+ }
+ }
+
+ if (found) {
+ continue;
+ }
+
+ if (displayType == "overlap") {
+ auto *display_widget = new OverlappedCardGroupDisplayWidget(
+ cardGroupContainer, deckListModel, zoneName, cardGroup, activeGroupCriteria, activeSortCriteria,
+ subBannerOpacity, cardSizeWidget);
+ connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
+ SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
+ connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
+ connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, display_widget,
+ &CardGroupDisplayWidget::onActiveSortCriteriaChanged);
+ cardGroupLayout->addWidget(display_widget);
+ } else if (displayType == "flat") {
+ auto *display_widget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, zoneName,
+ cardGroup, activeGroupCriteria, activeSortCriteria,
+ subBannerOpacity, cardSizeWidget);
+ connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
+ SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
+ connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
+ connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, display_widget,
+ &CardGroupDisplayWidget::onActiveSortCriteriaChanged);
+ cardGroupLayout->addWidget(display_widget);
+ }
+ }
+}
+
+void DeckCardZoneDisplayWidget::deleteCardGroupIfItDoesNotExist()
+{
+ QList cardGroupsDisplayWidgets =
+ cardGroupContainer->findChildren();
+
+ QList validGroups = getGroupCriteriaValueList();
+
+ for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
+ if (!validGroups.contains(cardGroupDisplayWidget->cardGroupCategory)) {
+ cardGroupLayout->removeWidget(cardGroupDisplayWidget);
+ cardGroupDisplayWidget->deleteLater(); // Properly delete the widget after the event loop cycles
+ }
+ }
+}
+
+void DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged(QString _activeGroupCriteria)
+{
+ activeGroupCriteria = _activeGroupCriteria;
+ displayCards();
+}
+
+void DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSortCriteria)
+{
+ activeSortCriteria = _activeSortCriteria;
+ emit activeSortCriteriaChanged(activeSortCriteria);
+}
+
+QList DeckCardZoneDisplayWidget::getGroupCriteriaValueList()
+{
+ QList groupCriteriaValues;
+
+ QList