diff --git a/.ci/release_template.md b/.ci/release_template.md
index ecd758ebf..6e53a3ee1 100644
--- a/.ci/release_template.md
+++ b/.ci/release_template.md
@@ -4,24 +4,30 @@
git push -d origin --REPLACE-WITH-BETA-LIST--
-->
-
+
-Pre-compiled binaries we serve:
- - Windows 10+
- - Windows 7+
- - macOS 14+ ("Sonoma") / Apple M
- - macOS 13+ ("Ventura") / Intel
- - Ubuntu 24.04 LTS ("Noble Numbat")
- - Ubuntu 22.04 LTS ("Jammy Jellyfish")
- - Ubuntu 20.04 LTS ("Focal Fossa")
- - Debian 12 ("Bookworm")
- - Debian 11 ("Bullseye")
- - Fedora 41
- - Fedora 40
-
-We are also packaged in Arch Linux's official "extra" repository, courtesy of @FFY00
-General Linux support is available via a flatpak package (Flathub)
+Available pre-compiled binaries for installation:
+
+ Windows
+ • Windows 10+
+ • Windows 7+
+
+ macOS
+ • macOS 15+ Sequoia Apple M
+ • macOS 14+ Sonoma Apple M
+ • macOS 13+ Ventura Intel
+
+ Linux
+ • Ubuntu 24.04 LTS Noble Numbat
+ • Ubuntu 22.04 LTS Jammy Jellyfish
+ • Ubuntu 20.04 LTS Focal Fossa
+ • Debian 12 Bookworm
+ • Debian 11 Bullseye
+ • Fedora 41
+ • Fedora 40
+
+We are also packaged in Arch Linux's official extra repository, courtesy of @FFY00.
+General Linux support is available via a flatpak package at Flathub!
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea6647fb7..29ee84798 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,7 +74,7 @@ endif()
# A project name is needed for CPack
# Version can be overriden by git tags, see cmake/getversion.cmake
-project("Cockatrice" VERSION 2.10.1)
+project("Cockatrice" VERSION 2.11.0)
# Set release name if not provided via env/cmake var
if(NOT DEFINED GIT_TAG_RELEASENAME)
diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt
index 9b50c9469..b12b1a27a 100644
--- a/cockatrice/CMakeLists.txt
+++ b/cockatrice/CMakeLists.txt
@@ -4,9 +4,243 @@
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_card_details_display_widget.cpp
+ src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_list_display_widget.cpp
+ src/client/tabs/api/edhrec/display/commander/edhrec_commander_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/commander/edhrec_commander_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/commander/edhrec_commander_api_response_card_details.cpp
+ src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_card_list.cpp
+ src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_card_container.cpp
+ src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_card_prices.cpp
+ src/client/tabs/api/edhrec/api_response/commander/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/cockatrice_en@source.ts b/cockatrice/cockatrice_en@source.ts
index be95b2f58..9574f33a4 100644
--- a/cockatrice/cockatrice_en@source.ts
+++ b/cockatrice/cockatrice_en@source.ts
@@ -131,123 +131,128 @@ Please check that the directory is writable and try again.
AppearanceSettingsPage
-
+
Error
-
+
Could not create themes directory at '%1'.
-
+
Theme settings
-
+
Current theme:
-
+
Open themes folder
-
+
Menu settings
-
+
Show keyboard shortcuts in right-click menus
-
+
Card rendering
-
+
Display card names on cards having a picture
-
+
Auto-Rotate cards with sideways layout
-
+
Override all card art with personal set preference (Pre-ProviderID change behavior) [Requires Client restart]
-
+
Bump sets that the deck contains cards from to the top in the printing selector
-
+
Scale cards on mouse over
-
- Minimum overlap percentage of cards on the stack and in vertical hand
-
-
-
-
- Maximum initial height for card view window:
-
-
-
-
- rows
-
-
-
-
- Maximum expanded height for card view window:
+ Use rounded card corners
- Hand layout
+ Minimum overlap percentage of cards on the stack and in vertical hand
- Display hand horizontally (wastes space)
+ Maximum initial height for card view window:
- Enable left justification
-
-
-
- Table grid layout
+ rows
-
- Invert vertical coordinate
+
+ Maximum expanded height for card view window:
- Minimum player count for multi-column layout:
+ Hand layout
+ Display hand horizontally (wastes space)
+
+
+
+
+ Enable left justification
+
+
+
+
+ Table grid layout
+
+
+
+
+ Invert vertical coordinate
+
+
+
+
+ Minimum player count for multi-column layout:
+
+
+
+
Maximum font size for information displayed on cards:
@@ -533,22 +538,22 @@ This is only saved for moderators and cannot be seen by the banned person.
CardInfoPictureWidget
-
+
View related cards
-
+
Add card to deck
-
+
Mainboard
-
+
Sideboard
@@ -963,166 +968,166 @@ This is only saved for moderators and cannot be seen by the banned person.
DeckEditorSettingsPage
-
-
+
+
Update Spoilers
-
-
+
+
Success
-
+
Download URLs have been reset.
-
+
Downloaded card pictures have been reset.
-
+
Error
-
+
One or more downloaded card pictures could not be cleared.
-
+
Add URL
-
-
+
+
URL:
-
-
+
+
Edit URL
-
+
Network Cache Size:
-
+
Redirect Cache TTL:
-
+
How long cached redirects for urls are valid for.
-
+
Picture Cache Size:
-
+
Add New URL
-
+
Remove URL
-
+
Day(s)
-
+
Updating...
-
+
Choose path
-
+
URL Download Priority
-
+
Spoilers
-
+
Download Spoilers Automatically
-
+
Spoiler Location:
-
+
Last Change
-
+
Spoilers download automatically on launch
-
+
Press the button to manually update without relaunching
-
+
Do not close settings until manual update is complete
-
+
Download card pictures on the fly
-
+
How to add a custom URL
-
+
Delete Downloaded Images
-
+
Reset Download URLs
-
+
On-disk cache for downloaded pictures
-
+
In-memory cache for pictures not currently on screen
@@ -2496,12 +2501,12 @@ Your email will be used to verify your account.
DlgSettings
-
+
Unknown Error loading card database
-
+
Your card database is invalid.
Cockatrice may not function correctly with an invalid database
@@ -2512,7 +2517,7 @@ Would you like to change your database location setting?
-
+
Your card database version is too old.
This can cause problems loading card information or images
@@ -2523,7 +2528,7 @@ Would you like to change your database location setting?
-
+
Your card database did not finish loading
Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached
@@ -2532,21 +2537,21 @@ Would you like to change your database location setting?
-
+
File Error loading your card database.
Would you like to change your database location setting?
-
+
Your card database was loaded but contains no cards.
Would you like to change your database location setting?
-
+
Unknown card database load status
Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues
@@ -2555,59 +2560,59 @@ Would you like to change your database location setting?
-
-
-
+
+
+
Error
-
+
The path to your deck directory is invalid. Would you like to go back and set the correct path?
-
+
The path to your card pictures directory is invalid. Would you like to go back and set the correct path?
-
+
Settings
-
+
General
-
+
Appearance
-
+
User Interface
-
+
Card Sources
-
+
Chat
-
+
Sound
-
+
Shortcuts
@@ -3059,102 +3064,102 @@ You may have to build from source yourself.
GeneralSettingsPage
-
+
Reset all paths
-
+
All paths have been reset
-
-
-
-
-
-
+
+
+
+
+
+
Choose path
-
+
Personal settings
-
+
Language:
-
+
Paths (editing disabled in portable mode)
-
+
Paths
-
+
How to help with translations
-
+
Decks directory:
-
+
Replays directory:
-
+
Pictures directory:
-
+
Card database:
-
+
Custom database directory:
-
+
Token database:
-
+
Update channel
-
+
Check for client updates on startup
-
+
Notify if a feature supported by the server is missing in my client
-
+
Automatically run Oracle when running a new version of Cockatrice
-
+
Show tips on startup
@@ -4554,110 +4559,110 @@ Cockatrice will now reload the card database.
MessagesSettingsPage
-
+
Word1 Word2 Word3
-
+
Add New Message
-
+
Edit Message
-
+
Remove Message
-
+
Add message
-
-
+
+
Message:
-
+
Edit message
-
+
Chat settings
-
+
Custom alert words
-
+
Enable chat mentions
-
+
Enable mention completer
-
+
In-game message macros
-
+
How to use in-game message macros
-
+
Ignore chat room messages sent by unregistered users
-
+
Ignore private messages sent by unregistered users
-
-
+
+
Invert text color
-
+
Enable desktop notifications for private messages
-
+
Enable desktop notification for mentions
-
+
Enable room message history on join
-
-
+
+
(Color is hexadecimal)
-
+
Separate words with a space, alphanumeric characters only
@@ -5524,10 +5529,15 @@ Cockatrice will now reload the card database.
PrintingSelector
-
+
Display Navigation Buttons
+
+
+ <b>Warning:</b> You appear to be using custom card art, which has known bugs when also using the printing selector in this version of Cockatrice.
+
+
PrintingSelectorCardOverlayWidget
@@ -5919,53 +5929,53 @@ Cockatrice will now reload the card database.
ShortcutSettingsPage
-
-
+
+
Restore all default shortcuts
-
+
Do you really want to restore all default shortcuts?
-
+
Clear all default shortcuts
-
+
Do you really want to clear all shortcuts?
-
+
Section:
-
+
Action:
-
+
Shortcut:
-
+
How to set custom shortcuts
-
+
Clear all shortcuts
-
+
Search by shortcut name
@@ -6019,27 +6029,27 @@ Please check your shortcut settings!
SoundSettingsPage
-
+
Enable &sounds
-
+
Current sounds theme:
-
+
Test system sound engine
-
+
Sound settings
-
+
Master volume
@@ -7634,102 +7644,102 @@ Please refrain from engaging in this activity or further actions may be taken ag
UserInterfaceSettingsPage
-
+
General interface settings
-
+
&Double-click cards to play them (instead of single-click)
-
+
&Clicking plays all selected cards (instead of just the clicked card)
-
+
&Play all nonlands onto the stack (not the battlefield) by default
-
+
Close card view window when last card is removed
-
+
Annotate card text on tokens
-
+
Use tear-off menus, allowing right click menus to persist on screen
-
+
Notifications settings
-
+
Enable notifications in taskbar
-
+
Notify in the taskbar for game events while you are spectating
-
+
Notify in the taskbar when users in your buddy list connect
-
+
Animation settings
-
+
&Tap/untap animation
-
+
Deck editor/storage settings
-
+
Open deck in new tab by default
-
+
Prompt before converting .txt decks to .cod format
-
+
Always convert if not prompted
-
+
Use visual deck storage in game lobby
-
+
Replay settings
-
+
Buffer time for backwards skip via shortcut:
diff --git a/cockatrice/resources/config/qtlogging.ini b/cockatrice/resources/config/qtlogging.ini
index 60a123d5e..eeae47b7f 100644
--- a/cockatrice/resources/config/qtlogging.ini
+++ b/cockatrice/resources/config/qtlogging.ini
@@ -1,5 +1,9 @@
[Rules]
-# Uncomment a rule to disable logging for that category
+# The default log level is info
+*.debug = false
+
+# Uncomment a rule to disable logging for that category,
+# or set .debug = true for that category to see debug level logs
# main = false
# qt_translator = false
@@ -23,6 +27,7 @@
# servers_settings = false
# shortcuts_settings = false
+# local_client = false
# remote_client = false
# player = false
@@ -47,9 +52,11 @@
# card_info = false
# card_list = false
-flow_layout.debug = false
-flow_widget.debug = false
-flow_widget.size.debug = false
+#flow_layout = false
+#flow_widget = false
+#flow_widget.size = false
+
+# card_info_picture_widget = false
# pixel_map_generator = 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/network/release_channel.cpp b/cockatrice/src/client/network/release_channel.cpp
index ea2b485ed..9edfe7caf 100644
--- a/cockatrice/src/client/network/release_channel.cpp
+++ b/cockatrice/src/client/network/release_channel.cpp
@@ -38,7 +38,7 @@ ReleaseChannel::~ReleaseChannel()
void ReleaseChannel::checkForUpdates()
{
QString releaseChannelUrl = getReleaseChannelUrl();
- qCDebug(ReleaseChannelLog) << "Searching for updates on the channel: " << releaseChannelUrl;
+ qCInfo(ReleaseChannelLog) << "Searching for updates on the channel: " << releaseChannelUrl;
response = netMan->get(QNetworkRequest(releaseChannelUrl));
connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished);
}
@@ -152,15 +152,15 @@ void StableReleaseChannel::releaseListFinished()
QString shortHash = lastRelease->getCommitHash().left(GIT_SHORT_HASH_LEN);
QString myHash = QString(VERSION_COMMIT);
- qCDebug(ReleaseChannelLog) << "Current hash=" << myHash << "update hash=" << shortHash;
+ qCInfo(ReleaseChannelLog) << "Current hash=" << myHash << "update hash=" << shortHash;
- qCDebug(ReleaseChannelLog) << "Got reply from release server, name=" << lastRelease->getName()
- << "desc=" << lastRelease->getDescriptionUrl()
- << "date=" << lastRelease->getPublishDate() << "url=" << lastRelease->getDownloadUrl();
+ qCInfo(ReleaseChannelLog) << "Got reply from release server, name=" << lastRelease->getName()
+ << "desc=" << lastRelease->getDescriptionUrl() << "date=" << lastRelease->getPublishDate()
+ << "url=" << lastRelease->getDownloadUrl();
const QString &tagName = resultMap["tag_name"].toString();
QString url = QString(STABLETAG_URL) + tagName;
- qCDebug(ReleaseChannelLog) << "Searching for commit hash corresponding to stable channel tag: " << tagName;
+ qCInfo(ReleaseChannelLog) << "Searching for commit hash corresponding to stable channel tag: " << tagName;
response = netMan->get(QNetworkRequest(url));
connect(response, &QNetworkReply::finished, this, &StableReleaseChannel::tagListFinished);
}
@@ -185,11 +185,11 @@ void StableReleaseChannel::tagListFinished()
}
lastRelease->setCommitHash(resultMap["object"].toMap()["sha"].toString());
- qCDebug(ReleaseChannelLog) << "Got reply from tag server, commit=" << lastRelease->getCommitHash();
+ qCInfo(ReleaseChannelLog) << "Got reply from tag server, commit=" << lastRelease->getCommitHash();
QString shortHash = lastRelease->getCommitHash().left(GIT_SHORT_HASH_LEN);
QString myHash = QString(VERSION_COMMIT);
- qCDebug(ReleaseChannelLog) << "Current hash=" << myHash << "update hash=" << shortHash;
+ qCInfo(ReleaseChannelLog) << "Current hash=" << myHash << "update hash=" << shortHash;
const bool needToUpdate = (QString::compare(shortHash, myHash, Qt::CaseInsensitive) != 0);
emit finishedCheck(needToUpdate, lastRelease->isCompatibleVersionFound(), lastRelease);
@@ -256,13 +256,13 @@ void BetaReleaseChannel::releaseListFinished()
lastRelease->setName(QString("%1 (%2)").arg(resultMap["tag_name"].toString()).arg(shortHash));
lastRelease->setDescriptionUrl(QString(BETARELEASE_CHANGESURL).arg(VERSION_COMMIT, shortHash));
- qCDebug(ReleaseChannelLog) << "Got reply from release server, size=" << resultMap.size()
- << "name=" << lastRelease->getName() << "desc=" << lastRelease->getDescriptionUrl()
- << "commit=" << lastRelease->getCommitHash() << "date=" << lastRelease->getPublishDate();
+ qCInfo(ReleaseChannelLog) << "Got reply from release server, size=" << resultMap.size()
+ << "name=" << lastRelease->getName() << "desc=" << lastRelease->getDescriptionUrl()
+ << "commit=" << lastRelease->getCommitHash() << "date=" << lastRelease->getPublishDate();
QString betaBuildDownloadUrl = resultMap["assets_url"].toString();
- qCDebug(ReleaseChannelLog) << "Searching for a corresponding file on the beta channel: " << betaBuildDownloadUrl;
+ qCInfo(ReleaseChannelLog) << "Searching for a corresponding file on the beta channel: " << betaBuildDownloadUrl;
response = netMan->get(QNetworkRequest(betaBuildDownloadUrl));
connect(response, &QNetworkReply::finished, this, &BetaReleaseChannel::fileListFinished);
}
@@ -282,7 +282,7 @@ void BetaReleaseChannel::fileListFinished()
QVariantList resultList = jsonResponse.toVariant().toList();
QString shortHash = lastRelease->getCommitHash().left(GIT_SHORT_HASH_LEN);
QString myHash = QString(VERSION_COMMIT);
- qCDebug(ReleaseChannelLog) << "Current hash=" << myHash << "update hash=" << shortHash;
+ qCInfo(ReleaseChannelLog) << "Current hash=" << myHash << "update hash=" << shortHash;
bool needToUpdate = (QString::compare(shortHash, myHash, Qt::CaseInsensitive) != 0);
bool compatibleVersion = false;
@@ -299,7 +299,7 @@ void BetaReleaseChannel::fileListFinished()
if (downloadMatchesCurrentOS(*url)) {
compatibleVersion = true;
lastRelease->setDownloadUrl(*url);
- qCDebug(ReleaseChannelLog) << "Found compatible version url=" << *url;
+ qCInfo(ReleaseChannelLog) << "Found compatible version url=" << *url;
break;
}
}
diff --git a/cockatrice/src/client/network/spoiler_background_updater.cpp b/cockatrice/src/client/network/spoiler_background_updater.cpp
index ee1290a55..a8a3a4b81 100644
--- a/cockatrice/src/client/network/spoiler_background_updater.cpp
+++ b/cockatrice/src/client/network/spoiler_background_updater.cpp
@@ -28,7 +28,7 @@ SpoilerBackgroundUpdater::SpoilerBackgroundUpdater(QObject *apParent) : QObject(
// File exists means we're in spoiler season
startSpoilerDownloadProcess(SPOILERS_STATUS_URL, false);
} else {
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoilers Disabled";
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Spoilers Disabled";
}
}
@@ -67,7 +67,7 @@ void SpoilerBackgroundUpdater::actDownloadFinishedSpoilersFile()
reply->deleteLater();
emit spoilerCheckerDone();
} else {
- qCDebug(SpoilerBackgroundUpdaterLog) << "Error downloading spoilers file" << errorCode;
+ qCWarning(SpoilerBackgroundUpdaterLog) << "Error downloading spoilers file" << errorCode;
emit spoilerCheckerDone();
}
}
@@ -81,11 +81,11 @@ bool SpoilerBackgroundUpdater::deleteSpoilerFile()
// Delete the spoiler.xml file
if (file.exists() && file.remove()) {
- qCDebug(SpoilerBackgroundUpdaterLog) << "Deleting spoiler.xml";
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Deleting spoiler.xml";
return true;
}
- qCDebug(SpoilerBackgroundUpdaterLog) << "Error: Spoiler.xml not found or not deleted";
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Error: Spoiler.xml not found or not deleted";
return false;
}
@@ -101,24 +101,24 @@ void SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled()
trayIcon->showMessage(tr("Spoilers season has ended"), tr("Deleting spoiler.xml. Please run Oracle"));
}
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoiler Season Offline";
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Spoiler Season Offline";
emit spoilerCheckerDone();
} else if (errorCode == QNetworkReply::NoError) {
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoiler Service Online";
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Spoiler Service Online";
startSpoilerDownloadProcess(SPOILERS_URL, true);
} else if (errorCode == QNetworkReply::HostNotFoundError) {
if (trayIcon) {
trayIcon->showMessage(tr("Spoilers download failed"), tr("No internet connection"));
}
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoiler download failed due to no internet connection";
+ qCWarning(SpoilerBackgroundUpdaterLog) << "Spoiler download failed due to no internet connection";
emit spoilerCheckerDone();
} else {
if (trayIcon) {
trayIcon->showMessage(tr("Spoilers download failed"), tr("Error") + " " + (short)errorCode);
}
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoiler download failed with reason" << errorCode;
+ qCWarning(SpoilerBackgroundUpdaterLog) << "Spoiler download failed with reason" << errorCode;
emit spoilerCheckerDone();
}
}
@@ -139,19 +139,19 @@ bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data)
trayIcon->showMessage(tr("Spoilers already up to date"), tr("No new spoilers added"));
}
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoilers Up to Date";
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Spoilers Up to Date";
return false;
}
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoiler Service Error: File open (w) failed for" << fileName;
+ qCWarning(SpoilerBackgroundUpdaterLog) << "Spoiler Service Error: File open (w) failed for" << fileName;
file.close();
return false;
}
if (file.write(data) == -1) {
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoiler Service Error: File write (w) failed for" << fileName;
+ qCWarning(SpoilerBackgroundUpdaterLog) << "Spoiler Service Error: File write (w) failed for" << fileName;
file.close();
return false;
}
@@ -159,7 +159,7 @@ bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data)
file.close();
// Data written, so reload the card database
- qCDebug(SpoilerBackgroundUpdaterLog) << "Spoiler Service Data Written";
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Spoiler Service Data Written";
const auto reloadOk = QtConcurrent::run([] { CardDatabaseManager::getInstance()->loadCardDatabases(); });
// If the user has notifications enabled, let them know
@@ -202,12 +202,12 @@ QByteArray SpoilerBackgroundUpdater::getHash(const QString fileName)
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
hash.addData(bytes);
- qCDebug(SpoilerBackgroundUpdaterLog) << "File Hash =" << hash.result();
+ qCInfo(SpoilerBackgroundUpdaterLog) << "File Hash =" << hash.result();
file.close();
return hash.result();
} else {
- qCDebug(SpoilerBackgroundUpdaterLog) << "getHash ReadOnly failed!";
+ qCWarning(SpoilerBackgroundUpdaterLog) << "getHash ReadOnly failed!";
file.close();
return QByteArray();
}
@@ -221,7 +221,7 @@ QByteArray SpoilerBackgroundUpdater::getHash(QByteArray data)
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
hash.addData(bytes);
- qCDebug(SpoilerBackgroundUpdaterLog) << "Data Hash =" << hash.result();
+ qCInfo(SpoilerBackgroundUpdaterLog) << "Data Hash =" << hash.result();
return hash.result();
}
diff --git a/cockatrice/src/client/sound_engine.cpp b/cockatrice/src/client/sound_engine.cpp
index 0f6ca3c83..fef2df093 100644
--- a/cockatrice/src/client/sound_engine.cpp
+++ b/cockatrice/src/client/sound_engine.cpp
@@ -37,7 +37,7 @@ SoundEngine::~SoundEngine()
void SoundEngine::soundEnabledChanged()
{
if (SettingsCache::instance().getSoundEnabled()) {
- qCDebug(SoundEngineLog) << "SoundEngine: enabling sound with" << audioData.size() << "sounds";
+ qCInfo(SoundEngineLog) << "SoundEngine: enabling sound with" << audioData.size() << "sounds";
if (!player) {
player = new QMediaPlayer;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@@ -46,7 +46,7 @@ void SoundEngine::soundEnabledChanged()
#endif
}
} else {
- qCDebug(SoundEngineLog) << "SoundEngine: disabling sound";
+ qCInfo(SoundEngineLog) << "SoundEngine: disabling sound";
if (player) {
player->stop();
player->deleteLater();
@@ -90,7 +90,7 @@ void SoundEngine::ensureThemeDirectoryExists()
{
if (SettingsCache::instance().getSoundThemeName().isEmpty() ||
!getAvailableThemes().contains(SettingsCache::instance().getSoundThemeName())) {
- qCDebug(SoundEngineLog) << "Sounds theme name not set, setting default value";
+ qCInfo(SoundEngineLog) << "Sounds theme name not set, setting default value";
SettingsCache::instance().setSoundThemeName(DEFAULT_THEME_NAME);
}
}
@@ -131,7 +131,7 @@ QStringMap &SoundEngine::getAvailableThemes()
void SoundEngine::themeChangedSlot()
{
QString themeName = SettingsCache::instance().getSoundThemeName();
- qCDebug(SoundEngineLog) << "Sound theme changed:" << themeName;
+ qCInfo(SoundEngineLog) << "Sound theme changed:" << themeName;
QDir dir = getAvailableThemes().value(themeName);
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/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..fcf95ed3e
--- /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 "../commander/edhrec_commander_api_response_archidekt_links.h"
+#include "../commander/edhrec_commander_api_response_average_deck_statistics.h"
+#include "../commander/edhrec_commander_api_response_card_container.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;
+ EdhrecCommanderApiResponseCardContainer 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.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 100%
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
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/commander/edhrec_commander_api_response_archidekt_links.cpp
similarity index 100%
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/commander/edhrec_commander_api_response_archidekt_links.cpp
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/commander/edhrec_commander_api_response_archidekt_links.h
similarity index 100%
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/commander/edhrec_commander_api_response_archidekt_links.h
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_average_deck_statistics.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/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/edhrec_commander_api_response_card_container.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_card_container.cpp
similarity index 100%
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/commander/edhrec_commander_api_response_card_container.cpp
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/commander/edhrec_commander_api_response_card_container.h
similarity index 100%
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/commander/edhrec_commander_api_response_card_container.h
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_details.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_card_details.cpp
similarity index 100%
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/commander/edhrec_commander_api_response_card_details.cpp
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/commander/edhrec_commander_api_response_card_details.h
similarity index 100%
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/commander/edhrec_commander_api_response_card_details.h
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_list.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_card_list.cpp
similarity index 100%
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/commander/edhrec_commander_api_response_card_list.cpp
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/commander/edhrec_commander_api_response_card_list.h
similarity index 100%
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/commander/edhrec_commander_api_response_card_list.h
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_card_prices.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_card_prices.cpp
similarity index 100%
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/commander/edhrec_commander_api_response_card_prices.cpp
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/commander/edhrec_commander_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/commander/edhrec_commander_api_response_card_prices.h
diff --git a/cockatrice/src/client/tabs/api/edhrec/api_response/edhrec_commander_api_response_commander_details.cpp b/cockatrice/src/client/tabs/api/edhrec/api_response/commander/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/commander/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/commander/edhrec_commander_api_response_commander_details.h
similarity index 100%
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/commander/edhrec_commander_api_response_commander_details.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..0a9d14425
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.h
@@ -0,0 +1,22 @@
+#ifndef EDHREC_TOP_CARDS_API_RESPONSE_H
+#define EDHREC_TOP_CARDS_API_RESPONSE_H
+
+#include "../commander/edhrec_commander_api_response_card_container.h"
+
+#include
+#include
+#include
+
+// Represents the main structure of the JSON
+class EdhrecTopCardsApiResponse
+{
+public:
+ QString header;
+ QString description;
+ EdhrecCommanderApiResponseCardContainer 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..9aa2b80df
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.h
@@ -0,0 +1,22 @@
+#ifndef EDHREC_TOP_COMMANDERS_API_RESPONSE_H
+#define EDHREC_TOP_COMMANDERS_API_RESPONSE_H
+
+#include "../commander/edhrec_commander_api_response_card_container.h"
+
+#include
+#include
+#include
+
+// Represents the main structure of the JSON
+class EdhrecTopCommandersApiResponse
+{
+public:
+ QString header;
+ QString description;
+ EdhrecCommanderApiResponseCardContainer 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..32932149c
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.h
@@ -0,0 +1,22 @@
+#ifndef EDHREC_TOP_TAGS_API_RESPONSE_H
+#define EDHREC_TOP_TAGS_API_RESPONSE_H
+
+#include "../commander/edhrec_commander_api_response_card_container.h"
+
+#include
+#include
+#include
+
+// Represents the main structure of the JSON
+class EdhrecTopTagsApiResponse
+{
+public:
+ QString header;
+ QString description;
+ EdhrecCommanderApiResponseCardContainer 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/edhrec_commander_api_response_card_details_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_details_display_widget.cpp
similarity index 58%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp
rename to cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_details_display_widget.cpp
index 87853b9aa..9573cbcbd 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.cpp
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_details_display_widget.cpp
@@ -1,6 +1,7 @@
#include "edhrec_commander_api_response_card_details_display_widget.h"
-#include "../../../../game/cards/card_database_manager.h"
+#include "../../../../../../game/cards/card_database_manager.h"
+#include "../../tab_edhrec_main.h"
EdhrecCommanderApiResponseCardDetailsDisplayWidget::EdhrecCommanderApiResponseCardDetailsDisplayWidget(
QWidget *parent,
@@ -11,14 +12,17 @@ EdhrecCommanderApiResponseCardDetailsDisplayWidget::EdhrecCommanderApiResponseCa
setLayout(layout);
cardPictureWidget = new CardInfoPictureWidget(this);
- cardPictureWidget->setCard(CardDatabaseManager::getInstance()->getCard(toDisplay.name));
+ 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
- int inclusionRate = (toDisplay.numDecks * 100) / toDisplay.potentialDecks;
+ if (toDisplay.potentialDecks != 0) {
+ inclusionRate = (toDisplay.numDecks * 100) / toDisplay.potentialDecks;
+ }
QColor labelColor;
if (inclusionRate <= 30) {
@@ -38,4 +42,26 @@ EdhrecCommanderApiResponseCardDetailsDisplayWidget::EdhrecCommanderApiResponseCa
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,
+ &EdhrecCommanderApiResponseCardDetailsDisplayWidget::actRequestPageNavigation);
+ connect(this, &EdhrecCommanderApiResponseCardDetailsDisplayWidget::requestUrl, parentTab,
+ &TabEdhRecMain::actNavigatePage);
+ }
+}
+
+void EdhrecCommanderApiResponseCardDetailsDisplayWidget::actRequestPageNavigation()
+{
+ emit requestUrl(toDisplay.url);
}
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/display/commander/edhrec_commander_api_response_card_details_display_widget.h
similarity index 73%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.h
rename to cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_details_display_widget.h
index 1cd76989c..313f73c55 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_details_display_widget.h
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_details_display_widget.h
@@ -1,8 +1,8 @@
#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 "../../../../../ui/widgets/cards/card_info_picture_widget.h"
+#include "../../api_response/commander/edhrec_commander_api_response_card_details.h"
#include
#include
@@ -15,6 +15,10 @@ public:
explicit EdhrecCommanderApiResponseCardDetailsDisplayWidget(
QWidget *parent,
const EdhrecCommanderApiResponseCardDetails &_toDisplay);
+public slots:
+ void actRequestPageNavigation();
+signals:
+ void requestUrl(QString url);
private:
EdhrecCommanderApiResponseCardDetails toDisplay;
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/display/commander/edhrec_commander_api_response_card_list_display_widget.cpp
similarity index 93%
rename from cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp
rename to cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_list_display_widget.cpp
index 0d1f1e8f3..7b1ae1488 100644
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_card_list_display_widget.cpp
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_card_list_display_widget.cpp
@@ -1,6 +1,6 @@
#include "edhrec_commander_api_response_card_list_display_widget.h"
-#include "../../../ui/widgets/general/display/banner_widget.h"
+#include "../../../../../ui/widgets/general/display/banner_widget.h"
#include "edhrec_commander_api_response_card_details_display_widget.h"
#include
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/commander/edhrec_commander_api_response_card_list_display_widget.h
similarity index 77%
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/commander/edhrec_commander_api_response_card_list_display_widget.h
index 9df667929..63428c2c1 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/commander/edhrec_commander_api_response_card_list_display_widget.h
@@ -1,9 +1,9 @@
#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/commander/edhrec_commander_api_response_card_list.h"
#include
#include
diff --git a/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_commander_details_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_commander_details_display_widget.cpp
new file mode 100644
index 000000000..322c65aac
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_commander_details_display_widget.cpp
@@ -0,0 +1,75 @@
+#include "edhrec_commander_api_response_commander_details_display_widget.h"
+
+#include "../../../../../../game/cards/card_database_manager.h"
+#include "../../../../../ui/widgets/cards/card_info_picture_widget.h"
+#include "../../tab_edhrec_main.h"
+
+#include
+#include
+
+EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
+ QWidget *parent,
+ const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails)
+ : QWidget(parent), commanderDetails(_commanderDetails)
+{
+ layout = new QVBoxLayout(this);
+ setLayout(layout);
+
+ commanderPicture = new CardInfoPictureWidget(this);
+ commanderPicture->setCard(CardDatabaseManager::getInstance()->getCard(commanderDetails.getName()));
+
+ commanderDetails.debugPrint();
+
+ label = new QLabel(this);
+ label->setAlignment(Qt::AlignCenter);
+ salt = new QLabel(this);
+ salt->setAlignment(Qt::AlignCenter);
+
+ comboPushButton = new QPushButton(this);
+ averageDeckPushButton = new QPushButton(this);
+
+ layout->addWidget(commanderPicture);
+ layout->addWidget(label);
+ layout->addWidget(salt);
+ layout->addWidget(comboPushButton);
+ layout->addWidget(averageDeckPushButton);
+
+ QWidget *currentParent = parentWidget();
+ TabEdhRecMain *parentTab = nullptr;
+
+ while (currentParent) {
+ if ((parentTab = qobject_cast(currentParent))) {
+ break;
+ }
+ currentParent = currentParent->parentWidget();
+ }
+
+ if (parentTab) {
+ connect(comboPushButton, &QPushButton::clicked, this,
+ &EdhrecCommanderResponseCommanderDetailsDisplayWidget::actRequestComboNavigation);
+ connect(averageDeckPushButton, &QPushButton::clicked, this,
+ &EdhrecCommanderResponseCommanderDetailsDisplayWidget::actRequestAverageDeckNavigation);
+ connect(this, &EdhrecCommanderResponseCommanderDetailsDisplayWidget::requestUrl, parentTab,
+ &TabEdhRecMain::actNavigatePage);
+ }
+
+ retranslateUi();
+}
+
+void EdhrecCommanderResponseCommanderDetailsDisplayWidget::retranslateUi()
+{
+ label->setText(commanderDetails.getLabel());
+ salt->setText(tr("Salt: ") + QString::number(commanderDetails.getSalt()));
+ comboPushButton->setText(tr("Combos"));
+ averageDeckPushButton->setText(tr("Average Deck"));
+}
+
+void EdhrecCommanderResponseCommanderDetailsDisplayWidget::actRequestComboNavigation()
+{
+ emit requestUrl("/combos/" + commanderDetails.getSanitized());
+}
+
+void EdhrecCommanderResponseCommanderDetailsDisplayWidget::actRequestAverageDeckNavigation()
+{
+ emit requestUrl("/average-decks/" + commanderDetails.getSanitized());
+}
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_commander_api_response_commander_details_display_widget.h
similarity index 65%
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_commander_api_response_commander_details_display_widget.h
index 85970d503..b43ffdedb 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_commander_api_response_commander_details_display_widget.h
@@ -1,10 +1,11 @@
#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/commander/edhrec_commander_api_response_commander_details.h"
#include
+#include
#include
#include
@@ -17,9 +18,17 @@ public:
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails);
void retranslateUi();
+public slots:
+ void actRequestComboNavigation();
+ void actRequestAverageDeckNavigation();
+signals:
+ void requestUrl(QString url);
+
private:
QLabel *label;
QLabel *salt;
+ QPushButton *comboPushButton;
+ QPushButton *averageDeckPushButton;
QVBoxLayout *layout;
CardInfoPictureWidget *commanderPicture;
EdhrecCommanderApiResponseCommanderDetails commanderDetails;
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 96%
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..199608e8b 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,7 +1,7 @@
#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 "../../../../../ui/widgets/cards/card_info_picture_widget.h"
+#include "../../api_response/commander/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"
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 89%
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..e98841613 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
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..958594554
--- /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 "../commander/edhrec_commander_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 EdhrecCommanderApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
+ auto cardListDisplayWidget = new EdhrecCommanderApiResponseCardListDisplayWidget(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..8419ddad0
--- /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 "../commander/edhrec_commander_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 EdhrecCommanderApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
+ auto cardListDisplayWidget = new EdhrecCommanderApiResponseCardListDisplayWidget(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..fe727027d
--- /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 "../commander/edhrec_commander_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 EdhrecCommanderApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
+ auto cardListDisplayWidget = new EdhrecCommanderApiResponseCardListDisplayWidget(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_commander_details_display_widget.cpp b/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp
deleted file mode 100644
index 58b77df73..000000000
--- a/cockatrice/src/client/tabs/api/edhrec/edhrec_commander_api_response_commander_details_display_widget.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "edhrec_commander_api_response_commander_details_display_widget.h"
-
-#include "../../../../game/cards/card_database_manager.h"
-#include "../../../ui/widgets/cards/card_info_picture_widget.h"
-
-#include
-
-EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
- QWidget *parent,
- const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails)
- : QWidget(parent), commanderDetails(_commanderDetails)
-{
- layout = new QVBoxLayout(this);
- setLayout(layout);
-
- commanderPicture = new CardInfoPictureWidget(this);
- commanderPicture->setCard(CardDatabaseManager::getInstance()->getCard(commanderDetails.getName()));
-
- commanderDetails.debugPrint();
-
- label = new QLabel(this);
- label->setAlignment(Qt::AlignCenter);
- salt = new QLabel(this);
- salt->setAlignment(Qt::AlignCenter);
-
- layout->addWidget(commanderPicture);
- layout->addWidget(label);
- layout->addWidget(salt);
- retranslateUi();
-}
-
-void EdhrecCommanderResponseCommanderDetailsDisplayWidget::retranslateUi()
-{
- label->setText(commanderDetails.getLabel());
- salt->setText(tr("Salt: ") + QString::number(commanderDetails.getSalt()));
-}
\ No newline at end of file
diff --git a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec.cpp
index 18662aaab..251469ee0 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()
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..64a1d1d04
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp
@@ -0,0 +1,378 @@
+#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/")) {
+ processCommanderResponse(jsonObj);
+ } 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/")) {
+ 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)
+{
+ 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);
+ 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..5a4f3db2a
--- /dev/null
+++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.h
@@ -0,0 +1,61 @@
+#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 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;
+
+ void processCommanderResponse(QJsonObject reply);
+};
+
+#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 317778006..6334de6b8 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)
@@ -626,7 +628,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) {
@@ -634,7 +636,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);
@@ -751,7 +753,7 @@ void TabGame::processGameEventContainer(const GameEventContainer &cont,
default: {
Player *player = players.value(playerId, 0);
if (!player) {
- qCDebug(TabGameLog) << "unhandled game event: invalid player id";
+ qCWarning(TabGameLog) << "unhandled game event: invalid player id";
break;
}
player->processGameEvent(eventType, event, context, options);
@@ -782,8 +784,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);
}
@@ -794,8 +795,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);
}
@@ -1196,10 +1196,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
@@ -1287,34 +1288,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);
}
@@ -1373,40 +1374,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);
@@ -1457,7 +1458,7 @@ void TabGame::loadLayout()
aReplayDockFloating->setChecked(replayDock->isFloating());
}
- QTimer::singleShot(100, this, SLOT(freeDocksSize()));
+ QTimer::singleShot(100, this, &TabGame::freeDocksSize);
}
void TabGame::freeDocksSize()
@@ -1523,15 +1524,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);
@@ -1549,9 +1550,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
@@ -1583,13 +1583,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;
@@ -1609,7 +1609,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)
@@ -1650,7 +1650,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)
@@ -1672,26 +1672,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;
@@ -1719,8 +1721,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);
@@ -1746,7 +1748,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 66e2e3582..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,12 +140,12 @@ 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 {
- qCDebug(TabMessageLog) << "Error: trayIcon is NULL. TabMessage::showSystemPopup failed";
+ 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 632eae06d..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);
@@ -856,7 +906,7 @@ void TabSupervisor::processGameEventContainer(const GameEventContainer &cont)
if (tab)
tab->processGameEventContainer(cont, qobject_cast(sender()), {});
else
- qCDebug(TabSupervisorLog) << "gameEvent: invalid gameId";
+ qCInfo(TabSupervisorLog) << "gameEvent: invalid gameId" << cont.game_id();
}
void TabSupervisor::processUserMessageEvent(const Event_UserMessage &event)
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/tapped_out_interface.cpp b/cockatrice/src/client/tapped_out_interface.cpp
index 490fbe1fe..d6a47b018 100644
--- a/cockatrice/src/client/tapped_out_interface.cpp
+++ b/cockatrice/src/client/tapped_out_interface.cpp
@@ -33,7 +33,7 @@ void TappedOutInterface::queryFinished(QNetworkReply *reply)
* can be extracted from the header. The http status is a 302 "redirect".
*/
QString deckUrl = reply->rawHeader("Location");
- qCDebug(TappedOutInterfaceLog) << "Tappedout: good reply, http status" << httpStatus << "location" << deckUrl;
+ qCInfo(TappedOutInterfaceLog) << "Tappedout: good reply, http status" << httpStatus << "location" << deckUrl;
QDesktopServices::openUrl("https://tappedout.net" + deckUrl);
} else {
/*
@@ -57,8 +57,8 @@ void TappedOutInterface::queryFinished(QNetworkReply *reply)
}
QString errorMessage = errorMessageList.join("\n");
- qCDebug(TappedOutInterfaceLog) << "Tappedout: bad reply, http status" << httpStatus << "size" << data.size()
- << "message" << errorMessage;
+ qCWarning(TappedOutInterfaceLog) << "Tappedout: bad reply, http status" << httpStatus << "size" << data.size()
+ << "message" << errorMessage;
QMessageBox::critical(nullptr, tr("Error"), errorMessage);
}
diff --git a/cockatrice/src/client/ui/layouts/flow_layout.h b/cockatrice/src/client/ui/layouts/flow_layout.h
index f87386c76..d8c6d15c8 100644
--- a/cockatrice/src/client/ui/layouts/flow_layout.h
+++ b/cockatrice/src/client/ui/layouts/flow_layout.h
@@ -7,7 +7,7 @@
#include
#include
-inline Q_LOGGING_CATEGORY(FlowLayoutLog, "flow_layout");
+inline Q_LOGGING_CATEGORY(FlowLayoutLog, "flow_layout", QtInfoMsg);
class FlowLayout : public QLayout
{
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 7d981aee6..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()
@@ -115,7 +114,7 @@ void PictureLoader::getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size)
QPixmap bigPixmap;
if (QPixmapCache::find(key, &bigPixmap)) {
if (bigPixmap.isNull()) {
- qCWarning(PictureLoaderLog) << "Cached pixmap for key" << key << "is NULL!";
+ qCDebug(PictureLoaderLog) << "Cached pixmap for key" << key << "is NULL!";
return;
}
@@ -140,7 +139,11 @@ void PictureLoader::imageLoaded(CardInfoPtr card, const QImage &image)
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap());
} else {
if (card->getUpsideDownArt()) {
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0))
+ QImage mirrorImage = image.flipped(Qt::Horizontal | Qt::Vertical);
+#else
QImage mirrorImage = image.mirrored(true, true);
+#endif
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(mirrorImage));
} else {
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(image));
@@ -195,3 +198,31 @@ void PictureLoader::picsPathChanged()
{
QPixmapCache::clear();
}
+
+bool PictureLoader::hasCustomArt()
+{
+ auto picsPath = SettingsCache::instance().getPicsPath();
+ QDirIterator it(picsPath, QDir::Dirs | QDir::NoDotAndDotDot);
+
+ // Check if there is at least one non-directory file in the pics path, other
+ // than in the "downloadedPics" subdirectory.
+ while (it.hasNext()) {
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
+ QFileInfo dir(it.nextFileInfo());
+#else
+ // nextFileInfo() is only available in Qt 6.3+, for previous versions, we build
+ // the QFileInfo from a QString which requires more system calls.
+ QFileInfo dir(it.next());
+#endif
+
+ if (it.fileName() == "downloadedPics")
+ continue;
+
+ QDirIterator subIt(it.filePath(), QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
+ if (subIt.hasNext()) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/cockatrice/src/client/ui/picture_loader/picture_loader.h b/cockatrice/src/client/ui/picture_loader/picture_loader.h
index 8819bddac..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
@@ -36,6 +36,7 @@ public:
static void clearPixmapCache(CardInfoPtr card);
static void clearPixmapCache();
static void cacheCardPixmaps(QList cards);
+ static bool hasCustomArt();
public slots:
static void clearNetworkCache();
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 9d79d1a36..fce81fe6a 100644
--- a/cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp
+++ b/cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp
@@ -16,11 +16,16 @@ QStringList PictureLoaderWorker::md5Blacklist = QStringList() << "db0c48db407a90
PictureLoaderWorker::PictureLoaderWorker()
: QObject(nullptr), picsPath(SettingsCache::instance().getPicsPath()),
customPicsPath(SettingsCache::instance().getCustomPicsPath()),
- picDownload(SettingsCache::instance().getPicDownload()), downloadRunning(false), loadQueueRunning(false)
+ 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);
networkManager = new QNetworkAccessManager(this);
// We need a timeout to ensure requests don't hang indefinitely in case of
@@ -40,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();
@@ -83,11 +88,24 @@ void PictureLoaderWorker::processLoadQueue()
qCDebug(PictureLoaderWorkerLog).nospace()
<< "[card: " << cardName << " set: " << setName << "]: Trying to load picture";
- if (CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting(
- cardName, cardBeingLoaded.getCard()->getPixmapCacheKey())) {
- if (cardImageExistsOnDisk(setName, correctedCardName)) {
- continue;
- }
+ // FIXME: This is a hack so that to keep old Cockatrice behavior
+ // (ignoring provider ID) when the "override all card art with personal
+ // preference" is set.
+ //
+ // Figure out a proper way to integrate the two systems at some point.
+ //
+ // Note: need to go through a member for
+ // overrideAllCardArtWithPersonalPreference as reading from the
+ // SettingsCache instance from the PictureLoaderWorker thread could
+ // cause race conditions.
+ //
+ // XXX: Reading from the CardDatabaseManager instance from the
+ // PictureLoaderWorker thread might not be safe either
+ bool searchCustomPics = overrideAllCardArtWithPersonalPreference ||
+ CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting(
+ cardName, cardBeingLoaded.getCard()->getPixmapCacheKey());
+ if (searchCustomPics && cardImageExistsOnDisk(setName, correctedCardName, searchCustomPics)) {
+ continue;
}
qCDebug(PictureLoaderWorkerLog).nospace()
@@ -100,23 +118,26 @@ void PictureLoaderWorker::processLoadQueue()
}
}
-bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &correctedCardname)
+bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &correctedCardname, bool searchCustomPics)
{
QImage image;
QImageReader imgReader;
imgReader.setDecideFormatFromContent(true);
QList picsPaths = QList();
- QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
- // Recursively check all subdirectories of the CUSTOM folder
- while (it.hasNext()) {
- QString thisPath(it.next());
- QFileInfo thisFileInfo(thisPath);
+ if (searchCustomPics) {
+ QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
- if (thisFileInfo.isFile() &&
- (thisFileInfo.fileName() == correctedCardname || thisFileInfo.completeBaseName() == correctedCardname ||
- thisFileInfo.baseName() == correctedCardname)) {
- picsPaths << thisPath; // Card found in the CUSTOM directory, somewhere
+ // Recursively check all subdirectories of the CUSTOM folder
+ while (it.hasNext()) {
+ QString thisPath(it.next());
+ QFileInfo thisFileInfo(thisPath);
+
+ if (thisFileInfo.isFile() &&
+ (thisFileInfo.fileName() == correctedCardname || thisFileInfo.completeBaseName() == correctedCardname ||
+ thisFileInfo.baseName() == correctedCardname)) {
+ picsPaths << thisPath; // Card found in the CUSTOM directory, somewhere
+ }
}
}
@@ -194,7 +215,7 @@ void PictureLoaderWorker::picDownloadFailed()
loadQueue.prepend(cardBeingDownloaded);
mutex.unlock();
} else {
- qCDebug(PictureLoaderWorkerLog).nospace()
+ qCWarning(PictureLoaderWorkerLog).nospace()
<< "[card: " << cardBeingDownloaded.getCard()->getCorrectedName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Picture NOT found, "
<< (picDownload ? "download failed" : "downloads disabled")
@@ -225,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);
}
@@ -405,7 +443,7 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
}
if (logSuccessMessage) {
- qCDebug(PictureLoaderWorkerLog).nospace()
+ qCInfo(PictureLoaderWorkerLog).nospace()
<< "[card: " << cardBeingDownloaded.getCard()->getName() << " set: " << cardBeingDownloaded.getSetName()
<< "]: Image successfully " << (isFromCache ? "loaded from cached" : "downloaded from") << " url "
<< reply->url().toDisplayString();
@@ -451,7 +489,12 @@ void PictureLoaderWorker::picsPathChanged()
customPicsPath = SettingsCache::instance().getCustomPicsPath();
}
+void PictureLoaderWorker::setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference)
+{
+ overrideAllCardArtWithPersonalPreference = _overrideAllCardArtWithPersonalPreference;
+}
+
void PictureLoaderWorker::clearNetworkCache()
{
networkManager->cache()->clear();
-}
\ No newline at end of file
+}
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 823bcd7ec..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
@@ -42,8 +42,16 @@ private:
PictureToLoad cardBeingLoaded;
PictureToLoad cardBeingDownloaded;
bool picDownload, downloadRunning, loadQueueRunning;
+ bool overrideAllCardArtWithPersonalPreference;
void startNextPicDownload();
- bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName);
+
+ /** Emit the `imageLoaded` signal and return `true` if a picture is found on
+ disk, return `false` otherwise.
+
+ If `searchCustomPics` is `true`, the CUSTOM folder is searched for a
+ matching image first; otherwise, only the set-based folders are used. */
+ bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName, bool searchCustomPics);
+
bool imageIsBlackListed(const QByteArray &);
QNetworkReply *makeRequest(const QUrl &url);
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
@@ -58,6 +66,7 @@ private slots:
void picDownloadChanged();
void picsPathChanged();
+ void setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference);
public slots:
void processLoadQueue();
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 3dff1b7ac..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();
}
@@ -32,7 +32,7 @@ void ThemeManager::ensureThemeDirectoryExists()
{
if (SettingsCache::instance().getThemeName().isEmpty() ||
!getAvailableThemes().contains(SettingsCache::instance().getThemeName())) {
- qCDebug(ThemeManagerLog) << "Theme name not set, setting default value";
+ qCInfo(ThemeManagerLog) << "Theme name not set, setting default value";
SettingsCache::instance().setThemeName(NONE_THEME_NAME);
}
}
@@ -105,7 +105,7 @@ QBrush ThemeManager::loadExtraBrush(QString fileName, QBrush &fallbackBrush)
void ThemeManager::themeChangedSlot()
{
QString themeName = SettingsCache::instance().getThemeName();
- qCDebug(ThemeManagerLog) << "Theme changed:" << themeName;
+ qCInfo(ThemeManagerLog) << "Theme changed:" << themeName;
QString dirPath = getAvailableThemes().value(themeName);
QDir dir = dirPath;
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..d2291bda3
--- /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);
+ delete widget;
+ }
+ }
+
+ // **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 546568a73..0a33594f4 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
@@ -37,7 +38,7 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool hoverTo
setMouseTracking(true);
}
- enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(this);
+ enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(this->window());
enlargedPixmapWidget->hide();
hoverTimer = new QTimer(this);
@@ -67,7 +68,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();
@@ -210,7 +211,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)};
}
/**
@@ -254,10 +256,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);
}
}
@@ -267,6 +283,8 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
if (event->button() == Qt::RightButton) {
createRightClickMenu()->popup(QCursor::pos());
}
+
+ emit cardClicked();
}
QMenu *CardInfoPictureWidget::createRightClickMenu()
@@ -312,11 +330,26 @@ QMenu *CardInfoPictureWidget::createViewRelatedCardsMenu()
return viewRelatedCards;
}
+/**
+ * Finds the single instance of the MainWindow in this application.
+ */
+static MainWindow *findMainWindow()
+{
+ for (auto widget : QApplication::topLevelWidgets()) {
+ if (auto mainWindow = qobject_cast(widget)) {
+ return mainWindow;
+ }
+ }
+ // This code should be unreachable
+ qCritical() << "Could not find MainWindow in QApplication::topLevelWidgets";
+ return nullptr;
+}
+
QMenu *CardInfoPictureWidget::createAddToOpenDeckMenu()
{
auto addToOpenDeckMenu = new QMenu(tr("Add card to deck"));
- auto *mainWindow = qobject_cast(window());
+ auto mainWindow = findMainWindow();
QList deckEditorTabs = mainWindow->getTabSupervisor()->getDeckEditorTabs();
if (deckEditorTabs.isEmpty()) {
@@ -355,13 +388,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 5f6500d92..04e7a852e 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,12 +1,14 @@
#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
+inline Q_LOGGING_CATEGORY(CardInfoPictureWidgetLog, "card_info_picture_widget");
+
class AbstractCardItem;
class QMenu;
@@ -32,6 +34,7 @@ signals:
void hoveredOnCard(CardInfoPtr hoveredCard);
void cardScaleFactorChanged(int _scale);
void cardChanged(CardInfoPtr card);
+ void cardClicked();
protected:
void resizeEvent(QResizeEvent *event) override;
@@ -58,7 +61,7 @@ private:
qreal aspectRatio = magicTheGatheringCardAspectRatio;
int baseWidth = 200;
int baseHeight = 200;
- double scaleFactor = 1.5;
+ double scaleFactor = 100;
QPixmap resizedPixmap;
bool pixmapDirty;
bool hoverToZoomEnabled;
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..b485d1c62
--- /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()) {
+ delete item->layout();
+ }
+ 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 cardsInZone = deckListModel->getCardsAsCardInfoPtrsForZone(zoneName);
+
+ for (CardInfoPtr cardInZone : cardsInZone) {
+ groupCriteriaValues.append(cardInZone->getProperty(activeGroupCriteria));
+ }
+
+ groupCriteriaValues.removeDuplicates();
+ groupCriteriaValues.sort();
+
+ return groupCriteriaValues;
+}
diff --git a/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.h b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.h
new file mode 100644
index 000000000..b80ca4332
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.h
@@ -0,0 +1,62 @@
+#ifndef DECK_CARD_ZONE_DISPLAY_WIDGET_H
+#define DECK_CARD_ZONE_DISPLAY_WIDGET_H
+
+#include "../../../../deck/deck_list_model.h"
+#include "../../../../game/cards/card_info.h"
+#include "../general/display/banner_widget.h"
+#include "../general/layout_containers/overlap_widget.h"
+#include "card_info_picture_with_text_overlay_widget.h"
+#include "card_size_widget.h"
+
+#include
+#include
+
+class DeckCardZoneDisplayWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ DeckCardZoneDisplayWidget(QWidget *parent,
+ DeckListModel *deckListModel,
+ QString zoneName,
+ QString activeGroupCriteria,
+ QStringList activeSortCriteria,
+ int bannerOpacity,
+ int subBannerOpacity,
+ CardSizeWidget *_cardSizeWidget);
+ DeckListModel *deckListModel;
+ QString zoneName;
+ void addCardsToOverlapWidget();
+ void resizeEvent(QResizeEvent *event) override;
+
+public slots:
+ void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
+ void onHover(CardInfoPtr card);
+ void displayCards();
+ void refreshDisplayType(const QString &displayType);
+ void addCardGroupIfItDoesNotExist();
+ void deleteCardGroupIfItDoesNotExist();
+ void onActiveGroupCriteriaChanged(QString activeGroupCriteria);
+ void onActiveSortCriteriaChanged(QStringList activeSortCriteria);
+ QList getGroupCriteriaValueList();
+
+signals:
+ void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card, QString zoneName);
+ void cardHovered(CardInfoPtr card);
+ void activeSortCriteriaChanged(QStringList activeSortCriteria);
+
+private:
+ QString activeGroupCriteria;
+ QStringList activeSortCriteria;
+ int bannerOpacity = 20;
+ int subBannerOpacity = 10;
+ CardSizeWidget *cardSizeWidget;
+ QVBoxLayout *layout;
+ BannerWidget *banner;
+ QWidget *cardGroupContainer;
+ QVBoxLayout *cardGroupLayout;
+ QString displayType = "flat";
+ OverlapWidget *overlapWidget;
+};
+
+#endif // DECK_CARD_ZONE_DISPLAY_WIDGET_H
diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp
new file mode 100644
index 000000000..02a4bfb5a
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp
@@ -0,0 +1,25 @@
+#include "deck_analytics_widget.h"
+
+DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListModel *_deckListModel)
+ : QWidget(parent), deckListModel(_deckListModel)
+{
+ mainLayout = new QVBoxLayout();
+ setLayout(mainLayout);
+
+ manaCurveWidget = new ManaCurveWidget(this, deckListModel);
+ mainLayout->addWidget(manaCurveWidget);
+
+ manaDevotionWidget = new ManaDevotionWidget(this, deckListModel);
+ mainLayout->addWidget(manaDevotionWidget);
+
+ manaBaseWidget = new ManaBaseWidget(this, deckListModel);
+ mainLayout->addWidget(manaBaseWidget);
+}
+
+void DeckAnalyticsWidget::refreshDisplays(DeckListModel *_deckModel)
+{
+ deckListModel = _deckModel;
+ manaCurveWidget->setDeckModel(_deckModel);
+ manaDevotionWidget->setDeckModel(_deckModel);
+ manaBaseWidget->setDeckModel(_deckModel);
+}
diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.h b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.h
new file mode 100644
index 000000000..6e7a273cf
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/deck_analytics/deck_analytics_widget.h
@@ -0,0 +1,34 @@
+#ifndef DECK_ANALYTICS_WIDGET_H
+#define DECK_ANALYTICS_WIDGET_H
+
+#include "../../../../deck/deck_list_model.h"
+#include "../../../ui/widgets/general/layout_containers/flow_widget.h"
+#include "mana_base_widget.h"
+#include "mana_curve_widget.h"
+#include "mana_devotion_widget.h"
+
+#include
+#include
+#include
+#include
+
+class DeckAnalyticsWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DeckAnalyticsWidget(QWidget *parent, DeckListModel *deckListModel);
+ void setDeckList(const DeckList &_deckListModel);
+ std::map analyzeManaCurve();
+ void refreshDisplays(DeckListModel *_deckListModel);
+
+private:
+ DeckListModel *deckListModel;
+ QVBoxLayout *mainLayout;
+
+ ManaCurveWidget *manaCurveWidget;
+ ManaDevotionWidget *manaDevotionWidget;
+ ManaBaseWidget *manaBaseWidget;
+};
+
+#endif // DECK_ANALYTICS_WIDGET_H
diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp
new file mode 100644
index 000000000..895e2d64d
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp
@@ -0,0 +1,135 @@
+#include "mana_base_widget.h"
+
+#include "../../../../deck/deck_loader.h"
+#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_database_manager.h"
+#include "../general/display/banner_widget.h"
+#include "../general/display/bar_widget.h"
+
+#include
+#include
+#include
+
+ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListModel *_deckListModel)
+ : QWidget(parent), deckListModel(_deckListModel)
+{
+ layout = new QVBoxLayout(this);
+ setLayout(layout);
+
+ bannerWidget = new BannerWidget(this, tr("Mana Base"), Qt::Vertical, 100);
+ bannerWidget->setMaximumHeight(100);
+ layout->addWidget(bannerWidget);
+
+ barContainer = new QWidget(this);
+ barLayout = new QHBoxLayout(barContainer);
+ layout->addWidget(barContainer);
+
+ connect(deckListModel, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase);
+
+ retranslateUi();
+}
+
+void ManaBaseWidget::retranslateUi()
+{
+ bannerWidget->setText(tr("Mana Base"));
+}
+
+void ManaBaseWidget::setDeckModel(DeckListModel *deckModel)
+{
+ deckListModel = deckModel;
+ connect(deckListModel, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase);
+ analyzeManaBase();
+}
+
+void ManaBaseWidget::updateDisplay()
+{
+ // Clear the layout first
+ QLayoutItem *item;
+ while ((item = barLayout->takeAt(0)) != nullptr) {
+ item->widget()->deleteLater();
+ delete item;
+ }
+
+ int highestEntry = 0;
+ for (auto entry : manaBaseMap) {
+ if (entry > highestEntry) {
+ highestEntry = entry;
+ }
+ }
+
+ // Define color mapping for mana types
+ QHash manaColors;
+ manaColors.insert("W", QColor(248, 231, 185));
+ manaColors.insert("U", QColor(14, 104, 171));
+ manaColors.insert("B", QColor(21, 11, 0));
+ manaColors.insert("R", QColor(211, 32, 42));
+ manaColors.insert("G", QColor(0, 115, 62));
+ manaColors.insert("C", QColor(150, 150, 150));
+
+ for (auto manaColor : manaBaseMap.keys()) {
+ QColor barColor = manaColors.value(manaColor, Qt::gray);
+ BarWidget *barWidget = new BarWidget(QString(manaColor), manaBaseMap[manaColor], highestEntry, barColor, this);
+ barLayout->addWidget(barWidget);
+ }
+
+ update();
+}
+
+QHash ManaBaseWidget::analyzeManaBase()
+{
+ manaBaseMap.clear();
+ InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
+ for (int i = 0; i < listRoot->size(); i++) {
+ InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i));
+ for (int j = 0; j < currentZone->size(); j++) {
+ DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j));
+ if (!currentCard)
+ continue;
+
+ for (int k = 0; k < currentCard->getNumber(); ++k) {
+ CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
+ if (info) {
+ auto devotion = determineManaProduction(info->getText());
+ mergeManaCounts(manaBaseMap, devotion);
+ }
+ }
+ }
+ }
+
+ updateDisplay();
+ return manaBaseMap;
+}
+
+QHash ManaBaseWidget::determineManaProduction(const QString &rulesText)
+{
+ QHash manaCounts = {{"W", 0}, {"U", 0}, {"B", 0}, {"R", 0}, {"G", 0}, {"C", 0}};
+
+ QString text = rulesText.toLower(); // Normalize case for matching
+
+ // Quick keyword-based checks for any color and colorless mana
+ if (text.contains("{t}: add one mana of any color") || text.contains("add one mana of any color")) {
+ for (const auto &color : {QStringLiteral("W"), QStringLiteral("U"), QStringLiteral("B"), QStringLiteral("R"),
+ QStringLiteral("G")}) {
+ manaCounts[color]++;
+ }
+ }
+ if (text.contains("{t}: add {c}") || text.contains("add one colorless mana")) {
+ manaCounts["C"]++;
+ }
+
+ // Optimized regex for specific mana symbols
+ static const QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{([WUBRG])\})");
+ QRegularExpressionMatch match = specificColorRegex.match(rulesText);
+ if (match.hasMatch()) {
+ manaCounts[match.captured(1)]++;
+ }
+
+ return manaCounts;
+}
+
+void ManaBaseWidget::mergeManaCounts(QHash &manaCounts1, const QHash &manaCounts2)
+{
+ for (auto it = manaCounts2.constBegin(); it != manaCounts2.constEnd(); ++it) {
+ manaCounts1[it.key()] += it.value();
+ }
+}
diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.h b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.h
new file mode 100644
index 000000000..66442f687
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.h
@@ -0,0 +1,37 @@
+#ifndef MANA_BASE_WIDGET_H
+#define MANA_BASE_WIDGET_H
+
+#include "../../../../deck/deck_list_model.h"
+#include "../general/display/banner_widget.h"
+
+#include
+#include
+#include
+#include
+
+class ManaBaseWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ManaBaseWidget(QWidget *parent, DeckListModel *deckListModel);
+ QHash analyzeManaBase();
+ void updateDisplay();
+
+ QHash determineManaProduction(const QString &manaString);
+ void mergeManaCounts(QHash &manaCounts1, const QHash &manaCounts2);
+
+public slots:
+ void setDeckModel(DeckListModel *deckModel);
+ void retranslateUi();
+
+private:
+ DeckListModel *deckListModel;
+ BannerWidget *bannerWidget;
+ QHash manaBaseMap;
+ QVBoxLayout *layout;
+ QWidget *barContainer;
+ QHBoxLayout *barLayout;
+};
+
+#endif // MANA_BASE_WIDGET_H
diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp
new file mode 100644
index 000000000..acf240e0e
--- /dev/null
+++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp
@@ -0,0 +1,99 @@
+#include "mana_curve_widget.h"
+
+#include "../../../../deck/deck_loader.h"
+#include "../../../../game/cards/card_database.h"
+#include "../../../../game/cards/card_database_manager.h"
+#include "../../../../main.h"
+#include "../general/display/banner_widget.h"
+#include "../general/display/bar_widget.h"
+
+#include
+#include
+
+ManaCurveWidget::ManaCurveWidget(QWidget *parent, DeckListModel *_deckListModel)
+ : QWidget(parent), deckListModel(_deckListModel)
+{
+ layout = new QVBoxLayout(this);
+ setLayout(layout);
+
+ bannerWidget = new BannerWidget(this, tr("Mana Curve"), Qt::Vertical, 100);
+ bannerWidget->setMaximumHeight(100);
+ layout->addWidget(bannerWidget);
+
+ barContainer = new QWidget(this);
+ barLayout = new QHBoxLayout(barContainer);
+ layout->addWidget(barContainer);
+
+ connect(deckListModel, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve);
+
+ retranslateUi();
+}
+
+void ManaCurveWidget::retranslateUi()
+{
+ bannerWidget->setText(tr("Mana Curve"));
+}
+
+void ManaCurveWidget::setDeckModel(DeckListModel *deckModel)
+{
+ deckListModel = deckModel;
+ connect(deckListModel, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve);
+ analyzeManaCurve();
+}
+
+std::unordered_map ManaCurveWidget::analyzeManaCurve()
+{
+ manaCurveMap.clear();
+ InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
+ for (int i = 0; i < listRoot->size(); i++) {
+ InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i));
+ for (int j = 0; j < currentZone->size(); j++) {
+ DecklistCardNode *currentCard = dynamic_cast