diff --git a/cockatrice/src/game_graphics/board/arrow_item.cpp b/cockatrice/src/game_graphics/board/arrow_item.cpp index af63d047d..ce8967bb5 100644 --- a/cockatrice/src/game_graphics/board/arrow_item.cpp +++ b/cockatrice/src/game_graphics/board/arrow_item.cpp @@ -4,14 +4,12 @@ #include "../../client/settings/cache_settings.h" #include "../../game/player/player_actions.h" #include "../../game/player/player_logic.h" -#include "../game_scene.h" #include "../player/player_target.h" #include "../z_values.h" #include "../zones/card_zone.h" #include "card_item.h" #include -#include #include #include #include @@ -20,27 +18,10 @@ #include #include #include -#include #include #include #include -namespace -{ -constexpr qreal kMinStrokeDurationMs = 200.0; -constexpr qreal kMaxStrokeDurationMs = 450.0; -constexpr qreal kMsPerPixel = 0.8; -constexpr qreal kGlowFadeDurationMs = 120.0; -constexpr qreal kSheenHalfWidth = 14.0; - -/// @brief Ease-out cubic, for a natural "slow in / slow out" reveal. -qreal easeOutCubic(qreal t) -{ - const qreal inverse = 1.0 - t; - return 1.0 - inverse * inverse * inverse; -} -} // namespace - ArrowItem::ArrowItem(QSharedPointer _data, ArrowTarget *_startItem, ArrowTarget *_targetItem) : data(std::move(_data)), startItem(_startItem), targetItem(_targetItem) { @@ -66,13 +47,6 @@ ArrowItem::ArrowItem(QSharedPointer _data, ArrowTarget *_startI } } -ArrowItem::~ArrowItem() -{ - if (auto *scene = qobject_cast(this->scene())) { - scene->unregisterAnimationItem(this); - } -} - void ArrowItem::onTargetDestroyed() { emit requestDeletion(data->creatorId, data->id); @@ -117,21 +91,16 @@ void ArrowItem::updatePath(const QPointF &endPoint) prepareGeometryChange(); if (lineLength < 30) { path = QPainterPath(); - bodyPath = QPainterPath(); - headPath = QPainterPath(); - shaftOutlinePath = QPainterPath(); - centerLine = QPainterPath(); - headBaseFraction = 1.0; } else { QPointF c(lineLength / 2, qTan(phi * M_PI / 180) * lineLength); - centerLine = QPainterPath(); + QPainterPath centerLine; centerLine.moveTo(0, 0); centerLine.quadTo(c, QPointF(lineLength, 0)); - headBaseFraction = 1 - headLength / lineLength; - QPointF arrowBodyEndPoint = centerLine.pointAtPercent(headBaseFraction); - QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(headBaseFraction + 0.001)); + double percentage = 1 - headLength / lineLength; + QPointF arrowBodyEndPoint = centerLine.pointAtPercent(percentage); + QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(percentage + 0.001)); qreal alpha = testLine.angle() - 90; QPointF endPoint1 = arrowBodyEndPoint + arrowWidth / 2 * QPointF(qCos(alpha * M_PI / 180), -qSin(alpha * M_PI / 180)); @@ -142,89 +111,20 @@ void ArrowItem::updatePath(const QPointF &endPoint) QPointF point2 = endPoint2 + (headWidth - arrowWidth) / 2 * QPointF(-qCos(alpha * M_PI / 180), qSin(alpha * M_PI / 180)); - QPointF start1 = -arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180)); - QPointF start2 = arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180)); - - path = QPainterPath(start1); + path = QPainterPath(-arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180))); path.quadTo(c, endPoint1); path.lineTo(point1); path.lineTo(QPointF(lineLength, 0)); path.lineTo(point2); path.lineTo(endPoint2); - path.quadTo(c, start2); - path.lineTo(start1); - - bodyPath = QPainterPath(start1); - bodyPath.quadTo(c, endPoint1); - bodyPath.lineTo(endPoint2); - bodyPath.quadTo(c, start2); - bodyPath.lineTo(start1); - - headPath = QPainterPath(endPoint1); - headPath.lineTo(point1); - headPath.lineTo(QPointF(lineLength, 0)); - headPath.lineTo(point2); - headPath.lineTo(endPoint2); - - shaftOutlinePath = QPainterPath(start1); - shaftOutlinePath.quadTo(c, endPoint1); - shaftOutlinePath.moveTo(endPoint2); - shaftOutlinePath.quadTo(c, start2); - shaftOutlinePath.lineTo(start1); + path.quadTo(c, arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180))); + path.lineTo(-arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180))); } setPos(startPoint); setTransform(QTransform().rotate(-line.angle())); } -void ArrowItem::startDrawAnimation() -{ - if (!SettingsCache::instance().cardsDisplay().getArrowDrawAnimation() || centerLine.isEmpty()) { - return; - } - - strokeDurationMs = qBound(kMinStrokeDurationMs, centerLine.length() * kMsPerPixel, kMaxStrokeDurationMs); - glowFadeDurationMs = kGlowFadeDurationMs; - // The clock is started on the first animationEvent() tick so that t=0 - // corresponds to the first rendered frame. Starting it here would count - // the time spent before the item's first paint (event-loop delays, bursts - // of arrows created together), making the arrow appear already partway - // drawn when it first shows up. - animationStarted = false; - drawProgress = 0.0; - glowAlpha = 1.0; - update(); - if (auto *scene = qobject_cast(this->scene())) { - scene->registerAnimationItem(this); - } -} - -bool ArrowItem::animationEvent() -{ - if (!animationStarted) { - animationClock.start(); - animationStarted = true; - } - - const qint64 elapsed = animationClock.elapsed(); - if (elapsed >= strokeDurationMs + glowFadeDurationMs) { - drawProgress = 1.0; - glowAlpha = 0.0; - update(); - return false; - } - - if (elapsed < strokeDurationMs) { - drawProgress = easeOutCubic(qBound(0.0, elapsed / strokeDurationMs, 1.0)); - glowAlpha = 1.0; - } else { - drawProgress = 1.0; - glowAlpha = 1.0 - (elapsed - strokeDurationMs) / glowFadeDurationMs; - } - update(); - return true; -} - void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { QColor paintColor(data->color); @@ -233,66 +133,8 @@ void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti } else { paintColor.setAlpha(150); } - - painter->save(); - const QPen outlinePen = painter->pen(); painter->setBrush(paintColor); - - const auto drawShaft = [this, painter, &outlinePen, paintColor]() { - painter->setPen(Qt::NoPen); - painter->drawPath(bodyPath); - painter->setPen(outlinePen); - painter->setBrush(Qt::NoBrush); - painter->drawPath(shaftOutlinePath); - painter->setBrush(paintColor); - }; - - if (drawProgress >= 1.0 || path.isEmpty()) { - painter->drawPath(path); - } else if (drawProgress < headBaseFraction) { - // The reveal edge and the sheen share the same arc-length parameterization, - // so the stroke stays exactly in sync with the trailing sheen. - const qreal revealX = centerLine.pointAtPercent(drawProgress).x(); - QPainterPath clip; - clip.addRect(QRectF(-glowExtent, path.boundingRect().top() - glowExtent, revealX + glowExtent, - path.boundingRect().height() + 2 * glowExtent)); - painter->setClipPath(clip); - drawShaft(); - } else { - // Once the reveal reaches the head base, pop the whole head in with a fade - // instead of slicing the triangle into a growing stub. - drawShaft(); - const qreal headFadeIn = (drawProgress - headBaseFraction) / (1.0 - headBaseFraction); - painter->setOpacity(headFadeIn); - painter->setPen(Qt::NoPen); - painter->drawPath(headPath); - painter->setPen(outlinePen); - painter->setBrush(Qt::NoBrush); - painter->drawPath(headPath); - painter->setOpacity(1.0); - painter->setBrush(paintColor); - } - - if (glowAlpha > 0.0 && !centerLine.isEmpty()) { - // Sweep a bright band across the arrow. Clipping to the - // silhouette keeps it flat against the shaft so it reads as a light reflection. - const qreal anticipation = qMin(1.0, drawProgress / 0.08); - const QPointF sweep = centerLine.pointAtPercent(qMin(drawProgress, 1.0)); - QLinearGradient sheen(sweep.x() - kSheenHalfWidth, 0.0, sweep.x() + kSheenHalfWidth, 0.0); - sheen.setColorAt(0.0, QColor(paintColor.red(), paintColor.green(), paintColor.blue(), 0)); - sheen.setColorAt(0.5, QColor(255, 255, 255, 200)); - sheen.setColorAt(1.0, QColor(paintColor.red(), paintColor.green(), paintColor.blue(), 0)); - painter->save(); - painter->setPen(Qt::NoPen); - painter->setClipPath(path); - painter->setBrush(sheen); - painter->setOpacity(glowAlpha * anticipation); - painter->drawRect(QRectF(sweep.x() - kSheenHalfWidth - glowExtent, path.boundingRect().top() - glowExtent, - (kSheenHalfWidth + glowExtent) * 2.0, - path.boundingRect().height() + glowExtent * 2.0)); - painter->restore(); - } - painter->restore(); + painter->drawPath(path); } void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event) diff --git a/cockatrice/src/game_graphics/board/arrow_item.h b/cockatrice/src/game_graphics/board/arrow_item.h index 21f991b77..1c306e065 100644 --- a/cockatrice/src/game_graphics/board/arrow_item.h +++ b/cockatrice/src/game_graphics/board/arrow_item.h @@ -2,12 +2,9 @@ #define ARROWITEM_H #include "../../game/board/arrow_data.h" -#include "../animated_item.h" #include "arrow_target.h" -#include #include -#include #include #include @@ -15,7 +12,7 @@ class CardItem; class QGraphicsSceneMouseEvent; class PlayerLogic; -class ArrowItem : public QObject, public QGraphicsItem, public IAnimatedItem +class ArrowItem : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) @@ -24,19 +21,6 @@ signals: private: QPainterPath path; - QPainterPath bodyPath; - QPainterPath headPath; - QPainterPath shaftOutlinePath; - QPainterPath centerLine; - qreal headBaseFraction = 1.0; - QElapsedTimer animationClock; - qreal strokeDurationMs = 0; - qreal glowFadeDurationMs = 0; - qreal drawProgress = 1.0; - qreal glowAlpha = 0.0; - bool animationStarted = false; - - static constexpr qreal glowExtent = 12.0; protected: QSharedPointer data; @@ -49,19 +33,16 @@ protected: public: ArrowItem(QSharedPointer _data, ArrowTarget *_startItem, ArrowTarget *_targetItem); - ~ArrowItem() override; void onTargetDestroyed(); void delArrow(); void updatePath(); void updatePath(const QPointF &endPoint); - void startDrawAnimation(); - bool animationEvent() override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; [[nodiscard]] QRectF boundingRect() const override { - return path.boundingRect().adjusted(-glowExtent, -glowExtent, glowExtent, glowExtent); + return path.boundingRect(); } [[nodiscard]] QPainterPath shape() const override { @@ -125,4 +106,4 @@ protected: void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; }; -#endif +#endif \ No newline at end of file diff --git a/cockatrice/src/game_graphics/game_scene.cpp b/cockatrice/src/game_graphics/game_scene.cpp index 4d3144ad4..25c5fbcf0 100644 --- a/cockatrice/src/game_graphics/game_scene.cpp +++ b/cockatrice/src/game_graphics/game_scene.cpp @@ -502,7 +502,6 @@ void GameScene::addArrow(QSharedPointer data) auto *arrow = new ArrowItem(data, startCard, targetItem); addItem(arrow); - arrow->startDrawAnimation(); arrowRegistry.insert(data, arrow); connect(arrow, &ArrowItem::requestDeletion, this, &GameScene::requestArrowDeletion); } diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp index a20d31652..3fa56dd48 100644 --- a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp @@ -116,10 +116,6 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() connect(&tapAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::setTapAnimation); - arrowDrawAnimationCheckBox.setChecked(SettingsCache::instance().cardsDisplay().getArrowDrawAnimation()); - connect(&arrowDrawAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().cardsDisplay(), - &CardsDisplaySettings::setArrowDrawAnimation); - lifeCounterAnimationsCheckBox.setChecked( SettingsCache::instance().userInterface().getLifeCounterAnimationsEnabled()); connect(&lifeCounterAnimationsCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().userInterface(), @@ -136,9 +132,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() animationGrid->addWidget(&enableAllAnimationsButton, 0, 0); animationGrid->addWidget(&disableAllAnimationsButton, 0, 1); animationGrid->addWidget(&tapAnimationCheckBox, 1, 0); - animationGrid->addWidget(&arrowDrawAnimationCheckBox, 2, 0); - animationGrid->addWidget(&lifeCounterAnimationsCheckBox, 3, 0); - animationGrid->addWidget(&battlefieldFlashCheckBox, 4, 0); + animationGrid->addWidget(&lifeCounterAnimationsCheckBox, 2, 0); + animationGrid->addWidget(&battlefieldFlashCheckBox, 3, 0); animationGroupBox = new QGroupBox; animationGroupBox->setLayout(animationGrid); @@ -292,7 +287,6 @@ void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i) void UserInterfaceSettingsPage::enableAllAnimations() { tapAnimationCheckBox.setChecked(true); - arrowDrawAnimationCheckBox.setChecked(true); lifeCounterAnimationsCheckBox.setChecked(true); battlefieldFlashCheckBox.setChecked(true); } @@ -300,7 +294,6 @@ void UserInterfaceSettingsPage::enableAllAnimations() void UserInterfaceSettingsPage::disableAllAnimations() { tapAnimationCheckBox.setChecked(false); - arrowDrawAnimationCheckBox.setChecked(false); lifeCounterAnimationsCheckBox.setChecked(false); battlefieldFlashCheckBox.setChecked(false); } @@ -350,7 +343,6 @@ void UserInterfaceSettingsPage::retranslateUi() enableAllAnimationsButton.setText(tr("&Enable all animations")); disableAllAnimationsButton.setText(tr("&Disable all animations")); tapAnimationCheckBox.setText(tr("&Tap/untap animation")); - arrowDrawAnimationCheckBox.setText(tr("&Arrow draw animation")); lifeCounterAnimationsCheckBox.setText(tr("Life counter flash")); battlefieldFlashCheckBox.setText(tr("Battlefield flash on damage")); deckEditorGroupBox->setTitle(tr("Deck editor/storage settings")); diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h index 2b9eba72c..f18ab8ccf 100644 --- a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h @@ -40,7 +40,6 @@ private: QPushButton enableAllAnimationsButton; QPushButton disableAllAnimationsButton; QCheckBox tapAnimationCheckBox; - QCheckBox arrowDrawAnimationCheckBox; QCheckBox lifeCounterAnimationsCheckBox; QCheckBox battlefieldFlashCheckBox; QCheckBox openDeckInNewTabCheckBox; 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 c15f614d8..0bc927eeb 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 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,9 @@ void TabDeckEditorVisual::createCentralFrame() centralFrame = new QVBoxLayout; centralWidget->setLayout(centralFrame); + auto databaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), true, this); + databaseModel->setObjectName("databaseModel"); + tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckStateManager->getModel(), databaseModel); connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChanged, this, 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 5ccfcc28f..2ee560859 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 @@ -25,8 +25,7 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, layout = new QVBoxLayout(this); setLayout(layout); - visualDeckView = new VisualDeckEditorWidget(this, deckModel, _deckEditor->deckDockWidget->getSelectionModel(), - _cardDatabaseModel); + 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_database_display/visual_database_display_filter_toolbar_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp index 4a558a5e0..62e1bf5ba 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp @@ -82,14 +82,17 @@ VisualDatabaseDisplayFilterToolbarWidget::VisualDatabaseDisplayFilterToolbarWidg void VisualDatabaseDisplayFilterToolbarWidget::initialize() { - auto sortLayout = new QHBoxLayout(sortGroupBox); + // create groupbox layouts + auto sortLayout = new QHBoxLayout(this); sortLayout->setContentsMargins(0, 0, 0, 0); sortLayout->setSpacing(0); + sortGroupBox->setLayout(sortLayout); sortLayout->setAlignment(Qt::AlignLeft); - auto filterLayout = new QHBoxLayout(filterGroupBox); + auto filterLayout = new QHBoxLayout(this); filterLayout->setContentsMargins(0, 0, 0, 0); filterLayout->setSpacing(2); + filterGroupBox->setLayout(filterLayout); filterLayout->setAlignment(Qt::AlignLeft); // create settings widgets diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp index 0cdf60d5d..dc98e6940 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -141,6 +140,9 @@ void VisualDatabaseDisplayWidget::initialize() { databaseLoadIndicator->setVisible(false); + filterContainer->initialize(); + filterContainer->setVisible(true); + searchContainer->addWidget(colorFilterWidget); searchContainer->addWidget(clearFilterWidget); searchContainer->addWidget(searchEdit); @@ -156,43 +158,17 @@ void VisualDatabaseDisplayWidget::initialize() mainLayout->addWidget(cardSizeWidget); + databaseDisplayModel->setFilterTree(filterModel->filterTree()); + connect(filterModel, &FilterTreeModel::layoutChanged, this, &VisualDatabaseDisplayWidget::onSearchModelChanged); - initializeFilters(); -} + loadCardsTimer = new QTimer(this); + loadCardsTimer->setSingleShot(true); // Ensure it only fires once after the timeout -void VisualDatabaseDisplayWidget::initializeFilters() -{ - if (filtersInitialized || !isVisible() || CardDatabaseManager::getInstance()->getLoadStatus() != LoadStatus::Ok) { - return; - } + connect(loadCardsTimer, &QTimer::timeout, this, [this]() { loadCurrentPage(); }); + loadCardsTimer->start(5000); - filtersInitialized = true; - - // The filter toolbar builds its widgets by iterating the entire card database - // (per-set, per-main-type, per-sub-type and per-format buttons). Building it - // inside showEvent would block the tab switch, so keep it hidden and defer the - // build to the next event loop turn, letting the tab paint first. The toolbar - // then appears one event loop turn later, shifting the grid down by the toolbar - // height -- the intended tradeoff of an responsive tab switch. - filterContainer->setVisible(false); - - QTimer::singleShot(0, this, [this] { - filterContainer->initialize(); - filterContainer->setVisible(true); - - databaseDisplayModel->setFilterTree(filterModel->filterTree()); - - QTimer::singleShot(5000, this, [this] { loadCurrentPage(); }); - - retranslateUi(); - }); -} - -void VisualDatabaseDisplayWidget::showEvent(QShowEvent *event) -{ - QWidget::showEvent(event); - initializeFilters(); + retranslateUi(); } void VisualDatabaseDisplayWidget::retranslateUi() @@ -316,17 +292,9 @@ void VisualDatabaseDisplayWidget::loadCurrentPage() { // Ensure only the initial page is loaded if (currentPage == 0) { - if (!initialLoadScheduled) { - initialLoadScheduled = true; - qCDebug(VisualDatabaseDisplayLog) << "Loading the first page"; - // Defer the first page so the tab switch stays responsive. The card - // grid builds one event loop turn later. This also applies to - // search-driven reloads, which reset currentPage back to 0. - QTimer::singleShot(0, this, [this] { - initialLoadScheduled = false; - populateCards(); - }); - } + // Only load the first page initially + qCDebug(VisualDatabaseDisplayLog) << "Loading the first page"; + populateCards(); } else if (nearEndOfPage()) { // If not the first page, just load the next page and append to the flow widget loadNextPage(); diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.h b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.h index 6e4d87876..a383e8ead 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.h +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.h @@ -115,20 +115,17 @@ private: OverlapControlWidget *overlapControlWidget; CardSizeWidget *cardSizeWidget; QTimer *debounceTimer; + QTimer *loadCardsTimer; int debounceTime = 300; // in Ms int currentPage = 0; // Current page index int cardsPerPage = 100; // Number of cards per page - bool filtersInitialized = false; - bool initialLoadScheduled = false; - void initializeFilters(); void highlightAllSearchEdit(); bool nearEndOfPage() const; protected: void resizeEvent(QResizeEvent *event) override; - void showEvent(QShowEvent *event) override; }; #endif // VISUAL_DATABASE_DISPLAY_WIDGET_H 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 e3261b346..6a4eaa382 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 @@ -29,10 +29,8 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel, - QItemSelectionModel *_selectionModel, - CardDatabaseModel *_cardDatabaseModel) - : QWidget(parent), deckListModel(_deckListModel), selectionModel(_selectionModel), - cardDatabaseModel(_cardDatabaseModel) + 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); @@ -96,9 +94,7 @@ void VisualDeckEditorWidget::initializeSearchBarAndCompleter() setFocusProxy(searchBar); setFocusPolicy(Qt::ClickFocus); - if (!cardDatabaseModel) { - cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this); - } + cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this); cardDatabaseDisplayModel = new CardDatabaseDisplayModel(this); cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel); 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 ac0d07efd..da02b5c1f 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 @@ -38,10 +38,7 @@ class VisualDeckEditorWidget : public QWidget Q_OBJECT public: - explicit VisualDeckEditorWidget(QWidget *parent, - DeckListModel *deckListModel, - QItemSelectionModel *selectionModel, - CardDatabaseModel *_cardDatabaseModel = nullptr); + explicit VisualDeckEditorWidget(QWidget *parent, DeckListModel *deckListModel, QItemSelectionModel *selectionModel); void retranslateUi(); void updateCompactMode(); void clearAllDisplayWidgets(); diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp index 5b9c5a4b5..174943333 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp +++ b/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp @@ -12,17 +12,6 @@ CardDatabaseQuerier::CardDatabaseQuerier(QObject *_parent, const ICardPreferenceProvider *prefs) : QObject(_parent), db(_db), prefs(prefs) { - // Invalidate the cached count maps whenever the database contents change. - connect(db, &CardDatabase::cardAdded, this, &CardDatabaseQuerier::invalidateCaches); - connect(db, &CardDatabase::cardRemoved, this, &CardDatabaseQuerier::invalidateCaches); - connect(db, &CardDatabase::cardDatabaseReset, this, &CardDatabaseQuerier::invalidateCaches); -} - -void CardDatabaseQuerier::invalidateCaches() -{ - mainCardTypeCountsCache.clear(); - subCardTypeCountsCache.clear(); - formatsCountCache.clear(); } /** @@ -315,43 +304,44 @@ QString CardDatabaseQuerier::getPreferredPrintingProviderId(const QString &cardN QStringList CardDatabaseQuerier::getAllMainCardTypes() const { - return getAllMainCardTypesWithCount().keys(); + QSet types; + for (const auto &card : db->cards.values()) { + types.insert(card->getMainCardType()); + } + return types.values(); } QMap CardDatabaseQuerier::getAllMainCardTypesWithCount() const { - // An empty cache is always recomputed correctly: a database with no cards - // produces an empty map, so the cache is only ever empty when it needs a - // (trivially cheap) rebuild. - if (mainCardTypeCountsCache.isEmpty()) { - for (const auto &card : db->cards.values()) { - QString type = card->getMainCardType(); - mainCardTypeCountsCache[type]++; - } + QMap typeCounts; + + for (const auto &card : db->cards.values()) { + QString type = card->getMainCardType(); + typeCounts[type]++; } - return mainCardTypeCountsCache; + return typeCounts; } QMap CardDatabaseQuerier::getAllSubCardTypesWithCount() const { - if (subCardTypeCountsCache.isEmpty()) { - for (const auto &card : db->cards.values()) { - QString type = card->getCardType(); + QMap typeCounts; - QStringList parts = type.split(" — "); + for (const auto &card : db->cards.values()) { + QString type = card->getCardType(); - if (parts.size() > 1) { // Ensure there are subtypes - QStringList subtypes = parts[1].split(" ", Qt::SkipEmptyParts); + QStringList parts = type.split(" — "); - for (const QString &subtype : subtypes) { - subCardTypeCountsCache[subtype]++; - } + if (parts.size() > 1) { // Ensure there are subtypes + QStringList subtypes = parts[1].split(" ", Qt::SkipEmptyParts); + + for (const QString &subtype : subtypes) { + typeCounts[subtype]++; } } } - return subCardTypeCountsCache; + return typeCounts; } FormatRulesPtr CardDatabaseQuerier::getFormat(const QString &formatName) const @@ -361,18 +351,18 @@ FormatRulesPtr CardDatabaseQuerier::getFormat(const QString &formatName) const QMap CardDatabaseQuerier::getAllFormatsWithCount() const { - if (formatsCountCache.isEmpty()) { - for (const auto &card : db->cards.values()) { - QStringList allProps = card->getProperties(); + QMap formatCounts; - for (const QString &prop : allProps) { - if (prop.startsWith("format-")) { - QString formatName = prop.mid(QStringLiteral("format-").size()); - formatsCountCache[formatName]++; - } + for (const auto &card : db->cards.values()) { + QStringList allProps = card->getProperties(); + + for (const QString &prop : allProps) { + if (prop.startsWith("format-")) { + QString formatName = prop.mid(QStringLiteral("format-").size()); + formatCounts[formatName]++; } } } - return formatsCountCache; + return formatCounts; } diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_querier.h b/libcockatrice_card/libcockatrice/card/database/card_database_querier.h index f195a8170..ff8d7958b 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database_querier.h +++ b/libcockatrice_card/libcockatrice/card/database/card_database_querier.h @@ -220,16 +220,6 @@ public: private: const CardDatabase *db; //!< Card database used for all lookups. const ICardPreferenceProvider *prefs; //!< Preference provider for preferred printings. - - // Count maps are expensive to compute (they iterate the whole database) and are - // queried every time a filter widget is built, so cache them and invalidate on - // any database mutation. Only the main thread reads or writes these. - mutable QMap mainCardTypeCountsCache; - mutable QMap subCardTypeCountsCache; - mutable QMap formatsCountCache; - -private slots: - void invalidateCaches(); }; #endif // COCKATRICE_CARD_DATABASE_QUERIER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h index 3f2cbbe8e..3ee3d2aef 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h @@ -15,7 +15,6 @@ public: [[nodiscard]] virtual bool getIncludeRebalancedCards() const = 0; [[nodiscard]] virtual bool getPrintingSelectorNavigationButtonsVisible() const = 0; [[nodiscard]] virtual bool getTapAnimation() const = 0; - [[nodiscard]] virtual bool getArrowDrawAnimation() const = 0; [[nodiscard]] virtual bool getAutoRotateSidewaysLayoutCards() const = 0; [[nodiscard]] virtual bool getScaleCards() const = 0; [[nodiscard]] virtual int getStackCardOverlapPercent() const = 0; diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp index ecd26a9f8..7b248a982 100644 --- a/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp +++ b/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp @@ -111,50 +111,19 @@ bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(const CardInfoPtr &card void CardDatabaseModel::cardDatabaseEnabledSetsChanged() { - // Build the new card list in a single pass. - QList newCardList; - newCardList.reserve(cardList.size()); + // remove all the cards no more present in at least one enabled set for (const CardInfoPtr &card : cardList) { - if (checkCardHasAtLeastOneEnabledSet(card)) { - newCardList.append(card); + if (!checkCardHasAtLeastOneEnabledSet(card)) { + cardRemoved(card); } } + + // re-check all the card currently not shown, maybe their part of a newly-enabled set for (const CardInfoPtr &card : db->getCardList()) { - if (!cardListSet.contains(card) && checkCardHasAtLeastOneEnabledSet(card)) { - newCardList.append(card); + if (!cardListSet.contains(card)) { + cardAdded(card); } } - - if (newCardList == cardList) { - return; - } - - // Rebuild the whole list inside a single model reset instead of emitting - // per-card insert/remove notifications. With tens of thousands of cards the - // per-card path is the dominant cost of constructing a CardDatabaseModel. - QSet oldCardListSet = cardListSet; - QSet newCardListSet(newCardList.begin(), newCardList.end()); - - beginResetModel(); - - // Disconnect cards that are no longer shown. - for (const CardInfoPtr &card : cardList) { - if (!newCardListSet.contains(card)) { - disconnect(card.data(), nullptr, this, nullptr); - } - } - - cardList = newCardList; - cardListSet = newCardListSet; - - // Connect cards that are now shown for the first time. - for (const CardInfoPtr &card : cardList) { - if (!oldCardListSet.contains(card)) { - connect(card.data(), &CardInfo::cardInfoChanged, this, &CardDatabaseModel::cardInfoChanged); - } - } - - endResetModel(); } void CardDatabaseModel::cardAdded(const CardInfoPtr &card) diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp index ba6ac4691..5b893799f 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp @@ -133,22 +133,6 @@ void Server_ProtocolHandler::sendProtocolItem(const RoomEvent &item) Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { - const auto isPreAuthSessionCommand = [](SessionCommand::SessionCommandType type) { - switch (type) { - case SessionCommand::PING: - case SessionCommand::LOGIN: - case SessionCommand::REGISTER: - case SessionCommand::ACTIVATE: - case SessionCommand::FORGOT_PASSWORD_REQUEST: - case SessionCommand::FORGOT_PASSWORD_RESET: - case SessionCommand::FORGOT_PASSWORD_CHALLENGE: - case SessionCommand::REQUEST_PASSWORD_SALT: - return true; - default: - return false; - } - }; - Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.session_command_size() - 1; i >= 0; --i) { Response::ResponseCode resp = Response::RespInvalidCommand; @@ -157,38 +141,33 @@ Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(co if (num != SessionCommand::PING) { // don't log ping commands logDebugMessage(getSafeDebugString(sc)); } - const auto commandType = static_cast(num); - if (authState == NotLoggedIn && !isPreAuthSessionCommand(commandType)) { - resp = Response::RespLoginNeeded; - } else { - switch (commandType) { - case SessionCommand::PING: - resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc); - break; - case SessionCommand::LOGIN: - resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc); - break; - case SessionCommand::MESSAGE: - resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc); - break; - case SessionCommand::GET_GAMES_OF_USER: - resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); - break; - case SessionCommand::GET_USER_INFO: - resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); - break; - case SessionCommand::LIST_ROOMS: - resp = cmdListRooms(sc.GetExtension(Command_ListRooms::ext), rc); - break; - case SessionCommand::JOIN_ROOM: - resp = cmdJoinRoom(sc.GetExtension(Command_JoinRoom::ext), rc); - break; - case SessionCommand::LIST_USERS: - resp = cmdListUsers(sc.GetExtension(Command_ListUsers::ext), rc); - break; - default: - resp = processExtendedSessionCommand(num, sc, rc); - } + switch ((SessionCommand::SessionCommandType)num) { + case SessionCommand::PING: + resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc); + break; + case SessionCommand::LOGIN: + resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc); + break; + case SessionCommand::MESSAGE: + resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc); + break; + case SessionCommand::GET_GAMES_OF_USER: + resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); + break; + case SessionCommand::GET_USER_INFO: + resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); + break; + case SessionCommand::LIST_ROOMS: + resp = cmdListRooms(sc.GetExtension(Command_ListRooms::ext), rc); + break; + case SessionCommand::JOIN_ROOM: + resp = cmdJoinRoom(sc.GetExtension(Command_JoinRoom::ext), rc); + break; + case SessionCommand::LIST_USERS: + resp = cmdListUsers(sc.GetExtension(Command_ListUsers::ext), rc); + break; + default: + resp = processExtendedSessionCommand(num, sc, rc); } if (resp != Response::RespOk) { finalResponseCode = resp; diff --git a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp index f528a7c4b..6ec1af962 100644 --- a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp @@ -50,11 +50,6 @@ bool CardsDisplaySettings::getTapAnimation() const return getValue("tapAnimation", QString(), QString(), true).toBool(); } -bool CardsDisplaySettings::getArrowDrawAnimation() const -{ - return getValue("arrowDrawAnimation", QString(), QString(), true).toBool(); -} - bool CardsDisplaySettings::getAutoRotateSidewaysLayoutCards() const { return getValue("autoRotateSidewaysLayoutCards", QString(), QString(), true).toBool(); @@ -164,11 +159,6 @@ void CardsDisplaySettings::setTapAnimation(bool _tapAnimation) setValue(_tapAnimation, "tapAnimation"); } -void CardsDisplaySettings::setArrowDrawAnimation(bool _arrowDrawAnimation) -{ - setValue(_arrowDrawAnimation, "arrowDrawAnimation"); -} - void CardsDisplaySettings::setAutoRotateSidewaysLayoutCards(bool _autoRotateSidewaysLayoutCards) { setValue(_autoRotateSidewaysLayoutCards, "autoRotateSidewaysLayoutCards"); diff --git a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h index dbafa32ae..15a3e3ff4 100644 --- a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h @@ -20,7 +20,6 @@ public: [[nodiscard]] bool getIncludeRebalancedCards() const override; [[nodiscard]] bool getPrintingSelectorNavigationButtonsVisible() const override; [[nodiscard]] bool getTapAnimation() const override; - [[nodiscard]] bool getArrowDrawAnimation() const override; [[nodiscard]] bool getAutoRotateSidewaysLayoutCards() const override; [[nodiscard]] bool getScaleCards() const override; [[nodiscard]] int getStackCardOverlapPercent() const override; @@ -41,7 +40,6 @@ public: void setIncludeRebalancedCards(bool _includeRebalancedCards); void setPrintingSelectorNavigationButtonsVisible(bool _navigationButtonsVisible); void setTapAnimation(bool _tapAnimation); - void setArrowDrawAnimation(bool _arrowDrawAnimation); void setAutoRotateSidewaysLayoutCards(bool _autoRotateSidewaysLayoutCards); void setCardScaling(bool _scaleCards); void setStackCardOverlapPercent(int _verticalCardOverlapPercent); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index d4a1b9217..842ddb4c8 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -896,10 +896,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayGetCode(const Com Response::ResponseCode AbstractServerSocketInterface::cmdReplaySubmitCode(const Command_ReplaySubmitCode &cmd, ResponseContainer & /*rc*/) { - if (authState != PasswordRight) { - return Response::RespFunctionNotAllowed; - } - // code is of the form - QString code = QString::fromStdString(cmd.replay_code()); QStringList split = code.split("-"); diff --git a/tests/settings/settings_defaults_test.cpp b/tests/settings/settings_defaults_test.cpp index 041a60d6f..dfdad4780 100644 --- a/tests/settings/settings_defaults_test.cpp +++ b/tests/settings/settings_defaults_test.cpp @@ -476,12 +476,6 @@ TEST_F(SettingsDefaultsTest, CardsDisplay_SampleHandSize_Default) ASSERT_EQ(s.getSampleHandSize(), 7); } -TEST_F(SettingsDefaultsTest, CardsDisplay_ArrowDrawAnimation_Default) -{ - CardsDisplaySettings s(settingsPath, nullptr); - ASSERT_EQ(s.getArrowDrawAnimation(), true); -} - // --- VisualDeckStorageSettings --- TEST_F(SettingsDefaultsTest, VisualDeckStorage_SortingOrder_Default)