Merge branch 'master' into tooomm-ci_updates

This commit is contained in:
tooomm 2025-05-10 19:33:49 +02:00 • committed by GitHub
commit b8354e3bfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
154 changed files with 2192 additions and 1004 deletions

View file

@ -1,4 +1,4 @@
FROM fedora:40 FROM fedora:42
RUN dnf install -y \ RUN dnf install -y \
ccache \ ccache \

View file

@ -22,8 +22,8 @@ Available pre-compiled binaries for installation:
• <kbd>Ubuntu 20.04 LTS</kbd> <sub><i>Focal Fossa</i></sub> • <kbd>Ubuntu 20.04 LTS</kbd> <sub><i>Focal Fossa</i></sub>
• <kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub> • <kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub>
• <kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub> • <kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub>
• <kbd>Fedora 42</kbd>
• <kbd>Fedora 41</kbd> • <kbd>Fedora 41</kbd>
• <kbd>Fedora 40</kbd>
<sub>We are also packaged in <kbd>Arch Linux</kbd>'s <a href="https://archlinux.org/packages/extra/x86_64/cockatrice">official extra repository</a>, courtesy of @FFY00.</sub> <sub>We are also packaged in <kbd>Arch Linux</kbd>'s <a href="https://archlinux.org/packages/extra/x86_64/cockatrice">official extra repository</a>, courtesy of @FFY00.</sub>
<sub>General Linux support is available via a <kbd>flatpak</kbd> package at <a href="https://flathub.org/apps/io.github.Cockatrice.cockatrice">Flathub</a>!</sub> <sub>General Linux support is available via a <kbd>flatpak</kbd> package at <a href="https://flathub.org/apps/io.github.Cockatrice.cockatrice">Flathub</a>!</sub>

View file

@ -9,6 +9,7 @@ on:
- 'webclient/**' - 'webclient/**'
- '.github/workflows/web-*.yml' - '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml' - '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
tags: tags:
- '*' - '*'
pull_request: pull_request:
@ -101,12 +102,12 @@ jobs:
package: DEB package: DEB
- distro: Fedora - distro: Fedora
version: 40 version: 41
package: RPM package: RPM
test: skip # Running tests on all distros is superfluous test: skip # Running tests on all distros is superfluous
- distro: Fedora - distro: Fedora
version: 41 version: 42
package: RPM package: RPM
- distro: Ubuntu - distro: Ubuntu

View file

@ -7,6 +7,7 @@ on:
- 'webclient/**' - 'webclient/**'
- '.github/workflows/web-*.yml' - '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml' - '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
jobs: jobs:
format: format:

71
.github/workflows/docker-release.yml vendored Normal file
View file

@ -0,0 +1,71 @@
name: Build Docker Image
on:
push:
tags:
- '*Release*'
branches:
- master
pull_request:
branches:
- master
paths:
- '.github/workflows/docker-release.yml'
- 'CMakeLists.txt'
- 'Dockerfile'
- 'servatrice/**'
- 'common/**'
- 'cmake/**'
- '!**.md'
jobs:
docker:
name: amd64 & arm64
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker metadata
id: metadata
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/cockatrice/servatrice
labels: |
org.opencontainers.image.title=Servatrice
org.opencontainers.image.url=https://cockatrice.github.io/
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
annotations: |
org.opencontainers.image.title=Servatrice
org.opencontainers.image.url=https://cockatrice.github.io/
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.ref_type == 'tag'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -16,7 +16,11 @@ RUN apt-get update && apt-get install -y\
qt6-tools-dev \ qt6-tools-dev \
qt6-tools-dev-tools qt6-tools-dev-tools
COPY . /home/servatrice/code/ COPY ./CMakeLists.txt ./LICENSE ./README.md /home/servatrice/code/
COPY ./cmake /home/servatrice/code/cmake
COPY ./common /home/servatrice/code/common
COPY ./servatrice /home/servatrice/code/servatrice
WORKDIR /home/servatrice/code WORKDIR /home/servatrice/code
WORKDIR build WORKDIR build

View file

@ -8,7 +8,7 @@
<a href="#related-repositories">Related</a> <b>|</b> <a href="#related-repositories">Related</a> <b>|</b>
<a href="#community-resources-">Community</a> <b>|</b> <a href="#community-resources-">Community</a> <b>|</b>
<a href="#contribute">Contribute</a> <b>|</b> <a href="#contribute">Contribute</a> <b>|</b>
<a href="#build--">Build</a> <b>|</b> <a href="#build---">Build</a> <b>|</b>
<a href="#run">Run</a> <a href="#run">Run</a>
</p> </p>
@ -38,8 +38,8 @@ Latest <kbd>stable</kbd> release:
</pre><pre> </pre><pre>
Latest <kbd>beta</kbd> version: Latest <kbd>beta</kbd> version:
[![Download Beta Release](https://img.shields.io/github/release/cockatrice/cockatrice/all.svg?label=version&colorB=f37f40 "Download Latest Beta Release")](https://github.com/cockatrice/cockatrice/releases) ![](https://img.shields.io/github/release-date-pre/Cockatrice/Cockatrice.svg?label=released&colorB=f37f40 "Release Date") [![](https://img.shields.io/github/downloads-pre/cockatrice/cockatrice/latest/total.svg?label=downloads&colorB=f37f40 "Number of Downloads")](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0) [![](https://img.shields.io/github/commits-since/Cockatrice/Cockatrice/latest.svg?label=changes&colorB=f37f40 "Changes over Stable Release")](https://github.com/Cockatrice/Cockatrice/pulls?q=is%3Apr+is%3Aclosed) [![Download Beta Release](https://img.shields.io/github/release/cockatrice/cockatrice/all.svg?label=version&colorB=f37f40 "Download Latest Beta Release")](https://github.com/cockatrice/cockatrice/releases) ![](https://img.shields.io/github/release-date-pre/Cockatrice/Cockatrice.svg?label=released&colorB=f37f40 "Release Date") [![](https://img.shields.io/github/downloads-pre/cockatrice/cockatrice/latest/total.svg?label=downloads&colorB=f37f40 "Number of Downloads")](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0) [![](https://img.shields.io/github/commits-since/Cockatrice/Cockatrice/latest.svg?label=changes&colorB=f37f40 "Changes over Stable Release")](https://github.com/Cockatrice/Cockatrice/pulls?q=is%3Apr+is%3Aclosed)
<sub><i>While incorporating the latest fixes and features, beta builds may not be stable and/or contain new bugs!</i></sub> <sub><i>While incorporating the latest fixes and features, beta builds may not be stable or contain new bugs!</i></sub>
<sub><b><i>Please report any findings when testing them!</i></b></sub> <sub><b><i>Please report any findings and open new issues when testing them!</i></b></sub>
</pre> </pre>
# Related Repositories # Related Repositories
@ -79,10 +79,11 @@ Cockatrice tries to use the [Google Developer Documentation Style Guide](https:/
<details> <details>
<summary><b>Kudos to our amazing contributors ❤️</b></summary> <summary><b>Kudos to our amazing contributors ❤️</b></summary>
<br>
<a href="https://github.com/Cockatrice/Cockatrice/graphs/contributors"> <a href="https://github.com/Cockatrice/Cockatrice/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Cockatrice/Cockatrice" /> <img src="https://contrib.rocks/image?repo=Cockatrice/Cockatrice" />
</a><br> </a><br>
<sub><i>Made with <a href="https://contrib.rocks">contrib.rocks</a>.</i></sub> <sub><i>Made with <a href="https://contrib.rocks">contrib.rocks</a></i></sub>
</details> </details>
### Translations [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://transifex.com/cockatrice/cockatrice/) ### Translations [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://transifex.com/cockatrice/cockatrice/)
@ -92,7 +93,7 @@ Cockatrice uses Transifex to manage translations. You can help us bring <kbd>Coc
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting invovled, and join a group of hundreds of others!<br> Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting invovled, and join a group of hundreds of others!<br>
# Build [![CI Desktop](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [![CI Web](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush) # Build [![CI Desktop](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [![CI Docker](https://github.com/Cockatrice/Cockatrice/actions/workflows/docker-release.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/docker-release.yml?query=branch%3Amaster+event%3Apush) [![CI Web](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush)
Dependencies: *(for minimum versions search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))* Dependencies: *(for minimum versions search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))*
- [Qt](https://www.qt.io/developers/) - [Qt](https://www.qt.io/developers/)
@ -143,17 +144,16 @@ The following flags (with their non-default values) can be passed to `cmake`:
# Run # Run
<kbd>Cockatrice</kbd> is the game client<br> <kbd>Cockatrice</kbd> is the game client<br>
<kbd>Oracle</kbd> fetches card data<br> <kbd>Oracle</kbd> fetches card data<br>
<kbd>Servatrice</kbd> is the server<br> <kbd>Servatrice</kbd> is the server<br>
#### Docker #### Docker
You can run an instance of <kbd>Servatrice</kbd> (the Cockatrice server) using [Docker](https://www.docker.com/resources/what-container/) and our Dockerfile.<br> You can build an image & deploy a <kbd>Servatrice</kbd> (Cockatrice server) container using [Docker](https://www.docker.com/resources/what-container/) and our Dockerfile yourself.<br>
For more information, have a look in our wiki section on [Setting up Servatrice](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).<br> For more details, look into our wiki section on [Setting up Servatrice](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).<br>
There, you'll also find more hints on our **docker-compose** file which will configure and run both a MySQL server and Servatrice. You'll also find more hints on our **pre-build image** there, or the **docker-compose** file which will configure and run both a MySQL server and Servatrice.
# License [![GPLv2 License](https://img.shields.io/github/license/Cockatrice/Cockatrice.svg)](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE) # License [![GPLv2 License](https://img.shields.io/github/license/Cockatrice/Cockatrice.svg)](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE)

View file

@ -24,7 +24,9 @@ set(cockatrice_SOURCES
src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp src/client/tabs/api/edhrec/display/top_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_commander/edhrec_top_commanders_api_response_display_widget.cpp
@ -99,6 +101,7 @@ set(cockatrice_SOURCES
src/client/ui/widgets/general/display/dynamic_font_size_label.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/dynamic_font_size_push_button.cpp
src/client/ui/widgets/general/display/labeled_input.cpp src/client/ui/widgets/general/display/labeled_input.cpp
src/client/ui/widgets/general/display/percent_bar_widget.cpp
src/client/ui/widgets/general/display/shadow_background_label.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/flow_widget.cpp
src/client/ui/widgets/general/layout_containers/overlap_control_widget.cpp src/client/ui/widgets/general/layout_containers/overlap_control_widget.cpp
@ -132,6 +135,7 @@ set(cockatrice_SOURCES
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_tag_item_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_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_folder_display_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_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_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_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_tag_filter_widget.cpp
@ -165,13 +169,16 @@ set(cockatrice_SOURCES
src/dialogs/dlg_tip_of_the_day.cpp src/dialogs/dlg_tip_of_the_day.cpp
src/dialogs/dlg_update.cpp src/dialogs/dlg_update.cpp
src/dialogs/dlg_view_log.cpp src/dialogs/dlg_view_log.cpp
src/game/board/abstract_card_drag_item.cpp
src/game/board/abstract_card_item.cpp
src/game/board/abstract_counter.cpp src/game/board/abstract_counter.cpp
src/game/board/abstract_graphics_item.cpp src/game/board/abstract_graphics_item.cpp
src/game/board/arrow_item.cpp src/game/board/arrow_item.cpp
src/game/board/arrow_target.cpp src/game/board/arrow_target.cpp
src/game/board/card_drag_item.cpp
src/game/board/card_item.cpp
src/game/board/card_list.cpp
src/game/board/counter_general.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_completer_proxy_model.cpp
src/game/cards/card_database.cpp src/game/cards/card_database.cpp
src/game/cards/card_database_manager.cpp src/game/cards/card_database_manager.cpp
@ -179,10 +186,7 @@ set(cockatrice_SOURCES
src/game/cards/card_database_parser/card_database_parser.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_3.cpp
src/game/cards/card_database_parser/cockatrice_xml_4.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_info.cpp
src/game/cards/card_item.cpp
src/game/cards/card_list.cpp
src/game/cards/card_search_model.cpp src/game/cards/card_search_model.cpp
src/game/deckview/deck_view.cpp src/game/deckview/deck_view.cpp
src/game/deckview/deck_view_container.cpp src/game/deckview/deck_view_container.cpp

View file

@ -50,6 +50,7 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
printingSelectorDockWidget = new DeckEditorPrintingSelectorDockWidget(this); printingSelectorDockWidget = new DeckEditorPrintingSelectorDockWidget(this);
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged); connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged);
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckModified, this, &AbstractTabDeckEditor::onDeckModified);
connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard); connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard);
connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard); connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard);
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this, connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this,
@ -77,6 +78,10 @@ void AbstractTabDeckEditor::updateCard(CardInfoPtr _card)
} }
void AbstractTabDeckEditor::onDeckChanged() void AbstractTabDeckEditor::onDeckChanged()
{
}
void AbstractTabDeckEditor::onDeckModified()
{ {
setModified(!isBlankNewDeck()); setModified(!isBlankNewDeck());
deckMenu->setSaveStatus(!isBlankNewDeck()); deckMenu->setSaveStatus(!isBlankNewDeck());

View file

@ -70,6 +70,7 @@ public:
public slots: public slots:
virtual void onDeckChanged(); virtual void onDeckChanged();
virtual void onDeckModified();
void updateCard(CardInfoPtr _card); void updateCard(CardInfoPtr _card);
void actAddCard(CardInfoPtr info); void actAddCard(CardInfoPtr info);
void actAddCardToSideboard(CardInfoPtr info); void actAddCardToSideboard(CardInfoPtr info);

View file

@ -14,34 +14,18 @@ EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWi
cardPictureWidget = new CardInfoPictureWidget(this); cardPictureWidget = new CardInfoPictureWidget(this);
cardPictureWidget->setCard(CardDatabaseManager::getInstance()->guessCard(toDisplay.sanitized)); cardPictureWidget->setCard(CardDatabaseManager::getInstance()->guessCard(toDisplay.sanitized));
label = new QLabel(this); nameLabel = new QLabel(this);
label->setText(toDisplay.name + "\n" + toDisplay.label); nameLabel->setText(toDisplay.name);
label->setAlignment(Qt::AlignHCenter); nameLabel->setAlignment(Qt::AlignHCenter);
int inclusionRate = 0; inclusionDisplayWidget = new EdhrecApiResponseCardInclusionDisplayWidget(this, toDisplay);
// Set label color based on inclusion rate
if (toDisplay.potentialDecks != 0) {
inclusionRate = (toDisplay.numDecks * 100) / toDisplay.potentialDecks;
}
QColor labelColor; synergyDisplayWidget = new EdhrecApiResponseCardSynergyDisplayWidget(this, toDisplay);
if (inclusionRate <= 30) {
labelColor = QColor(255, 0, 0); // Red
} else if (inclusionRate <= 60) {
int red = 255 - ((inclusionRate - 30) * 2);
int green = (inclusionRate - 30) * 4; // Adjust green to make the transition smoother
labelColor = QColor(red, green, 0); // purple-ish
} else if (inclusionRate <= 90) {
int green = (inclusionRate - 60) * 5; // Increase green
labelColor = QColor(100, green, 100); // Green shades
} else {
labelColor = QColor(100, 200, 100); // Dark Green
}
label->setStyleSheet(QString("color: %1").arg(labelColor.name()));
layout->addWidget(nameLabel);
layout->addWidget(cardPictureWidget); layout->addWidget(cardPictureWidget);
layout->addWidget(label); layout->addWidget(inclusionDisplayWidget);
layout->addWidget(synergyDisplayWidget);
QWidget *currentParent = parentWidget(); QWidget *currentParent = parentWidget();
TabEdhRecMain *parentTab = nullptr; TabEdhRecMain *parentTab = nullptr;
@ -54,8 +38,11 @@ EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWi
} }
if (parentTab) { if (parentTab) {
cardPictureWidget->setScaleFactor(parentTab->getCardSizeSlider()->getSlider()->value());
connect(cardPictureWidget, &CardInfoPictureWidget::cardClicked, this, connect(cardPictureWidget, &CardInfoPictureWidget::cardClicked, this,
&EdhrecApiResponseCardDetailsDisplayWidget::actRequestPageNavigation); &EdhrecApiResponseCardDetailsDisplayWidget::actRequestPageNavigation);
connect(parentTab->getCardSizeSlider()->getSlider(), &QSlider::valueChanged, cardPictureWidget,
&CardInfoPictureWidget::setScaleFactor);
connect(this, &EdhrecApiResponseCardDetailsDisplayWidget::requestUrl, parentTab, connect(this, &EdhrecApiResponseCardDetailsDisplayWidget::requestUrl, parentTab,
&TabEdhRecMain::actNavigatePage); &TabEdhRecMain::actNavigatePage);
} }

View file

@ -3,6 +3,8 @@
#include "../../../../../ui/widgets/cards/card_info_picture_widget.h" #include "../../../../../ui/widgets/cards/card_info_picture_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_details.h" #include "../../api_response/cards/edhrec_api_response_card_details.h"
#include "edhrec_api_response_card_inclusion_display_widget.h"
#include "edhrec_api_response_card_synergy_display_widget.h"
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
@ -22,7 +24,9 @@ private:
EdhrecApiResponseCardDetails toDisplay; EdhrecApiResponseCardDetails toDisplay;
QVBoxLayout *layout; QVBoxLayout *layout;
CardInfoPictureWidget *cardPictureWidget; CardInfoPictureWidget *cardPictureWidget;
QLabel *label; QLabel *nameLabel;
EdhrecApiResponseCardInclusionDisplayWidget *inclusionDisplayWidget;
EdhrecApiResponseCardSynergyDisplayWidget *synergyDisplayWidget;
}; };
#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H #endif // EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H

View file

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

View file

@ -0,0 +1,27 @@
#ifndef EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H
#define EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H
#include "../../../../../ui/widgets/general/display/percent_bar_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_details.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecApiResponseCardInclusionDisplayWidget : public QWidget
{
Q_OBJECT
public:
EdhrecApiResponseCardInclusionDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
void retranslateUi();
private:
QVBoxLayout *layout;
EdhrecApiResponseCardDetails toDisplay;
QLabel *commanderLabel;
QLabel *amountLabel;
QLabel *inclusionLabel;
PercentBarWidget *percentBarWidget;
};
#endif // EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H

View file

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

View file

@ -0,0 +1,25 @@
#ifndef EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H
#define EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H
#include "../../../../../ui/widgets/general/display/percent_bar_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_details.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
class EdhrecApiResponseCardSynergyDisplayWidget : public QWidget
{
Q_OBJECT
public:
EdhrecApiResponseCardSynergyDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
void retranslateUi();
private:
QVBoxLayout *layout;
EdhrecApiResponseCardDetails toDisplay;
QLabel *label;
PercentBarWidget *percentBarWidget;
};
#endif // EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H

View file

@ -2,6 +2,7 @@
#include "../../../../../../game/cards/card_database_manager.h" #include "../../../../../../game/cards/card_database_manager.h"
#include "../../../../../ui/widgets/cards/card_info_picture_widget.h" #include "../../../../../ui/widgets/cards/card_info_picture_widget.h"
#include "../../tab_edhrec_main.h"
#include "../card_prices/edhrec_api_response_card_prices_display_widget.h" #include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget( EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
@ -16,6 +17,22 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom
commanderPicture = new CardInfoPictureWidget(this); commanderPicture = new CardInfoPictureWidget(this);
commanderPicture->setCard(CardDatabaseManager::getInstance()->getCard(commanderDetails.getName())); commanderPicture->setCard(CardDatabaseManager::getInstance()->getCard(commanderDetails.getName()));
QWidget *currentParent = parentWidget();
TabEdhRecMain *parentTab = nullptr;
while (currentParent) {
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
break;
}
currentParent = currentParent->parentWidget();
}
if (parentTab) {
connect(parentTab->getCardSizeSlider()->getSlider(), &QSlider::valueChanged, commanderPicture,
&CardInfoPictureWidget::setScaleFactor);
commanderPicture->setScaleFactor(parentTab->getCardSizeSlider()->getSlider()->value());
}
commanderDetails.debugPrint(); commanderDetails.debugPrint();
label = new QLabel(this); label = new QLabel(this);

View file

@ -94,11 +94,18 @@ TabEdhRecMain::TabEdhRecMain(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor
searchPushButton = new QPushButton(navigationContainer); searchPushButton = new QPushButton(navigationContainer);
connect(searchPushButton, &QPushButton::clicked, this, [=, this]() { doSearch(); }); connect(searchPushButton, &QPushButton::clicked, this, [=, this]() { doSearch(); });
settingsButton = new SettingsButtonWidget(this);
cardSizeSlider = new CardSizeWidget(this);
settingsButton->addSettingsWidget(cardSizeSlider);
navigationLayout->addWidget(cardsPushButton); navigationLayout->addWidget(cardsPushButton);
navigationLayout->addWidget(topCommandersPushButton); navigationLayout->addWidget(topCommandersPushButton);
navigationLayout->addWidget(tagsPushButton); navigationLayout->addWidget(tagsPushButton);
navigationLayout->addWidget(searchBar); navigationLayout->addWidget(searchBar);
navigationLayout->addWidget(searchPushButton); navigationLayout->addWidget(searchPushButton);
navigationLayout->addWidget(settingsButton);
currentPageDisplay = new QWidget(container); currentPageDisplay = new QWidget(container);
currentPageLayout = new QVBoxLayout(currentPageDisplay); currentPageLayout = new QVBoxLayout(currentPageDisplay);
@ -349,7 +356,7 @@ void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply)
{ {
EdhrecAverageDeckApiResponse deckData; EdhrecAverageDeckApiResponse deckData;
deckData.fromJson(reply); deckData.fromJson(reply);
tabSupervisor->addVisualDeckEditorTab(deckData.deck.deckLoader); tabSupervisor->openDeckInNewTab(deckData.deck.deckLoader);
} }
void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel) void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel)

View file

@ -2,7 +2,9 @@
#define TAB_EDHREC_MAIN_H #define TAB_EDHREC_MAIN_H
#include "../../../../game/cards/card_database.h" #include "../../../../game/cards/card_database.h"
#include "../../../ui/widgets/cards/card_size_widget.h"
#include "../../../ui/widgets/general/layout_containers/flow_widget.h" #include "../../../ui/widgets/general/layout_containers/flow_widget.h"
#include "../../../ui/widgets/quick_settings/settings_button_widget.h"
#include "../../tab.h" #include "../../tab.h"
#include "display/commander/edhrec_commander_api_response_display_widget.h" #include "display/commander/edhrec_commander_api_response_display_widget.h"
@ -25,6 +27,11 @@ public:
return tr("EDHREC: ") + cardName; return tr("EDHREC: ") + cardName;
} }
CardSizeWidget *getCardSizeSlider()
{
return cardSizeSlider;
}
QNetworkAccessManager *networkManager; QNetworkAccessManager *networkManager;
public slots: public slots:
@ -53,6 +60,8 @@ private:
QPushButton *tagsPushButton; QPushButton *tagsPushButton;
QLineEdit *searchBar; QLineEdit *searchBar;
QPushButton *searchPushButton; QPushButton *searchPushButton;
SettingsButtonWidget *settingsButton;
CardSizeWidget *cardSizeSlider;
CardInfoPtr cardToQuery; CardInfoPtr cardToQuery;
EdhrecCommanderApiResponseDisplayWidget *displayWidget; EdhrecCommanderApiResponseDisplayWidget *displayWidget;
}; };

View file

@ -3,9 +3,9 @@
#include "../../client/ui/widgets/cards/card_info_frame_widget.h" #include "../../client/ui/widgets/cards/card_info_frame_widget.h"
#include "../../dialogs/dlg_create_game.h" #include "../../dialogs/dlg_create_game.h"
#include "../../game/board/arrow_item.h" #include "../../game/board/arrow_item.h"
#include "../../game/board/card_item.h"
#include "../../game/cards/card_database.h" #include "../../game/cards/card_database.h"
#include "../../game/cards/card_database_manager.h" #include "../../game/cards/card_database_manager.h"
#include "../../game/cards/card_item.h"
#include "../../game/deckview/deck_view_container.h" #include "../../game/deckview/deck_view_container.h"
#include "../../game/game_scene.h" #include "../../game/game_scene.h"
#include "../../game/game_view.h" #include "../../game/game_view.h"

View file

@ -590,7 +590,7 @@ void TabSupervisor::actTabDeckStorage(bool checked)
void TabSupervisor::openTabDeckStorage() void TabSupervisor::openTabDeckStorage()
{ {
tabDeckStorage = new TabDeckStorage(this, client, userInfo); tabDeckStorage = new TabDeckStorage(this, client, userInfo);
connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tabDeckStorage, aTabDeckStorage); myAddTab(tabDeckStorage, aTabDeckStorage);
connect(tabDeckStorage, &Tab::closed, this, [this] { connect(tabDeckStorage, &Tab::closed, this, [this] {
tabDeckStorage = nullptr; tabDeckStorage = nullptr;
@ -691,7 +691,7 @@ void TabSupervisor::gameJoined(const Event_GameJoined &event)
auto *tab = new TabGame(this, QList<AbstractClient *>() << client, event, roomGameTypes); auto *tab = new TabGame(this, QList<AbstractClient *>() << client, event, roomGameTypes);
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft); connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
connect(tab, &TabGame::openMessageDialog, this, &TabSupervisor::addMessageTab); connect(tab, &TabGame::openMessageDialog, this, &TabSupervisor::addMessageTab);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab); myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab); gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab); setCurrentWidget(tab);
@ -701,7 +701,7 @@ void TabSupervisor::localGameJoined(const Event_GameJoined &event)
{ {
auto *tab = new TabGame(this, localClients, event, QMap<int, QString>()); auto *tab = new TabGame(this, localClients, event, QMap<int, QString>());
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft); connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab); myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab); gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab); setCurrentWidget(tab);
@ -807,6 +807,29 @@ void TabSupervisor::talkLeft(TabMessage *tab)
removeTab(indexOf(tab)); removeTab(indexOf(tab));
} }
/**
* Creates a new deck editor tab and loads the deck into it.
* Creates either a classic or visual deck editor tab depending on settings
* @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
*/
void TabSupervisor::openDeckInNewTab(const DeckLoader *deckToOpen)
{
int type = SettingsCache::instance().getDefaultDeckEditorType();
switch (type) {
case ClassicDeckEditor:
addDeckEditorTab(deckToOpen);
break;
case VisualDeckEditor:
addVisualDeckEditorTab(deckToOpen);
break;
default:
qCWarning(TabSupervisorLog) << "Unknown DeckEditorType [" << type
<< "]; opening ClassicDeckEditor as fallback";
addDeckEditorTab(deckToOpen);
break;
}
}
/** /**
* Creates a new deck editor tab * Creates a new deck editor tab
* @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance. * @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.

View file

@ -75,6 +75,14 @@ protected:
class TabSupervisor : public QTabWidget class TabSupervisor : public QTabWidget
{ {
Q_OBJECT Q_OBJECT
public:
enum DeckEditorType
{
ClassicDeckEditor,
VisualDeckEditor
};
private: private:
ServerInfo_User *userInfo; ServerInfo_User *userInfo;
AbstractClient *client; AbstractClient *client;
@ -152,6 +160,7 @@ signals:
void showWindowIfHidden(); void showWindowIfHidden();
public slots: public slots:
void openDeckInNewTab(const DeckLoader *deckToOpen);
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen); TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen); TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen);
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab(); TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();

View file

@ -80,7 +80,7 @@ void TabDeckEditorVisual::createCentralFrame()
void TabDeckEditorVisual::onDeckChanged() void TabDeckEditorVisual::onDeckChanged()
{ {
AbstractTabDeckEditor::onDeckChanged(); AbstractTabDeckEditor::onDeckModified();
tabContainer->visualDeckView->decklistDataChanged(QModelIndex(), QModelIndex()); tabContainer->visualDeckView->decklistDataChanged(QModelIndex(), QModelIndex());
tabContainer->deckAnalytics->refreshDisplays(deckDockWidget->deckModel); tabContainer->deckAnalytics->refreshDisplays(deckDockWidget->deckModel);
tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel); tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel);

View file

@ -12,7 +12,7 @@
TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor) TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
: Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this)) : Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this))
{ {
connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::addVisualDeckEditorTab); connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::openDeckInNewTab);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this, connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this,
&TabDeckStorageVisual::actOpenLocalDeck); &TabDeckStorageVisual::actOpenLocalDeck);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this, connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this,

View file

@ -92,16 +92,9 @@ void TappedOutInterface::analyzeDeck(DeckList *deck)
manager->post(request, data); manager->post(request, data);
} }
struct CopyMainOrSide void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard)
{ {
CardDatabase &cardDatabase; auto copyMainOrSide = [this, &mainboard, &sideboard](const auto node, const auto card) {
DeckList &mainboard, &sideboard;
CopyMainOrSide(CardDatabase &_cardDatabase, DeckList &_mainboard, DeckList &_sideboard)
: cardDatabase(_cardDatabase), mainboard(_mainboard), sideboard(_sideboard){};
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
{
CardInfoPtr dbCard = cardDatabase.getCard(card->getName()); CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
if (!dbCard || dbCard->getIsToken()) if (!dbCard || dbCard->getIsToken())
return; return;
@ -112,11 +105,7 @@ struct CopyMainOrSide
else else
addedCard = mainboard.addCard(card->getName(), node->getName()); addedCard = mainboard.addCard(card->getName(), node->getName());
addedCard->setNumber(card->getNumber()); addedCard->setNumber(card->getNumber());
} };
};
void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard)
{
CopyMainOrSide copyMainOrSide(cardDatabase, mainboard, sideboard);
source.forEachCard(copyMainOrSide); source.forEachCard(copyMainOrSide);
} }

View file

@ -1,6 +1,7 @@
#include "picture_to_load.h" #include "picture_to_load.h"
#include "../../../settings/cache_settings.h" #include "../../../settings/cache_settings.h"
#include "../../../utility/card_set_comparator.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDate> #include <QDate>
@ -20,7 +21,7 @@ PictureToLoad::PictureToLoad(CardInfoPtr _card)
if (sortedSets.empty()) { if (sortedSets.empty()) {
sortedSets << CardSet::newInstance("", "", "", QDate()); sortedSets << CardSet::newInstance("", "", "", QDate());
} }
std::sort(sortedSets.begin(), sortedSets.end(), SetDownloadPriorityComparator()); std::sort(sortedSets.begin(), sortedSets.end(), SetPriorityComparator());
// If the user hasn't disabled arts other than their personal preference... // If the user hasn't disabled arts other than their personal preference...
if (!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) { if (!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {

View file

@ -10,24 +10,6 @@ inline Q_LOGGING_CATEGORY(PictureToLoadLog, "picture_loader.picture_to_load");
class PictureToLoad class PictureToLoad
{ {
private: private:
class SetDownloadPriorityComparator
{
public:
/*
* Returns true if a has higher download priority than b
* Enabled sets have priority over disabled sets
* Both groups follows the user-defined order
*/
inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const
{
if (a->getEnabled()) {
return !b->getEnabled() || a->getSortKey() < b->getSortKey();
} else {
return !b->getEnabled() && a->getSortKey() < b->getSortKey();
}
}
};
CardInfoPtr card; CardInfoPtr card;
QList<CardSetPtr> sortedSets; QList<CardSetPtr> sortedSets;
QList<QString> urlTemplates; QList<QString> urlTemplates;

View file

@ -20,12 +20,17 @@ ManaSymbolWidget::ManaSymbolWidget(QWidget *parent, QString _symbol, bool _isAct
&ManaSymbolWidget::updateOpacity); &ManaSymbolWidget::updateOpacity);
} }
void ManaSymbolWidget::toggleSymbol()
{
setColorActive(!isActive);
emit colorToggled(getSymbolChar(), isActive);
}
void ManaSymbolWidget::setColorActive(bool active) void ManaSymbolWidget::setColorActive(bool active)
{ {
if (isActive != active) { if (isActive != active) {
isActive = active; isActive = active;
updateOpacity(); updateOpacity();
emit colorToggled(getSymbolChar(), isActive);
} }
} }
@ -46,9 +51,7 @@ void ManaSymbolWidget::mousePressEvent(QMouseEvent *event)
{ {
Q_UNUSED(event); Q_UNUSED(event);
if (mayBeToggled) { if (mayBeToggled) {
isActive = !isActive; toggleSymbol();
updateOpacity();
emit colorToggled(getSymbolChar(), isActive);
} }
} }
@ -63,7 +66,7 @@ void ManaSymbolWidget::loadManaIcon()
QString filename = "theme:icons/mana/"; QString filename = "theme:icons/mana/";
if (symbol == "W" || symbol == "U" || symbol == "B" || symbol == "R" || symbol == "G") { if (symbol == "W" || symbol == "U" || symbol == "B" || symbol == "R" || symbol == "G") {
filename += symbol + ".svg"; filename += symbol;
} }
manaIcon = QPixmap(filename); manaIcon = QPixmap(filename);

View file

@ -11,9 +11,13 @@ class ManaSymbolWidget : public QLabel
public: public:
ManaSymbolWidget(QWidget *parent, QString symbol, bool isActive = true, bool mayBeToggled = false); ManaSymbolWidget(QWidget *parent, QString symbol, bool isActive = true, bool mayBeToggled = false);
void toggleSymbol();
void setColorActive(bool active); void setColorActive(bool active);
void updateOpacity(); void updateOpacity();
bool isColorActive() const; bool isColorActive() const
{
return isActive;
};
QString getSymbol() const QString getSymbol() const
{ {
return symbol; return symbol;

View file

@ -26,7 +26,7 @@ CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
banner = new BannerWidget(this, cardGroupCategory, Qt::Orientation::Vertical, bannerOpacity); banner = new BannerWidget(this, cardGroupCategory, Qt::Orientation::Vertical, bannerOpacity);
layout->addWidget(banner); layout->addWidget(banner);
updateCardDisplays(); CardGroupDisplayWidget::updateCardDisplays();
} }
void CardGroupDisplayWidget::updateCardDisplays() void CardGroupDisplayWidget::updateCardDisplays()

View file

@ -1,7 +1,7 @@
#include "card_info_display_widget.h" #include "card_info_display_widget.h"
#include "../../../../game/board/card_item.h"
#include "../../../../game/cards/card_database_manager.h" #include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/cards/card_item.h"
#include "../../../../main.h" #include "../../../../main.h"
#include "card_info_picture_widget.h" #include "card_info_picture_widget.h"
#include "card_info_text_widget.h" #include "card_info_text_widget.h"

View file

@ -1,7 +1,7 @@
#include "card_info_frame_widget.h" #include "card_info_frame_widget.h"
#include "../../../../game/board/card_item.h"
#include "../../../../game/cards/card_database_manager.h" #include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/cards/card_item.h"
#include "../../../../settings/cache_settings.h" #include "../../../../settings/cache_settings.h"
#include "card_info_display_widget.h" #include "card_info_display_widget.h"
#include "card_info_picture_widget.h" #include "card_info_picture_widget.h"

View file

@ -1,7 +1,7 @@
#include "card_info_picture_widget.h" #include "card_info_picture_widget.h"
#include "../../../../game/board/card_item.h"
#include "../../../../game/cards/card_database_manager.h" #include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/cards/card_item.h"
#include "../../../../settings/cache_settings.h" #include "../../../../settings/cache_settings.h"
#include "../../../tabs/tab_supervisor.h" #include "../../../tabs/tab_supervisor.h"
#include "../../picture_loader/picture_loader.h" #include "../../picture_loader/picture_loader.h"
@ -64,6 +64,12 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverT
}); });
} }
CardInfoPictureWidget::~CardInfoPictureWidget()
{
enlargedPixmapWidget->hide();
enlargedPixmapWidget->deleteLater();
}
/** /**
* @brief Sets the card to be displayed and updates the pixmap. * @brief Sets the card to be displayed and updates the pixmap.
* @param card A shared pointer to the card information (CardInfoPtr). * @param card A shared pointer to the card information (CardInfoPtr).
@ -341,6 +347,12 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
emit cardClicked(); emit cardClicked();
} }
void CardInfoPictureWidget::hideEvent(QHideEvent *event)
{
enlargedPixmapWidget->hide();
QWidget::hideEvent(event);
}
QMenu *CardInfoPictureWidget::createRightClickMenu() QMenu *CardInfoPictureWidget::createRightClickMenu()
{ {
auto *cardMenu = new QMenu(this); auto *cardMenu = new QMenu(this);

View file

@ -21,6 +21,7 @@ public:
explicit CardInfoPictureWidget(QWidget *parent = nullptr, explicit CardInfoPictureWidget(QWidget *parent = nullptr,
bool hoverToZoomEnabled = false, bool hoverToZoomEnabled = false,
bool raiseOnEnter = false); bool raiseOnEnter = false);
~CardInfoPictureWidget();
CardInfoPtr getInfo() CardInfoPtr getInfo()
{ {
return info; return info;
@ -52,6 +53,7 @@ protected:
void moveEvent(QMoveEvent *event) override; void moveEvent(QMoveEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void hideEvent(QHideEvent *event) override;
void loadPixmap(); void loadPixmap();
[[nodiscard]] const QPixmap &getResizedPixmap() const [[nodiscard]] const QPixmap &getResizedPixmap() const
{ {

View file

@ -1,6 +1,6 @@
#include "card_info_text_widget.h" #include "card_info_text_widget.h"
#include "../../../../game/cards/card_item.h" #include "../../../../game/board/card_item.h"
#include "../../../../game/game_specific_terms.h" #include "../../../../game/game_specific_terms.h"
#include <QGridLayout> #include <QGridLayout>

View file

@ -34,14 +34,7 @@ CardSizeWidget::CardSizeWidget(QWidget *parent, FlowWidget *_flowWidget, int def
// Debounce setup // Debounce setup
debounceTimer.setSingleShot(true); debounceTimer.setSingleShot(true);
connect(&debounceTimer, &QTimer::timeout, this, [this]() { connect(&debounceTimer, &QTimer::timeout, this, [this] { emit cardSizeSettingUpdated(pendingValue); });
// Check the type of the parent widget
if (qobject_cast<PrintingSelector *>(parentWidget())) {
SettingsCache::instance().setPrintingSelectorCardSize(pendingValue);
} else if (qobject_cast<VisualDeckStorageWidget *>(parentWidget())) {
SettingsCache::instance().setVisualDeckStorageCardSize(pendingValue);
}
});
connect(cardSizeSlider, &QSlider::valueChanged, this, &CardSizeWidget::updateCardSizeSetting); connect(cardSizeSlider, &QSlider::valueChanged, this, &CardSizeWidget::updateCardSizeSetting);
} }

View file

@ -17,9 +17,17 @@ public:
explicit CardSizeWidget(QWidget *parent, FlowWidget *flowWidget = nullptr, int defaultValue = 100); explicit CardSizeWidget(QWidget *parent, FlowWidget *flowWidget = nullptr, int defaultValue = 100);
[[nodiscard]] QSlider *getSlider() const; [[nodiscard]] QSlider *getSlider() const;
public slots: private slots:
void updateCardSizeSetting(int newValue); void updateCardSizeSetting(int newValue);
signals:
/**
* Emitted when the slider value changes, but on a debounce timer.
* Any parents that care about saving the value to settings should use this signal to indicate when to save the new
* value to settings.
*/
void cardSizeSettingUpdated(int newValue);
private: private:
QWidget *parent; QWidget *parent;
FlowWidget *flowWidget; FlowWidget *flowWidget;

View file

@ -93,6 +93,8 @@ void DeckCardZoneDisplayWidget::addCardGroupIfItDoesNotExist()
for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) { for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
if (cardGroupDisplayWidget->cardGroupCategory == cardGroup) { if (cardGroupDisplayWidget->cardGroupCategory == cardGroup) {
found = true; found = true;
cardGroupDisplayWidget->updateCardDisplays();
break;
} }
} }

View file

@ -6,14 +6,25 @@ DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListModel *_deckLi
mainLayout = new QVBoxLayout(); mainLayout = new QVBoxLayout();
setLayout(mainLayout); setLayout(mainLayout);
scrollArea = new QScrollArea(this);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(true);
mainLayout->addWidget(scrollArea);
container = new QWidget(scrollArea);
containerLayout = new QVBoxLayout(container);
container->setLayout(containerLayout);
scrollArea->setWidget(container);
manaCurveWidget = new ManaCurveWidget(this, deckListModel); manaCurveWidget = new ManaCurveWidget(this, deckListModel);
mainLayout->addWidget(manaCurveWidget); containerLayout->addWidget(manaCurveWidget);
manaDevotionWidget = new ManaDevotionWidget(this, deckListModel); manaDevotionWidget = new ManaDevotionWidget(this, deckListModel);
mainLayout->addWidget(manaDevotionWidget); containerLayout->addWidget(manaDevotionWidget);
manaBaseWidget = new ManaBaseWidget(this, deckListModel); manaBaseWidget = new ManaBaseWidget(this, deckListModel);
mainLayout->addWidget(manaBaseWidget); containerLayout->addWidget(manaBaseWidget);
} }
void DeckAnalyticsWidget::refreshDisplays(DeckListModel *_deckModel) void DeckAnalyticsWidget::refreshDisplays(DeckListModel *_deckModel)

View file

@ -8,9 +8,10 @@
#include "mana_devotion_widget.h" #include "mana_devotion_widget.h"
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget> #include <QWidget>
#include <decklist.h> #include <decklist.h>
#include <qscrollarea.h>
class DeckAnalyticsWidget : public QWidget class DeckAnalyticsWidget : public QWidget
{ {
@ -26,6 +27,11 @@ private:
DeckListModel *deckListModel; DeckListModel *deckListModel;
QVBoxLayout *mainLayout; QVBoxLayout *mainLayout;
QWidget *container;
QVBoxLayout *containerLayout;
QScrollArea *scrollArea;
ManaCurveWidget *manaCurveWidget; ManaCurveWidget *manaCurveWidget;
ManaDevotionWidget *manaDevotionWidget; ManaDevotionWidget *manaDevotionWidget;
ManaBaseWidget *manaBaseWidget; ManaBaseWidget *manaBaseWidget;

View file

@ -91,7 +91,7 @@ void ManaCurveWidget::updateDisplay()
// Add new widgets to the layout in sorted order // Add new widgets to the layout in sorted order
for (const auto &entry : sortedManaCurve) { for (const auto &entry : sortedManaCurve) {
BarWidget *barWidget = BarWidget *barWidget =
new BarWidget(QString::number(entry.first), entry.second, highestEntry, QColor(11, 11, 11), this); new BarWidget(QString::number(entry.first), entry.second, highestEntry, QColor(122, 122, 122), this);
barLayout->addWidget(barWidget); barLayout->addWidget(barWidget);
} }

View file

@ -58,6 +58,28 @@ void DeckEditorDeckDockWidget::createDeckDock()
nameEdit->setObjectName("nameEdit"); nameEdit->setObjectName("nameEdit");
nameLabel->setBuddy(nameEdit); nameLabel->setBuddy(nameEdit);
connect(nameEdit, &LineEditUnfocusable::textChanged, this, &DeckEditorDeckDockWidget::updateName); connect(nameEdit, &LineEditUnfocusable::textChanged, this, &DeckEditorDeckDockWidget::updateName);
quickSettingsWidget = new SettingsButtonWidget(this);
showBannerCardCheckBox = new QCheckBox();
showBannerCardCheckBox->setObjectName("showBannerCardCheckBox");
showBannerCardCheckBox->setChecked(SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
connect(showBannerCardCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setDeckEditorBannerCardComboBoxVisible);
connect(&SettingsCache::instance(), &SettingsCache::deckEditorBannerCardComboBoxVisibleChanged, this,
&DeckEditorDeckDockWidget::updateShowBannerCardComboBox);
showTagsWidgetCheckBox = new QCheckBox();
showTagsWidgetCheckBox->setObjectName("showTagsWidgetCheckBox");
showTagsWidgetCheckBox->setChecked(SettingsCache::instance().getDeckEditorTagsWidgetVisible());
connect(showTagsWidgetCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setDeckEditorTagsWidgetVisible);
connect(&SettingsCache::instance(), &SettingsCache::deckEditorTagsWidgetVisibleChanged, this,
&DeckEditorDeckDockWidget::updateShowTagsWidget);
quickSettingsWidget->addSettingsWidget(showBannerCardCheckBox);
quickSettingsWidget->addSettingsWidget(showTagsWidgetCheckBox);
commentsLabel = new QLabel(); commentsLabel = new QLabel();
commentsLabel->setObjectName("commentsLabel"); commentsLabel->setObjectName("commentsLabel");
commentsEdit = new QTextEdit; commentsEdit = new QTextEdit;
@ -69,6 +91,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
bannerCardLabel = new QLabel(); bannerCardLabel = new QLabel();
bannerCardLabel->setObjectName("bannerCardLabel"); bannerCardLabel->setObjectName("bannerCardLabel");
bannerCardLabel->setText(tr("Banner Card")); bannerCardLabel->setText(tr("Banner Card"));
bannerCardLabel->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
bannerCardComboBox = new QComboBox(this); bannerCardComboBox = new QComboBox(this);
connect(deckModel, &DeckListModel::dataChanged, this, [this]() { connect(deckModel, &DeckListModel::dataChanged, this, [this]() {
// Delay the update to avoid race conditions // Delay the update to avoid race conditions
@ -76,8 +99,10 @@ void DeckEditorDeckDockWidget::createDeckDock()
}); });
connect(bannerCardComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(bannerCardComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DeckEditorDeckDockWidget::setBannerCard); &DeckEditorDeckDockWidget::setBannerCard);
bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()); deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList());
deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible());
aIncrement = new QAction(QString(), this); aIncrement = new QAction(QString(), this);
aIncrement->setIcon(QPixmap("theme:icons/increment")); aIncrement->setIcon(QPixmap("theme:icons/increment"));
@ -109,6 +134,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
upperLayout->addWidget(nameLabel, 0, 0); upperLayout->addWidget(nameLabel, 0, 0);
upperLayout->addWidget(nameEdit, 0, 1); upperLayout->addWidget(nameEdit, 0, 1);
upperLayout->addWidget(quickSettingsWidget, 0, 2);
upperLayout->addWidget(commentsLabel, 1, 0); upperLayout->addWidget(commentsLabel, 1, 0);
upperLayout->addWidget(commentsEdit, 1, 1); upperLayout->addWidget(commentsEdit, 1, 1);
@ -202,19 +228,22 @@ void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*&current*/, const
void DeckEditorDeckDockWidget::updateName(const QString &name) void DeckEditorDeckDockWidget::updateName(const QString &name)
{ {
deckModel->getDeckList()->setName(name); deckModel->getDeckList()->setName(name);
emit deckChanged(); emit nameChanged();
emit deckModified();
} }
void DeckEditorDeckDockWidget::updateComments() void DeckEditorDeckDockWidget::updateComments()
{ {
deckModel->getDeckList()->setComments(commentsEdit->toPlainText()); deckModel->getDeckList()->setComments(commentsEdit->toPlainText());
emit deckChanged(); emit commentsChanged();
emit deckModified();
} }
void DeckEditorDeckDockWidget::updateHash() void DeckEditorDeckDockWidget::updateHash()
{ {
hashLabel->setText(deckModel->getDeckList()->getDeckHash()); hashLabel->setText(deckModel->getDeckList()->getDeckHash());
emit deckChanged(); emit hashChanged();
emit deckModified();
} }
void DeckEditorDeckDockWidget::updateBannerCardComboBox() void DeckEditorDeckDockWidget::updateBannerCardComboBox()
@ -268,6 +297,10 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
int restoredIndex = bannerCardComboBox->findText(currentText); int restoredIndex = bannerCardComboBox->findText(currentText);
if (restoredIndex != -1) { if (restoredIndex != -1) {
bannerCardComboBox->setCurrentIndex(restoredIndex); bannerCardComboBox->setCurrentIndex(restoredIndex);
if (deckModel->getDeckList()->getBannerCard().second !=
bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap()["uuid"].toString()) {
setBannerCard(restoredIndex);
}
} else { } else {
// Add a placeholder "-" and set it as the current selection // Add a placeholder "-" and set it as the current selection
int bannerIndex = bannerCardComboBox->findText(deckModel->getDeckList()->getBannerCard().first); int bannerIndex = bannerCardComboBox->findText(deckModel->getDeckList()->getBannerCard().first);
@ -288,7 +321,18 @@ void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */)
QVariantMap itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap(); QVariantMap itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap();
deckModel->getDeckList()->setBannerCard( deckModel->getDeckList()->setBannerCard(
QPair<QString, QString>(itemData["name"].toString(), itemData["uuid"].toString())); QPair<QString, QString>(itemData["name"].toString(), itemData["uuid"].toString()));
emit deckChanged(); emit deckModified();
}
void DeckEditorDeckDockWidget::updateShowBannerCardComboBox(const bool visible)
{
bannerCardLabel->setHidden(!visible);
bannerCardComboBox->setHidden(!visible);
}
void DeckEditorDeckDockWidget::updateShowTagsWidget(const bool visible)
{
deckTagsDisplayWidget->setHidden(!visible);
} }
/** /**
@ -325,8 +369,12 @@ void DeckEditorDeckDockWidget::cleanDeck()
{ {
deckModel->cleanList(); deckModel->cleanList();
nameEdit->setText(QString()); nameEdit->setText(QString());
emit nameChanged();
commentsEdit->setText(QString()); commentsEdit->setText(QString());
emit commentsChanged();
hashLabel->setText(QString()); hashLabel->setText(QString());
emit hashChanged();
emit deckModified();
emit deckChanged(); emit deckChanged();
updateBannerCardComboBox(); updateBannerCardComboBox();
deckTagsDisplayWidget->connectDeckList(deckModel->getDeckList()); deckTagsDisplayWidget->connectDeckList(deckModel->getDeckList());
@ -385,7 +433,7 @@ void DeckEditorDeckDockWidget::actSwapCard()
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
if (isModified) { if (isModified) {
emit deckChanged(); emit deckModified();
} }
update(); update();
@ -480,7 +528,7 @@ void DeckEditorDeckDockWidget::actRemoveCard()
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
if (isModified) { if (isModified) {
emit deckChanged(); emit deckModified();
} }
} }
@ -498,7 +546,7 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int of
else else
deckModel->setData(numberIndex, new_count, Qt::EditRole); deckModel->setData(numberIndex, new_count, Qt::EditRole);
emit deckChanged(); emit deckModified();
} }
void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point) void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point)
@ -526,6 +574,9 @@ void DeckEditorDeckDockWidget::retranslateUi()
setWindowTitle(tr("Deck")); setWindowTitle(tr("Deck"));
nameLabel->setText(tr("Deck &name:")); nameLabel->setText(tr("Deck &name:"));
quickSettingsWidget->setToolTip(tr("Banner Card/Tags Visibility Settings"));
showBannerCardCheckBox->setText(tr("Show banner card selection menu"));
showTagsWidgetCheckBox->setText(tr("Show tags selection menu"));
commentsLabel->setText(tr("&Comments:")); commentsLabel->setText(tr("&Comments:"));
hashLabel1->setText(tr("Hash:")); hashLabel1->setText(tr("Hash:"));

View file

@ -49,7 +49,11 @@ public slots:
void offsetCountAtIndex(const QModelIndex &idx, int offset); void offsetCountAtIndex(const QModelIndex &idx, int offset);
signals: signals:
void nameChanged();
void commentsChanged();
void hashChanged();
void deckChanged(); void deckChanged();
void deckModified();
void cardChanged(CardInfoPtr _card); void cardChanged(CardInfoPtr _card);
private: private:
@ -57,6 +61,9 @@ private:
KeySignals deckViewKeySignals; KeySignals deckViewKeySignals;
QLabel *nameLabel; QLabel *nameLabel;
LineEditUnfocusable *nameEdit; LineEditUnfocusable *nameEdit;
SettingsButtonWidget *quickSettingsWidget;
QCheckBox *showBannerCardCheckBox;
QCheckBox *showTagsWidgetCheckBox;
QLabel *commentsLabel; QLabel *commentsLabel;
QTextEdit *commentsEdit; QTextEdit *commentsEdit;
QLabel *bannerCardLabel; QLabel *bannerCardLabel;
@ -77,6 +84,8 @@ private slots:
void setBannerCard(int); void setBannerCard(int);
void updateHash(); void updateHash();
void refreshShortcuts(); void refreshShortcuts();
void updateShowBannerCardComboBox(bool visible);
void updateShowTagsWidget(bool visible);
}; };
#endif // DECK_EDITOR_DECK_DOCK_WIDGET_H #endif // DECK_EDITOR_DECK_DOCK_WIDGET_H

View file

@ -0,0 +1,46 @@
#include "percent_bar_widget.h"
PercentBarWidget::PercentBarWidget(QWidget *parent, double initialValue) : QWidget(parent), valueToDisplay(initialValue)
{
setMinimumSize(50, 10);
}
void PercentBarWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
QRect rect = this->rect();
const int midX = rect.width() / 2;
const int height = rect.height();
// Draw background border (no fill)
painter.setPen(QPen(Qt::black, 1));
painter.setBrush(Qt::NoBrush);
painter.drawRect(rect.adjusted(0, 0, -1, -1)); // Avoid right/bottom overflow
const double halfWidth = rect.width() / 2.0;
const int barLength = static_cast<int>((qAbs(valueToDisplay) / 100.0) * halfWidth);
QRect fillRect;
if (valueToDisplay > 0.0) {
fillRect = QRect(midX, 0, barLength, height);
painter.fillRect(fillRect, Qt::green);
} else if (valueToDisplay < 0.0) {
fillRect = QRect(midX - barLength, 0, barLength, height);
painter.fillRect(fillRect, Qt::red);
}
// Draw center line at 0
painter.fillRect(midX - 1, 0, 3, height, Qt::white);
// Draw tick marks every 10%
const int tickHeight = 4;
for (int percent = -100; percent <= 100; percent += 10) {
int x = midX + static_cast<int>((percent / 100.0) * halfWidth);
painter.drawLine(x, height - tickHeight, x, height);
}
}

View file

@ -0,0 +1,33 @@
#ifndef PERCENT_BAR_WIDGET_H
#define PERCENT_BAR_WIDGET_H
#include <QColor>
#include <QPainter>
#include <QWidget>
class PercentBarWidget : public QWidget
{
Q_OBJECT
public:
explicit PercentBarWidget(QWidget *parent, double initialValue);
void setValue(double newValue)
{
valueToDisplay = qBound(-100.0, newValue, 100.0); // Clamp to [-100, 100]
update(); // Trigger repaint
}
double value() const
{
return valueToDisplay;
}
protected:
void paintEvent(QPaintEvent *event) override;
private:
double valueToDisplay; // Ranges from -100 to 100
};
#endif // PERCENT_BAR_WIDGET_H

View file

@ -70,6 +70,8 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
cardSizeWidget = cardSizeWidget =
new CardSizeWidget(displayOptionsWidget, flowWidget, SettingsCache::instance().getPrintingSelectorCardSize()); new CardSizeWidget(displayOptionsWidget, flowWidget, SettingsCache::instance().getPrintingSelectorCardSize());
connect(cardSizeWidget, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance(),
&SettingsCache::setPrintingSelectorCardSize);
displayOptionsWidget->addSettingsWidget(sortToolBar); displayOptionsWidget->addSettingsWidget(sortToolBar);
displayOptionsWidget->addSettingsWidget(navigationCheckBox); displayOptionsWidget->addSettingsWidget(navigationCheckBox);

View file

@ -27,9 +27,6 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q
layout->addWidget(manaSymbol); layout->addWidget(manaSymbol);
// Initialize the activeColors map
activeColors[color] = false;
// Connect the color toggled signal // Connect the color toggled signal
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this, connect(manaSymbol, &ManaSymbolWidget::colorToggled, this,
&VisualDatabaseDisplayColorFilterWidget::handleColorToggled); &VisualDatabaseDisplayColorFilterWidget::handleColorToggled);
@ -41,16 +38,8 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q
// Connect the button's toggled signal // Connect the button's toggled signal
connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplayColorFilterWidget::updateFilterMode); connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplayColorFilterWidget::updateFilterMode);
connect(this, &VisualDatabaseDisplayColorFilterWidget::activeColorsChanged, this, connect(filterModel, &FilterTreeModel::layoutChanged, this,
&VisualDatabaseDisplayColorFilterWidget::updateColorFilter); [this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel); });
connect(this, &VisualDatabaseDisplayColorFilterWidget::filterModeChanged, this,
&VisualDatabaseDisplayColorFilterWidget::updateColorFilter);
connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() {
if (blockSync) {
return; // Skip sync if we're blocking it
}
QTimer::singleShot(100, this, &VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel);
});
// Call retranslateUi to set the initial text // Call retranslateUi to set the initial text
retranslateUi(); retranslateUi();
@ -69,95 +58,94 @@ void VisualDatabaseDisplayColorFilterWidget::retranslateUi()
toggleButton->setText(tr("Mode: Include/Exclude")); toggleButton->setText(tr("Mode: Include/Exclude"));
break; break;
} }
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
} }
void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active) void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active)
{ {
activeColors[color] = active; if (active) {
emit activeColorsChanged(); // Notify listeners that the active colors have changed addFilter(color);
} else {
removeFilter(color);
}
} }
void VisualDatabaseDisplayColorFilterWidget::updateColorFilter() void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color)
{ {
blockSync = true; QString colorString = color;
QString typeStr;
// Clear previous filters // Remove previous filters
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
QSet<QString> selectedColors; QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QSet<QString> excludedColors; QList<const CardFilter *> matchingFilters;
// Collect active colors in the selected and excluded sets for (const CardFilter *filter : allColorFilters) {
for (const auto &color : activeColors.keys()) { if (filter->term() == color) {
if (activeColors[color]) { matchingFilters.append(filter);
selectedColors.insert(color); // Include this color
} else {
excludedColors.insert(color); // Exclude this color
} }
} }
for (const CardFilter *filter : matchingFilters) {
filterModel->removeFilter(filter);
}
// Add actual filter
switch (currentMode) { switch (currentMode) {
case FilterMode::ExactMatch: case FilterMode::ExactMatch:
// Exact Match Mode: Only selected colors are allowed filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
if (!selectedColors.isEmpty()) {
// Require all selected colors (TypeAnd)
for (const auto &color : selectedColors) {
QString colorString = color;
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
}
// Exclude all other colors
QStringList allPossibleColors = {"W", "U", "B", "R", "G"};
for (const auto &color : allPossibleColors) {
if (!selectedColors.contains(color)) {
QString colorString = color;
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
}
}
}
break; break;
case FilterMode::Includes: case FilterMode::Includes:
// Includes Mode: Just include selected colors without restrictions filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
for (const auto &color : selectedColors) {
QString colorString = color;
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr,
CardFilter::Attr::AttrColor)); // OR for selected colors
}
break; break;
case FilterMode::IncludeExclude: case FilterMode::IncludeExclude:
// Include/Exclude Mode: Include selected colors and exclude unselected colors filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
for (const auto &color : selectedColors) {
QString colorString = color;
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr,
CardFilter::Attr::AttrColor)); // OR for selected colors
}
for (const auto &color : excludedColors) {
QString colorString = color;
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAndNot,
CardFilter::Attr::AttrColor)); // AND NOT for excluded colors
}
break; break;
} }
}
filterModel->blockSignals(false); void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color)
filterModel->filterTree()->blockSignals(false); {
QString colorString = color;
emit filterModel->filterTree()->changed(); // Remove inclusion filters
emit filterModel->layoutChanged(); QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<const CardFilter *> matchingFilters;
blockSync = false; for (const CardFilter *filter : allColorFilters) {
if (filter->term() == color) {
matchingFilters.append(filter);
}
}
for (const CardFilter *filter : matchingFilters) {
filterModel->removeFilter(filter);
}
// Add exclusion filters if the mode demands it
switch (currentMode) {
case FilterMode::ExactMatch:
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
break;
case FilterMode::IncludeExclude:
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
break;
case FilterMode::Includes:
// No exclusion in Includes mode
break;
}
} }
void VisualDatabaseDisplayColorFilterWidget::updateFilterMode() void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
{ {
blockSync = true;
switch (currentMode) { switch (currentMode) {
case FilterMode::ExactMatch: case FilterMode::ExactMatch:
currentMode = FilterMode::Includes; // Switch to Includes currentMode = FilterMode::Includes; // Switch to Includes
@ -170,59 +158,100 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
break; break;
} }
retranslateUi(); // Update button text based on the mode filterModel->blockSignals(true);
emit filterModeChanged(currentMode); // Signal mode change filterModel->filterTree()->blockSignals(true);
updateColorFilter(); // Reapply the filter based on the new mode
blockSync = false; filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
}
void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel()
{
blockSync = true;
QSet<QString> currentFilters;
// Get current filters of type color
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::Attr::AttrColor)) {
if (filter->type() == CardFilter::Type::TypeAnd || filter->type() == CardFilter::Type::TypeOr) {
currentFilters.insert(filter->term());
}
}
QSet<QString> activeFilterList;
// Iterate over the activeColors map and collect the active colors as strings
for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) {
if (it.value()) { // Only add active colors
activeFilterList.insert(QString(it.key()));
}
}
// Check if the filters in the model match the active filters
if (currentFilters == activeFilterList) {
return;
}
// Remove filters that are in the UI but not in the model
for (const auto &color : activeFilterList) {
if (!currentFilters.contains(color)) {
activeColors[color[0]] = false; // Disable the color
}
}
// Add filters that are in the model but not in the UI
for (const auto &color : currentFilters) {
if (!activeFilterList.contains(color)) {
activeColors[color[0]] = true; // Enable the color
}
}
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>(); QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) { for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
manaSymbolWidget->setColorActive(activeColors[manaSymbolWidget->getSymbolChar()]); handleColorToggled(manaSymbolWidget->getSymbolChar(), manaSymbolWidget->isColorActive());
} }
updateColorFilter(); filterModel->blockSignals(false);
blockSync = false; filterModel->filterTree()->blockSignals(false);
}
emit filterModel->filterTree()->changed();
emit filterModel->layoutChanged();
retranslateUi(); // Update button text based on the mode
emit filterModeChanged(currentMode); // Signal mode change
}
void VisualDatabaseDisplayColorFilterWidget::setManaSymbolActive(QChar color, bool active)
{
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
if (manaSymbolWidget->getSymbolChar() == color) {
manaSymbolWidget->setColorActive(active);
}
}
}
QList<QChar> VisualDatabaseDisplayColorFilterWidget::getActiveColors()
{
QList<QChar> activeColors;
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
if (manaSymbolWidget->isColorActive()) {
activeColors.append(manaSymbolWidget->getSymbolChar());
}
}
return activeColors;
}
void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel()
{
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
bool found = false;
for (const CardFilter *filter : allColorFilters) {
if (manaSymbolWidget->getSymbolChar() == filter->term()) {
switch (currentMode) {
case FilterMode::ExactMatch:
switch (filter->type()) {
case CardFilter::Type::TypeAnd:
setManaSymbolActive(filter->term().at(0), true);
break;
default:
setManaSymbolActive(filter->term().at(0), false);
break;
}
break;
case FilterMode::Includes:
switch (filter->type()) {
case CardFilter::Type::TypeOr:
setManaSymbolActive(filter->term().at(0), true);
break;
default:
setManaSymbolActive(filter->term().at(0), false);
break;
}
break;
case FilterMode::IncludeExclude:
switch (filter->type()) {
case CardFilter::Type::TypeOr:
setManaSymbolActive(filter->term().at(0), true);
break;
default:
setManaSymbolActive(filter->term().at(0), false);
break;
}
break;
}
found = true;
}
}
if (!found) {
setManaSymbolActive(manaSymbolWidget->getSymbolChar(), false);
}
}
}

View file

@ -43,21 +43,21 @@ public:
signals: signals:
void filterModeChanged(FilterMode filterMode); void filterModeChanged(FilterMode filterMode);
void activeColorsChanged();
private slots: private slots:
void handleColorToggled(QChar color, bool active); void handleColorToggled(QChar color, bool active);
void updateColorFilter(); void addFilter(QChar color);
void removeFilter(QChar color);
void updateFilterMode(); void updateFilterMode();
void setManaSymbolActive(QChar color, bool active);
QList<QChar> getActiveColors();
void syncWithFilterModel(); void syncWithFilterModel();
private: private:
FilterTreeModel *filterModel; FilterTreeModel *filterModel;
QHBoxLayout *layout; QHBoxLayout *layout;
QPushButton *toggleButton; QPushButton *toggleButton;
QMap<QChar, bool> activeColors;
FilterMode currentMode = FilterMode::Includes; // Default mode FilterMode currentMode = FilterMode::Includes; // Default mode
bool blockSync = false;
}; };
#endif // VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H #endif // VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H

View file

@ -39,6 +39,7 @@ VisualDatabaseDisplayFilterSaveLoadWidget::VisualDatabaseDisplayFilterSaveLoadWi
void VisualDatabaseDisplayFilterSaveLoadWidget::retranslateUi() void VisualDatabaseDisplayFilterSaveLoadWidget::retranslateUi()
{ {
saveButton->setText(tr("Save Filter")); saveButton->setText(tr("Save Filter"));
saveButton->setToolTip(tr("Save all currently applied filters to a file"));
filenameInput->setPlaceholderText(tr("Enter filename...")); filenameInput->setPlaceholderText(tr("Enter filename..."));
} }

View file

@ -47,6 +47,14 @@ VisualDatabaseDisplayMainTypeFilterWidget::VisualDatabaseDisplayMainTypeFilterWi
createMainTypeButtons(); // Populate buttons initially createMainTypeButtons(); // Populate buttons initially
updateFilterMode(false); // Initialize toggle button text updateFilterMode(false); // Initialize toggle button text
retranslateUi();
}
void VisualDatabaseDisplayMainTypeFilterWidget::retranslateUi()
{
spinBox->setToolTip(tr("Do not display card main-types with less than this amount of cards in the database"));
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
} }
void VisualDatabaseDisplayMainTypeFilterWidget::createMainTypeButtons() void VisualDatabaseDisplayMainTypeFilterWidget::createMainTypeButtons()

View file

@ -16,6 +16,7 @@ class VisualDatabaseDisplayMainTypeFilterWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit VisualDatabaseDisplayMainTypeFilterWidget(QWidget *parent, FilterTreeModel *filterModel); explicit VisualDatabaseDisplayMainTypeFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
void retranslateUi();
void createMainTypeButtons(); void createMainTypeButtons();
void updateMainTypeButtonsVisibility(); void updateMainTypeButtonsVisibility();
int getMaxMainTypeCount() const; int getMaxMainTypeCount() const;

View file

@ -45,6 +45,7 @@ void VisualDatabaseDisplayNameFilterWidget::retranslateUi()
{ {
searchBox->setPlaceholderText(tr("Filter by name...")); searchBox->setPlaceholderText(tr("Filter by name..."));
loadFromDeckButton->setText(tr("Load from Deck")); loadFromDeckButton->setText(tr("Load from Deck"));
loadFromDeckButton->setToolTip(tr("Apply all card names in currently loaded deck as exact match name filters"));
} }
void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck() void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck()

View file

@ -3,12 +3,44 @@
#include "../../../../game/cards/card_database_manager.h" #include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/filters/filter_tree.h" #include "../../../../game/filters/filter_tree.h"
#include "../../../../game/filters/filter_tree_model.h" #include "../../../../game/filters/filter_tree_model.h"
#include "../../../../settings/cache_settings.h"
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton> #include <QPushButton>
#include <QTimer> #include <QTimer>
#include <algorithm> #include <algorithm>
VisualDatabaseDisplayRecentSetFilterSettingsWidget::VisualDatabaseDisplayRecentSetFilterSettingsWidget(QWidget *parent)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
setLayout(layout);
filterToMostRecentSetsCheckBox = new QCheckBox(this);
filterToMostRecentSetsCheckBox->setChecked(
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsEnabled());
connect(filterToMostRecentSetsCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled);
filterToMostRecentSetsAmount = new QSpinBox(this);
filterToMostRecentSetsAmount->setMinimum(1);
filterToMostRecentSetsAmount->setMaximum(100);
filterToMostRecentSetsAmount->setValue(
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount());
connect(filterToMostRecentSetsAmount, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount);
layout->addWidget(filterToMostRecentSetsCheckBox);
layout->addWidget(filterToMostRecentSetsAmount);
retranslateUi();
}
void VisualDatabaseDisplayRecentSetFilterSettingsWidget::retranslateUi()
{
filterToMostRecentSetsCheckBox->setText(tr("Filter to most recent sets"));
}
VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidget *parent, VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidget *parent,
FilterTreeModel *_filterModel) FilterTreeModel *_filterModel)
: QWidget(parent), filterModel(_filterModel) : QWidget(parent), filterModel(_filterModel)
@ -19,6 +51,14 @@ VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidg
layout = new QVBoxLayout(this); layout = new QVBoxLayout(this);
setLayout(layout); setLayout(layout);
recentSetsSettingsWidget = new VisualDatabaseDisplayRecentSetFilterSettingsWidget(this);
layout->addWidget(recentSetsSettingsWidget);
connect(&SettingsCache::instance(), &SettingsCache::visualDatabaseDisplayFilterToMostRecentSetsEnabledChanged, this,
&VisualDatabaseDisplaySetFilterWidget::filterToRecentSets);
connect(&SettingsCache::instance(), &SettingsCache::visualDatabaseDisplayFilterToMostRecentSetsAmountChanged, this,
&VisualDatabaseDisplaySetFilterWidget::filterToRecentSets);
searchBox = new QLineEdit(this); searchBox = new QLineEdit(this);
searchBox->setPlaceholderText(tr("Search sets...")); searchBox->setPlaceholderText(tr("Search sets..."));
layout->addWidget(searchBox); layout->addWidget(searchBox);
@ -36,24 +76,26 @@ VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidg
connect(filterModel, &FilterTreeModel::layoutChanged, this, connect(filterModel, &FilterTreeModel::layoutChanged, this,
[this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplaySetFilterWidget::syncWithFilterModel); }); [this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplaySetFilterWidget::syncWithFilterModel); });
createSetButtons(); // Populate buttons initially createSetButtons(); // Populate buttons initially
updateFilterMode(false); // Initialize toggle button text retranslateUi();
}
void VisualDatabaseDisplaySetFilterWidget::retranslateUi()
{
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
} }
void VisualDatabaseDisplaySetFilterWidget::createSetButtons() void VisualDatabaseDisplaySetFilterWidget::createSetButtons()
{ {
SetList shared_pointerses = CardDatabaseManager::getInstance()->getSetList(); SetList allSets = CardDatabaseManager::getInstance()->getSetList();
// Sort by release date // Sort by release date
std::sort(shared_pointerses.begin(), shared_pointerses.end(), std::sort(allSets.begin(), allSets.end(),
[](const auto &a, const auto &b) { return a->getReleaseDate() > b->getReleaseDate(); }); [](const auto &a, const auto &b) { return a->getReleaseDate() > b->getReleaseDate(); });
int setsToPreactivate = 10; for (const auto &set : allSets) {
int setsActivated = 0; QString shortName = set->getShortName();
QString longName = set->getLongName();
for (const auto &shared_pointer : shared_pointerses) {
QString shortName = shared_pointer->getShortName();
QString longName = shared_pointer->getLongName();
auto *button = new QPushButton(longName + " (" + shortName + ")", flowWidget); auto *button = new QPushButton(longName + " (" + shortName + ")", flowWidget);
button->setCheckable(true); button->setCheckable(true);
@ -66,14 +108,47 @@ void VisualDatabaseDisplaySetFilterWidget::createSetButtons()
// Connect toggle signal // Connect toggle signal
connect(button, &QPushButton::toggled, this, connect(button, &QPushButton::toggled, this,
[this, shortName](bool checked) { handleSetToggled(shortName, checked); }); [this, shortName](bool checked) { handleSetToggled(shortName, checked); });
if (setsActivated < setsToPreactivate) {
setsActivated++;
activeSets[shortName] = true;
button->setChecked(true);
}
} }
updateSetFilter();
updateSetButtonsVisibility(); // Ensure visibility is updated initially filterToRecentSets();
}
void VisualDatabaseDisplaySetFilterWidget::filterToRecentSets()
{
if (SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsEnabled()) {
for (auto set : activeSets.keys()) {
activeSets[set] = false;
}
SetList allSets = CardDatabaseManager::getInstance()->getSetList();
// Sort by release date
std::sort(allSets.begin(), allSets.end(),
[](const auto &a, const auto &b) { return a->getReleaseDate() > b->getReleaseDate(); });
int setsToPreactivate = SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount();
int setsActivated = 0;
for (const auto &set : allSets) {
QString shortName = set->getShortName();
QString longName = set->getLongName();
auto button = setButtons[shortName];
if (setsActivated < setsToPreactivate) {
setsActivated++;
activeSets[shortName] = true;
button->blockSignals(true);
button->setChecked(true);
button->blockSignals(false);
} else {
break;
}
}
updateSetFilter();
updateSetButtonsVisibility();
}
} }
void VisualDatabaseDisplaySetFilterWidget::updateSetButtonsVisibility() void VisualDatabaseDisplaySetFilterWidget::updateSetButtonsVisibility()
@ -183,13 +258,15 @@ void VisualDatabaseDisplaySetFilterWidget::syncWithFilterModel()
for (const auto &key : setButtons.keys()) { for (const auto &key : setButtons.keys()) {
bool active = selectedSets.contains(key); bool active = selectedSets.contains(key);
activeSets[key] = active; activeSets[key] = active;
setButtons[key]->blockSignals(true);
setButtons[key]->setChecked(active); setButtons[key]->setChecked(active);
setButtons[key]->blockSignals(false);
} }
} }
void VisualDatabaseDisplaySetFilterWidget::updateFilterMode(bool checked) void VisualDatabaseDisplaySetFilterWidget::updateFilterMode(bool checked)
{ {
exactMatchMode = checked; exactMatchMode = checked;
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
updateSetFilter(); updateSetFilter();
} retranslateUi();
}

View file

@ -4,19 +4,36 @@
#include "../../../../game/filters/filter_tree_model.h" #include "../../../../game/filters/filter_tree_model.h"
#include "../general/layout_containers/flow_widget.h" #include "../general/layout_containers/flow_widget.h"
#include <QCheckBox>
#include <QLineEdit> #include <QLineEdit>
#include <QMap> #include <QMap>
#include <QPushButton> #include <QPushButton>
#include <QSpinBox>
#include <QToolButton> #include <QToolButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
class VisualDatabaseDisplayRecentSetFilterSettingsWidget : public QWidget
{
Q_OBJECT
public:
VisualDatabaseDisplayRecentSetFilterSettingsWidget(QWidget *parent);
void retranslateUi();
private:
QHBoxLayout *layout;
QCheckBox *filterToMostRecentSetsCheckBox;
QSpinBox *filterToMostRecentSetsAmount;
};
class VisualDatabaseDisplaySetFilterWidget : public QWidget class VisualDatabaseDisplaySetFilterWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit VisualDatabaseDisplaySetFilterWidget(QWidget *parent, FilterTreeModel *filterModel); explicit VisualDatabaseDisplaySetFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
void retranslateUi();
void createSetButtons(); void createSetButtons();
void filterToRecentSets();
void updateSetButtonsVisibility(); void updateSetButtonsVisibility();
void handleSetToggled(const QString &setShortName, bool active); void handleSetToggled(const QString &setShortName, bool active);
@ -28,6 +45,7 @@ private:
FilterTreeModel *filterModel; FilterTreeModel *filterModel;
QMap<QString, int> allMainCardTypesWithCount; QMap<QString, int> allMainCardTypesWithCount;
QVBoxLayout *layout; QVBoxLayout *layout;
VisualDatabaseDisplayRecentSetFilterSettingsWidget *recentSetsSettingsWidget;
QLineEdit *searchBox; QLineEdit *searchBox;
FlowWidget *flowWidget; FlowWidget *flowWidget;
QPushButton *toggleButton; // Mode switch button QPushButton *toggleButton; // Mode switch button

View file

@ -51,6 +51,14 @@ VisualDatabaseDisplaySubTypeFilterWidget::VisualDatabaseDisplaySubTypeFilterWidg
createSubTypeButtons(); // Populate buttons initially createSubTypeButtons(); // Populate buttons initially
updateFilterMode(false); // Initialize the toggle button text updateFilterMode(false); // Initialize the toggle button text
retranslateUi();
}
void VisualDatabaseDisplaySubTypeFilterWidget::retranslateUi()
{
spinBox->setToolTip(tr("Do not display card sub-types with less than this amount of cards in the database"));
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
} }
void VisualDatabaseDisplaySubTypeFilterWidget::createSubTypeButtons() void VisualDatabaseDisplaySubTypeFilterWidget::createSubTypeButtons()

View file

@ -16,6 +16,7 @@ class VisualDatabaseDisplaySubTypeFilterWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit VisualDatabaseDisplaySubTypeFilterWidget(QWidget *parent, FilterTreeModel *filterModel); explicit VisualDatabaseDisplaySubTypeFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
void retranslateUi();
void createSubTypeButtons(); void createSubTypeButtons();
void updateSubTypeButtonsVisibility(); void updateSubTypeButtonsVisibility();
int getMaxSubTypeCount() const; int getMaxSubTypeCount() const;

View file

@ -62,9 +62,6 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
filterModel = new FilterTreeModel(); filterModel = new FilterTreeModel();
filterModel->setObjectName("filterModel"); filterModel->setObjectName("filterModel");
databaseDisplayModel->setFilterTree(filterModel->filterTree());
connect(filterModel, &FilterTreeModel::layoutChanged, this, &VisualDatabaseDisplayWidget::searchModelChanged);
searchKeySignals.setObjectName("searchKeySignals"); searchKeySignals.setObjectName("searchKeySignals");
connect(searchEdit, &QLineEdit::textChanged, this, &VisualDatabaseDisplayWidget::updateSearch); connect(searchEdit, &QLineEdit::textChanged, this, &VisualDatabaseDisplayWidget::updateSearch);
@ -144,11 +141,27 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
connect(debounceTimer, &QTimer::timeout, this, &VisualDatabaseDisplayWidget::searchModelChanged); connect(debounceTimer, &QTimer::timeout, this, &VisualDatabaseDisplayWidget::searchModelChanged);
databaseDisplayModel->setFilterTree(filterModel->filterTree());
connect(filterModel, &FilterTreeModel::layoutChanged, this, &VisualDatabaseDisplayWidget::searchModelChanged);
loadCardsTimer = new QTimer(this); loadCardsTimer = new QTimer(this);
loadCardsTimer->setSingleShot(true); // Ensure it only fires once after the timeout loadCardsTimer->setSingleShot(true); // Ensure it only fires once after the timeout
connect(loadCardsTimer, &QTimer::timeout, this, [this]() { loadCurrentPage(); }); connect(loadCardsTimer, &QTimer::timeout, this, [this]() { loadCurrentPage(); });
loadCardsTimer->start(5000); loadCardsTimer->start(5000);
retranslateUi();
}
void VisualDatabaseDisplayWidget::retranslateUi()
{
clearFilterWidget->setToolTip(tr("Clear all filters"));
quickFilterSaveLoadWidget->setToolTip(tr("Save and load filters"));
quickFilterNameWidget->setToolTip(tr("Filter by exact card name"));
quickFilterSubTypeWidget->setToolTip(tr("Filter by card sub-type"));
quickFilterSetWidget->setToolTip(tr("Filter by set"));
} }
void VisualDatabaseDisplayWidget::resizeEvent(QResizeEvent *event) void VisualDatabaseDisplayWidget::resizeEvent(QResizeEvent *event)
@ -195,13 +208,30 @@ void VisualDatabaseDisplayWidget::populateCards()
databaseDisplayModel->fetchMore(QModelIndex()); databaseDisplayModel->fetchMore(QModelIndex());
} }
QList<const CardFilter *> setFilters = filterModel->getFiltersOfType(CardFilter::AttrSet);
const CardFilter *setFilter = nullptr;
if (setFilters.length() == 1) {
setFilter = setFilters.at(0);
}
for (int row = start; row < end; ++row) { for (int row = start; row < end; ++row) {
qCDebug(VisualDatabaseDisplayLog) << "Adding " << row; qCDebug(VisualDatabaseDisplayLog) << "Adding " << row;
QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn); QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn);
QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole); QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole);
qCDebug(VisualDatabaseDisplayLog) << name.toString(); qCDebug(VisualDatabaseDisplayLog) << name.toString();
if (CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(name.toString())) { if (CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(name.toString())) {
addCard(info); if (setFilter) {
CardInfoPerSetMap setMap = info->getSets();
if (setMap.contains(setFilter->term())) {
for (CardInfoPerSet cardSetInstance : setMap[setFilter->term()]) {
addCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(
name.toString(), cardSetInstance.getProperty("uuid")));
}
}
} else {
addCard(info);
}
} else { } else {
qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!"; qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!";
} }

View file

@ -38,6 +38,7 @@ public:
AbstractTabDeckEditor *deckEditor, AbstractTabDeckEditor *deckEditor,
CardDatabaseModel *database_model, CardDatabaseModel *database_model,
CardDatabaseDisplayModel *database_display_model); CardDatabaseDisplayModel *database_display_model);
void retranslateUi();
void adjustCardsPerPage(); void adjustCardsPerPage();
void populateCards(); void populateCards();

View file

@ -2,6 +2,7 @@
#include "../../../../deck/deck_loader.h" #include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database_manager.h" #include "../../../../game/cards/card_database_manager.h"
#include "../../../../settings/cache_settings.h"
#include "../cards/card_info_picture_widget.h" #include "../cards/card_info_picture_widget.h"
#include <random> #include <random>
@ -12,23 +13,45 @@ VisualDeckEditorSampleHandWidget::VisualDeckEditorSampleHandWidget(QWidget *pare
layout = new QVBoxLayout(this); layout = new QVBoxLayout(this);
setLayout(layout); setLayout(layout);
resetAndHandSizeContainerWidget = new QWidget(this);
resetAndHandSizeLayout = new QHBoxLayout(resetAndHandSizeContainerWidget);
resetAndHandSizeContainerWidget->setLayout(resetAndHandSizeLayout);
resetButton = new QPushButton(this); resetButton = new QPushButton(this);
connect(resetButton, SIGNAL(clicked()), this, SLOT(updateDisplay())); connect(resetButton, SIGNAL(clicked()), this, SLOT(updateDisplay()));
layout->addWidget(resetButton); resetAndHandSizeLayout->addWidget(resetButton);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff); handSizeSpinBox = new QSpinBox(this);
handSizeSpinBox->setValue(SettingsCache::instance().getVisualDeckEditorSampleHandSize());
handSizeSpinBox->setMinimum(1);
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setVisualDeckEditorSampleHandSize);
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
&VisualDeckEditorSampleHandWidget::updateDisplay);
resetAndHandSizeLayout->addWidget(handSizeSpinBox);
layout->addWidget(resetAndHandSizeContainerWidget);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
layout->addWidget(flowWidget); layout->addWidget(flowWidget);
for (CardInfoPtr card : getRandomCards(7)) { cardSizeWidget = new CardSizeWidget(this, flowWidget);
layout->addWidget(cardSizeWidget);
for (CardInfoPtr card : getRandomCards(handSizeSpinBox->value())) {
auto displayWidget = new CardInfoPictureWidget(this); auto displayWidget = new CardInfoPictureWidget(this);
displayWidget->setCard(card); displayWidget->setCard(card);
displayWidget->setScaleFactor(cardSizeWidget->getSlider()->value());
flowWidget->addWidget(displayWidget); flowWidget->addWidget(displayWidget);
} }
retranslateUi();
} }
void VisualDeckEditorSampleHandWidget::retranslateUi() void VisualDeckEditorSampleHandWidget::retranslateUi()
{ {
resetButton->setText(tr("Reset")); resetButton->setText(tr("Draw a new sample hand"));
handSizeSpinBox->setToolTip(tr("Sample hand size"));
} }
void VisualDeckEditorSampleHandWidget::setDeckModel(DeckListModel *deckModel) void VisualDeckEditorSampleHandWidget::setDeckModel(DeckListModel *deckModel)
@ -41,9 +64,12 @@ void VisualDeckEditorSampleHandWidget::setDeckModel(DeckListModel *deckModel)
void VisualDeckEditorSampleHandWidget::updateDisplay() void VisualDeckEditorSampleHandWidget::updateDisplay()
{ {
flowWidget->clearLayout(); flowWidget->clearLayout();
for (CardInfoPtr card : getRandomCards(7)) { for (CardInfoPtr card : getRandomCards(handSizeSpinBox->value())) {
auto displayWidget = new CardInfoPictureWidget(this); auto displayWidget = new CardInfoPictureWidget(this);
displayWidget->setCard(card); displayWidget->setCard(card);
displayWidget->setScaleFactor(cardSizeWidget->getSlider()->value());
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, displayWidget,
&CardInfoPictureWidget::setScaleFactor);
flowWidget->addWidget(displayWidget); flowWidget->addWidget(displayWidget);
} }
} }

View file

@ -1,10 +1,12 @@
#ifndef VISUAL_DECK_EDITOR_SAMPLE_HAND_WIDGET_H #ifndef VISUAL_DECK_EDITOR_SAMPLE_HAND_WIDGET_H
#define VISUAL_DECK_EDITOR_SAMPLE_HAND_WIDGET_H #define VISUAL_DECK_EDITOR_SAMPLE_HAND_WIDGET_H
#include "../../../../client/ui/widgets/cards/card_size_widget.h"
#include "../../../../deck/deck_list_model.h" #include "../../../../deck/deck_list_model.h"
#include "../general/layout_containers/flow_widget.h" #include "../general/layout_containers/flow_widget.h"
#include <QPushButton> #include <QPushButton>
#include <QSpinBox>
#include <QWidget> #include <QWidget>
class VisualDeckEditorSampleHandWidget : public QWidget class VisualDeckEditorSampleHandWidget : public QWidget
@ -22,8 +24,12 @@ public slots:
private: private:
DeckListModel *deckListModel; DeckListModel *deckListModel;
QVBoxLayout *layout; QVBoxLayout *layout;
QWidget *resetAndHandSizeContainerWidget;
QHBoxLayout *resetAndHandSizeLayout;
QPushButton *resetButton; QPushButton *resetButton;
QSpinBox *handSizeSpinBox;
FlowWidget *flowWidget; FlowWidget *flowWidget;
CardSizeWidget *cardSizeWidget;
}; };
#endif // VISUAL_DECK_EDITOR_SAMPLE_HAND_WIDGET_H #endif // VISUAL_DECK_EDITOR_SAMPLE_HAND_WIDGET_H

View file

@ -29,7 +29,6 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_
connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged); connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged);
// The Main Widget and Main Layout, which contain a single Widget: The Scroll Area // The Main Widget and Main Layout, which contain a single Widget: The Scroll Area
setMinimumSize(0, 0);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout = new QVBoxLayout(this); mainLayout = new QVBoxLayout(this);
setLayout(mainLayout); setLayout(mainLayout);
@ -162,7 +161,7 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_
groupAndSortLayout->addWidget(sortCriteriaButton); groupAndSortLayout->addWidget(sortCriteriaButton);
groupAndSortLayout->addWidget(displayTypeButton); groupAndSortLayout->addWidget(displayTypeButton);
scrollArea = new QScrollArea(); scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true); scrollArea->setWidgetResizable(true);
scrollArea->setMinimumSize(0, 0); scrollArea->setMinimumSize(0, 0);
@ -191,7 +190,12 @@ void VisualDeckEditorWidget::retranslateUi()
{ {
sortLabel->setText(tr("Click and drag to change the sort order within the groups")); sortLabel->setText(tr("Click and drag to change the sort order within the groups"));
searchPushButton->setText(tr("Quick search and add card")); searchPushButton->setText(tr("Quick search and add card"));
searchPushButton->setToolTip(tr("Search for closest match in the database (with auto-suggestions) and add "
"preferred printing to the deck on pressing enter"));
sortCriteriaButton->setToolTip(tr("Configure how cards are sorted within their groups"));
displayTypeButton->setText(tr("Flat Layout")); displayTypeButton->setText(tr("Flat Layout"));
displayTypeButton->setToolTip(
tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)"));
} }
void VisualDeckEditorWidget::updateZoneWidgets() void VisualDeckEditorWidget::updateZoneWidgets()
@ -227,6 +231,7 @@ void VisualDeckEditorWidget::addZoneIfDoesNotExist()
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) { for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
if (displayWidget->zoneName == zone) { if (displayWidget->zoneName == zone) {
found = true; found = true;
displayWidget->displayCards();
break; break;
} }
} }

View file

@ -47,6 +47,7 @@ void DeckPreviewColorIdentityFilterWidget::retranslateUi()
{ {
// Set the toggle button text based on the current mode // Set the toggle button text based on the current mode
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes")); toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
toggleButton->setToolTip(tr("Color identity filter mode (AND/OR/NOT conjunctions of filters)"));
} }
void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar color, bool active) void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar color, bool active)

View file

@ -11,7 +11,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const QStringList &activeTags, QWidget *parent) DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const QStringList &activeTags, QWidget *parent)
: QDialog(parent), activeTags_(activeTags) : QDialog(parent), activeTags(activeTags)
{ {
resize(400, 500); resize(400, 500);
@ -100,13 +100,13 @@ DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const Q
combinedTags.removeDuplicates(); combinedTags.removeDuplicates();
// Main layout // Main layout
auto *mainLayout = new QVBoxLayout(this); mainLayout = new QVBoxLayout(this);
// Filter bar // Filter bar
filterInput_ = new QLineEdit(this); filterInput = new QLineEdit(this);
mainLayout->addWidget(filterInput_); mainLayout->addWidget(filterInput);
connect(filterInput_, &QLineEdit::textChanged, this, &DeckPreviewTagDialog::filterTags); connect(filterInput, &QLineEdit::textChanged, this, &DeckPreviewTagDialog::filterTags);
// Instruction label // Instruction label
instructionLabel = new QLabel(this); instructionLabel = new QLabel(this);
@ -114,31 +114,34 @@ DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const Q
mainLayout->addWidget(instructionLabel); mainLayout->addWidget(instructionLabel);
// Tag list view // Tag list view
tagListView_ = new QListWidget(this); tagListView = new QListWidget(this);
mainLayout->addWidget(tagListView_); mainLayout->addWidget(tagListView);
// Populate combined tags // Populate combined tags
for (const auto &tag : combinedTags) { for (const auto &tag : combinedTags) {
auto *item = new QListWidgetItem(tagListView_); auto *item = new QListWidgetItem(tagListView);
auto *tagWidget = new DeckPreviewTagItemWidget(tag, activeTags.contains(tag), this); auto *tagWidget = new DeckPreviewTagItemWidget(tag, activeTags.contains(tag), this);
tagListView_->addItem(item); tagListView->addItem(item);
tagListView_->setItemWidget(item, tagWidget); tagListView->setItemWidget(item, tagWidget);
connect(tagWidget->checkBox(), &QCheckBox::toggled, this, &DeckPreviewTagDialog::onCheckboxStateChanged); connect(tagWidget->checkBox(), &QCheckBox::toggled, this, &DeckPreviewTagDialog::onCheckboxStateChanged);
} }
// Add tag input layout // Add tag input layout
auto *addTagLayout = new QHBoxLayout(); addTagLayout = new QHBoxLayout(this);
newTagInput_ = new QLineEdit(this); newTagInput = new QLineEdit(this);
addTagButton_ = new QPushButton(this); addTagButton = new QPushButton(this);
addTagLayout->addWidget(newTagInput_); addTagButton->setEnabled(false);
addTagLayout->addWidget(addTagButton_); addTagLayout->addWidget(newTagInput);
addTagLayout->addWidget(addTagButton);
mainLayout->addLayout(addTagLayout); mainLayout->addLayout(addTagLayout);
connect(addTagButton_, &QPushButton::clicked, this, &DeckPreviewTagDialog::addTag); connect(addTagButton, &QPushButton::clicked, this, &DeckPreviewTagDialog::addTag);
connect(newTagInput, &QLineEdit::textChanged, this,
[this](const QString &text) { addTagButton->setEnabled(!text.trimmed().isEmpty()); });
// OK and Cancel buttons // OK and Cancel buttons
auto *buttonLayout = new QHBoxLayout(); buttonLayout = new QHBoxLayout(this);
okButton = new QPushButton(this); okButton = new QPushButton(this);
cancelButton = new QPushButton(this); cancelButton = new QPushButton(this);
buttonLayout->addStretch(); buttonLayout->addStretch();
@ -155,30 +158,30 @@ void DeckPreviewTagDialog::retranslateUi()
{ {
setWindowTitle(tr("Deck Tags Manager")); setWindowTitle(tr("Deck Tags Manager"));
instructionLabel->setText(tr("Manage your deck tags. Check or uncheck tags as needed, or add new ones:")); instructionLabel->setText(tr("Manage your deck tags. Check or uncheck tags as needed, or add new ones:"));
newTagInput_->setPlaceholderText(tr("Add a new tag (e.g., Aggro️)")); newTagInput->setPlaceholderText(tr("Add a new tag (e.g., Aggro️)"));
addTagButton_->setText(tr("Add Tag")); addTagButton->setText(tr("Add Tag"));
filterInput_->setPlaceholderText(tr("Filter tags...")); filterInput->setPlaceholderText(tr("Filter tags..."));
okButton->setText(tr("OK")); okButton->setText(tr("OK"));
cancelButton->setText(tr("Cancel")); cancelButton->setText(tr("Cancel"));
} }
QStringList DeckPreviewTagDialog::getActiveTags() const QStringList DeckPreviewTagDialog::getActiveTags() const
{ {
return activeTags_; return activeTags;
} }
void DeckPreviewTagDialog::addTag() void DeckPreviewTagDialog::addTag()
{ {
QString newTag = newTagInput_->text().trimmed(); QString newTag = newTagInput->text().trimmed();
if (newTag.isEmpty()) { if (newTag.isEmpty()) {
QMessageBox::warning(this, tr("Invalid Input"), tr("Tag name cannot be empty!")); QMessageBox::warning(this, tr("Invalid Input"), tr("Tag name cannot be empty!"));
return; return;
} }
// Prevent duplicate tags // Prevent duplicate tags
for (int i = 0; i < tagListView_->count(); ++i) { for (int i = 0; i < tagListView->count(); ++i) {
auto *item = tagListView_->item(i); auto *item = tagListView->item(i);
auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView_->itemWidget(item)); auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView->itemWidget(item));
if (tagWidget && tagWidget->checkBox()->text() == newTag) { if (tagWidget && tagWidget->checkBox()->text() == newTag) {
QMessageBox::warning(this, tr("Duplicate Tag"), tr("This tag already exists.")); QMessageBox::warning(this, tr("Duplicate Tag"), tr("This tag already exists."));
return; return;
@ -186,23 +189,23 @@ void DeckPreviewTagDialog::addTag()
} }
// Add the new tag // Add the new tag
auto *item = new QListWidgetItem(tagListView_); auto *item = new QListWidgetItem(tagListView);
auto *tagWidget = new DeckPreviewTagItemWidget(newTag, true, this); auto *tagWidget = new DeckPreviewTagItemWidget(newTag, true, this);
tagListView_->addItem(item); tagListView->addItem(item);
tagListView_->setItemWidget(item, tagWidget); tagListView->setItemWidget(item, tagWidget);
activeTags_.append(newTag); activeTags.append(newTag);
connect(tagWidget->checkBox(), &QCheckBox::toggled, this, &DeckPreviewTagDialog::onCheckboxStateChanged); connect(tagWidget->checkBox(), &QCheckBox::toggled, this, &DeckPreviewTagDialog::onCheckboxStateChanged);
// Clear the input field // Clear the input field
newTagInput_->clear(); newTagInput->clear();
} }
void DeckPreviewTagDialog::filterTags(const QString &text) void DeckPreviewTagDialog::filterTags(const QString &text)
{ {
for (int i = 0; i < tagListView_->count(); ++i) { for (int i = 0; i < tagListView->count(); ++i) {
auto *item = tagListView_->item(i); auto *item = tagListView->item(i);
auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView_->itemWidget(item)); auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView->itemWidget(item));
if (tagWidget) { if (tagWidget) {
bool matches = tagWidget->checkBox()->text().contains(text, Qt::CaseInsensitive); bool matches = tagWidget->checkBox()->text().contains(text, Qt::CaseInsensitive);
item->setHidden(!matches); item->setHidden(!matches);
@ -212,12 +215,12 @@ void DeckPreviewTagDialog::filterTags(const QString &text)
void DeckPreviewTagDialog::onCheckboxStateChanged() void DeckPreviewTagDialog::onCheckboxStateChanged()
{ {
activeTags_.clear(); activeTags.clear();
for (int i = 0; i < tagListView_->count(); ++i) { for (int i = 0; i < tagListView->count(); ++i) {
auto *item = tagListView_->item(i); auto *item = tagListView->item(i);
auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView_->itemWidget(item)); auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView->itemWidget(item));
if (tagWidget && tagWidget->checkBox()->isChecked()) { if (tagWidget && tagWidget->checkBox()->isChecked()) {
activeTags_.append(tagWidget->checkBox()->text()); activeTags.append(tagWidget->checkBox()->text());
} }
} }
} }

View file

@ -8,6 +8,7 @@
#include <QListWidget> #include <QListWidget>
#include <QPushButton> #include <QPushButton>
#include <QStringList> #include <QStringList>
#include <QVBoxLayout>
class DeckPreviewTagDialog : public QDialog class DeckPreviewTagDialog : public QDialog
{ {
@ -26,14 +27,17 @@ private slots:
void retranslateUi(); void retranslateUi();
private: private:
QVBoxLayout *mainLayout;
QLabel *instructionLabel; QLabel *instructionLabel;
QListWidget *tagListView_; QListWidget *tagListView;
QLineEdit *filterInput_; QLineEdit *filterInput;
QLineEdit *newTagInput_; QHBoxLayout *addTagLayout;
QPushButton *addTagButton_; QLineEdit *newTagInput;
QPushButton *addTagButton;
QHBoxLayout *buttonLayout;
QPushButton *okButton; QPushButton *okButton;
QPushButton *cancelButton; QPushButton *cancelButton;
QStringList activeTags_; QStringList activeTags;
}; };
#endif // DECK_PREVIEW_TAG_DIALOG_H #endif // DECK_PREVIEW_TAG_DIALOG_H

View file

@ -6,7 +6,7 @@
#include <QPainter> #include <QPainter>
DeckPreviewTagDisplayWidget::DeckPreviewTagDisplayWidget(QWidget *parent, const QString &_tagName) DeckPreviewTagDisplayWidget::DeckPreviewTagDisplayWidget(QWidget *parent, const QString &_tagName)
: QWidget(parent), tagName(_tagName), isSelected(false) : QWidget(parent), tagName(_tagName), state(TagState::NotSelected)
{ {
// Create layout // Create layout
auto *layout = new QHBoxLayout(this); auto *layout = new QHBoxLayout(this);
@ -48,36 +48,58 @@ QSize DeckPreviewTagDisplayWidget::sizeHint() const
void DeckPreviewTagDisplayWidget::mousePressEvent(QMouseEvent *event) void DeckPreviewTagDisplayWidget::mousePressEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { switch (event->button()) {
setSelected(!isSelected); case Qt::LeftButton:
emit tagClicked(); setState(TagState::Selected);
break;
case Qt::RightButton:
setState(TagState::Excluded);
break;
case Qt::MiddleButton:
setState(TagState::NotSelected);
break;
default:
break;
} }
QWidget::mousePressEvent(event);
}
void DeckPreviewTagDisplayWidget::setSelected(bool selected) emit tagClicked();
{ QWidget::mousePressEvent(event);
isSelected = selected;
update(); // Trigger a repaint
} }
void DeckPreviewTagDisplayWidget::paintEvent(QPaintEvent *event) void DeckPreviewTagDisplayWidget::paintEvent(QPaintEvent *event)
{ {
QPainter painter(this); QPainter painter(this);
// Set background color QColor backgroundColor;
QColor backgroundColor = isSelected ? QColor(173, 216, 230) : Qt::white; QColor borderColor;
int borderWidth;
switch (state) {
case TagState::Selected:
backgroundColor = QColor(173, 216, 230); // Light blue
borderColor = Qt::blue;
borderWidth = 2;
break;
case TagState::Excluded:
backgroundColor = QColor(255, 182, 193); // Light red/pink
borderColor = Qt::red;
borderWidth = 2;
break;
case TagState::NotSelected:
default:
backgroundColor = Qt::white;
borderColor = Qt::gray;
borderWidth = 1;
break;
}
painter.setBrush(backgroundColor); painter.setBrush(backgroundColor);
painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen);
// Draw background
painter.drawRect(rect()); painter.drawRect(rect());
// Draw border QPen borderPen(borderColor, borderWidth);
QColor borderColor = isSelected ? Qt::blue : Qt::gray;
QPen borderPen(borderColor, isSelected ? 2 : 1);
painter.setPen(borderPen); painter.setPen(borderPen);
painter.drawRect(rect().adjusted(0, 0, -1, -1)); // Adjust for pen width painter.drawRect(rect().adjusted(0, 0, -1, -1));
// Calculate font size based on widget height // Calculate font size based on widget height
QFont font = painter.font(); QFont font = painter.font();

View file

@ -6,6 +6,13 @@
#include <QString> #include <QString>
#include <QWidget> #include <QWidget>
enum class TagState
{
NotSelected,
Selected,
Excluded
};
class DeckPreviewTagDisplayWidget : public QWidget class DeckPreviewTagDisplayWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -22,16 +29,16 @@ public:
{ {
return tagName; return tagName;
} }
bool getSelected() const TagState getState() const
{ {
return isSelected; return state;
} }
/** void setState(const TagState newState)
* @brief Sets the selected state of the tag. {
* @param selected True if the tag is selected, false otherwise. state = newState;
*/ update();
void setSelected(bool selected); };
signals: signals:
/** /**
@ -61,7 +68,7 @@ private:
QLabel *tagLabel; ///< Label for displaying the tag name. QLabel *tagLabel; ///< Label for displaying the tag name.
QPushButton *closeButton; ///< Button to close/remove the tag. QPushButton *closeButton; ///< Button to close/remove the tag.
QString tagName; ///< The name of the tag. QString tagName; ///< The name of the tag.
bool isSelected; ///< Indicates whether the tag is selected. TagState state; ///< Indicates whether the tag is unselected, selected, or excluded.
}; };
#endif // DECK_PREVIEW_TAG_DISPLAY_WIDGET_H #endif // DECK_PREVIEW_TAG_DISPLAY_WIDGET_H

View file

@ -13,6 +13,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QMouseEvent> #include <QMouseEvent>
#include <QSet> #include <QSet>
#include <QStandardItemModel>
#include <QVBoxLayout> #include <QVBoxLayout>
DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent, DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
@ -27,6 +28,8 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
deckLoader = new DeckLoader(); deckLoader = new DeckLoader();
deckLoader->setParent(this); deckLoader->setParent(this);
connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi); connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi);
/* TODO: We shouldn't update the tags on *every* deck load, since it's kinda expensive. We should instead count how
many deck loads have finished already and if we've loaded all decks and THEN load all the tags at once. */
connect(deckLoader, &DeckLoader::loadFinished, visualDeckStorageWidget->tagFilterWidget, connect(deckLoader, &DeckLoader::loadFinished, visualDeckStorageWidget->tagFilterWidget,
&VisualDeckStorageTagFilterWidget::refreshTags); &VisualDeckStorageTagFilterWidget::refreshTags);
deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false); deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false);
@ -194,6 +197,7 @@ void DeckPreviewWidget::updateBannerCardComboBox()
// Block signals temporarily // Block signals temporarily
bool wasBlocked = bannerCardComboBox->blockSignals(true); bool wasBlocked = bannerCardComboBox->blockSignals(true);
bannerCardComboBox->setUpdatesEnabled(false);
// Clear the existing items in the combo box // Clear the existing items in the combo box
bannerCardComboBox->clear(); bannerCardComboBox->clear();
@ -209,12 +213,7 @@ void DeckPreviewWidget::updateBannerCardComboBox()
continue; continue;
for (int k = 0; k < currentCard->getNumber(); ++k) { for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCardByNameAndProviderId( bannerCardSet.insert(QPair<QString, QString>(currentCard->getName(), currentCard->getCardProviderId()));
currentCard->getName(), currentCard->getCardProviderId());
if (info) {
bannerCardSet.insert(
QPair<QString, QString>(currentCard->getName(), currentCard->getCardProviderId()));
}
} }
} }
} }
@ -226,14 +225,19 @@ void DeckPreviewWidget::updateBannerCardComboBox()
return a.first.toLower() < b.first.toLower(); return a.first.toLower() < b.first.toLower();
}); });
for (const auto &pair : pairList) { // This is *slightly* more performant than using addItem in a loop.
QVariantMap dataMap;
dataMap["name"] = pair.first;
dataMap["uuid"] = pair.second;
bannerCardComboBox->addItem(pair.first, dataMap); QStandardItemModel *model = new QStandardItemModel(pairList.size(), 1, bannerCardComboBox);
int row = 0;
for (const auto &pair : pairList) {
QStandardItem *item = new QStandardItem(pair.first);
item->setData(QVariant::fromValue(pair), Qt::UserRole);
model->setItem(row++, 0, item);
} }
bannerCardComboBox->setModel(model);
// Try to restore the previous selection by finding the currentText // Try to restore the previous selection by finding the currentText
int restoredIndex = bannerCardComboBox->findText(currentText); int restoredIndex = bannerCardComboBox->findText(currentText);
if (restoredIndex != -1) { if (restoredIndex != -1) {
@ -251,15 +255,16 @@ void DeckPreviewWidget::updateBannerCardComboBox()
// Restore the previous signal blocking state // Restore the previous signal blocking state
bannerCardComboBox->blockSignals(wasBlocked); bannerCardComboBox->blockSignals(wasBlocked);
bannerCardComboBox->setUpdatesEnabled(true);
} }
void DeckPreviewWidget::setBannerCard(int /* changedIndex */) void DeckPreviewWidget::setBannerCard(int /* changedIndex */)
{ {
QVariantMap itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap(); QVariant itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex());
deckLoader->setBannerCard(QPair<QString, QString>(itemData["name"].toString(), itemData["uuid"].toString())); deckLoader->setBannerCard(QPair<QString, QString>(bannerCardComboBox->currentText(), itemData.toString()));
deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath)); deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath));
bannerCardDisplayWidget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId( bannerCardDisplayWidget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(
itemData["name"].toString(), itemData["uuid"].toString())); bannerCardComboBox->currentText(), itemData.toString()));
} }
void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance) void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance)

View file

@ -7,6 +7,8 @@
#include "../visual_deck_storage_widget.h" #include "../visual_deck_storage_widget.h"
#include "deck_preview_deck_tags_display_widget.h" #include "deck_preview_deck_tags_display_widget.h"
#include <QAbstractItemView>
#include <QApplication>
#include <QComboBox> #include <QComboBox>
#include <QEvent> #include <QEvent>
#include <QVBoxLayout> #include <QVBoxLayout>
@ -74,7 +76,22 @@ protected:
bool eventFilter(QObject *obj, QEvent *event) override bool eventFilter(QObject *obj, QEvent *event) override
{ {
if (event->type() == QEvent::Wheel) { if (event->type() == QEvent::Wheel) {
return true; // Blocks the event if (auto *combo = qobject_cast<QComboBox *>(obj)) {
// If popup is not open, forward event to parent scroll area
if (!combo->view()->isVisible()) {
// Try to find a scrollable parent and manually send the event
QWidget *parent = combo->parentWidget();
while (parent) {
if (auto *scroll = qobject_cast<QAbstractScrollArea *>(parent)) {
QApplication::sendEvent(scroll->viewport(), event);
return true; // Mark event as handled
}
parent = parent->parentWidget();
}
// If no scrollable parent found, just block
return true;
}
}
} }
return QObject::eventFilter(obj, event); return QObject::eventFilter(obj, event);
} }

View file

@ -90,9 +90,9 @@ void VisualDeckStorageFolderDisplayWidget::createWidgetsForFiles()
&VisualDeckStorageWidget::deckLoadRequested); &VisualDeckStorageWidget::deckLoadRequested);
connect(display, &DeckPreviewWidget::openDeckEditor, visualDeckStorageWidget, connect(display, &DeckPreviewWidget::openDeckEditor, visualDeckStorageWidget,
&VisualDeckStorageWidget::openDeckEditor); &VisualDeckStorageWidget::openDeckEditor);
connect(visualDeckStorageWidget->cardSizeWidget->getSlider(), &QSlider::valueChanged, connect(visualDeckStorageWidget->settings(), &VisualDeckStorageQuickSettingsWidget::cardSizeChanged,
display->bannerCardDisplayWidget, &CardInfoPictureWidget::setScaleFactor); display->bannerCardDisplayWidget, &CardInfoPictureWidget::setScaleFactor);
display->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->cardSizeWidget->getSlider()->value()); display->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->settings()->getCardSize());
allDecks.append(display); allDecks.append(display);
} }

View file

@ -0,0 +1,155 @@
#include "visual_deck_storage_quick_settings_widget.h"
#include "../../../../settings/cache_settings.h"
#include "visual_deck_storage_widget.h"
#include <QCheckBox>
#include <QSpinBox>
VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidget *parent)
: SettingsButtonWidget(parent)
{
// show folders checkbox
showFoldersCheckBox = new QCheckBox(this);
showFoldersCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowFolders());
connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageQuickSettingsWidget::showFoldersChanged);
connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowFolders);
// show tag filter widget checkbox
showTagFilterCheckBox = new QCheckBox(this);
showTagFilterCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowTagFilter());
connect(showTagFilterCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageQuickSettingsWidget::showTagFilterChanged);
connect(showTagFilterCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowTagFilter);
// show tags on DeckPreviewWidget checkbox
showTagsOnDeckPreviewsCheckBox = new QCheckBox(this);
showTagsOnDeckPreviewsCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowTagsOnDeckPreviews());
connect(showTagsOnDeckPreviewsCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageQuickSettingsWidget::showTagsOnDeckPreviewsChanged);
connect(showTagsOnDeckPreviewsCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowTagsOnDeckPreviews);
// show banner card selector checkbox
showBannerCardComboBoxCheckBox = new QCheckBox(this);
showBannerCardComboBoxCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowBannerCardComboBox());
connect(showBannerCardComboBoxCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageQuickSettingsWidget::showBannerCardComboBoxChanged);
connect(showBannerCardComboBoxCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowBannerCardComboBox);
// search folder names checkbox
searchFolderNamesCheckBox = new QCheckBox(this);
searchFolderNamesCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageSearchFolderNames());
connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageQuickSettingsWidget::searchFolderNamesChanged);
connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageSearchFolderNames);
// draw unused color identities checkbox
drawUnusedColorIdentitiesCheckBox = new QCheckBox(this);
drawUnusedColorIdentitiesCheckBox->setChecked(
SettingsCache::instance().getVisualDeckStorageDrawUnusedColorIdentities());
connect(drawUnusedColorIdentitiesCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageQuickSettingsWidget::drawUnusedColorIdentitiesChanged);
connect(drawUnusedColorIdentitiesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageDrawUnusedColorIdentities);
// color identity opacity selector
auto unusedColorIdentityOpacityWidget = new QWidget(this);
unusedColorIdentitiesOpacityLabel = new QLabel(unusedColorIdentityOpacityWidget);
unusedColorIdentitiesOpacitySpinBox = new QSpinBox(unusedColorIdentityOpacityWidget);
unusedColorIdentitiesOpacitySpinBox->setMinimum(0);
unusedColorIdentitiesOpacitySpinBox->setMaximum(100);
unusedColorIdentitiesOpacitySpinBox->setValue(
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
&VisualDeckStorageQuickSettingsWidget::unusedColorIdentitiesOpacityChanged);
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
&SettingsCache::instance(), &SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
unusedColorIdentitiesOpacityLabel->setBuddy(unusedColorIdentitiesOpacitySpinBox);
auto unusedColorIdentityOpacityLayout = new QHBoxLayout(unusedColorIdentityOpacityWidget);
unusedColorIdentityOpacityLayout->setContentsMargins(11, 0, 11, 0);
unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacityLabel);
unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacitySpinBox);
// card size slider
cardSizeWidget = new CardSizeWidget(this, nullptr, SettingsCache::instance().getVisualDeckStorageCardSize());
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, this,
&VisualDeckStorageQuickSettingsWidget::cardSizeChanged);
connect(cardSizeWidget, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageCardSize);
// putting everything together
this->addSettingsWidget(showFoldersCheckBox);
this->addSettingsWidget(showTagFilterCheckBox);
this->addSettingsWidget(showTagsOnDeckPreviewsCheckBox);
this->addSettingsWidget(showBannerCardComboBoxCheckBox);
this->addSettingsWidget(searchFolderNamesCheckBox);
this->addSettingsWidget(drawUnusedColorIdentitiesCheckBox);
this->addSettingsWidget(unusedColorIdentityOpacityWidget);
this->addSettingsWidget(cardSizeWidget);
connect(&SettingsCache::instance(), &SettingsCache::langChanged, this,
&VisualDeckStorageQuickSettingsWidget::retranslateUi);
retranslateUi();
}
void VisualDeckStorageQuickSettingsWidget::retranslateUi()
{
showFoldersCheckBox->setText(tr("Show Folders"));
showTagFilterCheckBox->setText(tr("Show Tag Filter"));
showTagsOnDeckPreviewsCheckBox->setText(tr("Show Tags On Deck Previews"));
showBannerCardComboBoxCheckBox->setText(tr("Show Banner Card Selection Option"));
searchFolderNamesCheckBox->setText(tr("Include Folder Names in Search"));
drawUnusedColorIdentitiesCheckBox->setText(tr("Draw unused Color Identities"));
unusedColorIdentitiesOpacityLabel->setText(tr("Unused Color Identities Opacity"));
unusedColorIdentitiesOpacitySpinBox->setSuffix("%");
}
bool VisualDeckStorageQuickSettingsWidget::getShowFolders() const
{
return showFoldersCheckBox->isChecked();
}
bool VisualDeckStorageQuickSettingsWidget::getDrawUnusedColorIdentities() const
{
return drawUnusedColorIdentitiesCheckBox->isChecked();
}
bool VisualDeckStorageQuickSettingsWidget::getShowBannerCardComboBox() const
{
return showBannerCardComboBoxCheckBox->isChecked();
}
bool VisualDeckStorageQuickSettingsWidget::getShowTagFilter() const
{
return showTagFilterCheckBox->isChecked();
}
bool VisualDeckStorageQuickSettingsWidget::getShowTagsOnDeckPreviews() const
{
return showTagsOnDeckPreviewsCheckBox->isChecked();
}
bool VisualDeckStorageQuickSettingsWidget::getSearchFolderNames() const
{
return searchFolderNamesCheckBox->isChecked();
}
int VisualDeckStorageQuickSettingsWidget::getUnusedColorIdentitiesOpacity() const
{
return unusedColorIdentitiesOpacitySpinBox->value();
}
int VisualDeckStorageQuickSettingsWidget::getCardSize() const
{
return cardSizeWidget->getSlider()->value();
}

View file

@ -0,0 +1,55 @@
#ifndef VISUAL_DECK_STORAGE_QUICK_SETTINGS_WIDGET_H
#define VISUAL_DECK_STORAGE_QUICK_SETTINGS_WIDGET_H
#include "../quick_settings/settings_button_widget.h"
class CardSizeWidget;
class QLabel;
class QSpinBox;
class QCheckBox;
/**
* The VDS's quick settings menu.
* Manages the widgets in the quick settings menu dropdown, as well as syncing their values with SettingsCache.
* The current values of the settings are exposed through getters and signals.
*/
class VisualDeckStorageQuickSettingsWidget : public SettingsButtonWidget
{
Q_OBJECT
QCheckBox *showFoldersCheckBox;
QCheckBox *drawUnusedColorIdentitiesCheckBox;
QCheckBox *showBannerCardComboBoxCheckBox;
QCheckBox *showTagFilterCheckBox;
QCheckBox *showTagsOnDeckPreviewsCheckBox;
QCheckBox *searchFolderNamesCheckBox;
QLabel *unusedColorIdentitiesOpacityLabel;
QSpinBox *unusedColorIdentitiesOpacitySpinBox;
CardSizeWidget *cardSizeWidget;
public:
explicit VisualDeckStorageQuickSettingsWidget(QWidget *parent = nullptr);
void retranslateUi();
bool getShowFolders() const;
bool getDrawUnusedColorIdentities() const;
bool getShowBannerCardComboBox() const;
bool getShowTagFilter() const;
bool getShowTagsOnDeckPreviews() const;
bool getSearchFolderNames() const;
int getUnusedColorIdentitiesOpacity() const;
int getCardSize() const;
signals:
void showFoldersChanged(bool enabled);
void drawUnusedColorIdentitiesChanged(bool enabled);
void showBannerCardComboBoxChanged(bool enabled);
void showTagFilterChanged(bool enabled);
void showTagsOnDeckPreviewsChanged(bool enabled);
void searchFolderNamesChanged(bool enabled);
void unusedColorIdentitiesOpacityChanged(int opacity);
void cardSizeChanged(int scale);
};
#endif // VISUAL_DECK_STORAGE_QUICK_SETTINGS_WIDGET_H

View file

@ -33,57 +33,69 @@ void VisualDeckStorageTagFilterWidget::showEvent(QShowEvent *event)
void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const
{ {
// Collect selected tags from DeckPreviewTagDisplayWidget
QStringList selectedTags; QStringList selectedTags;
QStringList excludedTags;
// Collect selected and excluded tags
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) { for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
if (tagWidget->getSelected()) { switch (tagWidget->getState()) {
selectedTags.append(tagWidget->getTagName()); case TagState::Selected:
selectedTags.append(tagWidget->getTagName());
break;
case TagState::Excluded:
excludedTags.append(tagWidget->getTagName());
break;
default:
break;
} }
} }
// If no tags are selected, set all decks as visible // If no tags are selected or excluded, show all
if (selectedTags.isEmpty()) { if (selectedTags.isEmpty() && excludedTags.isEmpty()) {
for (DeckPreviewWidget *deckPreview : deckPreviews) { for (DeckPreviewWidget *deckPreview : deckPreviews) {
deckPreview->filteredByTags = false; deckPreview->filteredByTags = false;
} }
return; return;
} }
// Filter DeckPreviewWidgets that contain all of the selected tags
QList<DeckPreviewWidget *> filteredDecks;
for (DeckPreviewWidget *deckPreview : deckPreviews) { for (DeckPreviewWidget *deckPreview : deckPreviews) {
QStringList deckTags = deckPreview->deckLoader->getTags(); QStringList deckTags = deckPreview->deckLoader->getTags();
// Check if all selectedTags are in deckTags bool hasAllSelected = std::all_of(selectedTags.begin(), selectedTags.end(),
bool allTagsPresent = std::all_of(selectedTags.begin(), selectedTags.end(),
[&deckTags](const QString &tag) { return deckTags.contains(tag); }); [&deckTags](const QString &tag) { return deckTags.contains(tag); });
deckPreview->filteredByTags = !allTagsPresent; bool hasAnyExcluded = std::any_of(excludedTags.begin(), excludedTags.end(),
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
// Filter out if any excluded tag is present or if any selected tag is missing
deckPreview->filteredByTags = !(hasAllSelected && !hasAnyExcluded);
} }
} }
void VisualDeckStorageTagFilterWidget::refreshTags() void VisualDeckStorageTagFilterWidget::refreshTags()
{ {
QStringList allTags = gatherAllTags(); QSet<QString> allTags = gatherAllTags();
removeTagsNotInList(gatherAllTags()); removeTagsNotInList(allTags);
addTagsIfNotPresent(gatherAllTags()); addTagsIfNotPresent(allTags);
sortTags(); sortTags();
} }
void VisualDeckStorageTagFilterWidget::removeTagsNotInList(const QStringList &tags) void VisualDeckStorageTagFilterWidget::removeTagsNotInList(const QSet<QString> &tags)
{ {
// Iterate through all DeckPreviewTagDisplayWidgets auto *flowWidget = findChild<FlowWidget *>();
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) { for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
// If the tag is not in the provided tags list, remove the widget const QString &tagName = tagWidget->getTagName();
if (!tags.contains(tagWidget->getTagName())) {
auto *flowWidget = findChild<FlowWidget *>(); // Keep the tag widget if it is either selected or excluded
if (!tags.contains(tagName) && tagWidget->getState() == TagState::NotSelected) {
flowWidget->removeWidget(tagWidget); flowWidget->removeWidget(tagWidget);
tagWidget->deleteLater(); // Safely delete the widget tagWidget->deleteLater();
} }
} }
} }
void VisualDeckStorageTagFilterWidget::addTagsIfNotPresent(const QStringList &tags) void VisualDeckStorageTagFilterWidget::addTagsIfNotPresent(const QSet<QString> &tags)
{ {
for (const QString &tag : tags) { for (const QString &tag : tags) {
addTagIfNotPresent(tag); addTagIfNotPresent(tag);
@ -136,20 +148,22 @@ void VisualDeckStorageTagFilterWidget::sortTags()
} }
} }
QStringList VisualDeckStorageTagFilterWidget::gatherAllTags() QSet<QString> VisualDeckStorageTagFilterWidget::gatherAllTags() const
{ {
QStringList allTags; QSet<QString> allTags;
QList<DeckPreviewWidget *> deckWidgets = parent->findChildren<DeckPreviewWidget *>(); QList<DeckPreviewWidget *> deckWidgets = parent->findChildren<DeckPreviewWidget *>();
for (DeckPreviewWidget *widget : deckWidgets) { for (DeckPreviewWidget *widget : deckWidgets) {
if (widget->checkVisibility()) { if (widget->checkVisibility()) {
allTags << widget->deckLoader->getTags(); for (const QString &tag : widget->deckLoader->getTags()) {
allTags.insert(tag);
}
} }
} }
return allTags; return allTags;
} }
QStringList VisualDeckStorageTagFilterWidget::getAllKnownTags() QStringList VisualDeckStorageTagFilterWidget::getAllKnownTags() const
{ {
QStringList allTags; QStringList allTags;

View file

@ -10,16 +10,18 @@ class VisualDeckStorageTagFilterWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: VisualDeckStorageWidget *parent;
explicit VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent);
QStringList gatherAllTags(); QSet<QString> gatherAllTags() const;
void filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const; void removeTagsNotInList(const QSet<QString> &tags);
void removeTagsNotInList(const QStringList &tags); void addTagsIfNotPresent(const QSet<QString> &tags);
void addTagsIfNotPresent(const QStringList &tags);
void addTagIfNotPresent(const QString &tag); void addTagIfNotPresent(const QString &tag);
void sortTags(); void sortTags();
QStringList getAllKnownTags();
VisualDeckStorageWidget *parent; public:
explicit VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent);
QStringList getAllKnownTags() const;
void filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const;
public slots: public slots:
void refreshTags(); void refreshTags();

View file

@ -41,79 +41,13 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
refreshButton->setFixedSize(32, 32); refreshButton->setFixedSize(32, 32);
connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible); connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible);
// quick settings menu quickSettingsWidget = new VisualDeckStorageQuickSettingsWidget(this);
showFoldersCheckBox = new QCheckBox(this); connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showFoldersChanged, this,
showFoldersCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowFolders()); &VisualDeckStorageWidget::updateShowFolders);
connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &VisualDeckStorageWidget::updateShowFolders); connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showTagFilterChanged, this,
connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowFolders);
tagFilterVisibilityCheckBox = new QCheckBox(this);
tagFilterVisibilityCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowTagFilter());
connect(tagFilterVisibilityCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageWidget::updateTagsVisibility); &VisualDeckStorageWidget::updateTagsVisibility);
connect(tagFilterVisibilityCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::searchFolderNamesChanged, this,
&SettingsCache::setVisualDeckStorageShowTagFilter);
tagsOnWidgetsVisibilityCheckBox = new QCheckBox(this);
tagsOnWidgetsVisibilityCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowTagsOnDeckPreviews());
connect(tagsOnWidgetsVisibilityCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowTagsOnDeckPreviews);
drawUnusedColorIdentitiesCheckBox = new QCheckBox(this);
drawUnusedColorIdentitiesCheckBox->setChecked(
SettingsCache::instance().getVisualDeckStorageDrawUnusedColorIdentities());
connect(drawUnusedColorIdentitiesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageDrawUnusedColorIdentities);
bannerCardComboBoxVisibilityCheckBox = new QCheckBox(this);
bannerCardComboBoxVisibilityCheckBox->setChecked(
SettingsCache::instance().getVisualDeckStorageShowBannerCardComboBox());
connect(bannerCardComboBoxVisibilityCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowBannerCardComboBox);
searchFolderNamesCheckBox = new QCheckBox(this);
searchFolderNamesCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageSearchFolderNames());
connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageWidget::updateSearchFilter); &VisualDeckStorageWidget::updateSearchFilter);
connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageSearchFolderNames);
// color identity opacity selector
auto unusedColorIdentityOpacityWidget = new QWidget(this);
unusedColorIdentitiesOpacityLabel = new QLabel(unusedColorIdentityOpacityWidget);
unusedColorIdentitiesOpacitySpinBox = new QSpinBox(unusedColorIdentityOpacityWidget);
unusedColorIdentitiesOpacitySpinBox->setMinimum(0);
unusedColorIdentitiesOpacitySpinBox->setMaximum(100);
unusedColorIdentitiesOpacitySpinBox->setValue(
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
&SettingsCache::instance(), &SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
unusedColorIdentitiesOpacityLabel->setBuddy(unusedColorIdentitiesOpacitySpinBox);
unusedColorIdentitiesOpacitySpinBox->setValue(
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
auto unusedColorIdentityOpacityLayout = new QHBoxLayout(unusedColorIdentityOpacityWidget);
unusedColorIdentityOpacityLayout->setContentsMargins(11, 0, 11, 0);
unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacityLabel);
unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacitySpinBox);
// card size slider
cardSizeWidget = new CardSizeWidget(this, nullptr, SettingsCache::instance().getVisualDeckStorageCardSize());
quickSettingsWidget = new SettingsButtonWidget(this);
quickSettingsWidget->addSettingsWidget(showFoldersCheckBox);
quickSettingsWidget->addSettingsWidget(tagFilterVisibilityCheckBox);
quickSettingsWidget->addSettingsWidget(tagsOnWidgetsVisibilityCheckBox);
quickSettingsWidget->addSettingsWidget(bannerCardComboBoxVisibilityCheckBox);
quickSettingsWidget->addSettingsWidget(searchFolderNamesCheckBox);
quickSettingsWidget->addSettingsWidget(drawUnusedColorIdentitiesCheckBox);
quickSettingsWidget->addSettingsWidget(unusedColorIdentityOpacityWidget);
quickSettingsWidget->addSettingsWidget(cardSizeWidget);
searchAndSortLayout->addWidget(deckPreviewColorIdentityFilterWidget); searchAndSortLayout->addWidget(deckPreviewColorIdentityFilterWidget);
searchAndSortLayout->addWidget(sortWidget); searchAndSortLayout->addWidget(sortWidget);
@ -186,14 +120,16 @@ void VisualDeckStorageWidget::retranslateUi()
{ {
databaseLoadIndicator->setText(tr("Loading database ...")); databaseLoadIndicator->setText(tr("Loading database ..."));
showFoldersCheckBox->setText(tr("Show Folders")); refreshButton->setToolTip(tr("Refresh loaded files"));
tagFilterVisibilityCheckBox->setText(tr("Show Tag Filter")); quickSettingsWidget->setToolTip(tr("Visual Deck Storage Settings"));
tagsOnWidgetsVisibilityCheckBox->setText(tr("Show Tags On Deck Previews")); }
bannerCardComboBoxVisibilityCheckBox->setText(tr("Show Banner Card Selection Option"));
searchFolderNamesCheckBox->setText(tr("Include Folder Names in Search")); /**
drawUnusedColorIdentitiesCheckBox->setText(tr("Draw unused Color Identities")); * Gets a const pointer to the quick settings so that the values can be accessed.
unusedColorIdentitiesOpacityLabel->setText(tr("Unused Color Identities Opacity")); */
unusedColorIdentitiesOpacitySpinBox->setSuffix("%"); const VisualDeckStorageQuickSettingsWidget *VisualDeckStorageWidget::settings() const
{
return quickSettingsWidget;
} }
/** /**
@ -210,7 +146,7 @@ void VisualDeckStorageWidget::reapplySortAndFilters()
void VisualDeckStorageWidget::createRootFolderWidget() void VisualDeckStorageWidget::createRootFolderWidget()
{ {
folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath(), false, folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath(), false,
showFoldersCheckBox->isChecked()); quickSettingsWidget->getShowFolders());
scrollArea->setWidget(folderWidget); // this automatically destroys the old folderWidget scrollArea->setWidget(folderWidget); // this automatically destroys the old folderWidget
scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width()); scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width());
@ -261,7 +197,7 @@ void VisualDeckStorageWidget::updateSearchFilter()
{ {
if (folderWidget) { if (folderWidget) {
searchWidget->filterWidgets(folderWidget->findChildren<DeckPreviewWidget *>(), searchWidget->getSearchText(), searchWidget->filterWidgets(folderWidget->findChildren<DeckPreviewWidget *>(), searchWidget->getSearchText(),
searchFolderNamesCheckBox->isChecked()); quickSettingsWidget->getSearchFolderNames());
folderWidget->updateVisibility(); folderWidget->updateVisibility();
} }
} }

View file

@ -8,6 +8,7 @@
#include "deck_preview/deck_preview_color_identity_filter_widget.h" #include "deck_preview/deck_preview_color_identity_filter_widget.h"
#include "deck_preview/deck_preview_widget.h" #include "deck_preview/deck_preview_widget.h"
#include "visual_deck_storage_folder_display_widget.h" #include "visual_deck_storage_folder_display_widget.h"
#include "visual_deck_storage_quick_settings_widget.h"
#include "visual_deck_storage_search_widget.h" #include "visual_deck_storage_search_widget.h"
#include "visual_deck_storage_sort_widget.h" #include "visual_deck_storage_sort_widget.h"
#include "visual_deck_storage_tag_filter_widget.h" #include "visual_deck_storage_tag_filter_widget.h"
@ -29,10 +30,11 @@ public:
void refreshIfPossible(); void refreshIfPossible();
void retranslateUi(); void retranslateUi();
CardSizeWidget *cardSizeWidget;
VisualDeckStorageTagFilterWidget *tagFilterWidget; VisualDeckStorageTagFilterWidget *tagFilterWidget;
bool deckPreviewSelectionAnimationEnabled; bool deckPreviewSelectionAnimationEnabled;
const VisualDeckStorageQuickSettingsWidget *settings() const;
public slots: public slots:
void createRootFolderWidget(); // Refresh the display of cards based on the current sorting option void createRootFolderWidget(); // Refresh the display of cards based on the current sorting option
void updateShowFolders(bool enabled); void updateShowFolders(bool enabled);
@ -60,15 +62,7 @@ private:
VisualDeckStorageSearchWidget *searchWidget; VisualDeckStorageSearchWidget *searchWidget;
DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget; DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget;
QToolButton *refreshButton; QToolButton *refreshButton;
SettingsButtonWidget *quickSettingsWidget; VisualDeckStorageQuickSettingsWidget *quickSettingsWidget;
QCheckBox *showFoldersCheckBox;
QCheckBox *drawUnusedColorIdentitiesCheckBox;
QCheckBox *bannerCardComboBoxVisibilityCheckBox;
QCheckBox *tagFilterVisibilityCheckBox;
QCheckBox *tagsOnWidgetsVisibilityCheckBox;
QCheckBox *searchFolderNamesCheckBox;
QLabel *unusedColorIdentitiesOpacityLabel;
QSpinBox *unusedColorIdentitiesOpacitySpinBox;
QScrollArea *scrollArea; QScrollArea *scrollArea;
VisualDeckStorageFolderDisplayWidget *folderWidget; VisualDeckStorageFolderDisplayWidget *folderWidget;

View file

@ -239,54 +239,33 @@ static QString getDomainForWebsite(DeckLoader::DecklistWebsite website)
} }
} }
// This struct is here to support the forEachCard function call, defined in decklist. It /**
// requires a function to be called for each card, and passes an inner node and a card for * Converts the card to the String that represents it in the decklist export
// each card in the decklist. */
struct FormatDeckListForExport static QString toDecklistExportString(const DecklistCardNode *card)
{ {
// Create refrences for the strings that will be passed in. QString cardString;
QString &mainBoardCards; // Get the number of cards and add the card name
QString &sideBoardCards; cardString += QString::number(card->getNumber());
// create main operator for struct, allowing the foreachcard to work. // Add a space between card num and name
FormatDeckListForExport(QString &_mainBoardCards, QString &_sideBoardCards) cardString += "%20";
: mainBoardCards(_mainBoardCards), sideBoardCards(_sideBoardCards){}; // Add card name
cardString += card->getName();
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const if (!card->getCardSetShortName().isNull()) {
{ cardString += "%20";
// Get the card name cardString += "(" + card->getCardSetShortName() + ")";
CardInfoPtr dbCard = CardDatabaseManager::getInstance()->getCard(card->getName());
if (!dbCard || dbCard->getIsToken()) {
// If it's a token, we don't care about the card.
return;
}
// Check if it's a sideboard card.
if (node->getName() == DECK_ZONE_SIDE) {
// Get the number of cards and add the card name
sideBoardCards += QString::number(card->getNumber());
// Add a space between card num and name
sideBoardCards += "%20";
// Add card name
sideBoardCards += card->getName();
// Add a return at the end of the card
sideBoardCards += "%0A";
} else // If it's a mainboard card, do the same thing, but for the mainboard card string
{
mainBoardCards += QString::number(card->getNumber());
mainBoardCards += "%20";
mainBoardCards += card->getName();
if (!card->getCardSetShortName().isNull()) {
mainBoardCards += "%20";
mainBoardCards += "(" + card->getCardSetShortName() + ")";
}
if (!card->getCardCollectorNumber().isNull()) {
mainBoardCards += "%20";
mainBoardCards += card->getCardCollectorNumber();
}
mainBoardCards += "%0A";
}
} }
}; if (!card->getCardCollectorNumber().isNull()) {
cardString += "%20";
cardString += card->getCardCollectorNumber();
}
// Add a return at the end of the card
cardString += "%0A";
return cardString;
}
/** /**
* Export deck to decklist function, called to format the deck in a way to be sent to a server * Export deck to decklist function, called to format the deck in a way to be sent to a server
@ -298,8 +277,25 @@ QString DeckLoader::exportDeckToDecklist(DecklistWebsite website)
QString deckString = "https://" + getDomainForWebsite(website) + "/?"; QString deckString = "https://" + getDomainForWebsite(website) + "/?";
// Create two strings to pass to function // Create two strings to pass to function
QString mainBoardCards, sideBoardCards; QString mainBoardCards, sideBoardCards;
// Set up the struct to call.
FormatDeckListForExport formatDeckListForExport(mainBoardCards, sideBoardCards); // Set up the function to call
auto formatDeckListForExport = [&mainBoardCards, &sideBoardCards](const auto *node, const auto *card) {
// Get the card name
CardInfoPtr dbCard = CardDatabaseManager::getInstance()->getCard(card->getName());
if (!dbCard || dbCard->getIsToken()) {
// If it's a token, we don't care about the card.
return;
}
// Check if it's a sideboard card.
if (node->getName() == DECK_ZONE_SIDE) {
sideBoardCards += toDecklistExportString(card);
} else {
// If it's a mainboard card, do the same thing, but for the mainboard card string
mainBoardCards += toDecklistExportString(card);
}
};
// call our struct function for each card in the deck // call our struct function for each card in the deck
forEachCard(formatDeckListForExport); forEachCard(formatDeckListForExport);
// Remove the extra return at the end of the last cards // Remove the extra return at the end of the last cards
@ -316,17 +312,12 @@ QString DeckLoader::exportDeckToDecklist(DecklistWebsite website)
return deckString; return deckString;
} }
// This struct is here to support the forEachCard function call, defined in decklist. /**
// It requires a function to be called for each card, and it will set the providerId. * Sets the providerId on each card in the decklist based on its set name and collector number.
struct SetProviderId */
void DeckLoader::resolveSetNameAndNumberToProviderID()
{ {
// Main operator for struct, allowing the foreachcard to work. auto setProviderId = [](const auto node, const auto card) {
SetProviderId()
{
}
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
Q_UNUSED(node); Q_UNUSED(node);
// Retrieve the providerId based on setName and collectorNumber // Retrieve the providerId based on setName and collectorNumber
QString providerId = QString providerId =
@ -336,50 +327,23 @@ struct SetProviderId
// Set the providerId on the card // Set the providerId on the card
card->setCardProviderId(providerId); card->setCardProviderId(providerId);
} };
};
/**
* This function iterates through each card in the decklist and sets the providerId
* on each card based on its set name and collector number.
*/
void DeckLoader::resolveSetNameAndNumberToProviderID()
{
// Set up the struct to call.
SetProviderId setProviderId;
// Call the forEachCard method for each card in the deck
forEachCard(setProviderId); forEachCard(setProviderId);
} }
// This struct is here to support the forEachCard function call, defined in decklist.
// It requires a function to be called for each card, and it will set the providerId.
struct ClearSetNameAndNumber
{
// Main operator for struct, allowing the foreachcard to work.
ClearSetNameAndNumber()
{
}
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
Q_UNUSED(node);
// Set the providerId on the card
card->setCardSetShortName(nullptr);
card->setCardCollectorNumber(nullptr);
}
};
/** /**
* This function iterates through each card in the decklist and sets the providerId * Clears the set name and numbers on each card in the decklist.
* on each card based on its set name and collector number.
*/ */
void DeckLoader::clearSetNamesAndNumbers() void DeckLoader::clearSetNamesAndNumbers()
{ {
// Set up the struct to call. auto clearSetNameAndNumber = [](const auto node, auto card) {
ClearSetNameAndNumber clearSetNameAndNumber; Q_UNUSED(node)
// Set the providerId on the card
card->setCardSetShortName(nullptr);
card->setCardCollectorNumber(nullptr);
};
// Call the forEachCard method for each card in the deck
forEachCard(clearSetNameAndNumber); forEachCard(clearSetNameAndNumber);
} }

View file

@ -67,26 +67,15 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
manager->post(request, data); manager->post(request, data);
} }
struct CopyIfNotAToken void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
{ {
CardDatabase &cardDatabase; auto copyIfNotAToken = [this, &destination](const auto node, const auto card) {
DeckList &destination;
CopyIfNotAToken(CardDatabase &_cardDatabase, DeckList &_destination)
: cardDatabase(_cardDatabase), destination(_destination){};
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
{
CardInfoPtr dbCard = cardDatabase.getCard(card->getName()); CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
if (dbCard && !dbCard->getIsToken()) { if (dbCard && !dbCard->getIsToken()) {
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName()); DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName());
addedCard->setNumber(card->getNumber()); addedCard->setNumber(card->getNumber());
} }
} };
};
void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
{
CopyIfNotAToken copyIfNotAToken(cardDatabase, destination);
source.forEachCard(copyIfNotAToken); source.forEachCard(copyIfNotAToken);
} }

View file

@ -63,6 +63,9 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table")); destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));
destroyCheckBox->setChecked(true); destroyCheckBox->setChecked(true);
faceDownCheckBox = new QCheckBox(tr("Create face-down (Only hides name)"));
connect(faceDownCheckBox, &QCheckBox::toggled, this, &DlgCreateToken::faceDownCheckBoxToggled);
QGridLayout *grid = new QGridLayout; QGridLayout *grid = new QGridLayout;
grid->addWidget(nameLabel, 0, 0); grid->addWidget(nameLabel, 0, 0);
grid->addWidget(nameEdit, 0, 1); grid->addWidget(nameEdit, 0, 1);
@ -73,6 +76,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
grid->addWidget(annotationLabel, 3, 0); grid->addWidget(annotationLabel, 3, 0);
grid->addWidget(annotationEdit, 3, 1); grid->addWidget(annotationEdit, 3, 1);
grid->addWidget(destroyCheckBox, 4, 0, 1, 2); grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
grid->addWidget(faceDownCheckBox, 5, 0, 1, 2);
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data")); QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
tokenDataGroupBox->setLayout(grid); tokenDataGroupBox->setLayout(grid);
@ -155,6 +159,21 @@ void DlgCreateToken::closeEvent(QCloseEvent *event)
SettingsCache::instance().setTokenDialogGeometry(saveGeometry()); SettingsCache::instance().setTokenDialogGeometry(saveGeometry());
} }
void DlgCreateToken::faceDownCheckBoxToggled(bool checked)
{
if (checked) {
colorEdit->setCurrentIndex(6);
colorEdit->setEnabled(false);
ptEdit->clear();
ptEdit->clearFocus();
ptEdit->setEnabled(false);
} else {
colorEdit->setEnabled(true);
ptEdit->setEnabled(true);
annotationEdit->setEnabled(true);
}
}
void DlgCreateToken::tokenSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/) void DlgCreateToken::tokenSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
{ {
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current); const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
@ -230,5 +249,6 @@ TokenInfo DlgCreateToken::getTokenInfo() const
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(), .color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
.pt = ptEdit->text(), .pt = ptEdit->text(),
.annotation = annotationEdit->text(), .annotation = annotationEdit->text(),
.destroy = destroyCheckBox->isChecked()}; .destroy = destroyCheckBox->isChecked(),
.faceDown = faceDownCheckBox->isChecked()};
} }

View file

@ -24,6 +24,7 @@ struct TokenInfo
QString pt; QString pt;
QString annotation; QString annotation;
bool destroy = true; bool destroy = true;
bool faceDown = false;
}; };
class DlgCreateToken : public QDialog class DlgCreateToken : public QDialog
@ -36,6 +37,7 @@ public:
protected: protected:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
private slots: private slots:
void faceDownCheckBoxToggled(bool checked);
void tokenSelectionChanged(const QModelIndex &current, const QModelIndex &previous); void tokenSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
void updateSearch(const QString &search); void updateSearch(const QString &search);
void actChooseTokenFromAll(bool checked); void actChooseTokenFromAll(bool checked);
@ -51,6 +53,7 @@ private:
QComboBox *colorEdit; QComboBox *colorEdit;
QLineEdit *nameEdit, *ptEdit, *annotationEdit; QLineEdit *nameEdit, *ptEdit, *annotationEdit;
QCheckBox *destroyCheckBox; QCheckBox *destroyCheckBox;
QCheckBox *faceDownCheckBox;
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton; QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
CardInfoPictureWidget *pic; CardInfoPictureWidget *pic;
QTreeView *chooseTokenView; QTreeView *chooseTokenView;

View file

@ -161,7 +161,7 @@ void DlgEditTokens::actAddToken()
} }
} }
QString setName = CardDatabase::TOKENS_SETNAME; QString setName = CardSet::TOKENS_SETNAME;
CardInfoPerSetMap sets; CardInfoPerSetMap sets;
sets[setName].append(CardInfoPerSet(databaseModel->getDatabase()->getSet(setName))); sets[setName].append(CardInfoPerSet(databaseModel->getDatabase()->getSet(setName)));
CardInfoPtr card = CardInfo::newInstance(name, "", true, QVariantHash(), QList<CardRelation *>(), CardInfoPtr card = CardInfo::newInstance(name, "", true, QVariantHash(), QList<CardRelation *>(),

View file

@ -4,6 +4,7 @@
#include "../client/network/release_channel.h" #include "../client/network/release_channel.h"
#include "../client/network/spoiler_background_updater.h" #include "../client/network/spoiler_background_updater.h"
#include "../client/sound_engine.h" #include "../client/sound_engine.h"
#include "../client/tabs/tab_supervisor.h"
#include "../client/ui/picture_loader/picture_loader.h" #include "../client/ui/picture_loader/picture_loader.h"
#include "../client/ui/theme_manager.h" #include "../client/ui/theme_manager.h"
#include "../deck/custom_line_edit.h" #include "../deck/custom_line_edit.h"
@ -629,6 +630,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setCloseEmptyCardView); &SettingsCache::setCloseEmptyCardView);
focusCardViewSearchBarCheckBox.setChecked(SettingsCache::instance().getFocusCardViewSearchBar());
connect(&focusCardViewSearchBarCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setFocusCardViewSearchBar);
annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens()); annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens());
connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setAnnotateTokens); &SettingsCache::setAnnotateTokens);
@ -642,8 +647,9 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0); generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0);
generalGrid->addWidget(&playToStackCheckBox, 2, 0); generalGrid->addWidget(&playToStackCheckBox, 2, 0);
generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0); generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0);
generalGrid->addWidget(&annotateTokensCheckBox, 4, 0); generalGrid->addWidget(&focusCardViewSearchBarCheckBox, 4, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 5, 0); generalGrid->addWidget(&annotateTokensCheckBox, 5, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 6, 0);
generalGroupBox = new QGroupBox; generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid); generalGroupBox->setLayout(generalGrid);
@ -699,12 +705,20 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
index == visualDeckStoragePromptForConversionIndexAlways); index == visualDeckStoragePromptForConversionIndexAlways);
}); });
defaultDeckEditorTypeSelector.addItem(""); // these will be set in retranslateUI
defaultDeckEditorTypeSelector.addItem("");
defaultDeckEditorTypeSelector.setCurrentIndex(SettingsCache::instance().getDefaultDeckEditorType());
connect(&defaultDeckEditorTypeSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
&SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType);
auto *deckEditorGrid = new QGridLayout; auto *deckEditorGrid = new QGridLayout;
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0); deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0); deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0);
deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0); deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0); deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1); deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1);
deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0);
deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1);
deckEditorGroupBox = new QGroupBox; deckEditorGroupBox = new QGroupBox;
deckEditorGroupBox->setLayout(deckEditorGrid); deckEditorGroupBox->setLayout(deckEditorGrid);
@ -754,6 +768,7 @@ void UserInterfaceSettingsPage::retranslateUi()
clickPlaysAllSelectedCheckBox.setText(tr("&Clicking plays all selected cards (instead of just the clicked card)")); clickPlaysAllSelectedCheckBox.setText(tr("&Clicking plays all selected cards (instead of just the clicked card)"));
playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default")); playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default"));
closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed")); closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed"));
focusCardViewSearchBarCheckBox.setText(tr("Auto focus search bar when card view window is opened"));
annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); annotateTokensCheckBox.setText(tr("Annotate card text on tokens"));
useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen"));
notificationsGroupBox->setTitle(tr("Notifications settings")); notificationsGroupBox->setTitle(tr("Notifications settings"));
@ -774,6 +789,9 @@ void UserInterfaceSettingsPage::retranslateUi()
tr("ask to convert to .cod")); tr("ask to convert to .cod"));
visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways, visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways,
tr("always convert to .cod")); tr("always convert to .cod"));
defaultDeckEditorTypeLabel.setText(tr("Default deck editor type"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor"));
replayGroupBox->setTitle(tr("Replay settings")); replayGroupBox->setTitle(tr("Replay settings"));
rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:")); rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:"));
rewindBufferingMsBox.setSuffix(" ms"); rewindBufferingMsBox.setSuffix(" ms");

View file

@ -147,6 +147,7 @@ private:
QCheckBox clickPlaysAllSelectedCheckBox; QCheckBox clickPlaysAllSelectedCheckBox;
QCheckBox playToStackCheckBox; QCheckBox playToStackCheckBox;
QCheckBox closeEmptyCardViewCheckBox; QCheckBox closeEmptyCardViewCheckBox;
QCheckBox focusCardViewSearchBarCheckBox;
QCheckBox annotateTokensCheckBox; QCheckBox annotateTokensCheckBox;
QCheckBox useTearOffMenusCheckBox; QCheckBox useTearOffMenusCheckBox;
QCheckBox tapAnimationCheckBox; QCheckBox tapAnimationCheckBox;
@ -155,6 +156,8 @@ private:
QComboBox visualDeckStoragePromptForConversionSelector; QComboBox visualDeckStoragePromptForConversionSelector;
QCheckBox visualDeckStorageInGameCheckBox; QCheckBox visualDeckStorageInGameCheckBox;
QCheckBox visualDeckStorageSelectionAnimationCheckBox; QCheckBox visualDeckStorageSelectionAnimationCheckBox;
QLabel defaultDeckEditorTypeLabel;
QComboBox defaultDeckEditorTypeSelector;
QLabel rewindBufferingMsLabel; QLabel rewindBufferingMsLabel;
QSpinBox rewindBufferingMsBox; QSpinBox rewindBufferingMsBox;
QGroupBox *generalGroupBox; QGroupBox *generalGroupBox;

View file

@ -3,23 +3,40 @@
#include "../settings/cache_settings.h" #include "../settings/cache_settings.h"
#include "../utility/logger.h" #include "../utility/logger.h"
#include <QClipboard>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QPushButton>
#include <QRegularExpression>
#include <QVBoxLayout> #include <QVBoxLayout>
DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent) DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
{ {
logArea = new QPlainTextEdit; logArea = new QPlainTextEdit;
logArea->setReadOnly(true); logArea->setReadOnly(true);
auto *mainLayout = new QVBoxLayout; auto *mainLayout = new QVBoxLayout;
mainLayout->setSpacing(3);
mainLayout->setContentsMargins(20, 20, 20, 6);
mainLayout->addWidget(logArea); mainLayout->addWidget(logArea);
auto *bottomLayout = new QHBoxLayout;
coClearLog = new QCheckBox; coClearLog = new QCheckBox;
coClearLog->setText(tr("Clear log when closing")); coClearLog->setText(tr("Clear log when closing"));
coClearLog->setChecked(SettingsCache::instance().servers().getClearDebugLogStatus(false)); coClearLog->setChecked(SettingsCache::instance().servers().getClearDebugLogStatus(false));
connect(coClearLog, &QCheckBox::toggled, this, &DlgViewLog::actCheckBoxChanged); connect(coClearLog, &QCheckBox::toggled, this, &DlgViewLog::actCheckBoxChanged);
mainLayout->addWidget(coClearLog);
copyToClipboardButton = new QPushButton;
copyToClipboardButton->setText(tr("Copy to clipboard"));
copyToClipboardButton->setAutoDefault(false);
connect(copyToClipboardButton, &QPushButton::clicked, this, &DlgViewLog::actCopyToClipboard);
bottomLayout->addWidget(coClearLog);
bottomLayout->addStretch();
bottomLayout->addWidget(copyToClipboardButton);
mainLayout->addLayout(bottomLayout);
setLayout(mainLayout); setLayout(mainLayout);
@ -27,7 +44,7 @@ DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
resize(800, 500); resize(800, 500);
loadInitialLogBuffer(); loadInitialLogBuffer();
connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::logEntryAdded); connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::appendLogEntry);
} }
void DlgViewLog::actCheckBoxChanged(bool abNewValue) void DlgViewLog::actCheckBoxChanged(bool abNewValue)
@ -35,16 +52,26 @@ void DlgViewLog::actCheckBoxChanged(bool abNewValue)
SettingsCache::instance().servers().setClearDebugLogStatus(abNewValue); SettingsCache::instance().servers().setClearDebugLogStatus(abNewValue);
} }
void DlgViewLog::actCopyToClipboard()
{
QApplication::clipboard()->setText(logArea->toPlainText());
}
void DlgViewLog::loadInitialLogBuffer() void DlgViewLog::loadInitialLogBuffer()
{ {
QList<QString> logBuffer = Logger::getInstance().getLogBuffer(); QList<QString> logBuffer = Logger::getInstance().getLogBuffer();
for (const QString &message : logBuffer) for (const QString &message : logBuffer)
logEntryAdded(message); appendLogEntry(message);
} }
void DlgViewLog::logEntryAdded(QString message) void DlgViewLog::appendLogEntry(const QString &message)
{ {
logArea->appendPlainText(message); static auto colorEscapeCodePattern = QRegularExpression("\033\\[\\d+m");
QString sanitizedMessage = message;
sanitizedMessage.replace(colorEscapeCodePattern, "");
logArea->appendPlainText(sanitizedMessage);
} }
void DlgViewLog::closeEvent(QCloseEvent * /* event */) void DlgViewLog::closeEvent(QCloseEvent * /* event */)

View file

@ -19,11 +19,13 @@ protected:
private: private:
QPlainTextEdit *logArea; QPlainTextEdit *logArea;
QCheckBox *coClearLog; QCheckBox *coClearLog;
QPushButton *copyToClipboardButton;
void loadInitialLogBuffer(); void loadInitialLogBuffer();
private slots: private slots:
void logEntryAdded(QString message); void appendLogEntry(const QString &message);
void actCheckBoxChanged(bool abNewValue); void actCheckBoxChanged(bool abNewValue);
void actCopyToClipboard();
}; };
#endif #endif

View file

@ -39,6 +39,8 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
prepareGeometryChange(); prepareGeometryChange();
update(); update();
}); });
connect(item, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater);
} }
AbstractCardDragItem::~AbstractCardDragItem() AbstractCardDragItem::~AbstractCardDragItem()

View file

@ -2,9 +2,9 @@
#include "../../client/ui/picture_loader/picture_loader.h" #include "../../client/ui/picture_loader/picture_loader.h"
#include "../../settings/cache_settings.h" #include "../../settings/cache_settings.h"
#include "../cards/card_database.h"
#include "../cards/card_database_manager.h"
#include "../game_scene.h" #include "../game_scene.h"
#include "card_database.h"
#include "card_database_manager.h"
#include <QCursor> #include <QCursor>
#include <QGraphicsScene> #include <QGraphicsScene>

View file

@ -1,8 +1,8 @@
#ifndef ABSTRACTCARDITEM_H #ifndef ABSTRACTCARDITEM_H
#define ABSTRACTCARDITEM_H #define ABSTRACTCARDITEM_H
#include "../board/arrow_target.h" #include "../cards/card_info.h"
#include "card_info.h" #include "arrow_target.h"
class Player; class Player;

View file

@ -3,10 +3,10 @@
#include "../../settings/cache_settings.h" #include "../../settings/cache_settings.h"
#include "../cards/card_info.h" #include "../cards/card_info.h"
#include "../cards/card_item.h"
#include "../player/player.h" #include "../player/player.h"
#include "../player/player_target.h" #include "../player/player_target.h"
#include "../zones/card_zone.h" #include "../zones/card_zone.h"
#include "card_item.h"
#include "color.h" #include "color.h"
#include "pb/command_attach_card.pb.h" #include "pb/command_attach_card.pb.h"
#include "pb/command_create_arrow.pb.h" #include "pb/command_create_arrow.pb.h"

View file

@ -47,10 +47,25 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos)
cursorZone = zoneViewZone; cursorZone = zoneViewZone;
else if (cardZone) else if (cardZone)
cursorZone = cardZone; cursorZone = cardZone;
if (!cursorZone)
return; // Always update the current zone, even if its null, to cancel the drag
// instead of dropping cards into an non-intuitive location.
currentZone = cursorZone; currentZone = cursorZone;
if (!cursorZone) {
// Avoid the cards getting stuck visually when not over
// any zone.
QPointF newPos = cursorScenePos - hotSpot;
if (newPos != pos()) {
for (int i = 0; i < childDrags.size(); i++)
childDrags[i]->setPos(newPos + childDrags[i]->getHotSpot());
setPos(newPos);
}
return;
}
QPointF zonePos = currentZone->scenePos(); QPointF zonePos = currentZone->scenePos();
QPointF cursorPosInZone = cursorScenePos - zonePos; QPointF cursorPosInZone = cursorScenePos - zonePos;

View file

@ -2,14 +2,14 @@
#include "../../client/tabs/tab_game.h" #include "../../client/tabs/tab_game.h"
#include "../../settings/cache_settings.h" #include "../../settings/cache_settings.h"
#include "../board/arrow_item.h" #include "../cards/card_info.h"
#include "../game_scene.h" #include "../game_scene.h"
#include "../player/player.h" #include "../player/player.h"
#include "../zones/card_zone.h" #include "../zones/card_zone.h"
#include "../zones/table_zone.h" #include "../zones/table_zone.h"
#include "../zones/view_zone.h" #include "../zones/view_zone.h"
#include "arrow_item.h"
#include "card_drag_item.h" #include "card_drag_item.h"
#include "card_info.h"
#include "pb/serverinfo_card.pb.h" #include "pb/serverinfo_card.pb.h"
#include <QApplication> #include <QApplication>
@ -336,8 +336,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() < if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
2 * QApplication::startDragDistance()) 2 * QApplication::startDragDistance())
return; return;
if (zone->getIsView()) { if (const ZoneViewZone *view = qobject_cast<const ZoneViewZone *>(zone)) {
const ZoneViewZone *view = static_cast<const ZoneViewZone *>(zone);
if (view->getRevealZone() && !view->getWriteableRevealZone()) if (view->getRevealZone() && !view->getWriteableRevealZone())
return; return;
} else if (!owner->getLocalOrJudge()) } else if (!owner->getLocalOrJudge())
@ -394,10 +393,8 @@ void CardItem::playCard(bool faceDown)
*/ */
static bool isUnwritableRevealZone(CardZone *zone) static bool isUnwritableRevealZone(CardZone *zone)
{ {
if (zone && zone->getIsView()) { if (auto *view = qobject_cast<ZoneViewZone *>(zone)) {
if (auto *view = static_cast<ZoneViewZone *>(zone)) { return view->getRevealZone() && !view->getWriteableRevealZone();
return view->getRevealZone() && !view->getWriteableRevealZone();
}
} }
return false; return false;
} }

View file

@ -1,6 +1,6 @@
#include "card_list.h" #include "card_list.h"
#include "card_info.h" #include "../cards/card_info.h"
#include "card_item.h" #include "card_item.h"
#include <QDebug> #include <QDebug>

View file

@ -16,8 +16,6 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
const char *CardDatabase::TOKENS_SETNAME = "TK";
CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded) CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded)
{ {
qRegisterMetaType<CardInfoPtr>("CardInfoPtr"); qRegisterMetaType<CardInfoPtr>("CardInfoPtr");
@ -386,9 +384,10 @@ CardInfoPerSet CardDatabase::getSpecificSetForCard(const QString &cardName,
for (const auto &cardInfoPerSetList : setMap) { for (const auto &cardInfoPerSetList : setMap) {
for (auto &cardInfoForSet : cardInfoPerSetList) { for (auto &cardInfoForSet : cardInfoPerSetList) {
if (cardInfoForSet.getPtr()->getShortName() == setShortName && if (cardInfoForSet.getPtr()->getShortName() == setShortName) {
cardInfoForSet.getProperty("num") == collectorNumber) { if (cardInfoForSet.getProperty("num") == collectorNumber || collectorNumber.isEmpty()) {
return cardInfoForSet; return cardInfoForSet;
}
} }
} }
} }
@ -559,16 +558,15 @@ void CardDatabase::notifyEnabledSetsChanged()
bool CardDatabase::saveCustomTokensToFile() bool CardDatabase::saveCustomTokensToFile()
{ {
QString fileName = QString fileName = SettingsCache::instance().getCustomCardDatabasePath() + "/" + CardSet::TOKENS_SETNAME + ".xml";
SettingsCache::instance().getCustomCardDatabasePath() + "/" + CardDatabase::TOKENS_SETNAME + ".xml";
SetNameMap tmpSets; SetNameMap tmpSets;
CardSetPtr customTokensSet = getSet(CardDatabase::TOKENS_SETNAME); CardSetPtr customTokensSet = getSet(CardSet::TOKENS_SETNAME);
tmpSets.insert(CardDatabase::TOKENS_SETNAME, customTokensSet); tmpSets.insert(CardSet::TOKENS_SETNAME, customTokensSet);
CardNameMap tmpCards; CardNameMap tmpCards;
for (const CardInfoPtr &card : cards) { for (const CardInfoPtr &card : cards) {
if (card->getSets().contains(CardDatabase::TOKENS_SETNAME)) { if (card->getSets().contains(CardSet::TOKENS_SETNAME)) {
tmpCards.insert(card->getName(), card); tmpCards.insert(card->getName(), card);
} }
} }

View file

@ -61,8 +61,6 @@ private:
*removeCardMutex = new QBasicMutex(); *removeCardMutex = new QBasicMutex();
public: public:
static const char *TOKENS_SETNAME;
explicit CardDatabase(QObject *parent = nullptr); explicit CardDatabase(QObject *parent = nullptr);
~CardDatabase() override; ~CardDatabase() override;
void clear(); void clear();

View file

@ -387,7 +387,7 @@ TokenEditModel::TokenEditModel(QObject *parent) : CardDatabaseDisplayModel(paren
bool TokenEditModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const bool TokenEditModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const
{ {
CardInfoPtr info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow); CardInfoPtr info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
return info->getIsToken() && info->getSets().contains(CardDatabase::TOKENS_SETNAME) && rowMatchesCardName(info); return info->getIsToken() && info->getSets().contains(CardSet::TOKENS_SETNAME) && rowMatchesCardName(info);
} }
int TokenEditModel::rowCount(const QModelIndex &parent) const int TokenEditModel::rowCount(const QModelIndex &parent) const

View file

@ -11,6 +11,8 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
const char *CardSet::TOKENS_SETNAME = "TK";
CardSet::CardSet(const QString &_shortName, CardSet::CardSet(const QString &_shortName,
const QString &_longName, const QString &_longName,
const QString &_setType, const QString &_setType,
@ -289,8 +291,12 @@ QString CardInfo::getCorrectedName() const
void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info) void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info)
{ {
_set->append(smartThis); if (!_set->contains(smartThis)) {
sets[_set->getShortName()].append(_info); _set->append(smartThis);
}
if (!sets[_set->getShortName()].contains(_info)) {
sets[_set->getShortName()].append(_info);
}
refreshCachedSetNames(); refreshCachedSetNames();
} }

View file

@ -43,6 +43,8 @@ public:
PriorityLowest = 100, PriorityLowest = 100,
}; };
static const char *TOKENS_SETNAME;
private: private:
QString shortName, longName; QString shortName, longName;
unsigned int sortKey; unsigned int sortKey;

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