From c75a483ee66e75a10627aef9166ed245f8ef8130 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Thu, 27 Nov 2025 22:16:12 +0100 Subject: [PATCH] [VDE] Add selection model (#6354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Took 22 minutes Took 1 minute Took 17 seconds Co-authored-by: Lukas BrĂ¼bach --- .../card_group_display_widget.cpp | 49 ++++++- .../card_group_display_widget.h | 5 + .../flat_card_group_display_widget.cpp | 2 + .../flat_card_group_display_widget.h | 1 + .../overlapped_card_group_display_widget.cpp | 5 +- .../overlapped_card_group_display_widget.h | 1 + ..._info_picture_with_text_overlay_widget.cpp | 47 ++++++- ...rd_info_picture_with_text_overlay_widget.h | 2 + .../cards/deck_card_zone_display_widget.cpp | 43 +++++-- .../cards/deck_card_zone_display_widget.h | 3 + .../deck_editor_deck_dock_widget.h | 5 + .../tab_deck_editor_visual.cpp | 70 ++++++++-- .../tab_deck_editor_visual.h | 5 +- .../tab_deck_editor_visual_tab_widget.cpp | 2 +- .../visual_deck_editor_widget.cpp | 121 ++++++++++++------ .../visual_deck_editor_widget.h | 11 +- 16 files changed, 303 insertions(+), 69 deletions(-) diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp index 81bd52702..7734a61d0 100644 --- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp @@ -10,6 +10,7 @@ CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent, DeckListModel *_deckListModel, + QItemSelectionModel *_selectionModel, QPersistentModelIndex _trackedIndex, QString _zoneName, QString _cardGroupCategory, @@ -17,8 +18,8 @@ CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent, QStringList _activeSortCriteria, int bannerOpacity, CardSizeWidget *_cardSizeWidget) - : QWidget(parent), deckListModel(_deckListModel), trackedIndex(_trackedIndex), zoneName(_zoneName), - cardGroupCategory(_cardGroupCategory), activeGroupCriteria(_activeGroupCriteria), + : QWidget(parent), deckListModel(_deckListModel), selectionModel(_selectionModel), trackedIndex(_trackedIndex), + zoneName(_zoneName), cardGroupCategory(_cardGroupCategory), activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria), cardSizeWidget(_cardSizeWidget) { layout = new QVBoxLayout(this); @@ -32,9 +33,47 @@ CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent, CardGroupDisplayWidget::updateCardDisplays(); connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::onCardAddition); + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &CardGroupDisplayWidget::onSelectionChanged); connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::onCardRemoval); } +void CardGroupDisplayWidget::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + auto proxyModel = qobject_cast(selectionModel->model()); + + for (auto &range : selected) { + for (int row = range.top(); row <= range.bottom(); ++row) { + QModelIndex idx = range.model()->index(row, 0, range.parent()); + + if (proxyModel) { + idx = proxyModel->mapToSource(idx); + } + + auto it = indexToWidgetMap.find(QPersistentModelIndex(idx)); + if (it != indexToWidgetMap.end()) { + if (auto displayWidget = qobject_cast(it.value())) { + displayWidget->setHighlighted(true); + } + } + } + } + + for (auto &range : deselected) { + for (int row = range.top(); row <= range.bottom(); ++row) { + QModelIndex idx = range.model()->index(row, 0, range.parent()); + if (proxyModel) + idx = proxyModel->mapToSource(idx); + + auto it = indexToWidgetMap.find(QPersistentModelIndex(idx)); + if (it != indexToWidgetMap.end()) { + if (auto displayWidget = qobject_cast(it.value())) { + displayWidget->setHighlighted(false); + } + } + } + } +} + void CardGroupDisplayWidget::clearAllDisplayWidgets() { for (auto idx : indexToWidgetMap.keys()) { @@ -138,6 +177,12 @@ void CardGroupDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSort updateCardDisplays(); } +void CardGroupDisplayWidget::mousePressEvent(QMouseEvent *event) +{ + QWidget::mousePressEvent(event); + selectionModel->clearSelection(); +} + void CardGroupDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card) { emit cardClicked(event, card); diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.h b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.h index a62a66803..4d911edde 100644 --- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.h @@ -11,6 +11,7 @@ #include "../card_info_picture_with_text_overlay_widget.h" #include "../card_size_widget.h" +#include #include #include #include @@ -24,6 +25,7 @@ class CardGroupDisplayWidget : public QWidget public: CardGroupDisplayWidget(QWidget *parent, DeckListModel *deckListModel, + QItemSelectionModel *selectionModel, QPersistentModelIndex trackedIndex, QString zoneName, QString cardGroupCategory, @@ -31,9 +33,11 @@ public: QStringList activeSortCriteria, int bannerOpacity, CardSizeWidget *cardSizeWidget); + void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void clearAllDisplayWidgets(); DeckListModel *deckListModel; + QItemSelectionModel *selectionModel; QPersistentModelIndex trackedIndex; QHash indexToWidgetMap; QString zoneName; @@ -43,6 +47,7 @@ public: CardSizeWidget *cardSizeWidget; public slots: + void mousePressEvent(QMouseEvent *event) override; void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void onHover(const ExactCard &card); virtual QWidget *constructWidgetForIndex(QPersistentModelIndex index); diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp index dad581874..5f3a2c2c0 100644 --- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp @@ -10,6 +10,7 @@ FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent, DeckListModel *_deckListModel, + QItemSelectionModel *_selectionModel, QPersistentModelIndex _trackedIndex, QString _zoneName, QString _cardGroupCategory, @@ -19,6 +20,7 @@ FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent, CardSizeWidget *_cardSizeWidget) : CardGroupDisplayWidget(parent, _deckListModel, + _selectionModel, std::move(_trackedIndex), _zoneName, _cardGroupCategory, diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.h b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.h index 8b536c5e5..e07152b34 100644 --- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.h @@ -17,6 +17,7 @@ class FlatCardGroupDisplayWidget : public CardGroupDisplayWidget public: FlatCardGroupDisplayWidget(QWidget *parent, DeckListModel *deckListModel, + QItemSelectionModel *selectionModel, QPersistentModelIndex trackedIndex, QString zoneName, QString cardGroupCategory, diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp index 4d8759cd8..27741a79c 100644 --- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp @@ -1,14 +1,12 @@ #include "overlapped_card_group_display_widget.h" -#include "../card_info_picture_with_text_overlay_widget.h" - #include -#include #include #include OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent, DeckListModel *_deckListModel, + QItemSelectionModel *_selectionModel, QPersistentModelIndex _trackedIndex, QString _zoneName, QString _cardGroupCategory, @@ -18,6 +16,7 @@ OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *pare CardSizeWidget *_cardSizeWidget) : CardGroupDisplayWidget(parent, _deckListModel, + _selectionModel, _trackedIndex, _zoneName, _cardGroupCategory, diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.h b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.h index 1440c37e7..288e46129 100644 --- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.h @@ -17,6 +17,7 @@ class OverlappedCardGroupDisplayWidget : public CardGroupDisplayWidget public: OverlappedCardGroupDisplayWidget(QWidget *parent, DeckListModel *deckListModel, + QItemSelectionModel *selectionModel, QPersistentModelIndex trackedIndex, QString zoneName, QString cardGroupCategory, diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp index 1545dc4f6..4cb61deab 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp @@ -25,7 +25,7 @@ CardInfoPictureWithTextOverlayWidget::CardInfoPictureWithTextOverlayWidget(QWidg const int fontSize, const Qt::Alignment alignment) : CardInfoPictureWidget(parent, hoverToZoomEnabled, raiseOnEnter), textColor(textColor), outlineColor(outlineColor), - fontSize(fontSize), textAlignment(alignment) + fontSize(fontSize), textAlignment(alignment), highlighted(false) { this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } @@ -82,6 +82,16 @@ void CardInfoPictureWithTextOverlayWidget::setTextAlignment(const Qt::Alignment update(); } +void CardInfoPictureWithTextOverlayWidget::setHighlighted(bool _highlighted) +{ + if (highlighted == _highlighted) { + return; + } + + highlighted = _highlighted; + update(); +} + void CardInfoPictureWithTextOverlayWidget::mousePressEvent(QMouseEvent *event) { emit imageClicked(event, this); @@ -98,11 +108,6 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event) // Call the base class's paintEvent to draw the card image CardInfoPictureWidget::paintEvent(event); - // If no overlay text, skip drawing the text - if (overlayText.isEmpty()) { - return; - } - QStylePainter painter(this); // Get the pixmap from the base class using the getter @@ -116,6 +121,36 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event) const QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2}; const QRect pixmapRect(topLeft, scaledSize); + if (highlighted) { + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + + // Soft glow and border around the pixmap + const int padding = 4; // glow extends a little beyond image + QRect glowRect = pixmapRect.adjusted(-padding, -padding, padding, padding); + + QPainterPath path; + int radius = 8; // rounded corners + path.addRoundedRect(glowRect, radius, radius); + + // Soft outer glow + QColor glowColor(0, 150, 255, 80); // subtle blu + painter.setPen(QPen(glowColor, 6)); + painter.drawPath(path); + + // Thin inner border for crispness + QColor borderColor(0, 150, 255, 200); + painter.setPen(QPen(borderColor, 2)); + painter.drawRoundedRect(pixmapRect, radius, radius); + + painter.restore(); + } + + // If no overlay text, skip drawing the text + if (overlayText.isEmpty()) { + return; + } + // Calculate the optimal font size QFont font = painter.font(); int optimalFontSize = fontSize; // Start with the user-defined font size diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.h b/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.h index 91c44cbb1..eed827292 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.h @@ -32,6 +32,7 @@ public: void setOutlineColor(const QColor &color); void setFontSize(int size); void setTextAlignment(Qt::Alignment alignment); + void setHighlighted(bool _highlighted); [[nodiscard]] QSize sizeHint() const override; signals: @@ -53,6 +54,7 @@ private: QColor outlineColor; int fontSize; Qt::Alignment textAlignment; + bool highlighted; }; #endif // CARD_PICTURE_WITH_TEXT_OVERLAY_H diff --git a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp index be32d4f0e..5529c35b6 100644 --- a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp @@ -9,6 +9,7 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent, DeckListModel *_deckListModel, + QItemSelectionModel *_selectionModel, QPersistentModelIndex _trackedIndex, QString _zoneName, QString _activeGroupCriteria, @@ -17,9 +18,10 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent, int bannerOpacity, int subBannerOpacity, CardSizeWidget *_cardSizeWidget) - : QWidget(parent), deckListModel(_deckListModel), trackedIndex(_trackedIndex), zoneName(_zoneName), - activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria), displayType(_displayType), - bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity), cardSizeWidget(_cardSizeWidget) + : QWidget(parent), deckListModel(_deckListModel), selectionModel(_selectionModel), trackedIndex(_trackedIndex), + zoneName(_zoneName), activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria), + displayType(_displayType), bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity), + cardSizeWidget(_cardSizeWidget) { layout = new QVBoxLayout(this); setLayout(layout); @@ -37,9 +39,34 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent, displayCards(); connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &DeckCardZoneDisplayWidget::onCategoryAddition); + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, + &DeckCardZoneDisplayWidget::onSelectionChanged); connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &DeckCardZoneDisplayWidget::onCategoryRemoval); } +void DeckCardZoneDisplayWidget::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + for (auto &range : selected) { + for (int row = range.top(); row <= range.bottom(); ++row) { + QModelIndex idx = range.model()->index(row, 0, range.parent()); + auto it = indexToWidgetMap.find(QPersistentModelIndex(idx)); + if (it != indexToWidgetMap.end()) { + // it.value()->setHighlighted(true); + } + } + } + + for (auto &range : deselected) { + for (int row = range.top(); row <= range.bottom(); ++row) { + QModelIndex idx = range.model()->index(row, 0, range.parent()); + auto it = indexToWidgetMap.find(QPersistentModelIndex(idx)); + if (it != indexToWidgetMap.end()) { + // it.value()->setHighlighted(false); + } + } + } +} + void DeckCardZoneDisplayWidget::cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget) { cardGroupLayout->removeWidget(displayWidget); @@ -60,8 +87,8 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex } if (displayType == DisplayType::Overlap) { auto *displayWidget = new OverlappedCardGroupDisplayWidget( - cardGroupContainer, deckListModel, index, zoneName, categoryName, activeGroupCriteria, activeSortCriteria, - subBannerOpacity, cardSizeWidget); + cardGroupContainer, deckListModel, selectionModel, index, zoneName, categoryName, activeGroupCriteria, + activeSortCriteria, subBannerOpacity, cardSizeWidget); connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this, &DeckCardZoneDisplayWidget::onClick); connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardHovered, this, @@ -73,9 +100,9 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex cardGroupLayout->addWidget(displayWidget); indexToWidgetMap.insert(index, displayWidget); } else if (displayType == DisplayType::Flat) { - auto *displayWidget = - new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, index, zoneName, categoryName, - activeGroupCriteria, activeSortCriteria, subBannerOpacity, cardSizeWidget); + auto *displayWidget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, selectionModel, index, + zoneName, categoryName, activeGroupCriteria, + activeSortCriteria, subBannerOpacity, cardSizeWidget); connect(displayWidget, &FlatCardGroupDisplayWidget::cardClicked, this, &DeckCardZoneDisplayWidget::onClick); connect(displayWidget, &FlatCardGroupDisplayWidget::cardHovered, this, &DeckCardZoneDisplayWidget::onHover); connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this, diff --git a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.h b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.h index dbaeaa9ae..ac53cb704 100644 --- a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.h +++ b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.h @@ -26,6 +26,7 @@ class DeckCardZoneDisplayWidget : public QWidget public: DeckCardZoneDisplayWidget(QWidget *parent, DeckListModel *deckListModel, + QItemSelectionModel *selectionModel, QPersistentModelIndex trackedIndex, QString zoneName, QString activeGroupCriteria, @@ -34,7 +35,9 @@ public: int bannerOpacity, int subBannerOpacity, CardSizeWidget *_cardSizeWidget); + void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); DeckListModel *deckListModel; + QItemSelectionModel *selectionModel; QPersistentModelIndex trackedIndex; QString zoneName; void addCardsToOverlapWidget(); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index b633bdaed..78d5da9f7 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -51,6 +51,11 @@ public: return activeGroupCriteriaComboBox; } + [[nodiscard]] QItemSelectionModel *getSelectionModel() const + { + return deckView->selectionModel(); + } + public slots: void cleanDeck(); void updateBannerCardComboBox(); diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index 109815816..7a811fd28 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -176,20 +176,74 @@ void TabDeckEditorVisual::changeModelIndexToCard(const ExactCard &activeCard) if (!index.isValid()) { index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_SIDE); } - deckDockWidget->deckView->setCurrentIndex(index); + if (!deckDockWidget->getSelectionModel()->hasSelection()) { + deckDockWidget->getSelectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + } } -/** @brief Handles clicks on cards in the mainboard deck. */ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, - QString zoneName) + const QString &zoneName) { + auto card = instance->getCard(); + + // Get the model index for the card + QModelIndex idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName); + if (!idx.isValid()) { + return; + } + + QItemSelectionModel *sel = deckDockWidget->getSelectionModel(); + + // Double click = swap + if (event->type() == QEvent::MouseButtonDblClick && event->button() == Qt::LeftButton) { + actSwapCard(card, zoneName); + idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName); + sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); + return; + } + + // Right-click = decrement + if (event->button() == Qt::RightButton) { + actDecrementCard(card); + // Keep selection intact. + return; + } + + // Alt + Left click = increment + if (event->button() == Qt::LeftButton && event->modifiers().testFlag(Qt::AltModifier)) { + // actIncrementCard(card); + // Keep selection intact. + return; + } + + // Normal selection behavior if (event->button() == Qt::LeftButton) { - actSwapCard(instance->getCard(), zoneName); - } else if (event->button() == Qt::RightButton) { - actDecrementCard(instance->getCard()); - } else if (event->button() == Qt::MiddleButton) { - deckDockWidget->actRemoveCard(); + Qt::KeyboardModifiers mods = event->modifiers(); + QItemSelectionModel::SelectionFlags flags; + + if (mods.testFlag(Qt::ControlModifier)) { + // CTRL + click = toggle selection + flags = QItemSelectionModel::Toggle; + } else if (mods.testFlag(Qt::ShiftModifier)) { + // SHIFT + click = select range + QModelIndex anchor = sel->currentIndex(); + if (!anchor.isValid()) { + anchor = idx; + } + + QItemSelection range(anchor, idx); + sel->select(range, QItemSelectionModel::SelectCurrent); + sel->setCurrentIndex(idx, QItemSelectionModel::NoUpdate); + return; + } else { + // Normal click = clear selection, select this, set current + deckDockWidget->deckView->setCurrentIndex(idx); + deckDockWidget->deckView->scrollTo(idx); + return; + } + + sel->setCurrentIndex(idx, flags); } } diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.h b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.h index 87f6c5df8..2f1d11d82 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.h +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.h @@ -176,8 +176,9 @@ public slots: * @param instance Widget representing the clicked card. * @param zoneName Deck zone of the card. */ - void - processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName); + void processMainboardCardClick(QMouseEvent *event, + CardInfoPictureWithTextOverlayWidget *instance, + const QString &zoneName); /** * @brief Handle card clicks in the database visual display. diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp index 08728dac0..98b6aff00 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp @@ -28,7 +28,7 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, layout = new QVBoxLayout(this); setLayout(layout); - visualDeckView = new VisualDeckEditorWidget(this, deckModel); + visualDeckView = new VisualDeckEditorWidget(this, deckModel, _deckEditor->deckDockWidget->getSelectionModel()); visualDeckView->setObjectName("visualDeckView"); connect(visualDeckView, &VisualDeckEditorWidget::activeCardChanged, this, &TabDeckEditorVisualTabWidget::onCardChanged); diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp index 005efd6db..3536359be 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp @@ -25,8 +25,10 @@ #include #include -VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel) - : QWidget(parent), deckListModel(_deckListModel) +VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, + DeckListModel *_deckListModel, + QItemSelectionModel *_selectionModel) + : QWidget(parent), deckListModel(_deckListModel), selectionModel(_selectionModel) { // The Main Widget and Main Layout, which contain a single Widget: The Scroll Area setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -204,6 +206,11 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_ connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &VisualDeckEditorWidget::onCardRemoval); constructZoneWidgetsFromDeckListModel(); + if (selectionModel) { + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, + &VisualDeckEditorWidget::onSelectionChanged); + } + retranslateUi(); } @@ -219,6 +226,47 @@ void VisualDeckEditorWidget::retranslateUi() tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)")); } +void VisualDeckEditorWidget::setSelectionModel(QItemSelectionModel *model) +{ + if (selectionModel == model) { + return; + } + + if (selectionModel) { + // TODO: Possibly disconnect old ones? + } + + selectionModel = model; + + if (selectionModel) { + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, + &VisualDeckEditorWidget::onSelectionChanged); + } +} + +void VisualDeckEditorWidget::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + for (auto &range : selected) { + for (int row = range.top(); row <= range.bottom(); ++row) { + QModelIndex idx = range.model()->index(row, 0, range.parent()); + auto it = indexToWidgetMap.find(QPersistentModelIndex(idx)); + if (it != indexToWidgetMap.end()) { + // it.value()->setHighlighted(true); + } + } + } + + for (auto &range : deselected) { + for (int row = range.top(); row <= range.bottom(); ++row) { + QModelIndex idx = range.model()->index(row, 0, range.parent()); + auto it = indexToWidgetMap.find(QPersistentModelIndex(idx)); + if (it != indexToWidgetMap.end()) { + // it.value()->setHighlighted(false); + } + } + } +} + void VisualDeckEditorWidget::clearAllDisplayWidgets() { for (auto idx : indexToWidgetMap.keys()) { @@ -250,25 +298,7 @@ void VisualDeckEditorWidget::onCardAddition(const QModelIndex &parent, int first continue; } - DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget( - zoneContainer, deckListModel, index, - deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString(), activeGroupCriteria, - activeSortCriteria, currentDisplayType, 20, 10, cardSizeWidget); - connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover); - connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this, - &VisualDeckEditorWidget::onCardClick); - connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::requestCleanup, this, - &VisualDeckEditorWidget::cleanupInvalidZones); - connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget, - &DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged); - connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget, - &DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged); - connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget, - &DeckCardZoneDisplayWidget::refreshDisplayType); - zoneDisplayWidget->refreshDisplayType(currentDisplayType); - zoneContainerLayout->addWidget(zoneDisplayWidget); - - indexToWidgetMap.insert(index, zoneDisplayWidget); + constructZoneWidgetForIndex(index); } } } @@ -287,6 +317,28 @@ void VisualDeckEditorWidget::onCardRemoval(const QModelIndex &parent, int first, } } +void VisualDeckEditorWidget::constructZoneWidgetForIndex(QPersistentModelIndex persistent) +{ + DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget( + zoneContainer, deckListModel, selectionModel, persistent, + deckListModel->data(persistent.sibling(persistent.row(), 1), Qt::EditRole).toString(), activeGroupCriteria, + activeSortCriteria, currentDisplayType, 20, 10, cardSizeWidget); + connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover); + connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this, &VisualDeckEditorWidget::onCardClick); + connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::requestCleanup, this, + &VisualDeckEditorWidget::cleanupInvalidZones); + connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget, + &DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged); + connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget, + &DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged); + connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget, + &DeckCardZoneDisplayWidget::refreshDisplayType); + zoneDisplayWidget->refreshDisplayType(currentDisplayType); + zoneContainerLayout->addWidget(zoneDisplayWidget); + + indexToWidgetMap.insert(persistent, zoneDisplayWidget); +} + void VisualDeckEditorWidget::constructZoneWidgetsFromDeckListModel() { QSortFilterProxyModel proxy; @@ -305,23 +357,7 @@ void VisualDeckEditorWidget::constructZoneWidgetsFromDeckListModel() continue; } - DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget( - zoneContainer, deckListModel, persistent, - deckListModel->data(persistent.sibling(persistent.row(), 1), Qt::EditRole).toString(), activeGroupCriteria, - activeSortCriteria, currentDisplayType, 20, 10, cardSizeWidget); - connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover); - connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this, &VisualDeckEditorWidget::onCardClick); - connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::requestCleanup, this, - &VisualDeckEditorWidget::cleanupInvalidZones); - connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget, - &DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged); - connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget, - &DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged); - connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget, - &DeckCardZoneDisplayWidget::refreshDisplayType); - zoneContainerLayout->addWidget(zoneDisplayWidget); - - indexToWidgetMap.insert(persistent, zoneDisplayWidget); + constructZoneWidgetForIndex(persistent); } } @@ -389,7 +425,16 @@ void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelInde void VisualDeckEditorWidget::onHover(const ExactCard &hoveredCard) { + // If user has any card selected, ignore hover + if (selectionModel->hasSelection()) { + return; + } + + // If nothing is selected -> this is our "active/preview" card emit activeCardChanged(hoveredCard); + + // TODO: highlight hovered card visually: + // highlightHoveredCard(hoveredCard); } void VisualDeckEditorWidget::onCardClick(QMouseEvent *event, diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h index 176086019..4cff68407 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h @@ -36,13 +36,20 @@ class VisualDeckEditorWidget : public QWidget Q_OBJECT public: - explicit VisualDeckEditorWidget(QWidget *parent, DeckListModel *deckListModel); + explicit VisualDeckEditorWidget(QWidget *parent, DeckListModel *deckListModel, QItemSelectionModel *selectionModel); void retranslateUi(); void clearAllDisplayWidgets(); void resizeEvent(QResizeEvent *event) override; void setDeckList(const DeckList &_deckListModel); + void setSelectionModel(QItemSelectionModel *model); + void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + QItemSelectionModel *getSelectionModel() const + { + return selectionModel; + } + QLineEdit *searchBar; CardSizeWidget *cardSizeWidget; @@ -53,6 +60,7 @@ public slots: void cleanupInvalidZones(DeckCardZoneDisplayWidget *displayWidget); void onCardAddition(const QModelIndex &parent, int first, int last); void onCardRemoval(const QModelIndex &parent, int first, int last); + void constructZoneWidgetForIndex(QPersistentModelIndex persistent); void constructZoneWidgetsFromDeckListModel(); signals: @@ -72,6 +80,7 @@ protected slots: private: DeckListModel *deckListModel; + QItemSelectionModel *selectionModel; QVBoxLayout *mainLayout; QWidget *searchContainer; QHBoxLayout *searchLayout;