[VDS] Allow tags to toggle to a NOT state to hide non-matching decks (#5920)

* Allow excluding tags.

* Lint.

* My linter is broken, don't ask.

* Zzz.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL
2025-05-07 03:23:49 +02:00
committed by GitHub
parent f16ba6861b
commit 46e146b34a
3 changed files with 82 additions and 41 deletions

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

@@ -33,32 +33,42 @@ 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);
}
}
@@ -72,13 +82,15 @@ void VisualDeckStorageTagFilterWidget::refreshTags()
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();
}
}
}