mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-29 10:03:55 -07:00
Home tab to replace generic deck editor on startup (#6114)
This commit is contained in:
parent
22c8268f02
commit
93c15c8151
26 changed files with 751 additions and 8 deletions
14
cockatrice/src/client/tabs/tab_home.cpp
Normal file
14
cockatrice/src/client/tabs/tab_home.cpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#include "tab_home.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QPushButton>
|
||||
|
||||
TabHome::TabHome(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client)
|
||||
{
|
||||
homeWidget = new HomeWidget(this, tabSupervisor);
|
||||
setCentralWidget(homeWidget);
|
||||
}
|
||||
|
||||
void TabHome::retranslateUi()
|
||||
{
|
||||
}
|
||||
29
cockatrice/src/client/tabs/tab_home.h
Normal file
29
cockatrice/src/client/tabs/tab_home.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef TAB_HOME_H
|
||||
#define TAB_HOME_H
|
||||
|
||||
#include "../../server/abstract_client.h"
|
||||
#include "../ui/widgets/general/home_widget.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <qgroupbox.h>
|
||||
|
||||
class AbstractClient;
|
||||
|
||||
class TabHome : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractClient *client;
|
||||
HomeWidget *homeWidget;
|
||||
|
||||
public:
|
||||
TabHome(TabSupervisor *_tabSupervisor, AbstractClient *_client);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Home");
|
||||
}
|
||||
};
|
||||
|
||||
#endif // TAB_HOME_H
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
#include "tab_deck_editor.h"
|
||||
#include "tab_deck_storage.h"
|
||||
#include "tab_game.h"
|
||||
#include "tab_home.h"
|
||||
#include "tab_logs.h"
|
||||
#include "tab_message.h"
|
||||
#include "tab_replays.h"
|
||||
|
|
@ -312,7 +313,9 @@ static void checkAndTrigger(QAction *checkableAction, bool checked)
|
|||
*/
|
||||
void TabSupervisor::initStartupTabs()
|
||||
{
|
||||
auto homeTab = addHomeTab();
|
||||
addDeckEditorTab(nullptr);
|
||||
setCurrentWidget(homeTab);
|
||||
|
||||
if (SettingsCache::instance().getTabVisualDeckStorageOpen()) {
|
||||
openTabVisualDeckStorage();
|
||||
|
|
@ -501,6 +504,8 @@ void TabSupervisor::actTabVisualDeckStorage(bool checked)
|
|||
if (checked && !tabVisualDeckStorage) {
|
||||
openTabVisualDeckStorage();
|
||||
setCurrentWidget(tabVisualDeckStorage);
|
||||
} else if (checked && tabVisualDeckStorage) {
|
||||
setCurrentWidget(tabVisualDeckStorage);
|
||||
} else if (!checked && tabVisualDeckStorage) {
|
||||
tabVisualDeckStorage->closeRequest();
|
||||
}
|
||||
|
|
@ -735,6 +740,17 @@ void TabSupervisor::roomLeft(TabRoom *tab)
|
|||
removeTab(indexOf(tab));
|
||||
}
|
||||
|
||||
void TabSupervisor::switchToFirstAvailableNetworkTab()
|
||||
{
|
||||
if (!roomTabs.isEmpty()) {
|
||||
setCurrentWidget(roomTabs.first());
|
||||
} else if (tabServer) {
|
||||
setCurrentWidget(tabServer);
|
||||
} else {
|
||||
openTabServer();
|
||||
}
|
||||
}
|
||||
|
||||
void TabSupervisor::openReplay(GameReplay *replay)
|
||||
{
|
||||
auto *replayTab = new TabGame(this, replay);
|
||||
|
|
@ -796,6 +812,14 @@ void TabSupervisor::talkLeft(TabMessage *tab)
|
|||
removeTab(indexOf(tab));
|
||||
}
|
||||
|
||||
TabHome *TabSupervisor::addHomeTab()
|
||||
{
|
||||
auto *tab = new TabHome(this, client);
|
||||
myAddTab(tab);
|
||||
setCurrentWidget(tab);
|
||||
return tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deck editor tab and loads the deck into it.
|
||||
* Creates either a classic or visual deck editor tab depending on settings
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class AbstractClient;
|
|||
class Tab;
|
||||
class TabServer;
|
||||
class TabRoom;
|
||||
class TabHome;
|
||||
class TabGame;
|
||||
class TabDeckStorage;
|
||||
class TabReplays;
|
||||
|
|
@ -167,15 +168,16 @@ 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);
|
||||
private slots:
|
||||
void refreshShortcuts();
|
||||
|
||||
void actTabVisualDeckStorage(bool checked);
|
||||
void actTabServer(bool checked);
|
||||
void actTabAccount(bool checked);
|
||||
void actTabDeckStorage(bool checked);
|
||||
void actTabReplays(bool checked);
|
||||
void actTabAdmin(bool checked);
|
||||
void actTabLog(bool checked);
|
||||
|
||||
|
|
@ -199,6 +201,7 @@ private slots:
|
|||
void processUserLeft(const QString &userName);
|
||||
void processUserJoined(const ServerInfo_User &userInfo);
|
||||
void talkLeft(TabMessage *tab);
|
||||
TabHome *addHomeTab();
|
||||
void deckEditorClosed(AbstractTabDeckEditor *tab);
|
||||
void tabUserEvent(bool globalEvent);
|
||||
void updateTabText(Tab *tab, const QString &newTabText);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
#include "card_info_picture_art_crop_widget.h"
|
||||
|
||||
#include "../../picture_loader/picture_loader.h"
|
||||
|
||||
CardInfoPictureArtCropWidget::CardInfoPictureArtCropWidget(QWidget *parent)
|
||||
: CardInfoPictureWidget(parent, false, false)
|
||||
{
|
||||
hide();
|
||||
}
|
||||
|
||||
QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &targetSize)
|
||||
{
|
||||
// Load the full-resolution card image, not a pre-scaled one
|
||||
QPixmap fullResPixmap;
|
||||
if (getCard()) {
|
||||
PictureLoader::getPixmap(fullResPixmap, getCard(), QSize(745, 1040)); // or a high default size
|
||||
} else {
|
||||
PictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040));
|
||||
}
|
||||
|
||||
// Fail-safe if loading failed
|
||||
if (fullResPixmap.isNull()) {
|
||||
return QPixmap(targetSize);
|
||||
}
|
||||
|
||||
const QSize sz = fullResPixmap.size();
|
||||
|
||||
int marginX = sz.width() * 0.07;
|
||||
int topMargin = sz.height() * 0.11;
|
||||
int bottomMargin = sz.height() * 0.45;
|
||||
|
||||
QRect foilRect(marginX, topMargin, sz.width() - 2 * marginX, sz.height() - topMargin - bottomMargin);
|
||||
|
||||
foilRect = foilRect.intersected(fullResPixmap.rect()); // always clamp to source bounds
|
||||
|
||||
// Crop first, then scale for best quality
|
||||
QPixmap cropped = fullResPixmap.copy(foilRect);
|
||||
QPixmap scaled = cropped.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
|
||||
return scaled;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef CARD_INFO_PICTURE_ART_CROP_WIDGET_H
|
||||
#define CARD_INFO_PICTURE_ART_CROP_WIDGET_H
|
||||
|
||||
#include "card_info_picture_widget.h"
|
||||
|
||||
class CardInfoPictureArtCropWidget : public CardInfoPictureWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CardInfoPictureArtCropWidget(QWidget *parent = nullptr);
|
||||
|
||||
// Returns a processed (cropped & scaled) version of the pixmap
|
||||
QPixmap getProcessedBackground(const QSize &targetSize);
|
||||
};
|
||||
|
||||
#endif // CARD_INFO_PICTURE_ART_CROP_WIDGET_H
|
||||
|
|
@ -65,8 +65,10 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverT
|
|||
|
||||
CardInfoPictureWidget::~CardInfoPictureWidget()
|
||||
{
|
||||
enlargedPixmapWidget->hide();
|
||||
enlargedPixmapWidget->deleteLater();
|
||||
if (enlargedPixmapWidget) {
|
||||
enlargedPixmapWidget->hide();
|
||||
enlargedPixmapWidget->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ private:
|
|||
bool hoverToZoomEnabled;
|
||||
bool raiseOnEnter;
|
||||
int hoverActivateThresholdInMs = 500;
|
||||
CardInfoPictureEnlargedWidget *enlargedPixmapWidget;
|
||||
CardInfoPictureEnlargedWidget *enlargedPixmapWidget = nullptr;
|
||||
int enlargedPixmapOffset = 10;
|
||||
QTimer *hoverTimer;
|
||||
QPropertyAnimation *animation;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
#include "background_sources.h"
|
||||
|
||||
// Required so moc generates Q_OBJECT macros
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
#include "home_styled_button.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <qgraphicseffect.h>
|
||||
|
||||
HomeStyledButton::HomeStyledButton(const QString &text, QPair<QColor, QColor> _gradientColors, QWidget *parent)
|
||||
: QPushButton(text, parent), gradientColors(_gradientColors)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setMinimumHeight(50);
|
||||
setStyleSheet(generateButtonStylesheet(gradientColors));
|
||||
}
|
||||
|
||||
void HomeStyledButton::updateStylesheet(const QPair<QColor, QColor> &colors)
|
||||
{
|
||||
gradientColors = colors;
|
||||
setStyleSheet(generateButtonStylesheet(gradientColors));
|
||||
}
|
||||
|
||||
QString HomeStyledButton::generateButtonStylesheet(const QPair<QColor, QColor> &colors)
|
||||
{
|
||||
QColor base1 = colors.first;
|
||||
QColor base2 = colors.second;
|
||||
|
||||
QColor hover1 = base1.lighter(120); // 20% lighter
|
||||
QColor hover2 = base2.lighter(120);
|
||||
|
||||
QColor pressed1 = base1.darker(130); // 30% darker
|
||||
QColor pressed2 = base2.darker(130);
|
||||
|
||||
return QString(R"(
|
||||
QPushButton {
|
||||
font-size: 34px;
|
||||
padding: 30px;
|
||||
color: white;
|
||||
border: 2px solid %1;
|
||||
border-radius: 20px;
|
||||
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
|
||||
stop:0 %2, stop:1 %3);
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
|
||||
stop:0 %4, stop:1 %5);
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 %6, stop:1 %7);
|
||||
}
|
||||
)")
|
||||
.arg(base1.name()) // border color
|
||||
.arg(base1.name()) // normal gradient start
|
||||
.arg(base2.name()) // normal gradient end
|
||||
.arg(hover1.name()) // hover start
|
||||
.arg(hover2.name()) // hover end
|
||||
.arg(pressed1.name()) // pressed start
|
||||
.arg(pressed2.name()); // pressed end
|
||||
}
|
||||
|
||||
void HomeStyledButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QString originalText = text();
|
||||
setText(""); // Prevent QPushButton from drawing the text
|
||||
|
||||
QPushButton::paintEvent(event); // Draw background, borders, etc.
|
||||
|
||||
setText(originalText); // Restore text for internal logic
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::TextAntialiasing);
|
||||
|
||||
QFont font = this->font();
|
||||
font.setBold(true);
|
||||
painter.setFont(font);
|
||||
|
||||
QFontMetrics fm(font);
|
||||
QSize textSize = fm.size(Qt::TextSingleLine, originalText);
|
||||
QPointF center((width() - textSize.width()) / 2.0, (height() + textSize.height() / 2.0) / 2.0);
|
||||
|
||||
QPainterPath path;
|
||||
path.addText(center, font, originalText);
|
||||
|
||||
painter.setPen(QPen(Qt::black, 2.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
painter.setBrush(Qt::white);
|
||||
painter.drawPath(path);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Created by ascor on 6/15/25.
|
||||
//
|
||||
|
||||
#ifndef HOME_STYLED_BUTTON_H
|
||||
#define HOME_STYLED_BUTTON_H
|
||||
#include <QPushButton>
|
||||
|
||||
class HomeStyledButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HomeStyledButton(const QString &text, QPair<QColor, QColor> gradientColors, QWidget *parent = nullptr);
|
||||
void updateStylesheet(const QPair<QColor, QColor> &colors);
|
||||
QString generateButtonStylesheet(const QPair<QColor, QColor> &colors);
|
||||
public slots:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QPair<QColor, QColor> gradientColors;
|
||||
};
|
||||
|
||||
#endif // HOME_STYLED_BUTTON_H
|
||||
298
cockatrice/src/client/ui/widgets/general/home_widget.cpp
Normal file
298
cockatrice/src/client/ui/widgets/general/home_widget.cpp
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
#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>
|
||||
#include <QPainterPath>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
||||
: QWidget(parent), tabSupervisor(_tabSupervisor), background("theme:backgrounds/home"), overlay("theme:cockatrice")
|
||||
{
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
layout = new QGridLayout(this);
|
||||
|
||||
backgroundSourceCard = new CardInfoPictureArtCropWidget(this);
|
||||
backgroundSourceDeck = new DeckLoader();
|
||||
|
||||
backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod",
|
||||
DeckLoader::CockatriceFormat, false);
|
||||
|
||||
gradientColors = extractDominantColors(background);
|
||||
|
||||
layout->addWidget(createButtons(), 1, 1, Qt::AlignVCenter | Qt::AlignHCenter);
|
||||
|
||||
layout->setRowStretch(0, 1);
|
||||
layout->setRowStretch(2, 1);
|
||||
layout->setColumnStretch(0, 1);
|
||||
layout->setColumnStretch(2, 1);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
cardChangeTimer = new QTimer(this);
|
||||
connect(cardChangeTimer, &QTimer::timeout, this, &HomeWidget::updateRandomCard);
|
||||
|
||||
initializeBackgroundFromSource();
|
||||
|
||||
updateConnectButton(tabSupervisor->getClient()->getStatus());
|
||||
|
||||
connect(tabSupervisor->getClient(), &RemoteClient::statusChanged, this, &HomeWidget::updateConnectButton);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::homeTabBackgroundSourceChanged, this,
|
||||
&HomeWidget::initializeBackgroundFromSource);
|
||||
}
|
||||
|
||||
void HomeWidget::initializeBackgroundFromSource()
|
||||
{
|
||||
if (CardDatabaseManager::getInstance()->getLoadStatus() != LoadStatus::Ok) {
|
||||
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
|
||||
&HomeWidget::initializeBackgroundFromSource);
|
||||
return;
|
||||
}
|
||||
|
||||
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
|
||||
|
||||
cardChangeTimer->stop();
|
||||
|
||||
switch (backgroundSourceType) {
|
||||
case BackgroundSources::Theme:
|
||||
background = QPixmap("theme:backgrounds/home");
|
||||
updateButtonsToBackgroundColor();
|
||||
update();
|
||||
break;
|
||||
case BackgroundSources::RandomCardArt:
|
||||
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
|
||||
break;
|
||||
case BackgroundSources::DeckFileArt:
|
||||
backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod",
|
||||
DeckLoader::CockatriceFormat, false);
|
||||
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWidget::updateRandomCard()
|
||||
{
|
||||
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
|
||||
|
||||
ExactCard newCard;
|
||||
|
||||
switch (backgroundSourceType) {
|
||||
case BackgroundSources::Theme:
|
||||
break;
|
||||
case BackgroundSources::RandomCardArt:
|
||||
do {
|
||||
newCard = CardDatabaseManager::getInstance()->getRandomCard();
|
||||
} while (newCard == backgroundSourceCard->getCard());
|
||||
break;
|
||||
case BackgroundSources::DeckFileArt:
|
||||
QList<CardRef> cardRefs = backgroundSourceDeck->getCardRefList();
|
||||
ExactCard oldCard = backgroundSourceCard->getCard();
|
||||
|
||||
if (!cardRefs.empty()) {
|
||||
if (cardRefs.size() == 1) {
|
||||
newCard = CardDatabaseManager::getInstance()->getCard(cardRefs.first());
|
||||
} else {
|
||||
// Keep picking until different
|
||||
do {
|
||||
int idx = QRandomGenerator::global()->bounded(cardRefs.size());
|
||||
newCard = CardDatabaseManager::getInstance()->getCard(cardRefs.at(idx));
|
||||
} while (newCard == oldCard);
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
newCard = CardDatabaseManager::getInstance()->getRandomCard();
|
||||
} while (newCard == oldCard);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!newCard)
|
||||
return;
|
||||
|
||||
connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties);
|
||||
backgroundSourceCard->setCard(newCard);
|
||||
background = backgroundSourceCard->getProcessedBackground(size());
|
||||
}
|
||||
|
||||
void HomeWidget::updateBackgroundProperties()
|
||||
{
|
||||
background = backgroundSourceCard->getProcessedBackground(size());
|
||||
updateButtonsToBackgroundColor();
|
||||
update(); // Triggers repaint
|
||||
}
|
||||
|
||||
void HomeWidget::updateButtonsToBackgroundColor()
|
||||
{
|
||||
gradientColors = extractDominantColors(background);
|
||||
for (HomeStyledButton *button : findChildren<HomeStyledButton *>()) {
|
||||
button->updateStylesheet(gradientColors);
|
||||
button->update();
|
||||
}
|
||||
}
|
||||
|
||||
QGroupBox *HomeWidget::createButtons()
|
||||
{
|
||||
QGroupBox *box = new QGroupBox(this);
|
||||
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 */
|
||||
}
|
||||
)");
|
||||
box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
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(tr("Create New Deck"), gradientColors);
|
||||
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
|
||||
[this] { tabSupervisor->addVisualDeckEditorTab(nullptr); });
|
||||
boxLayout->addWidget(visualDeckEditorButton);
|
||||
auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
|
||||
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
|
||||
[this] { tabSupervisor->actTabVisualDeckStorage(true); });
|
||||
boxLayout->addWidget(visualDeckStorageButton);
|
||||
auto visualDatabaseDisplayButton = new HomeStyledButton(tr("Browse Card Database"), gradientColors);
|
||||
connect(visualDatabaseDisplayButton, &QPushButton::clicked, tabSupervisor,
|
||||
&TabSupervisor::addVisualDatabaseDisplayTab);
|
||||
boxLayout->addWidget(visualDatabaseDisplayButton);
|
||||
auto edhrecButton = new HomeStyledButton(tr("Browse EDHRec"), gradientColors);
|
||||
connect(edhrecButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addEdhrecMainTab);
|
||||
boxLayout->addWidget(edhrecButton);
|
||||
auto replaybutton = new HomeStyledButton(tr("View Replays"), gradientColors);
|
||||
connect(replaybutton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
|
||||
boxLayout->addWidget(replaybutton);
|
||||
if (qobject_cast<MainWindow *>(tabSupervisor->parentWidget())) {
|
||||
auto exitButton = new HomeStyledButton(tr("Quit"), gradientColors);
|
||||
connect(exitButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
|
||||
&MainWindow::actExit);
|
||||
boxLayout->addWidget(exitButton);
|
||||
}
|
||||
|
||||
box->setLayout(boxLayout);
|
||||
return box;
|
||||
}
|
||||
|
||||
void HomeWidget::updateConnectButton(const ClientStatus status)
|
||||
{
|
||||
connectButton->disconnect();
|
||||
switch (status) {
|
||||
case StatusConnecting:
|
||||
connectButton->setText(tr("Connecting..."));
|
||||
connectButton->setEnabled(false);
|
||||
break;
|
||||
case StatusDisconnected:
|
||||
connectButton->setText(tr("Connect"));
|
||||
connectButton->setEnabled(true);
|
||||
connect(connectButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
|
||||
&MainWindow::actConnect);
|
||||
break;
|
||||
case StatusLoggedIn:
|
||||
connectButton->setText(tr("Play"));
|
||||
connectButton->setEnabled(true);
|
||||
connect(connectButton, &QPushButton::clicked, tabSupervisor,
|
||||
&TabSupervisor::switchToFirstAvailableNetworkTab);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QPair<QColor, QColor> HomeWidget::extractDominantColors(const QPixmap &pixmap)
|
||||
{
|
||||
if (SettingsCache::instance().getThemeName() == "Default" &&
|
||||
SettingsCache::instance().getHomeTabBackgroundSource() == BackgroundSources::toId(BackgroundSources::Theme)) {
|
||||
return QPair<QColor, QColor>(QColor::fromRgb(20, 140, 60), QColor::fromRgb(120, 200, 80));
|
||||
}
|
||||
|
||||
// Step 1: Downscale image for performance
|
||||
QImage image = pixmap.toImage()
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)
|
||||
.convertToFormat(QImage::Format_RGB32);
|
||||
|
||||
QMap<QRgb, int> colorCount;
|
||||
|
||||
// Step 2: Count quantized colors
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
const QRgb *scanLine = reinterpret_cast<const QRgb *>(image.scanLine(y));
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
QColor color = QColor::fromRgb(scanLine[x]);
|
||||
|
||||
int r = color.red() & 0xF0;
|
||||
int g = color.green() & 0xF0;
|
||||
int b = color.blue() & 0xF0;
|
||||
|
||||
QRgb quantized = qRgb(r, g, b);
|
||||
colorCount[quantized]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Sort by frequency
|
||||
QVector<QPair<QRgb, int>> sortedColors;
|
||||
for (auto it = colorCount.constBegin(); it != colorCount.constEnd(); ++it) {
|
||||
sortedColors.append(qMakePair(it.key(), it.value()));
|
||||
}
|
||||
|
||||
std::sort(sortedColors.begin(), sortedColors.end(),
|
||||
[](const QPair<QRgb, int> &a, const QPair<QRgb, int> &b) { return a.second > b.second; });
|
||||
|
||||
// Step 4: Pick top two distinct colors
|
||||
QColor first = QColor(sortedColors.value(0).first);
|
||||
QColor second = first;
|
||||
|
||||
for (int i = 1; i < sortedColors.size(); ++i) {
|
||||
QColor candidate = QColor(sortedColors[i].first);
|
||||
if (candidate != first) {
|
||||
second = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QPair<QColor, QColor>(first, second);
|
||||
}
|
||||
|
||||
void HomeWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
background = background.scaled(size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
|
||||
// Draw already-scaled background centered
|
||||
QSize widgetSize = size();
|
||||
QSize bgSize = background.size();
|
||||
QPoint topLeft((widgetSize.width() - bgSize.width()) / 2, (widgetSize.height() - bgSize.height()) / 2);
|
||||
|
||||
painter.drawPixmap(topLeft, background);
|
||||
|
||||
// Draw translucent black overlay with rounded corners
|
||||
QRectF overlayRect(5, 5, width() - 10, height() - 10); // 5px inset
|
||||
QPainterPath roundedRectPath;
|
||||
roundedRectPath.addRoundedRect(overlayRect, 20, 20); // 20px corner radius
|
||||
|
||||
QColor semiTransparentBlack(0, 0, 0, static_cast<int>(255 * 0.33)); // 33% opacity
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.fillPath(roundedRectPath, semiTransparentBlack);
|
||||
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
42
cockatrice/src/client/ui/widgets/general/home_widget.h
Normal file
42
cockatrice/src/client/ui/widgets/general/home_widget.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#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>
|
||||
#include <QWidget>
|
||||
|
||||
class HomeWidget : public QWidget
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HomeWidget(QWidget *parent, TabSupervisor *tabSupervisor);
|
||||
void updateRandomCard();
|
||||
QPair<QColor, QColor> extractDominantColors(const QPixmap &pixmap);
|
||||
|
||||
public slots:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void initializeBackgroundFromSource();
|
||||
void updateBackgroundProperties();
|
||||
void updateButtonsToBackgroundColor();
|
||||
QGroupBox *createButtons();
|
||||
void updateConnectButton(const ClientStatus status);
|
||||
|
||||
private:
|
||||
QGridLayout *layout;
|
||||
QTimer *cardChangeTimer;
|
||||
TabSupervisor *tabSupervisor;
|
||||
QPixmap background;
|
||||
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
|
||||
DeckLoader *backgroundSourceDeck;
|
||||
QPixmap overlay;
|
||||
QPair<QColor, QColor> gradientColors;
|
||||
HomeStyledButton *connectButton;
|
||||
};
|
||||
|
||||
#endif // HOME_WIDGET_H
|
||||
|
|
@ -59,6 +59,8 @@ public slots:
|
|||
void actCheckCardUpdatesBackground();
|
||||
void actCheckServerUpdates();
|
||||
void actCheckClientUpdates();
|
||||
void actConnect();
|
||||
void actExit();
|
||||
private slots:
|
||||
void updateTabMenu(const QList<QMenu *> &newMenuList);
|
||||
void statusChanged(ClientStatus _status);
|
||||
|
|
@ -77,14 +79,12 @@ private slots:
|
|||
void localGameEnded();
|
||||
void pixmapCacheSizeChanged(int newSizeInMBs);
|
||||
void notifyUserAboutUpdate();
|
||||
void actConnect();
|
||||
void actDisconnect();
|
||||
void actSinglePlayer();
|
||||
void actWatchReplay();
|
||||
void actFullScreen(bool checked);
|
||||
void actRegister();
|
||||
void actSettings();
|
||||
void actExit();
|
||||
void actForgotPasswordRequest();
|
||||
void actAbout();
|
||||
void actTips();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue