mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-21 09:05:10 -07:00
[VDS] Decouple tag filter and fix reordered-chips crash
This commit is contained in:
parent
d18e28a65b
commit
71febfa21d
8 changed files with 83 additions and 88 deletions
|
|
@ -1,11 +1,10 @@
|
||||||
#include "deck_preview_color_identity_filter_widget.h"
|
#include "deck_preview_color_identity_filter_widget.h"
|
||||||
|
|
||||||
#include "../../cards/additional_info/mana_symbol_widget.h"
|
#include "../../cards/additional_info/mana_symbol_widget.h"
|
||||||
#include "../visual_deck_storage_widget.h"
|
|
||||||
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(VisualDeckStorageWidget *parent)
|
DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(QWidget *parent)
|
||||||
: QWidget(parent), layout(new QHBoxLayout(this))
|
: QWidget(parent), layout(new QHBoxLayout(this))
|
||||||
{
|
{
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,12 @@
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
class VisualDeckStorageWidget;
|
|
||||||
|
|
||||||
class DeckPreviewColorIdentityFilterWidget : public QWidget
|
class DeckPreviewColorIdentityFilterWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DeckPreviewColorIdentityFilterWidget(VisualDeckStorageWidget *parent);
|
explicit DeckPreviewColorIdentityFilterWidget(QWidget *parent = nullptr);
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -39,3 +39,8 @@ VisualDeckStorageSearchWidget::VisualDeckStorageSearchWidget(QWidget *parent) :
|
||||||
|
|
||||||
connect(searchDebounceTimer, &QTimer::timeout, this, [this] { emit searchTextChanged(searchBar->text()); });
|
connect(searchDebounceTimer, &QTimer::timeout, this, [this] { emit searchTextChanged(searchBar->text()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VisualDeckStorageSearchWidget::setPlaceholderText(const QString &text)
|
||||||
|
{
|
||||||
|
searchBar->setPlaceholderText(text);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ class VisualDeckStorageSearchWidget : public QWidget
|
||||||
public:
|
public:
|
||||||
explicit VisualDeckStorageSearchWidget(QWidget *parent);
|
explicit VisualDeckStorageSearchWidget(QWidget *parent);
|
||||||
|
|
||||||
|
void setPlaceholderText(const QString &text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* Emitted once the debounce timer fires after the user stopped typing.
|
* Emitted once the debounce timer fires after the user stopped typing.
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,10 @@
|
||||||
|
|
||||||
#include "../general/layout_containers/flow_widget.h"
|
#include "../general/layout_containers/flow_widget.h"
|
||||||
#include "deck_preview/deck_preview_tag_display_widget.h"
|
#include "deck_preview/deck_preview_tag_display_widget.h"
|
||||||
#include "visual_deck_storage_model.h"
|
|
||||||
#include "visual_deck_storage_sort_filter_proxy_model.h"
|
|
||||||
#include "visual_deck_storage_widget.h"
|
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
VisualDeckStorageTagFilterWidget::VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent)
|
VisualDeckStorageTagFilterWidget::VisualDeckStorageTagFilterWidget(QWidget *parent) : QWidget(parent)
|
||||||
: QWidget(_parent), parent(_parent)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
|
@ -25,97 +21,62 @@ VisualDeckStorageTagFilterWidget::VisualDeckStorageTagFilterWidget(VisualDeckSto
|
||||||
layout->addWidget(flowWidget);
|
layout->addWidget(flowWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VisualDeckStorageTagFilterWidget::setAllTagsProvider(const std::function<QSet<QString>()> &provider)
|
||||||
|
{
|
||||||
|
allTagsProvider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
void VisualDeckStorageTagFilterWidget::showEvent(QShowEvent *event)
|
void VisualDeckStorageTagFilterWidget::showEvent(QShowEvent *event)
|
||||||
{
|
{
|
||||||
QWidget::showEvent(event);
|
QWidget::showEvent(event);
|
||||||
refreshTags();
|
refreshTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The tags of all decks currently accepted by the proxy model.
|
|
||||||
*/
|
|
||||||
QSet<QString> VisualDeckStorageTagFilterWidget::gatherAllTags() const
|
|
||||||
{
|
|
||||||
QSet<QString> allTags;
|
|
||||||
auto *proxy = parent->proxyModel();
|
|
||||||
|
|
||||||
for (int proxyRow = 0; proxyRow < proxy->rowCount(); ++proxyRow) {
|
|
||||||
const QModelIndex index = proxy->index(proxyRow, 0);
|
|
||||||
if (!index.data(VisualDeckStorageRoles::FilterMatchRole).toBool()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const QStringList deckTags = index.data(VisualDeckStorageRoles::TagsRole).toStringList();
|
|
||||||
for (const QString &tag : deckTags) {
|
|
||||||
allTags.insert(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckStorageTagFilterWidget::refreshTags()
|
void VisualDeckStorageTagFilterWidget::refreshTags()
|
||||||
{
|
{
|
||||||
QSet<QString> allTags = gatherAllTags();
|
const QSet<QString> allTags = allTagsProvider ? allTagsProvider() : QSet<QString>();
|
||||||
removeTagsNotInList(allTags);
|
|
||||||
addTagsIfNotPresent(allTags);
|
|
||||||
sortTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckStorageTagFilterWidget::removeTagsNotInList(const QSet<QString> &tags)
|
// Existing chips survive if their tag is still part of the deck set, or if the chip
|
||||||
{
|
// is currently selected/excluded. Everything else is dropped. Dropped chips must NOT
|
||||||
|
// be re-added to the layout afterwards: they are scheduled for a deferred delete, and
|
||||||
|
// the flow layout would keep a dangling reference to them once the deletion runs on
|
||||||
|
// the next event-loop cycle.
|
||||||
|
QList<DeckPreviewTagDisplayWidget *> chips;
|
||||||
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
|
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
|
||||||
const QString &tagName = tagWidget->getTagName();
|
if (tagWidget->getState() != TagState::NotSelected || allTags.contains(tagWidget->getTagName())) {
|
||||||
|
chips.append(tagWidget);
|
||||||
// Keep the tag widget if it is either selected or excluded
|
} else {
|
||||||
if (!tags.contains(tagName) && tagWidget->getState() == TagState::NotSelected) {
|
|
||||||
flowWidget->removeWidget(tagWidget);
|
flowWidget->removeWidget(tagWidget);
|
||||||
tagWidget->deleteLater();
|
tagWidget->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckStorageTagFilterWidget::addTagsIfNotPresent(const QSet<QString> &tags)
|
// Add chips for tags that are not shown yet.
|
||||||
{
|
for (const QString &tag : allTags) {
|
||||||
for (const QString &tag : tags) {
|
bool tagExists = false;
|
||||||
addTagIfNotPresent(tag);
|
for (DeckPreviewTagDisplayWidget *tagWidget : chips) {
|
||||||
}
|
if (tagWidget->getTagName() == tag) {
|
||||||
}
|
tagExists = true;
|
||||||
|
break;
|
||||||
void VisualDeckStorageTagFilterWidget::addTagIfNotPresent(const QString &tag)
|
}
|
||||||
{
|
}
|
||||||
// Check if the tag already exists in the flow widget
|
if (!tagExists) {
|
||||||
bool tagExists = false;
|
auto *newTagWidget = new DeckPreviewTagDisplayWidget(this, tag);
|
||||||
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
|
connect(newTagWidget, &DeckPreviewTagDisplayWidget::tagClicked, this,
|
||||||
if (tagWidget->getTagName() == tag) {
|
&VisualDeckStorageTagFilterWidget::filterChanged);
|
||||||
tagExists = true;
|
flowWidget->addWidget(newTagWidget);
|
||||||
break;
|
chips.append(newTagWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the tag doesn't exist, add a new DeckPreviewTagDisplayWidget
|
// Clear and re-add the chips in sorted order.
|
||||||
if (!tagExists) {
|
std::sort(chips.begin(), chips.end(), [](DeckPreviewTagDisplayWidget *a, DeckPreviewTagDisplayWidget *b) {
|
||||||
auto *newTagWidget = new DeckPreviewTagDisplayWidget(this, tag);
|
|
||||||
connect(newTagWidget, &DeckPreviewTagDisplayWidget::tagClicked, parent,
|
|
||||||
&VisualDeckStorageWidget::updateTagFilter);
|
|
||||||
flowWidget->addWidget(newTagWidget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckStorageTagFilterWidget::sortTags()
|
|
||||||
{
|
|
||||||
// Get all tag widgets
|
|
||||||
QList<DeckPreviewTagDisplayWidget *> tagWidgets = findChildren<DeckPreviewTagDisplayWidget *>();
|
|
||||||
|
|
||||||
// Sort widgets by tag name
|
|
||||||
std::sort(tagWidgets.begin(), tagWidgets.end(), [](DeckPreviewTagDisplayWidget *a, DeckPreviewTagDisplayWidget *b) {
|
|
||||||
return a->getTagName().toLower() < b->getTagName().toLower();
|
return a->getTagName().toLower() < b->getTagName().toLower();
|
||||||
});
|
});
|
||||||
|
for (DeckPreviewTagDisplayWidget *tagWidget : chips) {
|
||||||
// Clear and re-add widgets in sorted order
|
|
||||||
for (DeckPreviewTagDisplayWidget *tagWidget : tagWidgets) {
|
|
||||||
flowWidget->removeWidget(tagWidget);
|
flowWidget->removeWidget(tagWidget);
|
||||||
}
|
}
|
||||||
for (DeckPreviewTagDisplayWidget *tagWidget : tagWidgets) {
|
for (DeckPreviewTagDisplayWidget *tagWidget : chips) {
|
||||||
flowWidget->addWidget(tagWidget);
|
flowWidget->addWidget(tagWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,26 +9,27 @@
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
class FlowWidget;
|
class FlowWidget;
|
||||||
class VisualDeckStorageWidget;
|
class VisualDeckStorageWidget;
|
||||||
|
|
||||||
class VisualDeckStorageTagFilterWidget : public QWidget
|
class VisualDeckStorageTagFilterWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
VisualDeckStorageWidget *parent;
|
|
||||||
FlowWidget *flowWidget;
|
FlowWidget *flowWidget;
|
||||||
|
std::function<QSet<QString>()> allTagsProvider;
|
||||||
[[nodiscard]] QSet<QString> gatherAllTags() const;
|
|
||||||
void removeTagsNotInList(const QSet<QString> &tags);
|
|
||||||
void addTagsIfNotPresent(const QSet<QString> &tags);
|
|
||||||
void addTagIfNotPresent(const QString &tag);
|
|
||||||
void sortTags();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent);
|
explicit VisualDeckStorageTagFilterWidget(QWidget *parent = nullptr);
|
||||||
[[nodiscard]] QStringList getAllKnownTags() const;
|
[[nodiscard]] QStringList getAllKnownTags() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets a provider for the full set of tags to draw chips from.
|
||||||
|
*/
|
||||||
|
void setAllTagsProvider(const std::function<QSet<QString>()> &provider);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The tags currently in "selected" state.
|
* @brief The tags currently in "selected" state.
|
||||||
*/
|
*/
|
||||||
|
|
@ -39,9 +40,15 @@ public:
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] QStringList excludedTags() const;
|
[[nodiscard]] QStringList excludedTags() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* Emitted whenever a chip's selection/exclusion state changes.
|
||||||
|
*/
|
||||||
|
void filterChanged();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* @brief Rebuilds the tag chips from the tags of the currently visible decks.
|
* @brief Rebuilds the tag chips from the currently available tags.
|
||||||
*/
|
*/
|
||||||
void refreshTags();
|
void refreshTags();
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
|
||||||
|
|
||||||
// tag filter box
|
// tag filter box
|
||||||
tagFilterWidget = new VisualDeckStorageTagFilterWidget(this);
|
tagFilterWidget = new VisualDeckStorageTagFilterWidget(this);
|
||||||
|
tagFilterWidget->setAllTagsProvider([this] { return gatherVisibleTags(); });
|
||||||
|
connect(tagFilterWidget, &VisualDeckStorageTagFilterWidget::filterChanged, this,
|
||||||
|
&VisualDeckStorageWidget::updateTagFilter);
|
||||||
updateTagsVisibility(SettingsCache::instance().visualDeckStorage().getVisualDeckStorageShowTagFilter());
|
updateTagsVisibility(SettingsCache::instance().visualDeckStorage().getVisualDeckStorageShowTagFilter());
|
||||||
|
|
||||||
deckPreviewSelectionAnimationEnabled =
|
deckPreviewSelectionAnimationEnabled =
|
||||||
|
|
@ -216,6 +219,25 @@ void VisualDeckStorageWidget::updateTagFilter()
|
||||||
tagFilterWidget->refreshTags();
|
tagFilterWidget->refreshTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The tags of all decks currently accepted by the proxy model.
|
||||||
|
*/
|
||||||
|
QSet<QString> VisualDeckStorageWidget::gatherVisibleTags() const
|
||||||
|
{
|
||||||
|
QSet<QString> allTags;
|
||||||
|
for (int proxyRow = 0; proxyRow < storageProxyModel->rowCount(); ++proxyRow) {
|
||||||
|
const QModelIndex index = storageProxyModel->index(proxyRow, 0);
|
||||||
|
if (!index.data(VisualDeckStorageRoles::FilterMatchRole).toBool()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QStringList deckTags = index.data(VisualDeckStorageRoles::TagsRole).toStringList();
|
||||||
|
for (const QString &tag : deckTags) {
|
||||||
|
allTags.insert(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allTags;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pushes the color identity filter widget's state into the proxy model.
|
* Pushes the color identity filter widget's state into the proxy model.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reapplySortAndFilters();
|
void reapplySortAndFilters();
|
||||||
|
[[nodiscard]] QSet<QString> gatherVisibleTags() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVBoxLayout *layout;
|
QVBoxLayout *layout;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue