Compare commits

..

No commits in common. "d998b975302b67e98f13c2ba0e440052cf9dbb26" and "803fa2175330121c4c5d9011feb35290973e803f" have entirely different histories.

14 changed files with 64 additions and 477 deletions

View file

@ -403,7 +403,6 @@ set(cockatrice_SOURCES
src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_widget.cpp
src/interface/widgets/tabs/api/commander_spellbook/handle_commander_brackets.cpp
src/interface/widgets/onboarding/banner_shader_config.h
src/interface/widgets/onboarding/brand_colors.h
src/interface/widgets/onboarding/first_run_wizard.cpp
src/interface/widgets/onboarding/first_run_wizard.h
src/interface/widgets/onboarding/first_run_wizard_page.cpp
@ -531,7 +530,6 @@ qt6_add_shaders(
"src/interface/widgets/onboarding/shaders"
FILES
src/interface/widgets/onboarding/shaders/brand_banner.frag
src/interface/widgets/onboarding/shaders/brand_plate.frag
)
qt6_add_resources(

View file

@ -11,8 +11,6 @@
#include "home_tab_button_color.h"
#include <QGroupBox>
#include <QLabel>
#include <QLinearGradient>
#include <QPainter>
#include <QPainterPath>
#include <QPushButton>
@ -23,7 +21,8 @@
#include <libcockatrice/settings/paths_settings.h>
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
: QWidget(parent), tabSupervisor(_tabSupervisor), background(themePixmap(QStringLiteral("backgrounds/home")))
: QWidget(parent), tabSupervisor(_tabSupervisor), background(themePixmap(QStringLiteral("backgrounds/home"))),
overlay(themePixmap(QStringLiteral("cockatrice")))
{
layout = new QGridLayout(this);
@ -65,17 +64,12 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
// not on SettingsCache::themeChanged, so re-resolve the variant background.
connect(themeManager, &ThemeManager::themeChanged, this, &HomeWidget::initializeBackgroundFromSource);
connect(themeManager, &ThemeManager::paletteChanged, this, &HomeWidget::updateButtonsToBackgroundColor);
connect(themeManager, &ThemeManager::paletteChanged, this, &HomeWidget::updateLogoOverlay);
connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabButtonColorChanged, this,
&HomeWidget::updateButtonsToBackgroundColor);
}
void HomeWidget::initializeBackgroundFromSource()
{
// The featured logo is theme/scheme-derived too; reload it alongside the
// background so a theme or appearance switch doesn't leave it stale.
updateLogoOverlay();
if (CardDatabaseManager::getInstance()->getLoadStatus() != LoadStatus::Ok) {
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&HomeWidget::initializeBackgroundFromSource);
@ -235,10 +229,10 @@ QGroupBox *HomeWidget::createButtons()
QVBoxLayout *boxLayout = new QVBoxLayout;
boxLayout->setAlignment(Qt::AlignHCenter);
logoLabel = new QLabel;
QLabel *logoLabel = new QLabel;
logoLabel->setPixmap(overlay.scaledToWidth(200, Qt::SmoothTransformation));
logoLabel->setAlignment(Qt::AlignCenter);
boxLayout->addWidget(logoLabel);
updateLogoOverlay();
boxLayout->addSpacing(25);
connectButton = new HomeStyledButton("Connect/Play", gradientColors);
@ -439,56 +433,3 @@ void HomeWidget::paintEvent(QPaintEvent *event)
QWidget::paintEvent(event);
}
void HomeWidget::updateLogoOverlay()
{
// Emulate cockatrice.svg in Qt rather than rendering the baked-in SVG.
// The SVG has no separate plate: the gradient fills the bird's silhouette
// paths (light #c9fd62/AccentSoft at the top-left, dark #139740/AccentStrong
// toward the bottom-right — the SVG's linearGradient4265-7-8 stops along
// its userSpaceOnUse axis), and the white highlight path
// (cockatrice-logo-white) sits on top. So we paint that gradient clipped to
// the full logo silhouette (the full-color logo's alpha), then overlay the
// white mark. Colours stay fully theme-driven and independent of the static
// greens baked into the SVG.
const QColor strong = themeManager->appColor(AppColor::AccentStrong);
const QColor soft = themeManager->appColor(AppColor::AccentSoft);
const QPixmap silhouette = themePixmap(QStringLiteral("cockatrice")).scaledToWidth(200, Qt::SmoothTransformation);
const QPixmap whiteMark =
themePixmap(QStringLiteral("cockatrice-logo-white")).scaledToWidth(200, Qt::SmoothTransformation);
if (silhouette.isNull() || whiteMark.isNull()) {
return;
}
QPixmap composite(silhouette.size());
composite.fill(Qt::transparent);
{
QPainter painter(&composite);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
// Recreate cockatrice.svg's own gradient geometry (linearGradient
// 4265-7-8, userSpaceOnUse): light AccentSoft at S=(-8.097,-97.746),
// dark AccentStrong at E=(162.455,295.208), on the SVG's 300x300
// canvas. Scale those coordinates to this composite's size.
const qreal scale = composite.width() / 300.0;
QLinearGradient gradient(QPointF(-8.097, -97.746) * scale, QPointF(162.455, 295.208) * scale);
gradient.setColorAt(0.0, soft);
gradient.setColorAt(1.0, strong);
painter.fillRect(composite.rect(), gradient);
// Clip the gradient to the full logo silhouette exactly as the SVG's
// gradient paths are confined to the bird.
painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
painter.drawPixmap(0, 0, silhouette);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawPixmap(0, 0, whiteMark);
}
if (logoLabel) {
logoLabel->setPixmap(composite);
}
}

View file

@ -15,9 +15,6 @@
#include <QWidget>
#include <libcockatrice/network/client/abstract/abstract_client.h>
class QGridLayout;
class QLabel;
class HomeWidget : public QWidget
{
@ -44,14 +41,13 @@ private:
QPixmap background;
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
DeckList backgroundSourceDeck;
QLabel *logoLabel = nullptr;
QPixmap overlay;
QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton;
void setRandomCard(ExactCard &newCard);
void loadBackgroundSourceDeck();
QPair<QColor, QColor> determineButtonColor() const;
void updateLogoOverlay();
};
#endif // HOME_WIDGET_H

View file

@ -38,10 +38,6 @@ class BannerShaderConfig : public QObject
Q_PROPERTY(QColor colorA READ colorA WRITE setColorA NOTIFY colorAChanged)
Q_PROPERTY(QColor colorB READ colorB WRITE setColorB NOTIFY colorBChanged)
Q_PROPERTY(QColor accent READ accent WRITE setAccent NOTIFY accentChanged)
Q_PROPERTY(QColor glowColor READ glowColor WRITE setGlowColor NOTIFY glowColorChanged)
Q_PROPERTY(QColor brandStrong READ brandStrong WRITE setBrandStrong NOTIFY brandStrongChanged)
Q_PROPERTY(QColor brandSoft READ brandSoft WRITE setBrandSoft NOTIFY brandSoftChanged)
Q_PROPERTY(qreal vignetteMin READ vignetteMin WRITE setVignetteMin NOTIFY vignetteMinChanged)
Q_PROPERTY(bool logoVisible READ logoVisible WRITE setLogoVisible NOTIFY logoVisibleChanged)
Q_PROPERTY(qreal logoGlow READ logoGlow WRITE setLogoGlow NOTIFY logoGlowChanged)
@ -189,54 +185,6 @@ public:
}
}
QColor glowColor() const
{
return m_glowColor;
}
void setGlowColor(const QColor &c)
{
if (c != m_glowColor) {
m_glowColor = c;
emit glowColorChanged();
}
}
QColor brandStrong() const
{
return m_brandStrong;
}
void setBrandStrong(const QColor &c)
{
if (c != m_brandStrong) {
m_brandStrong = c;
emit brandStrongChanged();
}
}
QColor brandSoft() const
{
return m_brandSoft;
}
void setBrandSoft(const QColor &c)
{
if (c != m_brandSoft) {
m_brandSoft = c;
emit brandSoftChanged();
}
}
qreal vignetteMin() const
{
return m_vignetteMin;
}
void setVignetteMin(qreal v)
{
if (v != m_vignetteMin) {
m_vignetteMin = v;
emit vignetteMinChanged();
}
}
bool logoVisible() const
{
return m_logoVisible;
@ -274,10 +222,6 @@ signals:
void colorAChanged();
void colorBChanged();
void accentChanged();
void glowColorChanged();
void brandStrongChanged();
void brandSoftChanged();
void vignetteMinChanged();
void logoVisibleChanged();
void logoGlowChanged();
@ -295,16 +239,9 @@ private:
bool m_frontIsA = true;
// Curated fallback seed values -- BannerHost overwrites these with
// palette-derived colours (see shader_banner_widget.cpp) before the first
// paint, so they only matter as a safe pre-first-apply default.
QColor m_colorA{0x1A, 0x1A, 0x20};
QColor m_colorB{0x0E, 0x0E, 0x12};
QColor m_accent{0x8B, 0xDD, 0x6B};
QColor m_glowColor{Qt::white};
QColor m_brandStrong{0x13, 0x97, 0x40};
QColor m_brandSoft{0xC9, 0xFD, 0x62};
qreal m_vignetteMin = 0.62;
bool m_logoVisible = false;
qreal m_logoGlow = 1.0;

View file

@ -1,15 +0,0 @@
#ifndef BRAND_COLORS_H
#define BRAND_COLORS_H
#include <QColor>
/** @brief Cockatrice brand green.
*
* Single source of truth for the onboarding brand accent: it backs the
* banner's shader-accent uniform as the curated fallback when the active
* palette resolves no usable Highlight, and it preseads the wizard's
* QuickSetupPanel so a freshly generated palette keeps the brand identity
* until the user picks their own look. */
inline const QColor kCockatriceBrandGreen(0x8B, 0xDD, 0x6B);
#endif // BRAND_COLORS_H

View file

@ -14,31 +14,10 @@
#include <QGroupBox>
#include <QLabel>
#include <QMessageBox>
#include <QPalette>
#include <QVBoxLayout>
#include <libcockatrice/settings/paths_settings.h>
#include <libcockatrice/settings/personal_settings.h>
namespace
{
/** @brief A theme's shipped identity accent, immune to any user- or auto-
* generated palette that may currently be masking appColor(). */
QColor themeIdentityAccent(const QString &themeDirPath, const QString &themeName)
{
for (const QString &scheme : {QStringLiteral("Light"), QStringLiteral("Dark")}) {
const PaletteConfig cfg = ThemeManager::loadDefaultPaletteConfig(themeDirPath, themeName, scheme);
if (cfg.appColors.contains(AppColor::AccentStrong)) {
return cfg.appColors.value(AppColor::AccentStrong);
}
const QColor highlight = cfg.colors.value(QPalette::Active).value(QPalette::Highlight);
if (highlight.isValid()) {
return highlight;
}
}
return {};
}
} // namespace
ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
{
themeCombo = new QComboBox(this);
@ -51,15 +30,6 @@ ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
quickSetupPanel = new QuickSetupPanel(this);
// Seed the picker from the current theme's own identity accent rather than
// a hardcoded brand green: Plasma seeds violet, Fusion green, etc., and it
// is immune to stale generated palettes that may mask appColor(). This was
// initially a brand-green workaround from before Fusion became the default.
// setAccentColor blocks signals, so this never triggers a generation.
lastSeededTheme = SettingsCache::instance().getThemeName();
quickSetupPanel->setAccentColor(
themeIdentityAccent(themeManager->getAvailableThemes().value(lastSeededTheme), lastSeededTheme));
connect(themeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeSetupPage::onThemeChanged);
connect(schemeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeSetupPage::onSchemeChanged);
connect(quickSetupPanel, &QuickSetupPanel::valueChanged, this, &ThemeSetupPage::onGenerateFromAccent);
@ -77,8 +47,7 @@ ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
// Mirrors AppearanceSettingsPage's identical listener for the combo-sync
// half of this.
connect(themeManager, &ThemeManager::themeChanged, this, [this] {
const QString newTheme = SettingsCache::instance().getThemeName();
const QString newDir = themeManager->getAvailableThemes().value(newTheme);
const QString newDir = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName());
const ThemeConfig cfg = ThemeConfig::fromThemeDir(newDir);
const QString current = cfg.colorScheme;
@ -87,14 +56,6 @@ ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
schemeCombo->setCurrentIndex(idx >= 0 ? idx : 0);
schemeCombo->blockSignals(false);
// Keep the picker's accent in step with the theme's own identity; the
// swatch seeded at construction would otherwise stay stale (e.g. green
// from a previous theme) when the user toggles themes.
if (newTheme != lastSeededTheme) {
lastSeededTheme = newTheme;
quickSetupPanel->setAccentColor(themeIdentityAccent(newDir, newTheme));
}
maybeAutoGeneratePalette();
});
@ -197,14 +158,8 @@ void ThemeSetupPage::maybeAutoGeneratePalette()
const QString dirPath = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName());
const QString scheme = resolvedScheme();
// The theme dir may resolve to the user profile even for built-in themes
// (getAvailableThemes gives the user copy precedence), so consult the
// shipped palette too -- both via loadDefaultPaletteConfig's system fallback.
// Without it, scheme flips regenerate a fresh palette from the picker accent
// and clobber the curated colours the theme explicitly ships.
if (PaletteConfig::fromScheme(dirPath, scheme).hasPalette() ||
ThemeManager::loadDefaultPaletteConfig(dirPath, SettingsCache::instance().getThemeName(), scheme)
.hasPalette()) {
PaletteConfig::fromDefault(dirPath, scheme).hasPalette()) {
return; // theme already has something real to show -- leave it alone
}

View file

@ -53,9 +53,6 @@ private:
QComboBox *homeTabBackgroundCombo;
bool paletteDirty = false;
/// Theme whose identity accent currently seeds the picker.
QString lastSeededTheme;
};
#endif // THEME_SETUP_PAGE_H

View file

@ -16,8 +16,6 @@ Item {
property vector4d uColorA: Qt.vector4d(bannerConfig.colorA.r, bannerConfig.colorA.g, bannerConfig.colorA.b, 1.0)
property vector4d uColorB: Qt.vector4d(bannerConfig.colorB.r, bannerConfig.colorB.g, bannerConfig.colorB.b, 1.0)
property vector4d uAccent: Qt.vector4d(bannerConfig.accent.r, bannerConfig.accent.g, bannerConfig.accent.b, 1.0)
property vector4d uGlowColor: Qt.vector4d(bannerConfig.glowColor.r, bannerConfig.glowColor.g, bannerConfig.glowColor.b, 1.0)
property real uVignetteMin: bannerConfig.vignetteMin
property real uLogoGlow: bannerConfig.logoGlow
fragmentShader: "qrc:/onboarding/shaders/brand_banner.frag.qsb"
}
@ -35,62 +33,30 @@ Item {
property vector4d uColorA: Qt.vector4d(bannerConfig.colorA.r, bannerConfig.colorA.g, bannerConfig.colorA.b, 1.0)
property vector4d uColorB: Qt.vector4d(bannerConfig.colorB.r, bannerConfig.colorB.g, bannerConfig.colorB.b, 1.0)
property vector4d uAccent: Qt.vector4d(bannerConfig.accent.r, bannerConfig.accent.g, bannerConfig.accent.b, 1.0)
property vector4d uGlowColor: Qt.vector4d(bannerConfig.glowColor.r, bannerConfig.glowColor.g, bannerConfig.glowColor.b, 1.0)
property real uVignetteMin: bannerConfig.vignetteMin
property real uLogoGlow: bannerConfig.logoGlow
fragmentShader: "qrc:/onboarding/shaders/brand_banner.frag.qsb"
}
// The white logo sits at full opacity on top of the static gradient plate —
// no glow, no breathing. The plate matches home_widget's QPainter composite.
Item {
id: logoHost
visible: bannerConfig.logoVisible
// The hero logo itself — breathes cleanly over a 0.5–1.0 opacity range
Image {
id: logo
anchors.centerIn: parent
visible: bannerConfig.logoVisible
source: "qrc:/resources/cockatrice-logo-white.svg"
width: root.height * 0.6
height: width
height: width * (sourceSize.height > 0 ? sourceSize.height / Math.max(sourceSize.width, 1) : 1)
fillMode: Image.PreserveAspectFit
smooth: true
opacity: 0.5 + 0.5 * bannerConfig.logoGlow
sourceSize: Qt.size(256, 256)
// The full-color logo renders beneath the white mark and is consumed as
// a texture (layer.enabled) by the plate shader's silhouette mask, so
// the gradient is clipped to the bird exactly as the SVG's gradient
// paths are. It is never drawn to the screen itself.
Image {
id: silhouetteMask
anchors.fill: parent
source: "qrc:/resources/cockatrice.svg"
sourceSize: Qt.size(256, 256)
fillMode: Image.PreserveAspectFit
smooth: true
visible: false
layer.enabled: true
layer.smooth: true
}
Behavior on opacity { NumberAnimation { duration: 300; easing.type: Easing.InOutSine } }
// The logo's gradient plate, drawn behind the mark: a linear
// AccentSoft (light) -> AccentStrong (dark) sheet along the same
// top-left -> bottom-right userSpaceOnUse axis the baked-in SVG used,
// clipped to the bird silhouette via uSilhouette. The white highlight
// path above is theme independent. Sized to the logo itself — no
// rounded badge, matching home_widget's QPainter composite. Static.
// Small ShaderEffect, Qt 6.4-safe.
ShaderEffect {
id: brandPlate
anchors.fill: parent
property vector4d uStrong: Qt.vector4d(bannerConfig.brandStrong.r, bannerConfig.brandStrong.g,
bannerConfig.brandStrong.b, 1.0)
property vector4d uSoft: Qt.vector4d(bannerConfig.brandSoft.r, bannerConfig.brandSoft.g,
bannerConfig.brandSoft.b, 1.0)
property var uSilhouette: silhouetteMask
fragmentShader: "qrc:/onboarding/shaders/brand_plate.frag.qsb"
}
Image {
id: logoImage
anchors.fill: parent
source: "qrc:/resources/cockatrice-logo-white.svg"
sourceSize: Qt.size(256, 256)
fillMode: Image.PreserveAspectFit
smooth: true
transform: Scale {
origin.x: logo.width / 2
origin.y: logo.height / 2
xScale: 0.94 + 0.06 * bannerConfig.logoGlow
yScale: 0.94 + 0.06 * bannerConfig.logoGlow
}
}
}
}

View file

@ -1,10 +1,7 @@
#include "shader_banner_widget.h"
#include "../../theme_manager.h"
#include "banner_shader_config.h"
#include "brand_colors.h"
#include <QApplication>
#include <QPainter>
#include <QQmlContext>
#include <QQmlEngine>
@ -14,98 +11,11 @@
namespace
{
// Curated near-black stage -- used only when the active palette resolves no
// usable window colour. Matches the banner's original design (dark and quiet
// so the accent stands out) and satisfies design-plans §2.1's "identity
// survives a bare palette".
constexpr QRgb kFallbackColorA = 0x1A1A20;
constexpr QRgb kFallbackColorB = 0x0E0E12;
struct SuggestedColors
{
QColor colorA;
QColor colorB;
QColor accent;
QColor glowColor;
QColor brandStrong;
QColor brandSoft;
qreal vignetteMin = 0.62;
};
SuggestedColors suggestedBannerColors()
{
const QPalette &pal = qApp->palette();
const QColor window = pal.color(QPalette::Active, QPalette::Window);
// Identity accent: the theme's [AppColors] AccentStrong, which appColor()
// resolves to QPalette::Highlight when a theme doesn't pin AccentStrong.
// Reading bare Highlight ignored curated accent tokens (Plasma's violet
// vs Default's green) whenever a palette didn't set the role itself.
const QColor accentStrong = themeManager->appColor(AppColor::AccentStrong);
if (!window.isValid() || !accentStrong.isValid()) {
return {QColor(kFallbackColorA),
QColor(kFallbackColorB),
kCockatriceBrandGreen,
QColor(Qt::white),
kCockatriceBrandGreen,
QColor(0xC9, 0xFD, 0x62),
0.62};
}
// The theme's brand pair: AccentStrong is the deep green, AccentSoft the
// lime. These two appColors form the logo's "surrounding gradient" (deep
// core grading out to the soft, brand-toned glow) on both the banner and
// the home screen.
const QColor brandStrong = accentStrong;
const QColor brandSoft = themeManager->appColor(AppColor::AccentSoft);
// Dress the stage for the scheme so the banner never fights the
// surrounding window in either mode. Dark palettes keep the original
// quiet near-black stage (lightness 29 → 16) with the theme's window
// hue; light palettes get a pastel "frosted accent" treatment built from
// the accent hue instead of a plain near-white copy: a coloured wash that
// clearly belongs to the theme.
const qreal luma = 0.299 * window.red() + 0.587 * window.green() + 0.114 * window.blue();
const bool lightStage = luma > 115.0;
if (lightStage) {
const int hue = accentStrong.hslHue();
// Achromatic accents (grey) get a neutral near-white stage instead.
const int stageSat = hue < 0 ? 0 : 64;
const int hueSafe = hue < 0 ? 0 : hue;
// Depth is what stops a light stage reading as a washed-out near-white
// copy of the page behind the banner: deepen the lower pastel band and
// raise saturation so the hue is clearly present while staying frosted.
auto pastel = [hueSafe, stageSat](int lightness) { return QColor::fromHsl(hueSafe, stageSat, lightness); };
auto pastelLower = [hueSafe](int lightness) { return QColor::fromHsl(hueSafe, 76, lightness); };
// Brightness-lifted accent for additive glows: the raw accent on a
// light stage must be mid-bright to read instead of washing out, so
// lift lightness and saturation together.
const int accentLightness = qBound(158, accentStrong.lightness() + 82, 198);
const int accentSaturation = hue < 0 ? 0 : qMax(accentStrong.hslSaturation(), 180);
const QColor liftedAccent =
hue < 0 ? accentStrong : QColor::fromHsl(hueSafe, accentSaturation, accentLightness);
// The centre glow (and logo tint in QML) uses the deep accent itself:
// a coloured halo/fill behind the logo instead of a white or black one.
return {pastel(214), pastelLower(186), liftedAccent, accentStrong, brandStrong, brandSoft, 0.80};
}
// Dark stage: force the window hue down to the banner's curated darkness,
// scaling saturation away so chromatic palettes tint it without going
// muddy. The accent is the bright, brand-driven tone (hue from the accent
// itself, never the -- often grey -- window), and it drives both the
// embers/fog and the logo glow so the mark tints like the light stage.
auto stage = [&window](int lightness) {
const int hue = window.hslHue();
const int saturation = hue < 0 ? 0 : qBound(0, qRound(window.hslSaturation() * (lightness / 40.0)), 255);
return QColor::fromHsl(hue, saturation, lightness);
};
const int accentHue = accentStrong.hslHue();
const int accentHueSafe = accentHue < 0 ? 0 : accentHue;
const int accentLightness = qBound(150, accentStrong.lightness() + 70, 185);
const int accentSaturation = accentHue < 0 ? 0 : qMax(accentStrong.hslSaturation(), 160);
const QColor accent =
accentHue < 0 ? accentStrong : QColor::fromHsl(accentHueSafe, accentSaturation, accentLightness);
return {stage(29), stage(16), accent, accent, brandStrong, brandSoft, 0.62};
}
// Near-black base palette -- the background is dark and quiet so the green
// accent stands out.
constexpr QRgb kColorA = 0x1A1A20;
constexpr QRgb kColorB = 0x0E0E12;
constexpr QRgb kAccent = 0x8BDD6B;
} // namespace
class GradientFallbackWidget : public QWidget
@ -113,27 +23,15 @@ class GradientFallbackWidget : public QWidget
public:
using QWidget::QWidget;
void setColors(const QColor &a, const QColor &b)
{
if (a != colorA || b != colorB) {
colorA = a;
colorB = b;
}
}
protected:
void paintEvent(QPaintEvent *) override
{
QPainter painter(this);
QLinearGradient gradient(0, 0, width(), height());
gradient.setColorAt(0.0, colorA);
gradient.setColorAt(1.0, colorB);
gradient.setColorAt(0.0, QColor(kColorA));
gradient.setColorAt(1.0, QColor(kColorB));
painter.fillRect(rect(), gradient);
}
private:
QColor colorA{QColor(kFallbackColorA)};
QColor colorB{QColor(kFallbackColorB)};
};
BannerHost::BannerHost(QWidget *parent) : QWidget(parent)
@ -164,9 +62,6 @@ BannerHost::BannerHost(QWidget *parent) : QWidget(parent)
connect(&clock, &QTimer::timeout, this, &BannerHost::tick);
clock.setInterval(16); // ~60fps; the shader itself is cheap, this is just a wall clock
connect(themeManager, &ThemeManager::themeChanged, this, &BannerHost::applyThemeColors);
applyThemeColors();
applyMotifPreset(currentMotif);
updateAspect();
}
@ -228,9 +123,9 @@ void BannerHost::applyMotifPreset(Motif motif)
const Preset p = presetFor(motif);
config->setColorA(bannerColorA);
config->setColorB(bannerColorB);
config->setAccent(bannerAccent);
config->setColorA(QColor(kColorA));
config->setColorB(QColor(kColorB));
config->setAccent(QColor(kAccent));
config->setLogoVisible(motif == Motif::Welcome);
if (isFirstApply) {
@ -288,34 +183,8 @@ void BannerHost::hideEvent(QHideEvent *event)
clock.stop();
}
void BannerHost::applyThemeColors()
{
const SuggestedColors colors = suggestedBannerColors();
bannerColorA = colors.colorA;
bannerColorB = colors.colorB;
bannerAccent = colors.accent;
if (usingFallback) {
fallback->setColors(bannerColorA, bannerColorB);
fallback->update();
} else if (config) {
config->setColorA(bannerColorA);
config->setColorB(bannerColorB);
config->setAccent(bannerAccent);
config->setGlowColor(colors.glowColor);
config->setBrandStrong(colors.brandStrong);
config->setBrandSoft(colors.brandSoft);
config->setVignetteMin(colors.vignetteMin);
}
}
void BannerHost::tick()
{
// Palette previews (e.g. accent drags in the wizard's QuickSetupPanel)
// apply qApp->palette() without firing themeChanged, so re-derive here;
// BannerShaderConfig's setters are equality-guarded, so this is a no-op
// unless the colours actually changed.
applyThemeColors();
if (config) {
qreal t = elapsed.elapsed() / 1000.0;
config->setTime(t);

View file

@ -1,7 +1,6 @@
#ifndef SHADER_BANNER_WIDGET_H
#define SHADER_BANNER_WIDGET_H
#include <QColor>
#include <QElapsedTimer>
#include <QTimer>
#include <QWidget>
@ -54,7 +53,6 @@ protected:
private slots:
void tick();
void onSceneGraphFailed();
void applyThemeColors();
private:
struct Preset
@ -75,12 +73,6 @@ private:
BannerShaderConfig *config = nullptr;
GradientFallbackWidget *fallback = nullptr;
// Palette-derived banner colours -- the theme's window hue forced down to
// the banner's curated darkness, plus the theme's Highlight as accent.
QColor bannerColorA;
QColor bannerColorB;
QColor bannerAccent;
QTimer clock;
QElapsedTimer elapsed;
Motif currentMotif = Motif::Welcome;

View file

@ -28,8 +28,6 @@ layout(std140, binding = 0) uniform buf
vec4 uColorA;
vec4 uColorB;
vec4 uAccent;
vec4 uGlowColor;
float uVignetteMin;
float uLogoGlow;
};
@ -121,7 +119,7 @@ vec3 backgroundField(vec2 uv, float time)
// Accent-coloured fog layer: flowNoise peaks above 0.6 contribute accent
float fog = flowNoise(uv * 0.8 + vec2(100.0, 50.0), time * 0.02);
col += uAccent.rgb * max(fog - 0.6, 0.0) * 0.14;
col += uAccent.rgb * max(fog - 0.6, 0.0) * 0.10;
return col;
}
@ -138,17 +136,14 @@ vec3 motifWelcome(vec2 uv, vec3 bg, float t)
vec2 center = vec2(asp * 0.5, 0.5);
float cDist = length(ac - center);
// Centre bloom at logo position; intensity scales with uLogoGlow. The
// QML brandGlow halo now supplies the primary logo surround (the two
// brand appColors), so this shader bloom is deliberately kept as a subtle
// ambience rather than a competing glow.
// Centre bloom at logo position; intensity scales with uLogoGlow
float centreLight = bloom(cDist, 0.08 * asp, 0.40 * asp);
col += uGlowColor.rgb * centreLight * 0.20 * uLogoGlow;
col += centreLight * 0.20 * uLogoGlow;
// Flow-noise shimmer gated by Gaussian mask at centre
float shimmer = flowNoise(ac * 0.8 + vec2(55.0, 33.0), t * 0.05) * 0.5 + 0.5;
float shimmerMask = exp(-(cDist * cDist) / (0.18 * asp * 0.18 * asp));
col += uGlowColor.rgb * shimmer * shimmerMask * 0.04 * uLogoGlow;
col += shimmer * shimmerMask * 0.04 * uLogoGlow;
// 48 ember particles: hash-seeded position, speed, size, brightness.
// Embers within a distance threshold of centre are deflected into an
@ -167,8 +162,8 @@ vec3 motifWelcome(vec2 uv, vec3 bg, float t)
float pX = baseX * asp + sin(t * driftFreq + fi * 1.7) * driftAmp * asp;
float pY = fract(baseY + t * riseSpeed);
float size = 0.010 + hash21(vec2(fi * 2.9, uSeed * 4.7)) * 0.014;
float bright = 0.18 + hash21(vec2(fi * 6.1, uSeed * 0.9)) * 0.32;
float size = 0.006 + hash21(vec2(fi * 2.9, uSeed * 4.7)) * 0.012;
float bright = 0.15 + hash21(vec2(fi * 6.1, uSeed * 0.9)) * 0.30;
// Fade out near top/bottom edges
float edgeFade = smoothstep(0.0, 0.12, pY) * smoothstep(1.0, 0.88, pY);
@ -237,7 +232,7 @@ vec3 motifCardDatabase(vec2 uv, vec3 bg, float t)
// Semi-transparent dark fill
float fill = smoothstep(0.015, -0.005, d);
col = mix(col, uColorB.rgb * 0.60, fill * 0.62);
col = mix(col, uColorB.rgb * 0.55, fill * 0.50);
// Accent outline
float edge = smoothstep(0.035, 0.0, abs(d));
@ -314,7 +309,7 @@ vec3 motifAccount(vec2 uv, vec3 bg, float t)
// Node glow via bloom; intensity modulated by pulse
float dist = length(ac - pos);
col += uAccent.rgb * bloom(dist, 0.018, 0.08) * mix(0.30, 0.55, pulse);
col += uAccent.rgb * bloom(dist, 0.018, 0.08) * mix(0.20, 0.45, pulse);
}
// Edges: connect nodes within a radius threshold
@ -328,19 +323,19 @@ vec3 motifAccount(vec2 uv, vec3 bg, float t)
vec2 ba = nodePos[j] - nodePos[i];
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
float lineDist = length(pa - ba * h);
col += uAccent.rgb * smoothstep(0.010, 0.0, lineDist) * strength * 0.14;
col += uAccent.rgb * smoothstep(0.010, 0.0, lineDist) * strength * 0.10;
}
}
}
// Central bloom at banner centre
float cDist = length(ac - center);
col += uAccent.rgb * bloom(cDist, 0.04, 0.25) * 0.20;
col += uAccent.rgb * bloom(cDist, 0.04, 0.25) * 0.12;
// Periodic expanding ring from centre
float ripplePhase = t * 0.4;
float rippleDist = abs(cDist - fract(ripplePhase) * asp * 0.7);
col += uAccent.rgb * smoothstep(0.02, 0.0, rippleDist) * 0.14;
col += uAccent.rgb * smoothstep(0.02, 0.0, rippleDist) * 0.10;
return col;
}
@ -461,8 +456,6 @@ void main()
else if (uMode < 4.5) col = motifPreferences(uv, bg, t);
else col = motifFinish(uv, bg, t);
// Corner vignette; uVignetteMin is scheme-driven (0.62 on dark stages,
// gentler on light ones so near-white corners don't go muddy grey).
col *= mix(uVignetteMin, 1.0, vignette(uv));
col *= mix(0.62, 1.0, vignette(uv));
fragColor = vec4(col, 1.0) * qt_Opacity;
}

View file

@ -1,42 +0,0 @@
#version 440
// The logo's gradient plate, drawn by us rather than the baked-in SVG: a
// linear blend between the two brand appColors (light AccentSoft at the
// top-left grading to dark AccentStrong at the bottom-right, mirroring
// cockatrice.svg's linearGradient4265-7-8 userSpaceOnUse axis), clipped to
// the bird's full silhouette via the full-color logo's alpha (uSilhouette).
// The white highlight path (cockatrice-logo-white) is overlaid in QML on top,
// exactly as the SVG stacks its white path over the gradient paths. Fully
// static: no glow, no breathing — the plate just sits there like the home
// widget's QPainter composite.
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf
{
mat4 qt_Matrix;
float qt_Opacity;
vec4 uStrong;
vec4 uSoft;
};
// The full-color logo's alpha channel acts as the silhouette mask: the
// gradient only appears inside the bird, exactly like the SVG's gradient paths.
layout(binding = 1) uniform sampler2D uSilhouette;
void main()
{
// Recreate cockatrice.svg's own gradient geometry (linearGradient4265-7-8,
// userSpaceOnUse): light AccentSoft at the start point S=(-8.097,-97.746),
// dark AccentStrong at the end E=(162.455,295.208), on the SVG's 300x300
// canvas. Normalized to UV space, V=E-S=(0.5685,1.3098), so
// t = dot(uv - S_norm, V)/|V|^2 with S_norm=(-0.0270,-0.3258).
float t = clamp(dot(qt_TexCoord0 - vec2(-0.02699, -0.32582), vec2(0.56851, 1.30985)) / 2.03891, 0.0, 1.0);
vec3 color = mix(uSoft.rgb, uStrong.rgb, t);
// Anti-aliased silhouette clip from the full-color logo's alpha.
float alpha = texture(uSilhouette, qt_TexCoord0).a;
fragColor = vec4(color * alpha, alpha) * qt_Opacity;
}

View file

@ -11,15 +11,15 @@ ButtonText = #ffffffff
Base = #ff2d2d2d
Window = #ff1e1e1e
Shadow = #ff000000
Highlight = #ff139740
Highlight = #ff148c3c
HighlightedText = #ffffffff
Link = #ffc9fd62
LinkVisited = #ff9ad43e
Link = #ff00f652
LinkVisited = #ff00d346
AlternateBase = #ff353535
ToolTipBase = #ff3c3c3c
ToolTipText = #ffd4d4d4
PlaceholderText = #80ffffff
Accent = #ffc9fd62
Accent = #ff00d346
[Palette.Disabled]
WindowText = #ff9d9d9d
@ -34,7 +34,7 @@ ButtonText = #ff9d9d9d
Base = #ff1e1e1e
Window = #ff1e1e1e
Shadow = #ff000000
Highlight = #ff139740
Highlight = #ff148c3c
HighlightedText = #ffffffff
Link = #ff308cc6
LinkVisited = #ffff00ff
@ -59,8 +59,8 @@ Window = #ff1e1e1e
Shadow = #ff000000
Highlight = #ff1e1e1e
HighlightedText = #ffffffff
Link = #ffc9fd62
LinkVisited = #ff9ad43e
Link = #ff00f652
LinkVisited = #ff00d346
AlternateBase = #ff353535
ToolTipBase = #ff3c3c3c
ToolTipText = #ffd4d4d4
@ -69,5 +69,5 @@ Accent = #ff1e1e1e
[AppColors]
AccentStrong = #ff139740
AccentSoft = #ffc9fd62
AccentStrong = #ff148c3c
AccentSoft = #ff78c850

View file

@ -11,15 +11,15 @@ ButtonText = #ff000000
Base = #ffffffff
Window = #fff0f0f0
Shadow = #ff696969
Highlight = #ff139740
Highlight = #ff148c3c
HighlightedText = #ffffffff
Link = #ff0e6b31
LinkVisited = #ff0a521f
Link = #ff0d5f28
LinkVisited = #ff08401b
AlternateBase = #ffe9e7e3
ToolTipBase = #ffffffdc
ToolTipText = #ff000000
PlaceholderText = #80000000
Accent = #ff139740
Accent = #ff107532
[Palette.Disabled]
WindowText = #ff787878
@ -34,7 +34,7 @@ ButtonText = #ff787878
Base = #fff0f0f0
Window = #fff0f0f0
Shadow = #ff000000
Highlight = #ff139740
Highlight = #ff148c3c
HighlightedText = #ffffffff
Link = #ff0000ff
LinkVisited = #ffff00ff
@ -59,8 +59,8 @@ Window = #fff0f0f0
Shadow = #ff696969
Highlight = #fff0f0f0
HighlightedText = #ff000000
Link = #ff0e6b31
LinkVisited = #ff0a521f
Link = #ff0d5f28
LinkVisited = #ff08401b
AlternateBase = #ffe9e7e3
ToolTipBase = #ffffffdc
ToolTipText = #ff000000
@ -69,5 +69,5 @@ Accent = #fff0f0f0
[AppColors]
AccentStrong = #ff139740
AccentSoft = #ffc9fd62
AccentStrong = #ff148c3c
AccentSoft = #ff78c850