Home tab.

Took 2 hours 41 minutes
This commit is contained in:
Lukas Brübach 2025-09-10 23:48:02 +02:00
parent aaa2c05b87
commit 62a01a6ee8
14 changed files with 254 additions and 132 deletions

View file

@ -102,6 +102,7 @@ set(cockatrice_SOURCES
src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
src/client/ui/widgets/general/background_sources.cpp
src/client/ui/widgets/general/home_styled_button.cpp
src/client/ui/widgets/general/home_widget.cpp
src/client/ui/widgets/general/display/banner_widget.cpp

View file

@ -740,6 +740,15 @@ void TabSupervisor::roomLeft(TabRoom *tab)
removeTab(indexOf(tab));
}
void TabSupervisor::switchToFirstAvailableNetworkTab()
{
if (!roomTabs.isEmpty()) {
setCurrentWidget(roomTabs.first());
} else if (tabServer) {
setCurrentWidget(tabServer);
}
}
void TabSupervisor::openReplay(GameReplay *replay)
{
auto *replayTab = new TabGame(this, replay);

View file

@ -168,6 +168,7 @@ public slots:
TabEdhRecMain *addEdhrecMainTab();
TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false);
void openReplay(GameReplay *replay);
void switchToFirstAvailableNetworkTab();
void maximizeMainWindow();
void actTabVisualDeckStorage(bool checked);
void actTabReplays(bool checked);

View file

@ -0,0 +1,3 @@
#include "background_sources.h"
// Required so moc generates Q_OBJECT macros

View file

@ -0,0 +1,62 @@
#ifndef COCKATRICE_BACKGROUND_SOURCES_H
#define COCKATRICE_BACKGROUND_SOURCES_H
#include <QList>
#include <QObject>
#include <QString>
class BackgroundSources
{
Q_GADGET
public:
enum Type
{
Theme,
RandomCardArt,
DeckFileArt
};
Q_ENUM(Type)
struct Entry
{
Type type;
const char *id; // stable ID for settings
const char *trKey; // key for translation
};
static QList<Entry> all()
{
return {{Theme, "theme", QT_TR_NOOP("Theme")},
{RandomCardArt, "random_card_art", QT_TR_NOOP("Art crop of random card")},
{DeckFileArt, "deck_file_art", QT_TR_NOOP("Art crop of background.cod deck file")}};
}
static QString toId(Type type)
{
for (const auto &e : all()) {
if (e.type == type)
return e.id;
}
return {};
}
static Type fromId(const QString &id)
{
for (const auto &e : all()) {
if (id == e.id)
return e.type;
}
return Theme; // default
}
static QString toDisplay(Type type)
{
for (const auto &e : all()) {
if (e.type == type)
return QObject::tr(e.trKey);
}
return {};
}
};
#endif // COCKATRICE_BACKGROUND_SOURCES_H

View file

@ -1,7 +1,11 @@
#include "home_widget.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../server/remote/remote_client.h"
#include "../../../../settings/cache_settings.h"
#include "../../../tabs/tab_supervisor.h"
#include "../../window_main.h"
#include "background_sources.h"
#include "home_styled_button.h"
#include <QPainter>
@ -15,18 +19,11 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
setAttribute(Qt::WA_OpaquePaintEvent);
layout = new QGridLayout(this);
backgroundSource = new CardInfoPictureArtCropWidget(this);
backgroundSource->setCard(CardDatabaseManager::getInstance()->getRandomCard());
background = backgroundSource->getProcessedBackground(size());
backgroundSourceCard = new CardInfoPictureArtCropWidget(this);
gradientColors = extractDominantColors(background);
layout->addWidget(createSettingsButtonGroup("Settings"), 0, 0, Qt::AlignTop | Qt::AlignLeft);
layout->addWidget(createUpdatesButtonGroup("Updates"), 0, 2, Qt::AlignTop | Qt::AlignRight);
layout->addWidget(createNavigationButtonGroup("Navigation"), 2, 0, Qt::AlignBottom | Qt::AlignLeft);
layout->addWidget(createPlayButtonGroup("Play"), 2, 2, Qt::AlignBottom | Qt::AlignRight);
layout->addWidget(createButtons(), 1, 1, Qt::AlignVCenter | Qt::AlignHCenter);
layout->setRowStretch(0, 1);
layout->setRowStretch(2, 1);
@ -35,19 +32,39 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
setLayout(layout);
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&HomeWidget::startCardShuffleTimer);
if (CardDatabaseManager::getInstance()->getLoadStatus() == LoadStatus::Ok) {
startCardShuffleTimer();
}
}
void HomeWidget::startCardShuffleTimer()
{
cardChangeTimer = new QTimer(this);
connect(cardChangeTimer, &QTimer::timeout, this, &HomeWidget::updateRandomCard);
cardChangeTimer->start(5000); // 20 seconds
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&HomeWidget::initializeBackgroundFromSource);
if (CardDatabaseManager::getInstance()->getLoadStatus() == LoadStatus::Ok) {
initializeBackgroundFromSource();
}
connect(tabSupervisor->getClient(), &RemoteClient::statusChanged, this, &HomeWidget::updateConnectButton);
connect(&SettingsCache::instance(), &SettingsCache::homeTabBackgroundSourceChanged, this,
&HomeWidget::initializeBackgroundFromSource);
}
void HomeWidget::initializeBackgroundFromSource()
{
auto type = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
cardChangeTimer->stop();
switch (type) {
case BackgroundSources::Theme:
background = QPixmap("theme:backgrounds/home");
update();
break;
case BackgroundSources::RandomCardArt:
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
break;
case BackgroundSources::DeckFileArt:
// do deck file stuff if and when we implement it
break;
}
}
void HomeWidget::updateRandomCard()
@ -57,13 +74,13 @@ void HomeWidget::updateRandomCard()
return;
connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties);
backgroundSource->setCard(newCard);
backgroundSourceCard->setCard(newCard);
background = backgroundSourceCard->getProcessedBackground(size());
}
void HomeWidget::updateBackgroundProperties()
{
background = backgroundSource->getProcessedBackground(size());
background = backgroundSourceCard->getProcessedBackground(size());
gradientColors = extractDominantColors(background);
for (HomeStyledButton *button : findChildren<HomeStyledButton *>()) {
button->updateStylesheet(gradientColors);
@ -72,55 +89,9 @@ void HomeWidget::updateBackgroundProperties()
update(); // Triggers repaint
}
QGroupBox *HomeWidget::createSettingsButtonGroup(const QString &title)
QGroupBox *HomeWidget::createButtons()
{
QGroupBox *box = new QGroupBox(title);
box->setStyleSheet(R"(
QGroupBox {
font-size: 20px;
color: white; /* Title text color */
background: transparent;
}
QGroupBox::title {
color: white;
subcontrol-origin: margin;
subcontrol-position: top center; /* or top left / right */
}
)");
QVBoxLayout *layout = new QVBoxLayout;
layout->setAlignment(Qt::AlignHCenter); // Center widgets horizontally
layout->addWidget(new HomeStyledButton("Settings", gradientColors));
box->setLayout(layout);
return box;
}
QGroupBox *HomeWidget::createUpdatesButtonGroup(const QString &title)
{
QGroupBox *box = new QGroupBox(title);
box->setStyleSheet(R"(
QGroupBox {
font-size: 20px;
color: white; /* Title text color */
background: transparent;
}
QGroupBox::title {
color: white;
subcontrol-origin: margin;
subcontrol-position: top center; /* or top left / right */
}
)");
QVBoxLayout *layout = new QVBoxLayout;
layout->setAlignment(Qt::AlignHCenter); // Center widgets horizontally
layout->addWidget(new HomeStyledButton("Updates", gradientColors));
box->setLayout(layout);
return box;
}
QGroupBox *HomeWidget::createNavigationButtonGroup(const QString &title)
{
QGroupBox *box = new QGroupBox(title);
QGroupBox *box = new QGroupBox(this);
box->setStyleSheet(R"(
QGroupBox {
font-size: 20px;
@ -135,58 +106,65 @@ QGroupBox *HomeWidget::createNavigationButtonGroup(const QString &title)
}
)");
box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout *layout = new QVBoxLayout;
layout->setAlignment(Qt::AlignHCenter); // Center widgets horizontally
auto replaybutton = new HomeStyledButton("View Replays", gradientColors);
connect(replaybutton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
layout->addWidget(replaybutton);
auto edhrecButton = new HomeStyledButton("Browse EDHRec", gradientColors);
connect(edhrecButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addEdhrecMainTab);
layout->addWidget(edhrecButton);
auto visualDatabaseDisplayButton = new HomeStyledButton("Browse Card Database", gradientColors);
connect(visualDatabaseDisplayButton, &QPushButton::clicked, tabSupervisor,
&TabSupervisor::addVisualDatabaseDisplayTab);
layout->addWidget(visualDatabaseDisplayButton);
auto visualDeckStorageButton = new HomeStyledButton("Browse Decks", gradientColors);
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->actTabVisualDeckStorage(true); });
layout->addWidget(visualDeckStorageButton);
QVBoxLayout *boxLayout = new QVBoxLayout;
boxLayout->setAlignment(Qt::AlignHCenter);
QLabel *logoLabel = new QLabel;
logoLabel->setPixmap(overlay.scaledToWidth(200, Qt::SmoothTransformation));
logoLabel->setAlignment(Qt::AlignCenter);
boxLayout->addWidget(logoLabel);
boxLayout->addSpacing(25);
connectButton = new HomeStyledButton("Connect/Play", gradientColors);
boxLayout->addWidget(connectButton, 1);
auto visualDeckEditorButton = new HomeStyledButton("Create New Deck", gradientColors);
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->addVisualDeckEditorTab(nullptr); });
layout->addWidget(visualDeckEditorButton);
box->setLayout(layout);
boxLayout->addWidget(visualDeckEditorButton);
auto visualDeckStorageButton = new HomeStyledButton("Browse Decks", gradientColors);
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->actTabVisualDeckStorage(true); });
boxLayout->addWidget(visualDeckStorageButton);
auto visualDatabaseDisplayButton = new HomeStyledButton("Browse Card Database", gradientColors);
connect(visualDatabaseDisplayButton, &QPushButton::clicked, tabSupervisor,
&TabSupervisor::addVisualDatabaseDisplayTab);
boxLayout->addWidget(visualDatabaseDisplayButton);
auto edhrecButton = new HomeStyledButton("Browse EDHRec", gradientColors);
connect(edhrecButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addEdhrecMainTab);
boxLayout->addWidget(edhrecButton);
auto replaybutton = new HomeStyledButton("View Replays", gradientColors);
connect(replaybutton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
boxLayout->addWidget(replaybutton);
box->setLayout(boxLayout);
return box;
}
QGroupBox *HomeWidget::createPlayButtonGroup(const QString &title)
void HomeWidget::updateConnectButton(const ClientStatus status)
{
QGroupBox *box = new QGroupBox(title);
box->setStyleSheet(R"(
QGroupBox {
font-size: 20px;
color: white; /* Title text color */
background: transparent;
disconnect(connectButton);
switch (status) {
case StatusConnecting:
connectButton->setText("Connecting...");
connectButton->setEnabled(false);
break;
case StatusDisconnected:
connectButton->setText("Connect");
connectButton->setEnabled(true);
connect(connectButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
&MainWindow::actConnect);
break;
case StatusLoggedIn:
connectButton->setText("Play");
connectButton->setEnabled(true);
connect(connectButton, &QPushButton::clicked, tabSupervisor,
&TabSupervisor::switchToFirstAvailableNetworkTab);
break;
default:
break;
}
QGroupBox::title {
color: white;
subcontrol-origin: margin;
subcontrol-position: top center; /* or top left / right */
}
)");
box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout *layout = new QVBoxLayout;
layout->setAlignment(Qt::AlignHCenter); // Center widgets horizontally
auto connectButton = new HomeStyledButton("Connect", gradientColors);
layout->addWidget(connectButton, 1); // stretch factor 1
auto playButton = new HomeStyledButton("Play", gradientColors);
layout->addWidget(playButton, 1);
box->setLayout(layout);
return box;
}
QPair<QColor, QColor> HomeWidget::extractDominantColors(const QPixmap &pixmap)
@ -241,10 +219,7 @@ void HomeWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// Update background if we have a source
if (backgroundSource) {
background = backgroundSource->getProcessedBackground(size());
}
background = background.scaled(size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Draw already-scaled background centered
QSize widgetSize = size();
@ -262,10 +237,5 @@ void HomeWidget::paintEvent(QPaintEvent *event)
painter.setRenderHint(QPainter::Antialiasing);
painter.fillPath(roundedRectPath, semiTransparentBlack);
// Draw centered overlay image
QSize overlaySize = overlay.size();
QPoint center((width() - overlaySize.width()) / 2, (height() - overlaySize.height()) / 2);
painter.drawPixmap(center, overlay);
QWidget::paintEvent(event);
}

View file

@ -1,7 +1,9 @@
#ifndef HOME_WIDGET_H
#define HOME_WIDGET_H
#include "../../../../server/abstract_client.h"
#include "../../../tabs/tab_supervisor.h"
#include "../cards/card_info_picture_art_crop_widget.h"
#include "home_styled_button.h"
#include <QGridLayout>
#include <QGroupBox>
@ -14,26 +16,25 @@ class HomeWidget : public QWidget
public:
HomeWidget(QWidget *parent, TabSupervisor *tabSupervisor);
void startCardShuffleTimer();
void updateRandomCard();
QGroupBox *createSettingsButtonGroup(const QString &title);
QGroupBox *createUpdatesButtonGroup(const QString &title);
QGroupBox *createNavigationButtonGroup(const QString &title);
QGroupBox *createPlayButtonGroup(const QString &title);
QPair<QColor, QColor> extractDominantColors(const QPixmap &pixmap);
public slots:
void paintEvent(QPaintEvent *event) override;
void initializeBackgroundFromSource();
void updateBackgroundProperties();
QGroupBox *createButtons();
void updateConnectButton(const ClientStatus status);
private:
QGridLayout *layout;
QTimer *cardChangeTimer;
TabSupervisor *tabSupervisor;
QPixmap background;
CardInfoPictureArtCropWidget *backgroundSource = nullptr;
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
QPixmap overlay;
QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton;
};
#endif // HOME_WIDGET_H

View file

@ -59,6 +59,7 @@ public slots:
void actCheckCardUpdatesBackground();
void actCheckServerUpdates();
void actCheckClientUpdates();
void actConnect();
private slots:
void updateTabMenu(const QList<QMenu *> &newMenuList);
void statusChanged(ClientStatus _status);
@ -77,7 +78,6 @@ private slots:
void localGameEnded();
void pixmapCacheSizeChanged(int newSizeInMBs);
void notifyUserAboutUpdate();
void actConnect();
void actDisconnect();
void actSinglePlayer();
void actWatchReplay();

View file

@ -7,6 +7,7 @@
#include "../client/tabs/tab_supervisor.h"
#include "../client/ui/picture_loader/picture_loader.h"
#include "../client/ui/theme_manager.h"
#include "../client/ui/widgets/general/background_sources.h"
#include "../deck/custom_line_edit.h"
#include "../game/cards/card_database.h"
#include "../game/cards/card_database_manager.h"
@ -424,10 +425,36 @@ AppearanceSettingsPage::AppearanceSettingsPage()
connect(&themeBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppearanceSettingsPage::themeBoxChanged);
connect(&openThemeButton, &QPushButton::clicked, this, &AppearanceSettingsPage::openThemeLocation);
for (const auto &entry : BackgroundSources::all()) {
homeTabBackgroundSourceBox.addItem(QObject::tr(entry.trKey), QVariant::fromValue(entry.type));
}
QString homeTabBackgroundSource = SettingsCache::instance().getHomeTabBackgroundSource();
int homeTabBackgroundSourceId =
homeTabBackgroundSourceBox.findData(BackgroundSources::fromId(homeTabBackgroundSource));
if (homeTabBackgroundSourceId != -1) {
homeTabBackgroundSourceBox.setCurrentIndex(homeTabBackgroundSourceId);
}
connect(&homeTabBackgroundSourceBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this]() {
auto type = homeTabBackgroundSourceBox.currentData().value<BackgroundSources::Type>();
SettingsCache::instance().setHomeTabBackground(BackgroundSources::toId(type));
});
homeTabBackgroundShuffleFrequencySpinBox.setRange(0, 3600);
homeTabBackgroundShuffleFrequencySpinBox.setSuffix(tr(" seconds"));
homeTabBackgroundShuffleFrequencySpinBox.setValue(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency());
connect(&homeTabBackgroundShuffleFrequencySpinBox, qOverload<int>(&QSpinBox::valueChanged),
&SettingsCache::instance(), &SettingsCache::setHomeTabBackgroundShuffleFrequency);
auto *themeGrid = new QGridLayout;
themeGrid->addWidget(&themeLabel, 0, 0);
themeGrid->addWidget(&themeBox, 0, 1);
themeGrid->addWidget(&openThemeButton, 1, 1);
themeGrid->addWidget(&homeTabBackgroundSourceLabel, 2, 0);
themeGrid->addWidget(&homeTabBackgroundSourceBox, 2, 1);
themeGrid->addWidget(&homeTabBackgroundShuffleFrequencyLabel, 3, 0);
themeGrid->addWidget(&homeTabBackgroundShuffleFrequencySpinBox, 3, 1);
themeGroupBox = new QGroupBox;
themeGroupBox->setLayout(themeGrid);
@ -656,6 +683,8 @@ void AppearanceSettingsPage::retranslateUi()
themeGroupBox->setTitle(tr("Theme settings"));
themeLabel.setText(tr("Current theme:"));
openThemeButton.setText(tr("Open themes folder"));
homeTabBackgroundSourceLabel.setText(tr("Home tab background source:"));
homeTabBackgroundShuffleFrequencyLabel.setText(tr("Home tab background shuffle frequency:"));
menuGroupBox->setTitle(tr("Menu settings"));
showShortcutsCheckBox.setText(tr("Show keyboard shortcuts in right-click menus"));

View file

@ -107,6 +107,10 @@ private:
QLabel themeLabel;
QComboBox themeBox;
QPushButton openThemeButton;
QLabel homeTabBackgroundSourceLabel;
QComboBox homeTabBackgroundSourceBox;
QLabel homeTabBackgroundShuffleFrequencyLabel;
QSpinBox homeTabBackgroundShuffleFrequencySpinBox;
QLabel minPlayersForMultiColumnLayoutLabel;
QLabel maxFontSizeForCardsLabel;
QCheckBox showShortcutsCheckBox;

View file

@ -217,6 +217,9 @@ SettingsCache::SettingsCache()
themeName = settings->value("theme/name").toString();
homeTabBackgroundSource = settings->value("home/background", "themed").toString();
homeTabBackgroundShuffleFrequency = settings->value("home/background/shuffleTimer", 0).toInt();
tabVisualDeckStorageOpen = settings->value("tabs/visualDeckStorage", true).toBool();
tabServerOpen = settings->value("tabs/server", true).toBool();
tabAccountOpen = settings->value("tabs/account", true).toBool();
@ -550,6 +553,20 @@ void SettingsCache::setThemeName(const QString &_themeName)
emit themeChanged();
}
void SettingsCache::setHomeTabBackground(const QString &_backgroundSource)
{
homeTabBackgroundSource = _backgroundSource;
settings->setValue("home/background", homeTabBackgroundSource);
emit homeTabBackgroundSourceChanged();
}
void SettingsCache::setHomeTabBackgroundShuffleFrequency(int _frequency)
{
homeTabBackgroundShuffleFrequency = _frequency;
settings->setValue("home/background/shuffleTimer", homeTabBackgroundShuffleFrequency);
emit homeTabBackgroundShuffleFrequencyChanged();
}
void SettingsCache::setTabVisualDeckStorageOpen(bool value)
{
tabVisualDeckStorageOpen = value;

View file

@ -135,6 +135,8 @@ signals:
void picsPathChanged();
void cardDatabasePathChanged();
void themeChanged();
void homeTabBackgroundSourceChanged();
void homeTabBackgroundShuffleFrequencyChanged();
void picDownloadChanged();
void showStatusBarChanged(bool state);
void displayCardNamesChanged();
@ -195,7 +197,7 @@ private:
QByteArray setsDialogGeometry;
QString lang;
QString deckPath, filtersPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath,
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName;
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName, homeTabBackgroundSource;
bool tabVisualDeckStorageOpen, tabServerOpen, tabAccountOpen, tabDeckStorageOpen, tabReplaysOpen, tabAdminOpen,
tabLogOpen;
bool checkUpdatesOnStartup;
@ -208,6 +210,7 @@ private:
bool notifyAboutNewVersion;
bool showTipsOnStartup;
QList<int> seenTips;
int homeTabBackgroundShuffleFrequency;
bool mbDownloadSpoilers;
int updateReleaseChannel;
int maxFontSize;
@ -385,6 +388,14 @@ public:
{
return themeName;
}
QString getHomeTabBackgroundSource() const
{
return homeTabBackgroundSource;
}
int getHomeTabBackgroundShuffleFrequency() const
{
return homeTabBackgroundShuffleFrequency;
}
bool getTabVisualDeckStorageOpen() const
{
return tabVisualDeckStorageOpen;
@ -947,6 +958,8 @@ public slots:
void setSpoilerDatabasePath(const QString &_spoilerDatabasePath);
void setTokenDatabasePath(const QString &_tokenDatabasePath);
void setThemeName(const QString &_themeName);
void setHomeTabBackground(const QString &_backgroundSource);
void setHomeTabBackgroundShuffleFrequency(int _frequency);
void setTabVisualDeckStorageOpen(bool value);
void setTabServerOpen(bool value);
void setTabAccountOpen(bool value);