diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index 1d8121b19..bd1f1cd23 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -284,6 +284,8 @@ SettingsCache::SettingsCache() closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool(); focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool(); + showDragSelectionCount = settings->value("interface/showlassoselectioncount", true).toBool(); + showShortcuts = settings->value("menu/showshortcuts", true).toBool(); showGameSelectorFilterToolbar = settings->value("menu/showgameselectorfiltertoolbar", true).toBool(); displayCardNames = settings->value("cards/displaycardnames", true).toBool(); @@ -1308,6 +1310,12 @@ void SettingsCache::setRoundCardCorners(bool _roundCardCorners) emit roundCardCornersChanged(roundCardCorners); } +void SettingsCache::setShowDragSelectionCount(QT_STATE_CHANGED_T _showDragSelectionCount) +{ + showDragSelectionCount = static_cast(_showDragSelectionCount); + settings->setValue("interface/showlassoselectioncount", showDragSelectionCount); +} + void SettingsCache::loadPaths() { QString dataPath = getDataPath(); diff --git a/cockatrice/src/client/settings/cache_settings.h b/cockatrice/src/client/settings/cache_settings.h index 2bbf85352..b8b68cda4 100644 --- a/cockatrice/src/client/settings/cache_settings.h +++ b/cockatrice/src/client/settings/cache_settings.h @@ -340,6 +340,7 @@ private: bool isPortableBuild; bool roundCardCorners; bool showStatusBar; + bool showDragSelectionCount; public: SettingsCache(); @@ -455,6 +456,10 @@ public: { return showStatusBar; } + [[nodiscard]] bool getShowDragSelectionCount() const + { + return showDragSelectionCount; + } [[nodiscard]] bool getNotificationsEnabled() const { return notificationsEnabled; @@ -1120,5 +1125,6 @@ public slots: void setUpdateReleaseChannelIndex(int value); void setMaxFontSize(int _max); void setRoundCardCorners(bool _roundCardCorners); + void setShowDragSelectionCount(QT_STATE_CHANGED_T _showDragSelectionCount); }; #endif diff --git a/cockatrice/src/game/game_scene.cpp b/cockatrice/src/game/game_scene.cpp index 5dc3b48f7..034ff6947 100644 --- a/cockatrice/src/game/game_scene.cpp +++ b/cockatrice/src/game/game_scene.cpp @@ -521,9 +521,9 @@ void GameScene::startRubberBand(const QPointF &selectionOrigin) emit sigStartRubberBand(selectionOrigin); } -void GameScene::resizeRubberBand(const QPointF &cursorPoint) +void GameScene::resizeRubberBand(const QPointF &cursorPoint, int selectedCount) { - emit sigResizeRubberBand(cursorPoint); + emit sigResizeRubberBand(cursorPoint, selectedCount); } void GameScene::stopRubberBand() diff --git a/cockatrice/src/game/game_scene.h b/cockatrice/src/game/game_scene.h index 86fa0795a..f08e83aa4 100644 --- a/cockatrice/src/game/game_scene.h +++ b/cockatrice/src/game/game_scene.h @@ -163,7 +163,7 @@ public: /** Unregisters a card from animation updates. */ void unregisterAnimationItem(AbstractCardItem *card); void startRubberBand(const QPointF &selectionOrigin); - void resizeRubberBand(const QPointF &cursorPoint); + void resizeRubberBand(const QPointF &cursorPoint, int selectedCount); void stopRubberBand(); public slots: @@ -196,7 +196,7 @@ protected: signals: void sigStartRubberBand(const QPointF &selectionOrigin); - void sigResizeRubberBand(const QPointF &cursorPoint); + void sigResizeRubberBand(const QPointF &cursorPoint, int selectedCount); void sigStopRubberBand(); }; diff --git a/cockatrice/src/game/game_view.cpp b/cockatrice/src/game/game_view.cpp index dd5cc70c1..d9aee7d16 100644 --- a/cockatrice/src/game/game_view.cpp +++ b/cockatrice/src/game/game_view.cpp @@ -4,9 +4,32 @@ #include "game_scene.h" #include +#include #include #include +// QRubberBand calls raise() in showEvent() and changeEvent() to stay on top of siblings. +// This subclass disables that behavior so dragCountLabel can appear above it. +class SelectionRubberBand : public QRubberBand +{ +public: + using QRubberBand::QRubberBand; + +protected: + void showEvent(QShowEvent *event) override + { + QWidget::showEvent(event); // Skip QRubberBand's raise() + } + + void changeEvent(QEvent *event) override + { + if (event->type() == QEvent::ZOrderChange) { + return; // Skip QRubberBand's raise() on z-order changes + } + QRubberBand::changeEvent(event); + } +}; + GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, parent), rubberBand(0) { setBackgroundBrush(QBrush(QColor(0, 0, 0))); @@ -27,7 +50,19 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, &GameView::refreshShortcuts); refreshShortcuts(); - rubberBand = new QRubberBand(QRubberBand::Rectangle, this); + rubberBand = new SelectionRubberBand(QRubberBand::Rectangle, this); + + const QString countLabelStyle = "color: white; " + "font-size: 14px; " + "font-weight: bold; " + "background-color: rgba(0, 0, 0, 160); " + "border-radius: 3px; " + "padding: 1px 2px;"; + + dragCountLabel = new QLabel(this); + dragCountLabel->setStyleSheet(countLabelStyle); + dragCountLabel->hide(); + dragCountLabel->raise(); } void GameView::resizeEvent(QResizeEvent *event) @@ -48,20 +83,67 @@ void GameView::updateSceneRect(const QRectF &rect) void GameView::startRubberBand(const QPointF &_selectionOrigin) { + if (!rubberBand) + return; + selectionOrigin = _selectionOrigin; rubberBand->setGeometry(QRect(mapFromScene(selectionOrigin), QSize(0, 0))); rubberBand->show(); } -void GameView::resizeRubberBand(const QPointF &cursorPoint) +void GameView::resizeRubberBand(const QPointF &cursorPoint, int selectedCount) { - if (rubberBand) - rubberBand->setGeometry(QRect(mapFromScene(selectionOrigin), cursorPoint.toPoint()).normalized()); + if (!rubberBand) + return; + + constexpr int kLabelPaddingInPixels = 4; + + QPoint cursor = cursorPoint.toPoint(); + QRect rect = QRect(mapFromScene(selectionOrigin), cursor).normalized(); + rubberBand->setGeometry(rect); + + if (!SettingsCache::instance().getShowDragSelectionCount()) { + dragCountLabel->hide(); + return; + } + + if (selectedCount > 0) { + dragCountLabel->setText(QString::number(selectedCount)); + dragCountLabel->adjustSize(); + QSize labelSize = dragCountLabel->size(); + + if (rect.width() < labelSize.width() + 2 * kLabelPaddingInPixels || + rect.height() < labelSize.height() + 2 * kLabelPaddingInPixels) { + dragCountLabel->hide(); + return; + } + + const int minX = rect.left() + kLabelPaddingInPixels; + const int minY = rect.top() + kLabelPaddingInPixels; + + int x = qMax(minX, cursor.x() - labelSize.width() - kLabelPaddingInPixels); + int y = qMax(minY, cursor.y() - labelSize.height() - kLabelPaddingInPixels); + + bool isAtTopLeftCorner = (x == minX) && (y == minY); + if (isAtTopLeftCorner) { + constexpr int kCursorClearanceInPixels = 16; + x = qMin(cursor.x() + kCursorClearanceInPixels, rect.right() - labelSize.width() - kLabelPaddingInPixels); + } + + dragCountLabel->move(x, y); + dragCountLabel->show(); + } else { + dragCountLabel->hide(); + } } void GameView::stopRubberBand() { + if (!rubberBand) + return; + rubberBand->hide(); + dragCountLabel->hide(); } void GameView::refreshShortcuts() diff --git a/cockatrice/src/game/game_view.h b/cockatrice/src/game/game_view.h index 72df9cd08..3c1b1467b 100644 --- a/cockatrice/src/game/game_view.h +++ b/cockatrice/src/game/game_view.h @@ -10,6 +10,7 @@ #include class GameScene; +class QLabel; class QRubberBand; class GameView : public QGraphicsView @@ -18,13 +19,14 @@ class GameView : public QGraphicsView private: QAction *aCloseMostRecentZoneView; QRubberBand *rubberBand; + QLabel *dragCountLabel; QPointF selectionOrigin; protected: void resizeEvent(QResizeEvent *event) override; private slots: void startRubberBand(const QPointF &selectionOrigin); - void resizeRubberBand(const QPointF &cursorPoint); + void resizeRubberBand(const QPointF &cursorPoint, int selectedCount); void stopRubberBand(); void refreshShortcuts(); public slots: diff --git a/cockatrice/src/game/zones/select_zone.cpp b/cockatrice/src/game/zones/select_zone.cpp index 719eec148..9bf5f9faf 100644 --- a/cockatrice/src/game/zones/select_zone.cpp +++ b/cockatrice/src/game/zones/select_zone.cpp @@ -68,7 +68,8 @@ void SelectZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } } static_cast(scene())->resizeRubberBand( - deviceTransform(static_cast(scene())->getViewportTransform()).map(pos)); + deviceTransform(static_cast(scene())->getViewportTransform()).map(pos), + cardsInSelectionRect.size()); event->accept(); } } diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp index e7286f078..851230881 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp @@ -821,6 +821,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), &SettingsCache::setAnnotateTokens); + showDragSelectionCountCheckBox.setChecked(SettingsCache::instance().getShowDragSelectionCount()); + connect(&showDragSelectionCountCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setShowDragSelectionCount); + useTearOffMenusCheckBox.setChecked(SettingsCache::instance().getUseTearOffMenus()); connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); @@ -833,7 +837,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() generalGrid->addWidget(&closeEmptyCardViewCheckBox, 4, 0); generalGrid->addWidget(&focusCardViewSearchBarCheckBox, 5, 0); generalGrid->addWidget(&annotateTokensCheckBox, 6, 0); - generalGrid->addWidget(&useTearOffMenusCheckBox, 7, 0); + generalGrid->addWidget(&showDragSelectionCountCheckBox, 7, 0); + generalGrid->addWidget(&useTearOffMenusCheckBox, 8, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -955,6 +960,7 @@ void UserInterfaceSettingsPage::retranslateUi() 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")); + showDragSelectionCountCheckBox.setText(tr("Show selection counter during drag selection")); useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); notificationsGroupBox->setTitle(tr("Notifications settings")); notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar")); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.h b/cockatrice/src/interface/widgets/dialogs/dlg_settings.h index db107c6e2..1bcbbbdf2 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.h @@ -171,6 +171,7 @@ private: QCheckBox closeEmptyCardViewCheckBox; QCheckBox focusCardViewSearchBarCheckBox; QCheckBox annotateTokensCheckBox; + QCheckBox showDragSelectionCountCheckBox; QCheckBox useTearOffMenusCheckBox; QCheckBox tapAnimationCheckBox; QCheckBox openDeckInNewTabCheckBox;