mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
[VisualDatabase] Draw the card grid with a model/view (#7701)
* [VisualDatabase] Draw the card grid with a model/view instead of a widget per card The visual card database built one CardInfoPictureWithTextOverlayWidget per card, 100 per page, and tore them down on every search, resize and scroll page. It also looked each card up again by name from the model row, kept a shadow QList<ExactCard>, and carried paging state plus a 5 s timer that only existed to defer the first page. Replace that with a real view on a flattened model: - CardDatabaseDisplayModel gains CardInfoRole and cardInfoAt(), so a view can get the CardInfoPtr behind a proxy row without a name round trip. - New CardDatabaseVisualDisplayModel (libcockatrice_models) flattens the filtered and sorted proxy into one entry per displayed card picture: the preferred printing normally, every printing of the selected sets while set filters are active. Proxy changes rebuild it on a 30 ms coalescing timer. - New CardDatabaseVisualView (QListView IconMode) paints only visible rows through the new CardPictureDelegate, keeps the old interaction contract (left/right/middle press -> cardClicked, hover -> cardHovered, Ctrl+wheel resize, no selection, no focus stealing, scroll to top on a new result) and repaints when CardPictureLoader reports an async image. - CardSizeWidget::enableCtrlScrollResize() also watches scroll-area viewports, which is what makes Ctrl+wheel work over a QListView. - VisualDatabaseDisplayWidget drops the paging, the shadow list, the dead members and the 5 s timer. Behaviour-preserving: no gameplay change, no new user-facing strings. * [VisualDatabase] Fix the card grid wheel step QListView hands the delegate's item height to the scroll bar as its single step under ScrollPerPixel, so QAbstractSlider moved wheelScrollLines() whole card rows per notch. Handle the wheel in the view instead: one line is an eighth of the grid cell, so a notch moves about a third of a card row at any card size and still follows the OS line count. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
94e6281d87
commit
a1a8a59f4a
24 changed files with 1019 additions and 277 deletions
|
|
@ -0,0 +1,203 @@
|
|||
#include "card_database_visual_view.h"
|
||||
|
||||
#include "../../card_picture_loader/card_picture_loader.h"
|
||||
#include "../cards/card_info_picture_widget.h"
|
||||
#include "../cards/card_picture_delegate.h"
|
||||
#include "../cards/card_size_widget.h"
|
||||
#include "card_database_visual_display_model.h"
|
||||
#include "libcockatrice/card/printing/exact_card.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QAbstractItemView>
|
||||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
#include <QEvent>
|
||||
#include <QFrame>
|
||||
#include <QModelIndex>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QSize>
|
||||
#include <QWheelEvent>
|
||||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <qnamespace.h>
|
||||
|
||||
CardDatabaseVisualView::CardDatabaseVisualView(CardDatabaseVisualDisplayModel *_model, QWidget *parent)
|
||||
: QListView(parent), model(_model)
|
||||
{
|
||||
auto *cardDelegate = new CardPictureDelegate(model, this);
|
||||
setItemDelegate(cardDelegate);
|
||||
setModel(model);
|
||||
|
||||
setViewMode(QListView::IconMode);
|
||||
setFlow(QListView::LeftToRight);
|
||||
setWrapping(true);
|
||||
setResizeMode(QListView::Adjust);
|
||||
setMovement(QListView::Static);
|
||||
setUniformItemSizes(true);
|
||||
setLayoutMode(QListView::SinglePass);
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setFrameShape(QFrame::NoFrame);
|
||||
setWordWrap(false);
|
||||
|
||||
// QListView hands the delegate's item height to the scroll bar as its single
|
||||
// step under ScrollPerPixel, so one wheel notch would jump wheelScrollLines()
|
||||
// whole card rows. Give the bar a text line instead; the wheel handler below
|
||||
// scales that line to the grid. A positive step also stops Qt from taking the
|
||||
// item height back on the next layout.
|
||||
verticalScrollBar()->setSingleStep(qMax(1, fontMetrics().height()));
|
||||
|
||||
// The grid is a read-only surface: clicks are actions on a card, not a
|
||||
// selection, and they must not steal focus from the search field.
|
||||
setSelectionMode(QAbstractItemView::NoSelection);
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
// Hover drives the card preview.
|
||||
setMouseTracking(true);
|
||||
viewport()->setMouseTracking(true);
|
||||
|
||||
connect(&CardPictureLoader::getInstance(), &CardPictureLoader::pixmapLoaded, this,
|
||||
[this] { viewport()->update(); });
|
||||
|
||||
// A new search result starts at the top, like the widget-per-card grid did.
|
||||
connect(model, &QAbstractItemModel::modelReset, this, [this] {
|
||||
verticalScrollBar()->setValue(0);
|
||||
// The rows under a stationary cursor changed with the reset: re-resolve the
|
||||
// hover so the preview follows the grid, as enterEvent did per card widget.
|
||||
hoveredRow = -1;
|
||||
updateHoveredRow(viewport()->mapFromGlobal(QCursor::pos()));
|
||||
});
|
||||
|
||||
setCardSizeFactor(cardSizeFactor);
|
||||
}
|
||||
|
||||
void CardDatabaseVisualView::setCardSizeFactor(int factor)
|
||||
{
|
||||
cardSizeFactor = qBound(CardSizeWidget::MIN_CARD_SIZE, factor, CardSizeWidget::MAX_CARD_SIZE);
|
||||
|
||||
const QSize cardSize = CardInfoPictureWidget::sizeForScaleFactor(cardSizeFactor);
|
||||
if (auto *cardDelegate = qobject_cast<CardPictureDelegate *>(itemDelegate())) {
|
||||
cardDelegate->setCardSize(cardSize);
|
||||
}
|
||||
// The delegate's sizeHint does not emit sizeHintChanged, so the grid size is what
|
||||
// makes a resize take effect. Keep the two in step.
|
||||
setGridSize(cardSize + QSize(CardPictureDelegate::SPACING, CardPictureDelegate::SPACING));
|
||||
}
|
||||
|
||||
void CardDatabaseVisualView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
const QModelIndex index = indexAt(event->pos());
|
||||
if (index.isValid() && model != nullptr) {
|
||||
const ExactCard card = model->cardAt(index.row());
|
||||
if (card) {
|
||||
emit cardClicked(event, card);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QListView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void CardDatabaseVisualView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
QListView::mouseMoveEvent(event);
|
||||
updateHoveredRow(event->pos());
|
||||
}
|
||||
|
||||
bool CardDatabaseVisualView::viewportEvent(QEvent *event)
|
||||
{
|
||||
// A per-card widget reported its hover from enterEvent, which also fires when the
|
||||
// grid changes under a stationary cursor. A QListView only reports moves, so
|
||||
// re-resolve the hover on enter and clear it on leave.
|
||||
if (event->type() == QEvent::Enter) {
|
||||
updateHoveredRow(viewport()->mapFromGlobal(QCursor::pos()));
|
||||
} else if (event->type() == QEvent::Leave) {
|
||||
hoveredRow = -1;
|
||||
}
|
||||
|
||||
return QListView::viewportEvent(event);
|
||||
}
|
||||
|
||||
void CardDatabaseVisualView::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
// Ctrl+wheel is the card-size gesture and is filtered out before it reaches
|
||||
// the view; any other modifier keeps QAbstractSlider's own page scrolling.
|
||||
if (event->modifiers() != Qt::NoModifier) {
|
||||
QListView::wheelEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
QScrollBar *bar = verticalScrollBar();
|
||||
if (!bar->isVisible()) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
// Touchpads and high-resolution wheels already report pixels: follow them.
|
||||
if (!event->pixelDelta().isNull()) {
|
||||
bar->setValue(bar->value() - event->pixelDelta().y());
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->angleDelta().y() == 0) {
|
||||
QListView::wheelEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// Accumulate wheel deltas smaller than a notch, like QAbstractSlider does, so
|
||||
// a fine-grained wheel still moves.
|
||||
wheelRemainder += static_cast<qreal>(event->angleDelta().y()) / 120.0;
|
||||
const int notches = static_cast<int>(wheelRemainder);
|
||||
if (notches == 0) {
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
wheelRemainder -= notches;
|
||||
|
||||
const int pixels = -notches * wheelStep();
|
||||
bar->setValue(qBound(bar->minimum(), bar->value() + pixels, bar->maximum()));
|
||||
event->accept();
|
||||
}
|
||||
|
||||
int CardDatabaseVisualView::wheelStep() const
|
||||
{
|
||||
// A card row is far too coarse for one wheel line, and a text line is too
|
||||
// fine, so a line is an eighth of the grid cell. A notch then moves about a
|
||||
// third of a card row at any card size, and the OS line count still applies.
|
||||
const int lineHeight = qMax(1, gridSize().height() / 8);
|
||||
return qMax(1, QApplication::wheelScrollLines()) * lineHeight;
|
||||
}
|
||||
|
||||
void CardDatabaseVisualView::updateHoveredRow(const QPoint &position)
|
||||
{
|
||||
// indexAt() also answers for points outside the viewport, so a cursor parked
|
||||
// below the grid (on the size slider, say) would resolve to the row just under
|
||||
// the visible area. Only a point inside the viewport can hover a card.
|
||||
if (model == nullptr || !viewport()->rect().contains(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndex index = indexAt(position);
|
||||
const int row = index.isValid() ? index.row() : -1;
|
||||
if (row == hoveredRow) {
|
||||
return;
|
||||
}
|
||||
|
||||
hoveredRow = row;
|
||||
if (row >= 0) {
|
||||
const ExactCard card = model->cardAt(row);
|
||||
if (card) {
|
||||
emit cardHovered(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CardDatabaseVisualView::leaveEvent(QEvent *event)
|
||||
{
|
||||
hoveredRow = -1;
|
||||
QListView::leaveEvent(event);
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* @file card_database_visual_view.h
|
||||
* @ingroup VisualCardDatabaseWidgets
|
||||
* @brief Icon-mode view of the card database, one card picture per item.
|
||||
*/
|
||||
|
||||
#ifndef CARD_DATABASE_VISUAL_VIEW_H
|
||||
#define CARD_DATABASE_VISUAL_VIEW_H
|
||||
|
||||
#include <QListView>
|
||||
#include <QSize>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class CardDatabaseVisualDisplayModel;
|
||||
class ExactCard;
|
||||
class QEvent;
|
||||
class QMouseEvent;
|
||||
class QPoint;
|
||||
class QWheelEvent;
|
||||
|
||||
/**
|
||||
* @brief A wrapping grid of card pictures.
|
||||
*
|
||||
* The view virtualizes painting: it only asks the delegate for the items that are
|
||||
* on screen, so the whole card database can sit in the model without a widget (or
|
||||
* pixmap) per card. Clicks and hover are reported with the exact card under the
|
||||
* cursor, which is what the deck editor needs to add, decrement or preview a card.
|
||||
*/
|
||||
class CardDatabaseVisualView : public QListView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CardDatabaseVisualView(CardDatabaseVisualDisplayModel *_model, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Sets the card size as a percentage of the base card width.
|
||||
* @param factor Percentage in the range of the card size slider.
|
||||
*/
|
||||
void setCardSizeFactor(int factor);
|
||||
|
||||
signals:
|
||||
/** @brief A card was pressed. The event is valid for the duration of the emission. */
|
||||
void cardClicked(QMouseEvent *event, const ExactCard &card);
|
||||
/** @brief The cursor moved onto a different card. */
|
||||
void cardHovered(const ExactCard &card);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
bool viewportEvent(QEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
private:
|
||||
void updateHoveredRow(const QPoint &position);
|
||||
/// Pixels one wheel notch moves, scaled to the grid and the OS line count.
|
||||
[[nodiscard]] int wheelStep() const;
|
||||
|
||||
CardDatabaseVisualDisplayModel *model;
|
||||
/// Fraction of a line left over from a wheel event smaller than one notch.
|
||||
qreal wheelRemainder = 0.0;
|
||||
int cardSizeFactor = 100;
|
||||
int hoveredRow = -1;
|
||||
};
|
||||
|
||||
#endif // CARD_DATABASE_VISUAL_VIEW_H
|
||||
|
|
@ -5,40 +5,29 @@
|
|||
#include "../../../filters/syntax_help.h"
|
||||
#include "../../key_signals.h"
|
||||
#include "../../pixel_map_generator.h"
|
||||
#include "../cards/card_info_picture_widget.h"
|
||||
#include "../cards/card_info_picture_with_text_overlay_widget.h"
|
||||
#include "../cards/card_size_widget.h"
|
||||
#include "../deck_editor/card_database_view.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
#include "../utility/custom_line_edit.h"
|
||||
#include "card_database_display_model.h"
|
||||
#include "card_database_model.h"
|
||||
#include "card_database_visual_display_model.h"
|
||||
#include "card_database_visual_view.h"
|
||||
#include "deck_list_model.h"
|
||||
#include "libcockatrice/card/card_localization.h"
|
||||
#include "libcockatrice/card/database/card_database_loader.h"
|
||||
#include "libcockatrice/card/database/card_database_querier.h"
|
||||
#include "libcockatrice/card/printing/exact_card.h"
|
||||
#include "libcockatrice/card/printing/printing_info.h"
|
||||
#include "libcockatrice/filters/filter_card.h"
|
||||
#include "libcockatrice/filters/filter_tree.h"
|
||||
#include "visual_database_display_color_filter_widget.h"
|
||||
#include "visual_database_display_filter_toolbar_widget.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QList>
|
||||
#include <QMessageLogger>
|
||||
#include <QModelIndex>
|
||||
#include <QPair>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSharedPointer>
|
||||
#include <QSizePolicy>
|
||||
#include <QSlider>
|
||||
#include <QStringLiteral>
|
||||
|
|
@ -48,14 +37,11 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/card/card_info_comparator.h>
|
||||
#include <libcockatrice/card/database/card_database.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <utility>
|
||||
|
||||
class QMouseEvent;
|
||||
|
||||
|
|
@ -64,11 +50,6 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
|||
DeckListModel *deckListModel)
|
||||
: QWidget(parent)
|
||||
{
|
||||
debounceTimer = new QTimer(this);
|
||||
debounceTimer->setSingleShot(true); // Ensure it only fires once after the timeout
|
||||
|
||||
connect(debounceTimer, &QTimer::timeout, this, &VisualDatabaseDisplayWidget::onSearchModelChanged);
|
||||
|
||||
// Create display model
|
||||
databaseDisplayModel = new CardDatabaseDisplayModel(this);
|
||||
databaseDisplayModel->setObjectName("databaseDisplayModel");
|
||||
|
|
@ -86,23 +67,12 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
|||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardSearchLanguageChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
|
||||
cards = new QList<ExactCard>;
|
||||
connect(databaseDisplayModel, &CardDatabaseDisplayModel::modelDirty, this,
|
||||
&VisualDatabaseDisplayWidget::modelDirty);
|
||||
|
||||
// Set up main layout and widgets
|
||||
setMinimumSize(0, 0);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
mainLayout = QtUtils::makeLayout<QVBoxLayout>(this, 0, 1);
|
||||
setLayout(mainLayout);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
||||
cardSizeWidget = new CardSizeWidget(this, flowWidget,
|
||||
SettingsCache::instance().cardsDisplay().getVisualDatabaseDisplayCardSize());
|
||||
connect(cardSizeWidget, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance().cardsDisplay(),
|
||||
&CardsDisplaySettings::setVisualDatabaseDisplayCardSize);
|
||||
cardSizeWidget->enableCtrlScrollResize(flowWidget);
|
||||
|
||||
searchContainer = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
|
||||
|
||||
searchEdit = new SearchLineEdit();
|
||||
|
|
@ -159,6 +129,25 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
|||
connect(databaseView, &CardDatabaseView::relatedCardClicked, this,
|
||||
&VisualDatabaseDisplayWidget::onRelatedCardClicked);
|
||||
|
||||
// The visual grid is a real view on a flattened model: one row per displayed
|
||||
// card picture, painted on demand.
|
||||
visualDisplayModel = new CardDatabaseVisualDisplayModel(databaseDisplayModel, CardDatabaseManager::query(), this);
|
||||
|
||||
cardView = new CardDatabaseVisualView(visualDisplayModel, this);
|
||||
cardView->setObjectName("visualCardView");
|
||||
connect(cardView, &CardDatabaseVisualView::cardClicked, this,
|
||||
&VisualDatabaseDisplayWidget::cardClickedDatabaseDisplay);
|
||||
connect(cardView, &CardDatabaseVisualView::cardHovered, this,
|
||||
&VisualDatabaseDisplayWidget::cardHoveredDatabaseDisplay);
|
||||
|
||||
const int cardSize = SettingsCache::instance().cardsDisplay().getVisualDatabaseDisplayCardSize();
|
||||
cardSizeWidget = new CardSizeWidget(this, nullptr, cardSize);
|
||||
connect(cardSizeWidget, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance().cardsDisplay(),
|
||||
&CardsDisplaySettings::setVisualDatabaseDisplayCardSize);
|
||||
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, cardView, &CardDatabaseVisualView::setCardSizeFactor);
|
||||
cardView->setCardSizeFactor(cardSize);
|
||||
cardSizeWidget->enableCtrlScrollResize(cardView);
|
||||
|
||||
colorFilterWidget = new VisualDatabaseDisplayColorFilterWidget(this, filterModel);
|
||||
|
||||
filterContainer = new VisualDatabaseDisplayFilterToolbarWidget(this, deckListModel);
|
||||
|
|
@ -190,8 +179,6 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
|||
databaseLoadIndicator->setVisible(false);
|
||||
}
|
||||
|
||||
QScrollBar *scrollBar = flowWidget->scrollArea->verticalScrollBar();
|
||||
connect(scrollBar, &QScrollBar::valueChanged, [this](int /*value*/) { loadCurrentPage(); });
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +197,7 @@ void VisualDatabaseDisplayWidget::initialize()
|
|||
|
||||
mainLayout->addWidget(databaseView);
|
||||
|
||||
mainLayout->addWidget(flowWidget);
|
||||
mainLayout->addWidget(cardView);
|
||||
|
||||
mainLayout->addWidget(cardSizeWidget);
|
||||
|
||||
|
|
@ -241,8 +228,6 @@ void VisualDatabaseDisplayWidget::initializeFilters()
|
|||
|
||||
databaseDisplayModel->setFilterTree(filterModel->filterTree());
|
||||
|
||||
QTimer::singleShot(5000, this, [this] { loadCurrentPage(); });
|
||||
|
||||
retranslateUi();
|
||||
});
|
||||
}
|
||||
|
|
@ -278,71 +263,36 @@ void VisualDatabaseDisplayWidget::updateDisplayModeButton()
|
|||
displayModeButton->setText(displayModeButton->isChecked() ? tr("Table") : tr("Visual"));
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
loadCurrentPage();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::onDisplayModeChanged(bool checked)
|
||||
{
|
||||
updateDisplayModeButton();
|
||||
if (checked) {
|
||||
// Table mode
|
||||
flowWidget->setVisible(false);
|
||||
cardView->setVisible(false);
|
||||
cardSizeWidget->setVisible(false);
|
||||
databaseView->setItemDelegate(new QStyledItemDelegate(databaseView));
|
||||
databaseView->setVisible(true);
|
||||
} else {
|
||||
// Visual mode
|
||||
flowWidget->setVisible(true);
|
||||
cardView->setVisible(true);
|
||||
cardSizeWidget->setVisible(true);
|
||||
databaseView->setVisible(false);
|
||||
populateCards();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::onClick(QMouseEvent *event, const ExactCard &card)
|
||||
{
|
||||
emit cardClickedDatabaseDisplay(event, card);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::onHover(const ExactCard &hoveredCard)
|
||||
{
|
||||
emit cardHoveredDatabaseDisplay(hoveredCard);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::addCardToDisplay(const ExactCard &cardToAdd)
|
||||
{
|
||||
cards->append(cardToAdd);
|
||||
auto *display = new CardInfoPictureWithTextOverlayWidget(flowWidget, false);
|
||||
display->setScaleFactor(cardSizeWidget->getSlider()->value());
|
||||
display->setCard(cardToAdd);
|
||||
flowWidget->addWidget(display);
|
||||
connect(display, &CardInfoPictureWithTextOverlayWidget::cardClicked, this, &VisualDatabaseDisplayWidget::onClick);
|
||||
connect(display, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this, &VisualDatabaseDisplayWidget::onHover);
|
||||
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, display, &CardInfoPictureWidget::setScaleFactor);
|
||||
}
|
||||
|
||||
bool VisualDatabaseDisplayWidget::isVisualDisplayMode() const
|
||||
{
|
||||
return !displayModeButton->isChecked();
|
||||
}
|
||||
|
||||
QStringList VisualDatabaseDisplayWidget::collectSetFilterTerms() const
|
||||
{
|
||||
return filterModel->getFilterTermsOfType(CardFilter::AttrSet);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::onSearchModelChanged()
|
||||
{
|
||||
if (flowWidget->isVisible()) {
|
||||
// Clear the current page and prepare for new data
|
||||
flowWidget->clearLayout(); // Clear existing cards
|
||||
cards->clear(); // Clear the card list
|
||||
// Reset scrollbar position to the top after loading new cards
|
||||
QScrollBar *scrollBar = flowWidget->scrollArea->verticalScrollBar();
|
||||
scrollBar->setValue(0); // Reset scrollbar to top
|
||||
|
||||
currentPage = 0;
|
||||
loadCurrentPage();
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Search model changed";
|
||||
}
|
||||
visualDisplayModel->setSetFilterTerms(collectSetFilterTerms());
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::onSelectedCardChanged(const QString &cardName)
|
||||
|
|
@ -368,108 +318,3 @@ void VisualDatabaseDisplayWidget::onRelatedCardClicked(const QString &relatedCar
|
|||
ExactCard exactCard = CardDatabaseManager::query()->guessCard({relatedCard});
|
||||
emit cardInfoRequested(exactCard);
|
||||
}
|
||||
|
||||
bool VisualDatabaseDisplayWidget::nearEndOfPage() const
|
||||
{
|
||||
if (!flowWidget->isVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QScrollBar *scrollBar = flowWidget->scrollArea->verticalScrollBar();
|
||||
if (scrollBar->value() + scrollBar->pageStep() * 2 < scrollBar->maximum()) {
|
||||
return false; // there is at least two pages of space to scroll remaining
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
} else if (nearEndOfPage()) {
|
||||
// If not the first page, just load the next page and append to the flow widget
|
||||
loadNextPage();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::populateCards()
|
||||
{
|
||||
cards->clear();
|
||||
loadNextPage();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::loadNextPage()
|
||||
{
|
||||
// Calculate the start and end indices for the next page
|
||||
int rowCount = databaseDisplayModel->rowCount();
|
||||
int start = currentPage * cardsPerPage;
|
||||
int end = qMin(start + cardsPerPage, rowCount);
|
||||
|
||||
// Load the next page of cards and add them to the flow widget
|
||||
flowWidget->setUpdatesEnabled(false);
|
||||
loadPage(start, end);
|
||||
flowWidget->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::loadPage(int start, int end)
|
||||
{
|
||||
QList<const CardFilter *> setFilters = filterModel->getFiltersOfType(CardFilter::AttrSet);
|
||||
for (int row = start; row < end; ++row) {
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Adding " << row;
|
||||
QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn);
|
||||
QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole);
|
||||
qCDebug(VisualDatabaseDisplayLog) << name.toString();
|
||||
|
||||
if (CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(name.toString())) {
|
||||
if (!setFilters.empty()) {
|
||||
const auto &setMap = info->getSets();
|
||||
for (const CardFilter *setFilter : setFilters) {
|
||||
const auto it = setMap.constFind(setFilter->term());
|
||||
if (it != setMap.constEnd()) {
|
||||
for (const PrintingInfo &printing : it.value()) {
|
||||
addCardToDisplay(ExactCard(info, printing));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addCardToDisplay(CardDatabaseManager::query()->getPreferredCard(info));
|
||||
}
|
||||
} else {
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!";
|
||||
}
|
||||
}
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::modelDirty() const
|
||||
{
|
||||
debounceTimer->start(debounceTime);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::sortCardList(const QStringList &properties,
|
||||
Qt::SortOrder order = Qt::AscendingOrder) const
|
||||
{
|
||||
CardInfoComparator comparator(properties, order);
|
||||
std::sort(cards->begin(), cards->end(), [comparator](const ExactCard &a, const ExactCard &b) {
|
||||
return comparator(a.getCardPtr(), b.getCardPtr());
|
||||
});
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::databaseDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
(void)topLeft;
|
||||
(void)bottomRight;
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Database Data changed";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QMouseEvent>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QWidget>
|
||||
|
|
@ -21,26 +19,21 @@
|
|||
class CardDatabaseDisplayModel;
|
||||
class CardDatabaseModel;
|
||||
class CardDatabaseView;
|
||||
class CardDatabaseVisualDisplayModel;
|
||||
class CardDatabaseVisualView;
|
||||
class CardSizeWidget;
|
||||
class DeckList;
|
||||
class DeckListModel;
|
||||
class ExactCard;
|
||||
class FilterTreeModel;
|
||||
class FlowWidget;
|
||||
class OverlapControlWidget;
|
||||
class QLabel;
|
||||
class QModelIndex;
|
||||
class QMouseEvent;
|
||||
class QPushButton;
|
||||
class QScrollArea;
|
||||
class QTimer;
|
||||
class QToolButton;
|
||||
class QVBoxLayout;
|
||||
class SearchLineEdit;
|
||||
class VisualDatabaseDisplayColorFilterWidget;
|
||||
class VisualDatabaseDisplayFilterToolbarWidget;
|
||||
template <typename T> class QList;
|
||||
|
||||
inline Q_LOGGING_CATEGORY(VisualDatabaseDisplayLog, "visual_database_display");
|
||||
|
||||
class VisualDatabaseDisplayWidget : public QWidget
|
||||
{
|
||||
|
|
@ -52,14 +45,6 @@ public:
|
|||
DeckListModel *deckListModel = nullptr);
|
||||
void retranslateUi();
|
||||
|
||||
void adjustCardsPerPage();
|
||||
void populateCards();
|
||||
void loadPage(int start, int end);
|
||||
void loadNextPage();
|
||||
void loadCurrentPage();
|
||||
void sortCardList(const QStringList &properties, Qt::SortOrder order) const;
|
||||
void setDeckList(const DeckList &new_deck_list_model);
|
||||
|
||||
/**
|
||||
* @brief Sets the callback used to create a custom zone from the add-to-zone menu.
|
||||
* The callback returns the name of the created zone, or an empty string if creation was cancelled.
|
||||
|
|
@ -87,6 +72,13 @@ public:
|
|||
bool isVisualDisplayMode() const;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Pushes the active set filters to the visual grid.
|
||||
*
|
||||
* The grid's rows follow the display model's filter and sort signals on their
|
||||
* own; this only forwards the set filters, which decide which printings the
|
||||
* grid shows.
|
||||
*/
|
||||
void onSearchModelChanged();
|
||||
|
||||
signals:
|
||||
|
|
@ -101,11 +93,6 @@ signals:
|
|||
|
||||
protected slots:
|
||||
void initialize();
|
||||
void onClick(QMouseEvent *event, const ExactCard &card);
|
||||
void onHover(const ExactCard &hoveredCard);
|
||||
void addCardToDisplay(const ExactCard &cardToAdd);
|
||||
void databaseDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
void modelDirty() const;
|
||||
void onDisplayModeChanged(bool checked);
|
||||
|
||||
void onSelectedCardChanged(const QString &cardName);
|
||||
|
|
@ -125,31 +112,21 @@ private:
|
|||
QToolButton *clearFilterWidget;
|
||||
VisualDatabaseDisplayFilterToolbarWidget *filterContainer;
|
||||
CardDatabaseDisplayModel *databaseDisplayModel;
|
||||
CardDatabaseVisualDisplayModel *visualDisplayModel;
|
||||
CardDatabaseView *databaseView;
|
||||
CardDatabaseVisualView *cardView;
|
||||
std::function<QString()> newZoneCreator;
|
||||
QList<ExactCard> *cards;
|
||||
QVBoxLayout *mainLayout;
|
||||
QScrollArea *scrollArea;
|
||||
FlowWidget *flowWidget;
|
||||
QWidget *overlapCategories;
|
||||
QVBoxLayout *overlapCategoriesLayout;
|
||||
OverlapControlWidget *overlapControlWidget;
|
||||
CardSizeWidget *cardSizeWidget;
|
||||
QTimer *debounceTimer;
|
||||
|
||||
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();
|
||||
void updateDisplayModeButton();
|
||||
bool nearEndOfPage() const;
|
||||
[[nodiscard]] QStringList collectSetFilterTerms() const;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue