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 \
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>Debian 12</kbd> <sub><i>Bookworm</i></sub>
<kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub>
<kbd>Fedora 42</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>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/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
tags:
- '*'
pull_request:
@ -101,12 +102,12 @@ jobs:
package: DEB
- distro: Fedora
version: 40
version: 41
package: RPM
test: skip # Running tests on all distros is superfluous
- distro: Fedora
version: 41
version: 42
package: RPM
- distro: Ubuntu

View file

@ -7,6 +7,7 @@ on:
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
jobs:
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-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 build

View file

@ -8,7 +8,7 @@
<a href="#related-repositories">Related</a> <b>|</b>
<a href="#community-resources-">Community</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>
</p>
@ -38,8 +38,8 @@ Latest <kbd>stable</kbd> release:
</pre><pre>
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)
<sub><i>While incorporating the latest fixes and features, beta builds may not be stable and/or contain new bugs!</i></sub>
<sub><b><i>Please report any findings when testing them!</i></b></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 and open new issues when testing them!</i></b></sub>
</pre>
# Related Repositories
@ -79,10 +79,11 @@ Cockatrice tries to use the [Google Developer Documentation Style Guide](https:/
<details>
<summary><b>Kudos to our amazing contributors ❤️</b></summary>
<br>
<a href="https://github.com/Cockatrice/Cockatrice/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Cockatrice/Cockatrice" />
</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>
### 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>
# 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))*
- [Qt](https://www.qt.io/developers/)
@ -143,17 +144,16 @@ The following flags (with their non-default values) can be passed to `cmake`:
# Run
<kbd>Cockatrice</kbd> is the game client<br>
<kbd>Oracle</kbd> fetches card data<br>
<kbd>Servatrice</kbd> is the server<br>
#### 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>
There, you'll also find more hints on our **docker-compose** file which will configure and run both a MySQL server and Servatrice.
For more details, look into our wiki section on [Setting up Servatrice](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).<br>
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)

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/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_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_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/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
@ -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_push_button.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/layout_containers/flow_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_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_sort_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_update.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_graphics_item.cpp
src/game/board/arrow_item.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/cards/abstract_card_drag_item.cpp
src/game/cards/abstract_card_item.cpp
src/game/cards/card_completer_proxy_model.cpp
src/game/cards/card_database.cpp
src/game/cards/card_database_manager.cpp
@ -179,10 +186,7 @@ set(cockatrice_SOURCES
src/game/cards/card_database_parser/card_database_parser.cpp
src/game/cards/card_database_parser/cockatrice_xml_3.cpp
src/game/cards/card_database_parser/cockatrice_xml_4.cpp
src/game/cards/card_drag_item.cpp
src/game/cards/card_info.cpp
src/game/cards/card_item.cpp
src/game/cards/card_list.cpp
src/game/cards/card_search_model.cpp
src/game/deckview/deck_view.cpp
src/game/deckview/deck_view_container.cpp

View file

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

View file

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

View file

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

View file

@ -3,6 +3,8 @@
#include "../../../../../ui/widgets/cards/card_info_picture_widget.h"
#include "../../api_response/cards/edhrec_api_response_card_details.h"
#include "edhrec_api_response_card_inclusion_display_widget.h"
#include "edhrec_api_response_card_synergy_display_widget.h"
#include <QHBoxLayout>
#include <QLabel>
@ -22,7 +24,9 @@ private:
EdhrecApiResponseCardDetails toDisplay;
QVBoxLayout *layout;
CardInfoPictureWidget *cardPictureWidget;
QLabel *label;
QLabel *nameLabel;
EdhrecApiResponseCardInclusionDisplayWidget *inclusionDisplayWidget;
EdhrecApiResponseCardSynergyDisplayWidget *synergyDisplayWidget;
};
#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 "../../../../../ui/widgets/cards/card_info_picture_widget.h"
#include "../../tab_edhrec_main.h"
#include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
@ -16,6 +17,22 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom
commanderPicture = new CardInfoPictureWidget(this);
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();
label = new QLabel(this);

View file

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

View file

@ -2,7 +2,9 @@
#define TAB_EDHREC_MAIN_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/quick_settings/settings_button_widget.h"
#include "../../tab.h"
#include "display/commander/edhrec_commander_api_response_display_widget.h"
@ -25,6 +27,11 @@ public:
return tr("EDHREC: ") + cardName;
}
CardSizeWidget *getCardSizeSlider()
{
return cardSizeSlider;
}
QNetworkAccessManager *networkManager;
public slots:
@ -53,6 +60,8 @@ private:
QPushButton *tagsPushButton;
QLineEdit *searchBar;
QPushButton *searchPushButton;
SettingsButtonWidget *settingsButton;
CardSizeWidget *cardSizeSlider;
CardInfoPtr cardToQuery;
EdhrecCommanderApiResponseDisplayWidget *displayWidget;
};

View file

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

View file

@ -590,7 +590,7 @@ void TabSupervisor::actTabDeckStorage(bool checked)
void TabSupervisor::openTabDeckStorage()
{
tabDeckStorage = new TabDeckStorage(this, client, userInfo);
connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tabDeckStorage, aTabDeckStorage);
connect(tabDeckStorage, &Tab::closed, this, [this] {
tabDeckStorage = nullptr;
@ -691,7 +691,7 @@ void TabSupervisor::gameJoined(const Event_GameJoined &event)
auto *tab = new TabGame(this, QList<AbstractClient *>() << client, event, roomGameTypes);
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
connect(tab, &TabGame::openMessageDialog, this, &TabSupervisor::addMessageTab);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab);
@ -701,7 +701,7 @@ void TabSupervisor::localGameJoined(const Event_GameJoined &event)
{
auto *tab = new TabGame(this, localClients, event, QMap<int, QString>());
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab);
@ -807,6 +807,29 @@ void TabSupervisor::talkLeft(TabMessage *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
* @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
{
Q_OBJECT
public:
enum DeckEditorType
{
ClassicDeckEditor,
VisualDeckEditor
};
private:
ServerInfo_User *userInfo;
AbstractClient *client;
@ -152,6 +160,7 @@ signals:
void showWindowIfHidden();
public slots:
void openDeckInNewTab(const DeckLoader *deckToOpen);
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen);
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();

View file

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

View file

@ -12,7 +12,7 @@
TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
: 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,
&TabDeckStorageVisual::actOpenLocalDeck);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this,

View file

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

View file

@ -1,6 +1,7 @@
#include "picture_to_load.h"
#include "../../../settings/cache_settings.h"
#include "../../../utility/card_set_comparator.h"
#include <QCoreApplication>
#include <QDate>
@ -20,7 +21,7 @@ PictureToLoad::PictureToLoad(CardInfoPtr _card)
if (sortedSets.empty()) {
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 (!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {

View file

@ -10,24 +10,6 @@ inline Q_LOGGING_CATEGORY(PictureToLoadLog, "picture_loader.picture_to_load");
class PictureToLoad
{
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;
QList<CardSetPtr> sortedSets;
QList<QString> urlTemplates;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
#include "card_info_picture_widget.h"
#include "../../../../game/board/card_item.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/cards/card_item.h"
#include "../../../../settings/cache_settings.h"
#include "../../../tabs/tab_supervisor.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.
* @param card A shared pointer to the card information (CardInfoPtr).
@ -341,6 +347,12 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
emit cardClicked();
}
void CardInfoPictureWidget::hideEvent(QHideEvent *event)
{
enlargedPixmapWidget->hide();
QWidget::hideEvent(event);
}
QMenu *CardInfoPictureWidget::createRightClickMenu()
{
auto *cardMenu = new QMenu(this);

View file

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

View file

@ -1,6 +1,6 @@
#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 <QGridLayout>

View file

@ -34,14 +34,7 @@ CardSizeWidget::CardSizeWidget(QWidget *parent, FlowWidget *_flowWidget, int def
// Debounce setup
debounceTimer.setSingleShot(true);
connect(&debounceTimer, &QTimer::timeout, this, [this]() {
// 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(&debounceTimer, &QTimer::timeout, this, [this] { emit cardSizeSettingUpdated(pendingValue); });
connect(cardSizeSlider, &QSlider::valueChanged, this, &CardSizeWidget::updateCardSizeSetting);
}

View file

@ -17,9 +17,17 @@ public:
explicit CardSizeWidget(QWidget *parent, FlowWidget *flowWidget = nullptr, int defaultValue = 100);
[[nodiscard]] QSlider *getSlider() const;
public slots:
private slots:
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:
QWidget *parent;
FlowWidget *flowWidget;

View file

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

View file

@ -6,14 +6,25 @@ DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListModel *_deckLi
mainLayout = new QVBoxLayout();
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);
mainLayout->addWidget(manaCurveWidget);
containerLayout->addWidget(manaCurveWidget);
manaDevotionWidget = new ManaDevotionWidget(this, deckListModel);
mainLayout->addWidget(manaDevotionWidget);
containerLayout->addWidget(manaDevotionWidget);
manaBaseWidget = new ManaBaseWidget(this, deckListModel);
mainLayout->addWidget(manaBaseWidget);
containerLayout->addWidget(manaBaseWidget);
}
void DeckAnalyticsWidget::refreshDisplays(DeckListModel *_deckModel)

View file

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

View file

@ -91,7 +91,7 @@ void ManaCurveWidget::updateDisplay()
// Add new widgets to the layout in sorted order
for (const auto &entry : sortedManaCurve) {
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);
}

View file

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

View file

@ -49,7 +49,11 @@ public slots:
void offsetCountAtIndex(const QModelIndex &idx, int offset);
signals:
void nameChanged();
void commentsChanged();
void hashChanged();
void deckChanged();
void deckModified();
void cardChanged(CardInfoPtr _card);
private:
@ -57,6 +61,9 @@ private:
KeySignals deckViewKeySignals;
QLabel *nameLabel;
LineEditUnfocusable *nameEdit;
SettingsButtonWidget *quickSettingsWidget;
QCheckBox *showBannerCardCheckBox;
QCheckBox *showTagsWidgetCheckBox;
QLabel *commentsLabel;
QTextEdit *commentsEdit;
QLabel *bannerCardLabel;
@ -77,6 +84,8 @@ private slots:
void setBannerCard(int);
void updateHash();
void refreshShortcuts();
void updateShowBannerCardComboBox(bool visible);
void updateShowTagsWidget(bool visible);
};
#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 =
new CardSizeWidget(displayOptionsWidget, flowWidget, SettingsCache::instance().getPrintingSelectorCardSize());
connect(cardSizeWidget, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance(),
&SettingsCache::setPrintingSelectorCardSize);
displayOptionsWidget->addSettingsWidget(sortToolBar);
displayOptionsWidget->addSettingsWidget(navigationCheckBox);

View file

@ -27,9 +27,6 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q
layout->addWidget(manaSymbol);
// Initialize the activeColors map
activeColors[color] = false;
// Connect the color toggled signal
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this,
&VisualDatabaseDisplayColorFilterWidget::handleColorToggled);
@ -41,16 +38,8 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q
// Connect the button's toggled signal
connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplayColorFilterWidget::updateFilterMode);
connect(this, &VisualDatabaseDisplayColorFilterWidget::activeColorsChanged, this,
&VisualDatabaseDisplayColorFilterWidget::updateColorFilter);
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);
});
connect(filterModel, &FilterTreeModel::layoutChanged, this,
[this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel); });
// Call retranslateUi to set the initial text
retranslateUi();
@ -69,95 +58,94 @@ void VisualDatabaseDisplayColorFilterWidget::retranslateUi()
toggleButton->setText(tr("Mode: Include/Exclude"));
break;
}
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
}
void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active)
{
activeColors[color] = active;
emit activeColorsChanged(); // Notify listeners that the active colors have changed
if (active) {
addFilter(color);
} else {
removeFilter(color);
}
}
void VisualDatabaseDisplayColorFilterWidget::updateColorFilter()
void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color)
{
blockSync = true;
QString colorString = color;
QString typeStr;
// Clear previous filters
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
// Remove previous filters
QSet<QString> selectedColors;
QSet<QString> excludedColors;
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<const CardFilter *> matchingFilters;
// Collect active colors in the selected and excluded sets
for (const auto &color : activeColors.keys()) {
if (activeColors[color]) {
selectedColors.insert(color); // Include this color
} else {
excludedColors.insert(color); // Exclude this color
for (const CardFilter *filter : allColorFilters) {
if (filter->term() == color) {
matchingFilters.append(filter);
}
}
for (const CardFilter *filter : matchingFilters) {
filterModel->removeFilter(filter);
}
// Add actual filter
switch (currentMode) {
case FilterMode::ExactMatch:
// Exact Match Mode: Only selected colors are allowed
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));
}
}
}
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
break;
case FilterMode::Includes:
// Includes Mode: Just include selected colors without restrictions
for (const auto &color : selectedColors) {
QString colorString = color;
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr,
CardFilter::Attr::AttrColor)); // OR for selected colors
}
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
case FilterMode::IncludeExclude:
// Include/Exclude Mode: Include selected colors and exclude unselected colors
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
}
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
}
}
filterModel->blockSignals(false);
filterModel->filterTree()->blockSignals(false);
void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color)
{
QString colorString = color;
emit filterModel->filterTree()->changed();
emit filterModel->layoutChanged();
// Remove inclusion filters
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()
{
blockSync = true;
switch (currentMode) {
case FilterMode::ExactMatch:
currentMode = FilterMode::Includes; // Switch to Includes
@ -170,59 +158,100 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
break;
}
retranslateUi(); // Update button text based on the mode
emit filterModeChanged(currentMode); // Signal mode change
updateColorFilter(); // Reapply the filter based on the new mode
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
blockSync = false;
}
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
}
}
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
manaSymbolWidget->setColorActive(activeColors[manaSymbolWidget->getSymbolChar()]);
handleColorToggled(manaSymbolWidget->getSymbolChar(), manaSymbolWidget->isColorActive());
}
updateColorFilter();
blockSync = false;
}
filterModel->blockSignals(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:
void filterModeChanged(FilterMode filterMode);
void activeColorsChanged();
private slots:
void handleColorToggled(QChar color, bool active);
void updateColorFilter();
void addFilter(QChar color);
void removeFilter(QChar color);
void updateFilterMode();
void setManaSymbolActive(QChar color, bool active);
QList<QChar> getActiveColors();
void syncWithFilterModel();
private:
FilterTreeModel *filterModel;
QHBoxLayout *layout;
QPushButton *toggleButton;
QMap<QChar, bool> activeColors;
FilterMode currentMode = FilterMode::Includes; // Default mode
bool blockSync = false;
};
#endif // VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H

View file

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

View file

@ -47,6 +47,14 @@ VisualDatabaseDisplayMainTypeFilterWidget::VisualDatabaseDisplayMainTypeFilterWi
createMainTypeButtons(); // Populate buttons initially
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()

View file

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

View file

@ -45,6 +45,7 @@ void VisualDatabaseDisplayNameFilterWidget::retranslateUi()
{
searchBox->setPlaceholderText(tr("Filter by name..."));
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()

View file

@ -3,12 +3,44 @@
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/filters/filter_tree.h"
#include "../../../../game/filters/filter_tree_model.h"
#include "../../../../settings/cache_settings.h"
#include <QLineEdit>
#include <QPushButton>
#include <QTimer>
#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,
FilterTreeModel *_filterModel)
: QWidget(parent), filterModel(_filterModel)
@ -19,6 +51,14 @@ VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidg
layout = new QVBoxLayout(this);
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->setPlaceholderText(tr("Search sets..."));
layout->addWidget(searchBox);
@ -36,24 +76,26 @@ VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidg
connect(filterModel, &FilterTreeModel::layoutChanged, this,
[this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplaySetFilterWidget::syncWithFilterModel); });
createSetButtons(); // Populate buttons initially
updateFilterMode(false); // Initialize toggle button text
createSetButtons(); // Populate buttons initially
retranslateUi();
}
void VisualDatabaseDisplaySetFilterWidget::retranslateUi()
{
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
}
void VisualDatabaseDisplaySetFilterWidget::createSetButtons()
{
SetList shared_pointerses = CardDatabaseManager::getInstance()->getSetList();
SetList allSets = CardDatabaseManager::getInstance()->getSetList();
// 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(); });
int setsToPreactivate = 10;
int setsActivated = 0;
for (const auto &shared_pointer : shared_pointerses) {
QString shortName = shared_pointer->getShortName();
QString longName = shared_pointer->getLongName();
for (const auto &set : allSets) {
QString shortName = set->getShortName();
QString longName = set->getLongName();
auto *button = new QPushButton(longName + " (" + shortName + ")", flowWidget);
button->setCheckable(true);
@ -66,14 +108,47 @@ void VisualDatabaseDisplaySetFilterWidget::createSetButtons()
// Connect toggle signal
connect(button, &QPushButton::toggled, this,
[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()
@ -183,13 +258,15 @@ void VisualDatabaseDisplaySetFilterWidget::syncWithFilterModel()
for (const auto &key : setButtons.keys()) {
bool active = selectedSets.contains(key);
activeSets[key] = active;
setButtons[key]->blockSignals(true);
setButtons[key]->setChecked(active);
setButtons[key]->blockSignals(false);
}
}
void VisualDatabaseDisplaySetFilterWidget::updateFilterMode(bool checked)
{
exactMatchMode = checked;
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
updateSetFilter();
}
retranslateUi();
}

View file

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

View file

@ -51,6 +51,14 @@ VisualDatabaseDisplaySubTypeFilterWidget::VisualDatabaseDisplaySubTypeFilterWidg
createSubTypeButtons(); // Populate buttons initially
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()

View file

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

View file

@ -62,9 +62,6 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
filterModel = new FilterTreeModel();
filterModel->setObjectName("filterModel");
databaseDisplayModel->setFilterTree(filterModel->filterTree());
connect(filterModel, &FilterTreeModel::layoutChanged, this, &VisualDatabaseDisplayWidget::searchModelChanged);
searchKeySignals.setObjectName("searchKeySignals");
connect(searchEdit, &QLineEdit::textChanged, this, &VisualDatabaseDisplayWidget::updateSearch);
@ -144,11 +141,27 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
connect(debounceTimer, &QTimer::timeout, this, &VisualDatabaseDisplayWidget::searchModelChanged);
databaseDisplayModel->setFilterTree(filterModel->filterTree());
connect(filterModel, &FilterTreeModel::layoutChanged, this, &VisualDatabaseDisplayWidget::searchModelChanged);
loadCardsTimer = new QTimer(this);
loadCardsTimer->setSingleShot(true); // Ensure it only fires once after the timeout
connect(loadCardsTimer, &QTimer::timeout, this, [this]() { loadCurrentPage(); });
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)
@ -195,13 +208,30 @@ void VisualDatabaseDisplayWidget::populateCards()
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) {
qCDebug(VisualDatabaseDisplayLog) << "Adding " << row;
QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn);
QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole);
qCDebug(VisualDatabaseDisplayLog) << 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 {
qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!";
}

View file

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

View file

@ -2,6 +2,7 @@
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../settings/cache_settings.h"
#include "../cards/card_info_picture_widget.h"
#include <random>
@ -12,23 +13,45 @@ VisualDeckEditorSampleHandWidget::VisualDeckEditorSampleHandWidget(QWidget *pare
layout = new QVBoxLayout(this);
setLayout(layout);
resetAndHandSizeContainerWidget = new QWidget(this);
resetAndHandSizeLayout = new QHBoxLayout(resetAndHandSizeContainerWidget);
resetAndHandSizeContainerWidget->setLayout(resetAndHandSizeLayout);
resetButton = new QPushButton(this);
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);
for (CardInfoPtr card : getRandomCards(7)) {
cardSizeWidget = new CardSizeWidget(this, flowWidget);
layout->addWidget(cardSizeWidget);
for (CardInfoPtr card : getRandomCards(handSizeSpinBox->value())) {
auto displayWidget = new CardInfoPictureWidget(this);
displayWidget->setCard(card);
displayWidget->setScaleFactor(cardSizeWidget->getSlider()->value());
flowWidget->addWidget(displayWidget);
}
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)
@ -41,9 +64,12 @@ void VisualDeckEditorSampleHandWidget::setDeckModel(DeckListModel *deckModel)
void VisualDeckEditorSampleHandWidget::updateDisplay()
{
flowWidget->clearLayout();
for (CardInfoPtr card : getRandomCards(7)) {
for (CardInfoPtr card : getRandomCards(handSizeSpinBox->value())) {
auto displayWidget = new CardInfoPictureWidget(this);
displayWidget->setCard(card);
displayWidget->setScaleFactor(cardSizeWidget->getSlider()->value());
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, displayWidget,
&CardInfoPictureWidget::setScaleFactor);
flowWidget->addWidget(displayWidget);
}
}

View file

@ -1,10 +1,12 @@
#ifndef 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 "../general/layout_containers/flow_widget.h"
#include <QPushButton>
#include <QSpinBox>
#include <QWidget>
class VisualDeckEditorSampleHandWidget : public QWidget
@ -22,8 +24,12 @@ public slots:
private:
DeckListModel *deckListModel;
QVBoxLayout *layout;
QWidget *resetAndHandSizeContainerWidget;
QHBoxLayout *resetAndHandSizeLayout;
QPushButton *resetButton;
QSpinBox *handSizeSpinBox;
FlowWidget *flowWidget;
CardSizeWidget *cardSizeWidget;
};
#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);
// The Main Widget and Main Layout, which contain a single Widget: The Scroll Area
setMinimumSize(0, 0);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout = new QVBoxLayout(this);
setLayout(mainLayout);
@ -162,7 +161,7 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_
groupAndSortLayout->addWidget(sortCriteriaButton);
groupAndSortLayout->addWidget(displayTypeButton);
scrollArea = new QScrollArea();
scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
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"));
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->setToolTip(
tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)"));
}
void VisualDeckEditorWidget::updateZoneWidgets()
@ -227,6 +231,7 @@ void VisualDeckEditorWidget::addZoneIfDoesNotExist()
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
if (displayWidget->zoneName == zone) {
found = true;
displayWidget->displayCards();
break;
}
}

View file

@ -47,6 +47,7 @@ void DeckPreviewColorIdentityFilterWidget::retranslateUi()
{
// Set the toggle button text based on the current mode
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)

View file

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

View file

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

View file

@ -6,7 +6,7 @@
#include <QPainter>
DeckPreviewTagDisplayWidget::DeckPreviewTagDisplayWidget(QWidget *parent, const QString &_tagName)
: QWidget(parent), tagName(_tagName), isSelected(false)
: QWidget(parent), tagName(_tagName), state(TagState::NotSelected)
{
// Create layout
auto *layout = new QHBoxLayout(this);
@ -48,36 +48,58 @@ QSize DeckPreviewTagDisplayWidget::sizeHint() const
void DeckPreviewTagDisplayWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
setSelected(!isSelected);
emit tagClicked();
switch (event->button()) {
case Qt::LeftButton:
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)
{
isSelected = selected;
update(); // Trigger a repaint
emit tagClicked();
QWidget::mousePressEvent(event);
}
void DeckPreviewTagDisplayWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// Set background color
QColor backgroundColor = isSelected ? QColor(173, 216, 230) : Qt::white;
QColor backgroundColor;
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.setPen(Qt::NoPen);
// Draw background
painter.drawRect(rect());
// Draw border
QColor borderColor = isSelected ? Qt::blue : Qt::gray;
QPen borderPen(borderColor, isSelected ? 2 : 1);
QPen borderPen(borderColor, borderWidth);
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
QFont font = painter.font();

View file

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

View file

@ -13,6 +13,7 @@
#include <QMessageBox>
#include <QMouseEvent>
#include <QSet>
#include <QStandardItemModel>
#include <QVBoxLayout>
DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
@ -27,6 +28,8 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
deckLoader = new DeckLoader();
deckLoader->setParent(this);
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,
&VisualDeckStorageTagFilterWidget::refreshTags);
deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false);
@ -194,6 +197,7 @@ void DeckPreviewWidget::updateBannerCardComboBox()
// Block signals temporarily
bool wasBlocked = bannerCardComboBox->blockSignals(true);
bannerCardComboBox->setUpdatesEnabled(false);
// Clear the existing items in the combo box
bannerCardComboBox->clear();
@ -209,12 +213,7 @@ void DeckPreviewWidget::updateBannerCardComboBox()
continue;
for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCardByNameAndProviderId(
currentCard->getName(), currentCard->getCardProviderId());
if (info) {
bannerCardSet.insert(
QPair<QString, QString>(currentCard->getName(), currentCard->getCardProviderId()));
}
bannerCardSet.insert(QPair<QString, QString>(currentCard->getName(), currentCard->getCardProviderId()));
}
}
}
@ -226,14 +225,19 @@ void DeckPreviewWidget::updateBannerCardComboBox()
return a.first.toLower() < b.first.toLower();
});
for (const auto &pair : pairList) {
QVariantMap dataMap;
dataMap["name"] = pair.first;
dataMap["uuid"] = pair.second;
// This is *slightly* more performant than using addItem in a loop.
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
int restoredIndex = bannerCardComboBox->findText(currentText);
if (restoredIndex != -1) {
@ -251,15 +255,16 @@ void DeckPreviewWidget::updateBannerCardComboBox()
// Restore the previous signal blocking state
bannerCardComboBox->blockSignals(wasBlocked);
bannerCardComboBox->setUpdatesEnabled(true);
}
void DeckPreviewWidget::setBannerCard(int /* changedIndex */)
{
QVariantMap itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap();
deckLoader->setBannerCard(QPair<QString, QString>(itemData["name"].toString(), itemData["uuid"].toString()));
QVariant itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex());
deckLoader->setBannerCard(QPair<QString, QString>(bannerCardComboBox->currentText(), itemData.toString()));
deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath));
bannerCardDisplayWidget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(
itemData["name"].toString(), itemData["uuid"].toString()));
bannerCardComboBox->currentText(), itemData.toString()));
}
void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance)

View file

@ -7,6 +7,8 @@
#include "../visual_deck_storage_widget.h"
#include "deck_preview_deck_tags_display_widget.h"
#include <QAbstractItemView>
#include <QApplication>
#include <QComboBox>
#include <QEvent>
#include <QVBoxLayout>
@ -74,7 +76,22 @@ protected:
bool eventFilter(QObject *obj, QEvent *event) override
{
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);
}

View file

@ -90,9 +90,9 @@ void VisualDeckStorageFolderDisplayWidget::createWidgetsForFiles()
&VisualDeckStorageWidget::deckLoadRequested);
connect(display, &DeckPreviewWidget::openDeckEditor, visualDeckStorageWidget,
&VisualDeckStorageWidget::openDeckEditor);
connect(visualDeckStorageWidget->cardSizeWidget->getSlider(), &QSlider::valueChanged,
connect(visualDeckStorageWidget->settings(), &VisualDeckStorageQuickSettingsWidget::cardSizeChanged,
display->bannerCardDisplayWidget, &CardInfoPictureWidget::setScaleFactor);
display->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->cardSizeWidget->getSlider()->value());
display->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->settings()->getCardSize());
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
{
// Collect selected tags from DeckPreviewTagDisplayWidget
QStringList selectedTags;
QStringList excludedTags;
// Collect selected and excluded tags
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
if (tagWidget->getSelected()) {
selectedTags.append(tagWidget->getTagName());
switch (tagWidget->getState()) {
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 (selectedTags.isEmpty()) {
// If no tags are selected or excluded, show all
if (selectedTags.isEmpty() && excludedTags.isEmpty()) {
for (DeckPreviewWidget *deckPreview : deckPreviews) {
deckPreview->filteredByTags = false;
}
return;
}
// Filter DeckPreviewWidgets that contain all of the selected tags
QList<DeckPreviewWidget *> filteredDecks;
for (DeckPreviewWidget *deckPreview : deckPreviews) {
QStringList deckTags = deckPreview->deckLoader->getTags();
// Check if all selectedTags are in deckTags
bool allTagsPresent = std::all_of(selectedTags.begin(), selectedTags.end(),
bool hasAllSelected = std::all_of(selectedTags.begin(), selectedTags.end(),
[&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()
{
QStringList allTags = gatherAllTags();
removeTagsNotInList(gatherAllTags());
addTagsIfNotPresent(gatherAllTags());
QSet<QString> allTags = gatherAllTags();
removeTagsNotInList(allTags);
addTagsIfNotPresent(allTags);
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 *>()) {
// If the tag is not in the provided tags list, remove the widget
if (!tags.contains(tagWidget->getTagName())) {
auto *flowWidget = findChild<FlowWidget *>();
const QString &tagName = tagWidget->getTagName();
// Keep the tag widget if it is either selected or excluded
if (!tags.contains(tagName) && tagWidget->getState() == TagState::NotSelected) {
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) {
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 *>();
for (DeckPreviewWidget *widget : deckWidgets) {
if (widget->checkVisibility()) {
allTags << widget->deckLoader->getTags();
for (const QString &tag : widget->deckLoader->getTags()) {
allTags.insert(tag);
}
}
}
return allTags;
}
QStringList VisualDeckStorageTagFilterWidget::getAllKnownTags()
QStringList VisualDeckStorageTagFilterWidget::getAllKnownTags() const
{
QStringList allTags;

View file

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

View file

@ -41,79 +41,13 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
refreshButton->setFixedSize(32, 32);
connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible);
// quick settings menu
showFoldersCheckBox = new QCheckBox(this);
showFoldersCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowFolders());
connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &VisualDeckStorageWidget::updateShowFolders);
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,
quickSettingsWidget = new VisualDeckStorageQuickSettingsWidget(this);
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showFoldersChanged, this,
&VisualDeckStorageWidget::updateShowFolders);
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showTagFilterChanged, this,
&VisualDeckStorageWidget::updateTagsVisibility);
connect(tagFilterVisibilityCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&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,
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::searchFolderNamesChanged, this,
&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(sortWidget);
@ -186,14 +120,16 @@ void VisualDeckStorageWidget::retranslateUi()
{
databaseLoadIndicator->setText(tr("Loading database ..."));
showFoldersCheckBox->setText(tr("Show Folders"));
tagFilterVisibilityCheckBox->setText(tr("Show Tag Filter"));
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"));
unusedColorIdentitiesOpacityLabel->setText(tr("Unused Color Identities Opacity"));
unusedColorIdentitiesOpacitySpinBox->setSuffix("%");
refreshButton->setToolTip(tr("Refresh loaded files"));
quickSettingsWidget->setToolTip(tr("Visual Deck Storage Settings"));
}
/**
* Gets a const pointer to the quick settings so that the values can be accessed.
*/
const VisualDeckStorageQuickSettingsWidget *VisualDeckStorageWidget::settings() const
{
return quickSettingsWidget;
}
/**
@ -210,7 +146,7 @@ void VisualDeckStorageWidget::reapplySortAndFilters()
void VisualDeckStorageWidget::createRootFolderWidget()
{
folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath(), false,
showFoldersCheckBox->isChecked());
quickSettingsWidget->getShowFolders());
scrollArea->setWidget(folderWidget); // this automatically destroys the old folderWidget
scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width());
@ -261,7 +197,7 @@ void VisualDeckStorageWidget::updateSearchFilter()
{
if (folderWidget) {
searchWidget->filterWidgets(folderWidget->findChildren<DeckPreviewWidget *>(), searchWidget->getSearchText(),
searchFolderNamesCheckBox->isChecked());
quickSettingsWidget->getSearchFolderNames());
folderWidget->updateVisibility();
}
}

View file

@ -8,6 +8,7 @@
#include "deck_preview/deck_preview_color_identity_filter_widget.h"
#include "deck_preview/deck_preview_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_sort_widget.h"
#include "visual_deck_storage_tag_filter_widget.h"
@ -29,10 +30,11 @@ public:
void refreshIfPossible();
void retranslateUi();
CardSizeWidget *cardSizeWidget;
VisualDeckStorageTagFilterWidget *tagFilterWidget;
bool deckPreviewSelectionAnimationEnabled;
const VisualDeckStorageQuickSettingsWidget *settings() const;
public slots:
void createRootFolderWidget(); // Refresh the display of cards based on the current sorting option
void updateShowFolders(bool enabled);
@ -60,15 +62,7 @@ private:
VisualDeckStorageSearchWidget *searchWidget;
DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget;
QToolButton *refreshButton;
SettingsButtonWidget *quickSettingsWidget;
QCheckBox *showFoldersCheckBox;
QCheckBox *drawUnusedColorIdentitiesCheckBox;
QCheckBox *bannerCardComboBoxVisibilityCheckBox;
QCheckBox *tagFilterVisibilityCheckBox;
QCheckBox *tagsOnWidgetsVisibilityCheckBox;
QCheckBox *searchFolderNamesCheckBox;
QLabel *unusedColorIdentitiesOpacityLabel;
QSpinBox *unusedColorIdentitiesOpacitySpinBox;
VisualDeckStorageQuickSettingsWidget *quickSettingsWidget;
QScrollArea *scrollArea;
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
// each card in the decklist.
struct FormatDeckListForExport
/**
* Converts the card to the String that represents it in the decklist export
*/
static QString toDecklistExportString(const DecklistCardNode *card)
{
// Create refrences for the strings that will be passed in.
QString &mainBoardCards;
QString &sideBoardCards;
// create main operator for struct, allowing the foreachcard to work.
FormatDeckListForExport(QString &_mainBoardCards, QString &_sideBoardCards)
: mainBoardCards(_mainBoardCards), sideBoardCards(_sideBoardCards){};
QString cardString;
// Get the number of cards and add the card name
cardString += QString::number(card->getNumber());
// Add a space between card num and name
cardString += "%20";
// Add card name
cardString += card->getName();
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
{
// 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) {
// 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->getCardSetShortName().isNull()) {
cardString += "%20";
cardString += "(" + card->getCardSetShortName() + ")";
}
};
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
@ -298,8 +277,25 @@ QString DeckLoader::exportDeckToDecklist(DecklistWebsite website)
QString deckString = "https://" + getDomainForWebsite(website) + "/?";
// Create two strings to pass to function
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
forEachCard(formatDeckListForExport);
// Remove the extra return at the end of the last cards
@ -316,17 +312,12 @@ QString DeckLoader::exportDeckToDecklist(DecklistWebsite website)
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.
struct SetProviderId
/**
* Sets the providerId on each card in the decklist based on its set name and collector number.
*/
void DeckLoader::resolveSetNameAndNumberToProviderID()
{
// Main operator for struct, allowing the foreachcard to work.
SetProviderId()
{
}
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
auto setProviderId = [](const auto node, const auto card) {
Q_UNUSED(node);
// Retrieve the providerId based on setName and collectorNumber
QString providerId =
@ -336,50 +327,23 @@ struct SetProviderId
// Set the providerId on the card
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);
}
// 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
* on each card based on its set name and collector number.
* Clears the set name and numbers on each card in the decklist.
*/
void DeckLoader::clearSetNamesAndNumbers()
{
// Set up the struct to call.
ClearSetNameAndNumber clearSetNameAndNumber;
auto clearSetNameAndNumber = [](const auto node, auto card) {
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);
}

View file

@ -67,26 +67,15 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
manager->post(request, data);
}
struct CopyIfNotAToken
void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
{
CardDatabase &cardDatabase;
DeckList &destination;
CopyIfNotAToken(CardDatabase &_cardDatabase, DeckList &_destination)
: cardDatabase(_cardDatabase), destination(_destination){};
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
{
auto copyIfNotAToken = [this, &destination](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
if (dbCard && !dbCard->getIsToken()) {
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName());
addedCard->setNumber(card->getNumber());
}
}
};
};
void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
{
CopyIfNotAToken copyIfNotAToken(cardDatabase, destination);
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->setChecked(true);
faceDownCheckBox = new QCheckBox(tr("Create face-down (Only hides name)"));
connect(faceDownCheckBox, &QCheckBox::toggled, this, &DlgCreateToken::faceDownCheckBoxToggled);
QGridLayout *grid = new QGridLayout;
grid->addWidget(nameLabel, 0, 0);
grid->addWidget(nameEdit, 0, 1);
@ -73,6 +76,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
grid->addWidget(annotationLabel, 3, 0);
grid->addWidget(annotationEdit, 3, 1);
grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
grid->addWidget(faceDownCheckBox, 5, 0, 1, 2);
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
tokenDataGroupBox->setLayout(grid);
@ -155,6 +159,21 @@ void DlgCreateToken::closeEvent(QCloseEvent *event)
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*/)
{
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
@ -230,5 +249,6 @@ TokenInfo DlgCreateToken::getTokenInfo() const
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
.pt = ptEdit->text(),
.annotation = annotationEdit->text(),
.destroy = destroyCheckBox->isChecked()};
.destroy = destroyCheckBox->isChecked(),
.faceDown = faceDownCheckBox->isChecked()};
}

View file

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

View file

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

View file

@ -4,6 +4,7 @@
#include "../client/network/release_channel.h"
#include "../client/network/spoiler_background_updater.h"
#include "../client/sound_engine.h"
#include "../client/tabs/tab_supervisor.h"
#include "../client/ui/picture_loader/picture_loader.h"
#include "../client/ui/theme_manager.h"
#include "../deck/custom_line_edit.h"
@ -629,6 +630,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setCloseEmptyCardView);
focusCardViewSearchBarCheckBox.setChecked(SettingsCache::instance().getFocusCardViewSearchBar());
connect(&focusCardViewSearchBarCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setFocusCardViewSearchBar);
annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens());
connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setAnnotateTokens);
@ -642,8 +647,9 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0);
generalGrid->addWidget(&playToStackCheckBox, 2, 0);
generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0);
generalGrid->addWidget(&annotateTokensCheckBox, 4, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 5, 0);
generalGrid->addWidget(&focusCardViewSearchBarCheckBox, 4, 0);
generalGrid->addWidget(&annotateTokensCheckBox, 5, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 6, 0);
generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid);
@ -699,12 +705,20 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
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;
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0);
deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1);
deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0);
deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1);
deckEditorGroupBox = new QGroupBox;
deckEditorGroupBox->setLayout(deckEditorGrid);
@ -754,6 +768,7 @@ void UserInterfaceSettingsPage::retranslateUi()
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"));
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"));
useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen"));
notificationsGroupBox->setTitle(tr("Notifications settings"));
@ -774,6 +789,9 @@ void UserInterfaceSettingsPage::retranslateUi()
tr("ask to convert to .cod"));
visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways,
tr("always convert to .cod"));
defaultDeckEditorTypeLabel.setText(tr("Default deck editor type"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor"));
replayGroupBox->setTitle(tr("Replay settings"));
rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:"));
rewindBufferingMsBox.setSuffix(" ms");

View file

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

View file

@ -3,23 +3,40 @@
#include "../settings/cache_settings.h"
#include "../utility/logger.h"
#include <QClipboard>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QRegularExpression>
#include <QVBoxLayout>
DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
{
logArea = new QPlainTextEdit;
logArea->setReadOnly(true);
auto *mainLayout = new QVBoxLayout;
mainLayout->setSpacing(3);
mainLayout->setContentsMargins(20, 20, 20, 6);
mainLayout->addWidget(logArea);
auto *bottomLayout = new QHBoxLayout;
coClearLog = new QCheckBox;
coClearLog->setText(tr("Clear log when closing"));
coClearLog->setChecked(SettingsCache::instance().servers().getClearDebugLogStatus(false));
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);
@ -27,7 +44,7 @@ DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
resize(800, 500);
loadInitialLogBuffer();
connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::logEntryAdded);
connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::appendLogEntry);
}
void DlgViewLog::actCheckBoxChanged(bool abNewValue)
@ -35,16 +52,26 @@ void DlgViewLog::actCheckBoxChanged(bool abNewValue)
SettingsCache::instance().servers().setClearDebugLogStatus(abNewValue);
}
void DlgViewLog::actCopyToClipboard()
{
QApplication::clipboard()->setText(logArea->toPlainText());
}
void DlgViewLog::loadInitialLogBuffer()
{
QList<QString> logBuffer = Logger::getInstance().getLogBuffer();
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 */)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -47,10 +47,25 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos)
cursorZone = zoneViewZone;
else if (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;
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 cursorPosInZone = cursorScenePos - zonePos;

View file

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

View file

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

View file

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

View file

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

View file

@ -387,7 +387,7 @@ TokenEditModel::TokenEditModel(QObject *parent) : CardDatabaseDisplayModel(paren
bool TokenEditModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const
{
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

View file

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

View file

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

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