diff --git a/src/citron/game_list.cpp b/src/citron/game_list.cpp index 04c0d9bbb8..6b52c185c4 100644 --- a/src/citron/game_list.cpp +++ b/src/citron/game_list.cpp @@ -177,6 +177,11 @@ protected: painter.restore(); } + + // Draw center indicator line + const bool is_dark = Theme::IsDarkMode(); + painter.setPen(QPen(is_dark ? Qt::white : Qt::black, 4, Qt::SolidLine, Qt::RoundCap)); + painter.drawLine(widget_center_x, 20, widget_center_x, height() - 20); } private: @@ -277,6 +282,12 @@ public: QString accent = Theme::GetAccentColor(); if (accent.isEmpty()) accent = QStringLiteral("#0096ff"); + + QColor acc_color(accent); + if (!dark && acc_color.lightnessF() > 0.6) { + acc_color.setHslF(acc_color.hslHueF(), acc_color.hslSaturationF(), 0.5); + accent = acc_color.name(); + } QString nav_style = dark ? QStringLiteral( diff --git a/src/citron/main.cpp b/src/citron/main.cpp index f3217ea857..97f3744101 100644 --- a/src/citron/main.cpp +++ b/src/citron/main.cpp @@ -104,6 +104,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include #include #include +#include #include #include #include @@ -330,15 +331,17 @@ static void OverrideWindowsFont() { #endif bool GMainWindow::CheckDarkMode() { -#ifdef __unix__ +#ifdef _WIN32 + QSettings theme_settings( + QStringLiteral("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"), + QSettings::NativeFormat); + return theme_settings.value(QStringLiteral("AppsUseLightTheme"), 1).toInt() == 0; +#else const QPalette test_palette(qApp->palette()); const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text); const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); return (text_color.value() > window_color.value()); -#else - // TODO: Windows - return false; -#endif // __unix__ +#endif } GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan) @@ -6363,10 +6366,6 @@ void GMainWindow::UpdateUITheme() { current_theme = default_theme_name; } -#ifdef _WIN32 - QIcon::setThemeName(current_theme); - AdjustLinkColor(); -#else bool is_adaptive_theme = (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")); @@ -6386,15 +6385,14 @@ void GMainWindow::UpdateUITheme() { AdjustLinkColor(); } -#endif - - // The rest of the function remains the same, loading the resolved theme name. - if (current_theme != default_theme_name) { + // Always load the stylesheet unless the theme is the true default (no explicit QSS) + if (current_theme != QStringLiteral("default")) { QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)}; QFile f(theme_uri); if (!f.open(QFile::ReadOnly | QFile::Text)) { - LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", - UISettings::values.theme); + LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to empty stylesheet", + current_theme.toStdString()); + qApp->setStyleSheet(QStringLiteral("")); } else { qApp->setStyleSheet(QString::fromUtf8(f.readAll())); } diff --git a/src/citron/ui/game_carousel_view.cpp b/src/citron/ui/game_carousel_view.cpp index 0fc25b88c7..622983e459 100644 --- a/src/citron/ui/game_carousel_view.cpp +++ b/src/citron/ui/game_carousel_view.cpp @@ -416,7 +416,16 @@ QColor CinematicCarousel::CardBg() const { QColor CinematicCarousel::TextColor() const { return Theme::IsDarkMode() ? QColor(255, 255, 255) : QColor(45, 45, 48); } -QColor CinematicCarousel::AccentColor() const { const QString h = QString::fromStdString(UISettings::values.accent_color.GetValue()); return QColor(h).isValid() ? QColor(h) : QColor(0, 150, 255); } +QColor CinematicCarousel::AccentColor() const { + const QString h = QString::fromStdString(UISettings::values.accent_color.GetValue()); + QColor acc = QColor(h).isValid() ? QColor(h) : QColor(0, 150, 255); + if (!Theme::IsDarkMode() && acc.lightnessF() > 0.6) { + acc.setHslF(acc.hslHueF(), acc.hslSaturationF(), 0.5); + } else if (Theme::IsDarkMode() && acc.lightnessF() < 0.4) { + acc.setHslF(acc.hslHueF(), acc.hslSaturationF(), 0.6); + } + return acc; +} GameCarouselView::GameCarouselView(QWidget* parent) : QWidget(parent) { m_layout = new QVBoxLayout(this); diff --git a/src/citron/ui/game_grid_view.cpp b/src/citron/ui/game_grid_view.cpp index 1f89c5023a..3f631d1937 100644 --- a/src/citron/ui/game_grid_view.cpp +++ b/src/citron/ui/game_grid_view.cpp @@ -36,8 +36,8 @@ protected: painter.setRenderHint(QPainter::Antialiasing, false); if (UISettings::values.game_list_poster_view.GetValue()) { - // Modern, dark background for Poster View - painter.fillRect(e->rect(), QColor(18, 18, 20)); + // Modern background for Poster View + painter.fillRect(e->rect(), Theme::IsDarkMode() ? QColor(18, 18, 20) : QColor(248, 248, 250)); return; } @@ -279,7 +279,7 @@ GameGridView::GameGridView(QWidget* parent) : QWidget(parent) { void GameGridView::ApplyTheme() { const QString accent_color = Theme::GetAccentColor(); - [[maybe_unused]] const bool dark = UISettings::IsDarkTheme(); + const bool dark = Theme::IsDarkMode(); m_container->setAutoFillBackground(false); m_scroll_area->setAutoFillBackground(false); @@ -325,14 +325,27 @@ void GameGridView::ApplyTheme() { m_fav_view->setStyleSheet(list_style); m_main_view->setStyleSheet(list_style); - // Update labels with slightly better contrast if needed + // Update labels with theme-aware text colors + const QString text_color = dark ? QStringLiteral("#f5f5f5") : QStringLiteral("#1a1a1e"); QString label_style = QStringLiteral( - "QLabel { color: #f5f5f5; font-weight: bold; font-size: 16px; " + "QLabel { color: %2; font-weight: bold; font-size: 16px; " "padding: 8px 0px 6px 28px; " - "border-bottom: 2px solid %1; background: transparent; }").arg(accent_color); + "border-bottom: 2px solid %1; background: transparent; }").arg(accent_color, text_color); if (m_fav_label) m_fav_label->setStyleSheet(label_style); if (m_main_label) m_main_label->setStyleSheet(label_style + QStringLiteral("margin-top: 18px;")); + + if (m_top_help) { + m_top_help->setStyleSheet(QStringLiteral( + "QLabel { color: %1; font-weight: bold; font-family: 'Outfit', 'Inter', sans-serif; font-size: 14px; }" + ).arg(dark ? QStringLiteral("rgba(255, 255, 255, 140)") : QStringLiteral("rgba(30, 30, 35, 180)"))); + } + + if (m_bottom_hint) { + m_bottom_hint->setStyleSheet(QStringLiteral( + "QLabel { color: %1; font-style: italic; font-size: 13px; }" + ).arg(dark ? QStringLiteral("rgba(255, 255, 255, 100)") : QStringLiteral("rgba(30, 30, 35, 120)"))); + } } void GameGridView::setModels(QAbstractItemModel* fav_model, QAbstractItemModel* main_model) { diff --git a/src/citron/util/blackjack_widget.cpp b/src/citron/util/blackjack_widget.cpp index d6331e75cc..a34f878914 100644 --- a/src/citron/util/blackjack_widget.cpp +++ b/src/citron/util/blackjack_widget.cpp @@ -13,20 +13,38 @@ BlackjackWidget::BlackjackWidget(QWidget* parent) : QWidget(parent) { m_status_label = new QLabel(tr("Blackjack! Beat the dealer to win a surprise."), this); m_status_label->setAlignment(Qt::AlignCenter); - m_status_label->setStyleSheet(QStringLiteral("font-size: 14px; font-weight: bold; color: white;")); - + auto* btn_layout = new QHBoxLayout(); m_hit_button = new QPushButton(tr("Hit"), this); m_stand_button = new QPushButton(tr("Stand"), this); - + + const bool is_dark = Theme::IsDarkMode(); const QString accent = Theme::GetAccentColor(); + + m_status_label->setStyleSheet(QStringLiteral("font-size: 14px; font-weight: bold; color: %1;") + .arg(is_dark ? QStringLiteral("white") : QStringLiteral("#1a1a1e"))); + + const QString btn_bg = is_dark ? QStringLiteral("#32323a") : QStringLiteral("#f0f0f5"); + const QString btn_fg = is_dark ? QStringLiteral("white") : QStringLiteral("#1a1a1e"); + const QString btn_border = is_dark ? QStringLiteral("#42424a") : QStringLiteral("#d0d0d5"); + const QString btn_hover_bg = is_dark ? QStringLiteral("#3d3d45") : QStringLiteral("#e0e0e5"); + const QString btn_disabled_bg = is_dark ? QStringLiteral("#1a1a1e") : QStringLiteral("#e8e8ed"); + const QString btn_disabled_fg = is_dark ? QStringLiteral("#555555") : QStringLiteral("#aaaaaa"); + const QString btn_disabled_border = is_dark ? QStringLiteral("#24242a") : QStringLiteral("#dcdce0"); + + // Calculate contrast color for when the button is pressed (using accent color) + const QColor accent_color(accent); + const double accent_lum = (0.299 * accent_color.red() + 0.587 * accent_color.green() + 0.114 * accent_color.blue()) / 255.0; + const QString btn_pressed_fg = accent_lum > 0.65 ? QStringLiteral("black") : QStringLiteral("white"); + const QString btn_style = QStringLiteral( - "QPushButton { background-color: #32323a; color: white; border: 1px solid #42424a; " + "QPushButton { background-color: %2; color: %3; border: 1px solid %4; " "border-radius: 12px; padding: 12px 32px; font-weight: bold; font-size: 15px; } " - "QPushButton:hover { background-color: #3d3d45; border-color: %1; } " - "QPushButton:pressed { background-color: %1; color: black; } " - "QPushButton:disabled { background-color: #1a1a1e; color: #555555; border: 1px solid #24242a; }" - ).arg(accent); + "QPushButton:hover { background-color: %5; border-color: %1; } " + "QPushButton:pressed { background-color: %1; color: %6; } " + "QPushButton:disabled { background-color: %7; color: %8; border: 1px solid %9; }" + ).arg(accent, btn_bg, btn_fg, btn_border, btn_hover_bg, btn_pressed_fg, btn_disabled_bg, btn_disabled_fg, btn_disabled_border); + m_hit_button->setStyleSheet(btn_style); m_stand_button->setStyleSheet(btn_style); @@ -199,7 +217,7 @@ void BlackjackWidget::paintEvent(QPaintEvent*) { p.setRenderHint(QPainter::Antialiasing); auto drawHand = [&](std::vector& hand, int y_base, const QString& label) { - p.setPen(Qt::white); + p.setPen(Theme::IsDarkMode() ? Qt::white : QColor(26, 26, 30)); p.setFont(QFont(QStringLiteral("sans-serif"), 12, QFont::Bold)); qreal total_hand_width = hand.size() * 85 - 15; diff --git a/src/citron/util/cup_shuffle_widget.cpp b/src/citron/util/cup_shuffle_widget.cpp index ec3d4d6b73..d977305fd5 100644 --- a/src/citron/util/cup_shuffle_widget.cpp +++ b/src/citron/util/cup_shuffle_widget.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "citron/theme.h" CupShuffleWidget::CupShuffleWidget(QWidget* parent) : QWidget(parent) { m_citron_logo.load(QStringLiteral(":/citron.svg")); @@ -11,18 +12,29 @@ CupShuffleWidget::CupShuffleWidget(QWidget* parent) : QWidget(parent) { auto* layout = new QVBoxLayout(this); m_status_label = new QLabel(tr("Find the game icon!"), this); m_status_label->setAlignment(Qt::AlignCenter); - m_status_label->setStyleSheet(QStringLiteral("font-size: 16px; font-weight: bold; color: white;")); + const bool is_dark = Theme::IsDarkMode(); + m_status_label->setStyleSheet(QStringLiteral("font-size: 16px; font-weight: bold; color: %1;") + .arg(is_dark ? QStringLiteral("white") : QStringLiteral("#1a1a1e"))); m_start_button = new QPushButton(tr("Shuffle Cups"), this); + const QString accent = Theme::GetAccentColor(); + const QColor accent_color(accent); + const double accent_lum = (0.299 * accent_color.red() + 0.587 * accent_color.green() + 0.114 * accent_color.blue()) / 255.0; + const QString btn_pressed_fg = accent_lum > 0.65 ? QStringLiteral("black") : QStringLiteral("white"); + + const QString btn_bg = is_dark ? QStringLiteral("#262626") : QStringLiteral("#f0f0f5"); + const QString btn_fg = is_dark ? QStringLiteral("white") : QStringLiteral("#1a1a1e"); + const QString btn_border = is_dark ? QStringLiteral("#404040") : QStringLiteral("#d0d0d5"); + const QString btn_hover_bg = is_dark ? QStringLiteral("#333333") : QStringLiteral("#e0e0e5"); + const QString btn_disabled_bg = is_dark ? QStringLiteral("#1a1a1a") : QStringLiteral("#e8e8ed"); + const QString btn_disabled_fg = is_dark ? QStringLiteral("#666") : QStringLiteral("#aaa"); + m_start_button->setStyleSheet(QStringLiteral( - "QPushButton { background-color: #0096ff; color: white; border-radius: 8px; padding: 12px; font-weight: bold; font-size: 16px; } " - "QPushButton:hover { background-color: #0082df; } " - "QPushButton:disabled { background-color: #004d80; color: #aaa; }" - "QPushButton { background-color: #262626; color: #ffffff; border: 1px solid #404040; border-radius: 8px; padding: 12px; font-weight: bold; font-size: 16px; } " - "QPushButton:hover { background-color: #333333; } " - "QPushButton:pressed { background-color: #1a1a1a; } " - "QPushButton:disabled { background-color: #1a1a1a; color: #666; border: 1px solid #333; }" - )); + "QPushButton { background-color: %2; color: %3; border: 1px solid %4; border-radius: 8px; padding: 12px; font-weight: bold; font-size: 16px; } " + "QPushButton:hover { background-color: %5; } " + "QPushButton:pressed { background-color: %1; color: %6; border-color: %1; } " + "QPushButton:disabled { background-color: %7; color: %8; border: 1px solid %9; }" + ).arg(accent, btn_bg, btn_fg, btn_border, btn_hover_bg, btn_pressed_fg, btn_disabled_bg, btn_disabled_fg, btn_border)); layout->addStretch(); layout->addWidget(m_status_label); @@ -171,17 +183,23 @@ void CupShuffleWidget::paintEvent(QPaintEvent*) { } QLinearGradient grad(r.topLeft(), r.bottomRight()); - grad.setColorAt(0, QColor(70, 70, 85)); - grad.setColorAt(1, QColor(35, 35, 50)); + if (Theme::IsDarkMode()) { + grad.setColorAt(0, QColor(70, 70, 85)); + grad.setColorAt(1, QColor(35, 35, 50)); + p.setPen(QPen(QColor(110, 110, 130), 2)); + } else { + grad.setColorAt(0, Qt::white); + grad.setColorAt(1, QColor(230, 230, 235)); + p.setPen(QPen(QColor(200, 200, 205), 2)); + } p.setBrush(grad); - p.setPen(QPen(QColor(110, 110, 130), 2)); p.drawRoundedRect(r, 16, 16); if (cup.revealed) { if (cup.has_ball) { p.drawPixmap(r.adjusted(15, 15, -15, -15).toRect(), QPixmap::fromImage(m_citron_logo)); } else { - p.setPen(QColor(255, 255, 255, 50)); + p.setPen(Theme::IsDarkMode() ? QColor(255, 255, 255, 50) : QColor(0, 0, 0, 50)); p.setFont(QFont(QStringLiteral("sans-serif"), 32, QFont::Bold)); p.drawText(r, Qt::AlignCenter, QStringLiteral("?")); } diff --git a/src/citron/util/dice_widget.cpp b/src/citron/util/dice_widget.cpp index 067f3e5c92..2512ff7dd9 100644 --- a/src/citron/util/dice_widget.cpp +++ b/src/citron/util/dice_widget.cpp @@ -3,26 +3,42 @@ #include #include #include +#include "citron/theme.h" DiceWidget::DiceWidget(QWidget* parent) : QWidget(parent) { auto* layout = new QVBoxLayout(this); layout->setContentsMargins(20, 20, 20, 20); + const bool is_dark = Theme::IsDarkMode(); + const QString accent = Theme::GetAccentColor(); + m_status_label = new QLabel(tr("Pick your bet and roll!"), this); m_status_label->setAlignment(Qt::AlignCenter); - m_status_label->setStyleSheet(QStringLiteral("font-size: 16px; font-weight: bold; color: white;")); + m_status_label->setStyleSheet(QStringLiteral("font-size: 16px; font-weight: bold; color: %1;") + .arg(is_dark ? QStringLiteral("white") : QStringLiteral("#1a1a1e"))); auto* bet_layout = new QHBoxLayout(); m_bet_small = new QPushButton(tr("Small (2-6)"), this); m_bet_mid = new QPushButton(tr("Seven (7)"), this); m_bet_big = new QPushButton(tr("Big (8-12)"), this); + const QString btn_bg = is_dark ? QStringLiteral("#32323a") : QStringLiteral("#f0f0f5"); + const QString btn_fg = is_dark ? QStringLiteral("white") : QStringLiteral("#1a1a1e"); + const QString btn_border = is_dark ? QStringLiteral("#42424a") : QStringLiteral("#d0d0d5"); + const QString btn_hover_bg = is_dark ? QStringLiteral("#42424a") : QStringLiteral("#e0e0e5"); + const QString btn_disabled_bg = is_dark ? QStringLiteral("#1a1a1e") : QStringLiteral("#e8e8ed"); + const QString btn_disabled_fg = is_dark ? QStringLiteral("#555") : QStringLiteral("#aaa"); + + const QColor accent_color(accent); + const double accent_lum = (0.299 * accent_color.red() + 0.587 * accent_color.green() + 0.114 * accent_color.blue()) / 255.0; + const QString btn_checked_fg = accent_lum > 0.65 ? QStringLiteral("black") : QStringLiteral("white"); + const QString btn_style = QStringLiteral( - "QPushButton { background-color: #32323a; color: white; border: 1px solid #42424a; border-radius: 8px; padding: 8px; font-weight: bold; } " - "QPushButton:hover { background-color: #42424a; } " - "QPushButton:checked { background-color: #0096ff; border-color: #0096ff; } " - "QPushButton:disabled { background-color: #1a1a1e; color: #555; }" - ); + "QPushButton { background-color: %2; color: %3; border: 1px solid %4; border-radius: 8px; padding: 8px; font-weight: bold; } " + "QPushButton:hover { background-color: %5; } " + "QPushButton:checked { background-color: %1; border-color: %1; color: %6; } " + "QPushButton:disabled { background-color: %7; color: %8; border: 1px solid %9; }" + ).arg(accent, btn_bg, btn_fg, btn_border, btn_hover_bg, btn_checked_fg, btn_disabled_bg, btn_disabled_fg, btn_border); for (auto* btn : {m_bet_small, m_bet_mid, m_bet_big}) { btn->setCheckable(true); @@ -32,10 +48,11 @@ DiceWidget::DiceWidget(QWidget* parent) : QWidget(parent) { m_roll_button = new QPushButton(tr("Roll Dice"), this); m_roll_button->setStyleSheet(QStringLiteral( - "QPushButton { background-color: #2e7d32; color: white; border-radius: 8px; padding: 12px; font-weight: bold; font-size: 16px; } " + "QPushButton { background-color: #2e7d32; color: white; border-radius: 8px; padding: 12px; font-weight: bold; font-size: 16px; border: none; } " "QPushButton:hover { background-color: #388e3c; } " - "QPushButton:disabled { background-color: #1b5e20; color: #aaa; }" - )); + "QPushButton:disabled { background-color: %1; color: %2; }" + ).arg(is_dark ? QStringLiteral("#1b5e20") : QStringLiteral("#a5d6a7"), + is_dark ? QStringLiteral("#aaa") : QStringLiteral("#e8f5e9"))); m_roll_button->setEnabled(false); layout->addStretch();