mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
- Set the sizes the stylesheets carried with setPixelSize() rather than setPointSize(): home buttons (34), banner (24), home group box (20), the placeholder labels (14), the analytics drag handle (10), the EDHREC navigation buttons (24), the user info popup (11/14/11/10/11) and the game overlays (14/16/12). Points at 96 DPI were ~33% larger. - The drag/total counters and tally columns keep the monospace family the stylesheets set, so digit widths stop changing as counts tick and white-space: pre stays aligned. - applyDeckListFont() drops the cached tally rows before re-measuring, so previewing a larger DeckList font no longer leaves the tally clipped. - Tally labels and the constructor/fontsChanged pairs in the shared-deck preview, quick setup, analytics panel, home box and public decks build their font once in a local lambda instead of twice.
267 lines
11 KiB
C++
267 lines
11 KiB
C++
#include "tab_public_decks.h"
|
|
|
|
#include "../../../client/settings/cache_settings.h"
|
|
#include "../../deck_loader/deck_loader.h"
|
|
#include "../cards/card_size_widget.h"
|
|
#include "../general/layout_containers/flow_widget.h"
|
|
#include "../interface/theme_manager.h"
|
|
#include "../visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h"
|
|
#include "../visual_deck_storage/deck_preview/public_deck_preview_widget.h"
|
|
#include "../visual_deck_storage/remote_public_decks_model.h"
|
|
#include "../visual_deck_storage/visual_deck_storage_quick_settings_widget.h"
|
|
#include "../visual_deck_storage/visual_deck_storage_search_widget.h"
|
|
#include "../visual_deck_storage/visual_deck_storage_tag_filter_widget.h"
|
|
#include "tab_supervisor.h"
|
|
|
|
#include <QAbstractItemModel>
|
|
#include <QChar>
|
|
#include <QDebug>
|
|
#include <QFont>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QList>
|
|
#include <QMessageBox>
|
|
#include <QPixmap>
|
|
#include <QSet>
|
|
#include <QStringList>
|
|
#include <QToolButton>
|
|
#include <QVBoxLayout>
|
|
#include <QWidget>
|
|
#include <functional>
|
|
#include <libcockatrice/deck_list/loaded_deck.h>
|
|
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
|
#include <libcockatrice/protocol/pb/command_deck_download_public.pb.h>
|
|
#include <libcockatrice/protocol/pb/response.pb.h>
|
|
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
|
#include <libcockatrice/protocol/pending_command.h>
|
|
#include <libcockatrice/settings/cards_display_settings.h>
|
|
#include <libcockatrice/utility/qt_utils.h>
|
|
#include <optional>
|
|
#include <qnamespace.h>
|
|
|
|
TabPublicDecks::TabPublicDecks(TabSupervisor *_tabSupervisor, AbstractClient *_client, const QString &_userName)
|
|
: Tab(_tabSupervisor), client(_client), userName(_userName)
|
|
{
|
|
model = new RemotePublicDecksModel(client, this);
|
|
cardSize = SettingsCache::instance().cardsDisplay().getVisualDeckStorageCardSize();
|
|
|
|
titleLabel = new QLabel(tr("Public decks of %1").arg(userName.toHtmlEscaped()), this);
|
|
// Family comes from the InterfaceDefault theme role; bold stays caller-side.
|
|
auto applyTitleFont = [this]() {
|
|
titleLabel->setFont(QtUtils::boldFont(themeManager->appFont(AppFont::InterfaceDefault)));
|
|
};
|
|
applyTitleFont();
|
|
connect(themeManager, &ThemeManager::fontsChanged, this, applyTitleFont);
|
|
|
|
auto *headerLayout = new QHBoxLayout;
|
|
headerLayout->addWidget(titleLabel);
|
|
headerLayout->addStretch(1);
|
|
|
|
// Filter/toolbar row, matching the Visual Deck Storage: color identity filter
|
|
// first, the search bar stretching in the middle, and the quick settings
|
|
// cogwheel at the end. The card size slider lives inside the cogwheel popup.
|
|
emptyLabel = new QLabel(tr("This user has not published any decks."), this);
|
|
emptyLabel->setAlignment(Qt::AlignCenter);
|
|
emptyLabel->setVisible(false);
|
|
|
|
statusLabel = new QLabel(this);
|
|
statusLabel->setAlignment(Qt::AlignCenter);
|
|
statusLabel->setVisible(false);
|
|
|
|
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
|
flowWidget->setSpacing(8, 8);
|
|
|
|
colorIdentityFilter = new DeckPreviewColorIdentityFilterWidget(this);
|
|
searchWidget = new VisualDeckStorageSearchWidget(this);
|
|
refreshButton = new QToolButton(this);
|
|
refreshButton->setIcon(QPixmap("theme:icons/reload"));
|
|
refreshButton->setFixedSize(32, 32);
|
|
quickSettingsWidget = new VisualDeckStorageQuickSettingsWidget(this);
|
|
quickSettingsWidget->setPublicDecksMode(true);
|
|
|
|
auto *filterLayout = new QHBoxLayout;
|
|
filterLayout->addWidget(colorIdentityFilter);
|
|
filterLayout->addWidget(searchWidget, 1);
|
|
filterLayout->addWidget(refreshButton);
|
|
filterLayout->addWidget(quickSettingsWidget);
|
|
|
|
tagFilterWidget = new VisualDeckStorageTagFilterWidget(this);
|
|
tagFilterWidget->setAllTagsProvider([this] { return model->allTags(); });
|
|
updateTagsVisibility(quickSettingsWidget->getShowTagFilter());
|
|
|
|
auto *layout = new QVBoxLayout;
|
|
layout->addLayout(headerLayout);
|
|
layout->addLayout(filterLayout);
|
|
layout->addWidget(tagFilterWidget);
|
|
layout->addWidget(statusLabel);
|
|
layout->addWidget(emptyLabel);
|
|
layout->addWidget(flowWidget, 1);
|
|
|
|
auto *mainWidget = new QWidget(this);
|
|
mainWidget->setLayout(layout);
|
|
setCentralWidget(mainWidget);
|
|
|
|
connect(refreshButton, &QToolButton::clicked, this, [this] { model->refresh(userName); });
|
|
connect(model, &QAbstractItemModel::modelReset, this, &TabPublicDecks::rebuildGrid);
|
|
connect(model, &RemotePublicDecksModel::loadingChanged, this, &TabPublicDecks::updateLoadingState);
|
|
connect(model, &RemotePublicDecksModel::loadFailed, this, [this](const QString &message) {
|
|
lastFailureMessage = message;
|
|
statusLabel->setText(message);
|
|
statusLabel->setVisible(true);
|
|
flowWidget->setVisible(false);
|
|
emptyLabel->setVisible(false);
|
|
});
|
|
connect(searchWidget, &VisualDeckStorageSearchWidget::searchTextChanged, this,
|
|
[this](const QString &text) { model->setSearchText(text); });
|
|
connect(colorIdentityFilter, &DeckPreviewColorIdentityFilterWidget::activeColorsChanged, this,
|
|
&TabPublicDecks::updateColorFilter);
|
|
connect(colorIdentityFilter, &DeckPreviewColorIdentityFilterWidget::filterModeChanged, this,
|
|
&TabPublicDecks::updateColorFilter);
|
|
connect(tagFilterWidget, &VisualDeckStorageTagFilterWidget::filterChanged, this, &TabPublicDecks::updateTagFilter);
|
|
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::cardSizeChanged, this,
|
|
&TabPublicDecks::updateCardSize);
|
|
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showTagFilterChanged, this,
|
|
&TabPublicDecks::updateTagsVisibility);
|
|
quickSettingsWidget->getCardSizeWidget()->enableCtrlScrollResize(flowWidget);
|
|
|
|
retranslateUi();
|
|
model->refresh(userName);
|
|
}
|
|
|
|
QString TabPublicDecks::getTabText() const
|
|
{
|
|
return tr("Public decks of %1").arg(userName);
|
|
}
|
|
|
|
void TabPublicDecks::retranslateUi()
|
|
{
|
|
// The username is another user's data, so escape it for the AutoText QLabel.
|
|
titleLabel->setText(tr("Public decks of %1").arg(userName.toHtmlEscaped()));
|
|
// The same choice rebuildGrid makes, so a language change does not swap
|
|
// the "no match" variant for the "nothing published" one.
|
|
emptyLabel->setText(model->totalCount() > 0 ? tr("No decks match your filters.")
|
|
: tr("This user has not published any decks."));
|
|
refreshButton->setToolTip(tr("Refresh"));
|
|
refreshButton->setAccessibleName(tr("Refresh"));
|
|
quickSettingsWidget->setToolTip(tr("Public Decks Settings"));
|
|
// Re-show whatever the status label is showing so a language change picks up
|
|
// the new language or, for a failure message, at least does not hide it.
|
|
if (model->isLoading()) {
|
|
updateLoadingState(true);
|
|
} else if (!lastFailureMessage.isEmpty()) {
|
|
statusLabel->setText(lastFailureMessage);
|
|
statusLabel->setVisible(true);
|
|
flowWidget->setVisible(false);
|
|
emptyLabel->setVisible(false);
|
|
} else {
|
|
updateLoadingState(false);
|
|
}
|
|
emit tabTextChanged(this, getTabText());
|
|
}
|
|
|
|
bool TabPublicDecks::closeRequest()
|
|
{
|
|
emit closing(this);
|
|
return Tab::closeRequest();
|
|
}
|
|
|
|
void TabPublicDecks::rebuildGrid()
|
|
{
|
|
flowWidget->clearLayout();
|
|
|
|
const int count = model->rowCount();
|
|
if (count == 0) {
|
|
emptyLabel->setText(model->totalCount() > 0 ? tr("No decks match your filters.")
|
|
: tr("This user has not published any decks."));
|
|
}
|
|
emptyLabel->setVisible(count == 0);
|
|
for (int i = 0; i < count; ++i) {
|
|
auto *tile = new PublicDeckPreviewWidget(flowWidget, model->entryAt(i));
|
|
tile->setScaleFactor(cardSize);
|
|
connect(tile, &PublicDeckPreviewWidget::openDeckRequested, this, &TabPublicDecks::openDeck);
|
|
flowWidget->addWidget(tile);
|
|
}
|
|
|
|
// The deck set changed, so the tag filter chips are re-gathered from it.
|
|
tagFilterWidget->refreshTags();
|
|
}
|
|
|
|
void TabPublicDecks::updateColorFilter()
|
|
{
|
|
model->setColorFilter(colorIdentityFilter->getFilterMode(), colorIdentityFilter->getActiveColors());
|
|
}
|
|
|
|
void TabPublicDecks::updateTagFilter()
|
|
{
|
|
const QStringList selectedTags = tagFilterWidget->selectedTags();
|
|
const QStringList excludedTags = tagFilterWidget->excludedTags();
|
|
model->setTagFilter(QSet<QString>(selectedTags.cbegin(), selectedTags.cend()),
|
|
QSet<QString>(excludedTags.cbegin(), excludedTags.cend()));
|
|
tagFilterWidget->refreshTags();
|
|
}
|
|
|
|
void TabPublicDecks::updateTagsVisibility(bool visible)
|
|
{
|
|
tagFilterWidget->setVisible(visible);
|
|
}
|
|
|
|
void TabPublicDecks::updateLoadingState(bool loading)
|
|
{
|
|
if (loading) {
|
|
// A new attempt is under way, so the previously shown failure, if any,
|
|
// no longer describes the current state.
|
|
lastFailureMessage.clear();
|
|
statusLabel->setText(tr("Loading public decks…"));
|
|
statusLabel->setVisible(true);
|
|
flowWidget->setVisible(false);
|
|
emptyLabel->setVisible(false);
|
|
} else {
|
|
statusLabel->setVisible(false);
|
|
flowWidget->setVisible(true);
|
|
}
|
|
}
|
|
|
|
void TabPublicDecks::updateCardSize(int scale)
|
|
{
|
|
cardSize = scale;
|
|
applyCardSize(scale);
|
|
}
|
|
|
|
void TabPublicDecks::applyCardSize(int scale)
|
|
{
|
|
const auto tiles = flowWidget->findChildren<PublicDeckPreviewWidget *>();
|
|
for (PublicDeckPreviewWidget *tile : tiles) {
|
|
tile->setScaleFactor(scale);
|
|
}
|
|
flowWidget->setMinimumSizeToMaxSizeHint();
|
|
}
|
|
|
|
void TabPublicDecks::openDeck(int deckId)
|
|
{
|
|
Command_DeckDownloadPublic cmd;
|
|
cmd.set_deck_id(deckId);
|
|
|
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
|
connect(pend, &PendingCommand::finished, this, &TabPublicDecks::openDeckFinished);
|
|
client->sendCommand(pend);
|
|
}
|
|
|
|
void TabPublicDecks::openDeckFinished(const Response &response, const CommandContainer & /*commandContainer*/)
|
|
{
|
|
if (response.response_code() != Response::RespOk) {
|
|
QMessageBox::warning(this, tr("Open public deck"),
|
|
tr("Failed to open the public deck (server response code %1).")
|
|
.arg(QString::number(static_cast<int>(response.response_code()))));
|
|
return;
|
|
}
|
|
|
|
const Response_DeckDownload &resp = response.GetExtension(Response_DeckDownload::ext);
|
|
std::optional<LoadedDeck> deckOpt =
|
|
DeckLoader::loadFromRemote(QString::fromStdString(resp.deck()), LoadedDeck::LoadInfo::NON_REMOTE_ID);
|
|
if (!deckOpt) {
|
|
QMessageBox::warning(this, tr("Open public deck"), tr("The public deck could not be parsed."));
|
|
return;
|
|
}
|
|
|
|
tabSupervisor->openDeckInNewTab(deckOpt.value());
|
|
}
|