From 2e0ab11551bd807866a5a1d10851980715ff55b3 Mon Sep 17 00:00:00 2001 From: Collecting Date: Tue, 23 Dec 2025 19:35:19 +0000 Subject: [PATCH 1/6] edit: rainbow_style.cpp Signed-off-by: Collecting --- src/citron/util/rainbow_style.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/citron/util/rainbow_style.cpp b/src/citron/util/rainbow_style.cpp index fe3adef5c..1fb0b9fa1 100644 --- a/src/citron/util/rainbow_style.cpp +++ b/src/citron/util/rainbow_style.cpp @@ -4,8 +4,8 @@ #include "citron/uisettings.h" #include "citron/theme.h" #include -#include -#include +#include +#include float RainbowStyle::s_hue = 0.0f; @@ -16,10 +16,10 @@ RainbowStyle::RainbowStyle(QStyle* baseStyle) : QProxyStyle(baseStyle) { } void RainbowStyle::UpdateHue() { - if (UISettings::values.enable_rainbow_mode.GetValue()) { - s_hue += 0.005f; - if (s_hue > 1.0f) s_hue = 0.0f; - } + if (UISettings::values.enable_rainbow_mode.GetValue()) { + s_hue += 0.005f; + if (s_hue > 1.0f) s_hue = 0.0f; + } } QColor RainbowStyle::GetCurrentHighlightColor() { From 64dbf5ad672e3821749676f2449e56f4a9862d5b Mon Sep 17 00:00:00 2001 From: Collecting Date: Tue, 23 Dec 2025 19:36:32 +0000 Subject: [PATCH 2/6] feat(ui): RGB Mode Signed-off-by: Collecting --- src/citron/configuration/configure_dialog.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/citron/configuration/configure_dialog.h b/src/citron/configuration/configure_dialog.h index a5cde5c00..13bcf1492 100644 --- a/src/citron/configuration/configure_dialog.h +++ b/src/citron/configuration/configure_dialog.h @@ -69,6 +69,7 @@ private: void RetranslateUI(); void OnLanguageChanged(const QString& locale); + bool m_is_tab_animating{false}; QColor last_palette_text_color; std::unique_ptr ui; HotkeyRegistry& registry; From 55adf62c869d8d66432e58bed2d7b681805542fa Mon Sep 17 00:00:00 2001 From: Collecting Date: Tue, 23 Dec 2025 19:37:13 +0000 Subject: [PATCH 3/6] feat(ui): RGB Mode Signed-off-by: Collecting --- src/citron/configuration/configure_dialog.cpp | 70 +++++++++++++++++-- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/src/citron/configuration/configure_dialog.cpp b/src/citron/configuration/configure_dialog.cpp index 6cff3c14e..9abb6798f 100644 --- a/src/citron/configuration/configure_dialog.cpp +++ b/src/citron/configuration/configure_dialog.cpp @@ -238,16 +238,67 @@ void ConfigureDialog::UpdateTheme() { if (!rainbow_timer) { rainbow_timer = new QTimer(this); connect(rainbow_timer, &QTimer::timeout, this, [this] { - QString hue_hex = RainbowStyle::GetCurrentHighlightColor().name(); + // MODAL GUARD: If a color dialog or popup is open, pause updates. + // This makes the Color Picker buttons static and responsive. + if (m_is_tab_animating || !this->isVisible() || !this->isActiveWindow()) return; + + const int current_index = ui->stackedWidget->currentIndex(); + const int input_tab_index = 7; + + const QColor current_color = RainbowStyle::GetCurrentHighlightColor(); + const QString hue_hex = current_color.name(); + const QString hue_light = current_color.lighter(125).name(); + const QString hue_dark = current_color.darker(150).name(); + + // 1. Sidebar Tabs QString sidebar_css = QStringLiteral( "QPushButton.tabButton { border: 2px solid transparent; }" "QPushButton.tabButton:checked { color: %1; border: 2px solid %1; }" "QPushButton.tabButton:hover { border: 2px solid %1; }" "QPushButton.tabButton:pressed { background-color: %1; color: #ffffff; }" ).arg(hue_hex); - if (ui->topButtonWidget) ui->topButtonWidget->setStyleSheet(sidebar_css); if (ui->horizontalNavWidget) ui->horizontalNavWidget->setStyleSheet(sidebar_css); + + // 2. Action Buttons (OK/Apply/Cancel) + if (ui->buttonBox && !ui->buttonBox->underMouse()) { + ui->buttonBox->setStyleSheet(QStringLiteral( + "QPushButton { background-color: %1; color: #ffffff; border-radius: 4px; font-weight: bold; padding: 5px 15px; }" + "QPushButton:hover { background-color: %2; }" + "QPushButton:pressed { background-color: %3; }" + ).arg(hue_hex).arg(hue_light).arg(hue_dark)); + } + + // 3. Tab Content Area + if (current_index == input_tab_index) return; + + QWidget* currentContainer = ui->stackedWidget->currentWidget(); + if (currentContainer) { + QWidget* actualTab = currentContainer; + if (auto* scroll = qobject_cast(currentContainer)) { + actualTab = scroll->widget(); + } + + if (actualTab) { + QString tab_css = QStringLiteral( + "QCheckBox::indicator:checked, QRadioButton::indicator:checked { background-color: %1; border: 1px solid %1; }" + "QSlider::handle:horizontal { background-color: %1; border: 1px solid %1; border-radius: 7px; }" + "QScrollBar::handle:vertical, QScrollBar::handle:horizontal { background-color: %1; border-radius: 4px; min-height: 20px; }" + "QScrollBar:vertical, QScrollBar:horizontal { background: transparent; }" + "QComboBox { border: 1px solid %1; selection-background-color: %1; }" + "QComboBox QAbstractItemView { border: 2px solid %1; selection-background-color: %1; background-color: #2b2b2b; }" + "QComboBox QAbstractItemView::item:selected { background-color: %1; color: #ffffff; }" + "QPushButton, QToolButton { background-color: %1; color: #ffffff; border: none; border-radius: 4px; padding: 5px; }" + "QPushButton:hover, QToolButton:hover { background-color: %2; }" + "QPushButton:pressed, QToolButton:pressed { background-color: %3; }" + "QPushButton#aestheticTabButton { background-color: transparent; border: 2px solid %1; }" + "QPushButton#aestheticTabButton:checked { background-color: %1; }" + ).arg(hue_hex).arg(hue_light).arg(hue_dark); + + currentContainer->setStyleSheet(tab_css); + actualTab->setStyleSheet(tab_css); + } + } }); } rainbow_timer->start(33); @@ -255,6 +306,14 @@ void ConfigureDialog::UpdateTheme() { rainbow_timer->stop(); if (ui->topButtonWidget) ui->topButtonWidget->setStyleSheet({}); if (ui->horizontalNavWidget) ui->horizontalNavWidget->setStyleSheet({}); + if (ui->buttonBox) ui->buttonBox->setStyleSheet({}); + for (int i = 0; i < ui->stackedWidget->count(); ++i) { + QWidget* w = ui->stackedWidget->widget(i); + if (w) w->setStyleSheet({}); + if (auto* s = qobject_cast(w)) { + if (s->widget()) s->widget()->setStyleSheet({}); + } + } } } @@ -379,8 +438,7 @@ void ConfigureDialog::OnLanguageChanged(const QString& locale) { } void ConfigureDialog::AnimateTabSwitch(int id) { - static bool is_animating = false; - if (is_animating) { + if (m_is_tab_animating) { return; } @@ -452,13 +510,13 @@ void ConfigureDialog::AnimateTabSwitch(int id) { current_widget->hide(); current_widget->move(0, 0); - is_animating = false; + m_is_tab_animating = false; // Reset the flag for (auto button : tab_button_group->buttons()) { button->setEnabled(true); } }); - is_animating = true; + m_is_tab_animating = true; // Set the flag for (auto button : tab_button_group->buttons()) { button->setEnabled(false); } From 8aa2783c2fa8bbe91692f0f730fd28aefc08aa72 Mon Sep 17 00:00:00 2001 From: Collecting Date: Tue, 23 Dec 2025 19:37:39 +0000 Subject: [PATCH 4/6] feat(ui): RGB Mode Signed-off-by: Collecting --- src/citron/configuration/configure_per_game.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/citron/configuration/configure_per_game.h b/src/citron/configuration/configure_per_game.h index 8c6b82b75..8edc6b1fc 100644 --- a/src/citron/configuration/configure_per_game.h +++ b/src/citron/configuration/configure_per_game.h @@ -85,6 +85,7 @@ private: QGraphicsScene* scene; std::unique_ptr game_config; + bool m_is_tab_animating{false}; QColor last_palette_text_color; Core::System& system; From 36c915f2b7b48d2b2e4c2f3ea7ceed047d086f71 Mon Sep 17 00:00:00 2001 From: Collecting Date: Tue, 23 Dec 2025 19:38:17 +0000 Subject: [PATCH 5/6] feat(ui): RGB Mode Signed-off-by: Collecting --- .../configuration/configure_per_game.cpp | 102 ++++++++++++++---- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/src/citron/configuration/configure_per_game.cpp b/src/citron/configuration/configure_per_game.cpp index 2f0ad8d6d..a8ce0311d 100644 --- a/src/citron/configuration/configure_per_game.cpp +++ b/src/citron/configuration/configure_per_game.cpp @@ -256,9 +256,10 @@ void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file_) { void ConfigurePerGame::UpdateTheme() { const bool is_rainbow = UISettings::values.enable_rainbow_mode.GetValue(); - const QString accent = Theme::GetAccentColor(); const bool is_dark = IsDarkMode(); + const QString accent = is_rainbow ? QStringLiteral("palette(highlight)") : Theme::GetAccentColor(); + const QString bg = is_dark ? QStringLiteral("#2b2b2b") : QStringLiteral("#ffffff"); const QString txt = is_dark ? QStringLiteral("#ffffff") : QStringLiteral("#000000"); const QString sec = is_dark ? QStringLiteral("#3d3d3d") : QStringLiteral("#f0f0f0"); @@ -287,7 +288,6 @@ void ConfigurePerGame::UpdateTheme() { style_sheet += QStringLiteral( "QSlider::handle:horizontal { background-color: %1; }" "QCheckBox::indicator:checked { background-color: %1; border-color: %1; }" - "QToolButton { background-color: %1; color: #ffffff; border-radius: 4px; }" ).arg(accent); setStyleSheet(style_sheet); @@ -302,21 +302,91 @@ void ConfigurePerGame::UpdateTheme() { if (!rainbow_timer) { rainbow_timer = new QTimer(this); connect(rainbow_timer, &QTimer::timeout, this, [this] { - QString hue_hex = RainbowStyle::GetCurrentHighlightColor().name(); - QString button_css = QStringLiteral( - "QPushButton#aestheticTabButton { border: 2px solid transparent; }" - "QPushButton#aestheticTabButton:checked { color: %1; border: 2px solid %1; }" - "QPushButton#aestheticTabButton:hover { border: 2px solid %1; }" - "QPushButton#aestheticTabButton:pressed { background-color: %1; color: #ffffff; }" - ).arg(hue_hex); + if (m_is_tab_animating || !this->isVisible() || !this->isActiveWindow()) return; - if (ui->tabButtonsContainer) ui->tabButtonsContainer->setStyleSheet(button_css); + const QColor current_color = RainbowStyle::GetCurrentHighlightColor(); + const QString hue_hex = current_color.name(); + const QString hue_light = current_color.lighter(125).name(); + const QString hue_dark = current_color.darker(150).name(); + + // 1. Top Tab Buttons + QString tab_buttons_css = QStringLiteral( + "QPushButton.tabButton { border: 2px solid transparent; background: transparent; }" + "QPushButton.tabButton:checked { color: %1; border: 2px solid %1; }" + "QPushButton.tabButton:hover { border: 2px solid %1; }" + "QPushButton.tabButton:pressed { background-color: %1; color: #ffffff; }" + ).arg(hue_hex); + if (ui->tabButtonsContainer) ui->tabButtonsContainer->setStyleSheet(tab_buttons_css); + + // 2. Horizontal Scrollbar for Tabs + if (ui->tabButtonsScrollArea) { + ui->tabButtonsScrollArea->setStyleSheet(QStringLiteral( + "QScrollBar:horizontal { height: 14px; background: transparent; border-radius: 7px; }" + "QScrollBar::handle:horizontal { background-color: %1; border-radius: 6px; min-width: 30px; margin: 1px; }" + "QScrollBar::add-line, QScrollBar::sub-line { background: none; width: 0px; }" + ).arg(hue_hex)); + } + + // 3. Action Buttons (OK/Cancel) and Trim button + if (ui->buttonBox && !ui->buttonBox->underMouse()) { + ui->buttonBox->setStyleSheet(QStringLiteral( + "QPushButton { background-color: %1; color: #ffffff; border-radius: 4px; font-weight: bold; padding: 5px 15px; }" + "QPushButton:hover { background-color: %2; }" + "QPushButton:pressed { background-color: %3; }" + ).arg(hue_hex).arg(hue_light).arg(hue_dark)); + } + + if (ui->trim_xci_button && !ui->trim_xci_button->underMouse()) { + ui->trim_xci_button->setStyleSheet(QStringLiteral( + "QPushButton { background-color: %1; color: #ffffff; border: none; border-radius: 4px; padding: 10px; }" + "QPushButton:hover { background-color: %2; }" + "QPushButton:pressed { background-color: %3; }" + ).arg(hue_hex).arg(hue_light).arg(hue_dark)); + } + + // 4. Tab Content Area + QWidget* currentContainer = ui->stackedWidget->currentWidget(); + if (currentContainer) { + QWidget* actualTab = currentContainer; + if (auto* scroll = qobject_cast(currentContainer)) { + actualTab = scroll->widget(); + } + + if (actualTab) { + QString content_css = QStringLiteral( + "QCheckBox::indicator:checked, QRadioButton::indicator:checked { background-color: %1; border: 1px solid %1; }" + "QSlider::sub-page:horizontal { background: %1; border-radius: 4px; }" + "QSlider::handle:horizontal { background-color: %1; border: 1px solid %1; width: 18px; height: 18px; margin: -5px 0; border-radius: 9px; }" + "QComboBox { border: 1px solid %1; selection-background-color: %1; }" + "QComboBox QAbstractItemView { border: 2px solid %1; selection-background-color: %1; background-color: #2b2b2b; }" + "QComboBox QAbstractItemView::item:selected { background-color: %1; color: #ffffff; }" + "QScrollBar::handle:vertical, QScrollBar::handle:horizontal { background-color: %1; border-radius: 7px; }" + "QScrollBar:vertical, QScrollBar:horizontal { background: transparent; }" + "QPushButton, QToolButton { background-color: %1; color: #ffffff; border: none; border-radius: 4px; padding: 5px; }" + "QPushButton:hover, QToolButton:hover { background-color: %2; }" + "QPushButton:pressed, QToolButton:pressed { background-color: %3; }" + ).arg(hue_hex).arg(hue_light).arg(hue_dark); + + currentContainer->setStyleSheet(content_css); + actualTab->setStyleSheet(content_css); + } + } }); } rainbow_timer->start(33); } else if (rainbow_timer) { rainbow_timer->stop(); if (ui->tabButtonsContainer) ui->tabButtonsContainer->setStyleSheet({}); + if (ui->tabButtonsScrollArea) ui->tabButtonsScrollArea->setStyleSheet({}); + if (ui->buttonBox) ui->buttonBox->setStyleSheet({}); + if (ui->trim_xci_button) ui->trim_xci_button->setStyleSheet({}); + for (int i = 0; i < ui->stackedWidget->count(); ++i) { + QWidget* w = ui->stackedWidget->widget(i); + w->setStyleSheet({}); + if (auto* s = qobject_cast(w)) { + if (s->widget()) s->widget()->setStyleSheet({}); + } + } } } @@ -744,8 +814,7 @@ void ConfigurePerGame::OnTrimXCI() { } void ConfigurePerGame::AnimateTabSwitch(int id) { - static bool is_animating = false; - if (is_animating) { + if (m_is_tab_animating) { return; } @@ -758,19 +827,16 @@ void ConfigurePerGame::AnimateTabSwitch(int id) { const int duration = 350; - // Prepare Widgets for Live Animation next_widget->setGeometry(0, 0, ui->stackedWidget->width(), ui->stackedWidget->height()); next_widget->move(0, 0); next_widget->show(); next_widget->raise(); - // Animate OLD widget: SLIDE LEFT auto anim_old_pos = new QPropertyAnimation(current_widget, "pos"); anim_old_pos->setEndValue(QPoint(-ui->stackedWidget->width(), 0)); anim_old_pos->setDuration(duration); anim_old_pos->setEasingCurve(QEasingCurve::InOutQuart); - // Animate NEW widget: SLIDE IN FROM RIGHT and FADE IN auto anim_new_pos = new QPropertyAnimation(next_widget, "pos"); anim_new_pos->setStartValue(QPoint(ui->stackedWidget->width(), 0)); anim_new_pos->setEndValue(QPoint(0, 0)); @@ -785,13 +851,11 @@ void ConfigurePerGame::AnimateTabSwitch(int id) { anim_new_opacity->setDuration(duration); anim_new_opacity->setEasingCurve(QEasingCurve::InQuad); - // Group, Run, and Clean Up auto animation_group = new QParallelAnimationGroup(this); animation_group->addAnimation(anim_old_pos); animation_group->addAnimation(anim_new_pos); animation_group->addAnimation(anim_new_opacity); - // Use a context-aware connection to prevent crashes connect(animation_group, &QAbstractAnimation::finished, this, [this, current_widget, next_widget, id]() { ui->stackedWidget->setCurrentIndex(id); @@ -799,13 +863,13 @@ void ConfigurePerGame::AnimateTabSwitch(int id) { current_widget->hide(); current_widget->move(0, 0); - is_animating = false; + m_is_tab_animating = false; // Reset the flag for (auto button : button_group->buttons()) { button->setEnabled(true); } }); - is_animating = true; + m_is_tab_animating = true; // Set the flag for (auto button : button_group->buttons()) { button->setEnabled(false); } From 9bb2cf9c30168a33d287247f8adec77db5e71eeb Mon Sep 17 00:00:00 2001 From: Collecting Date: Tue, 23 Dec 2025 19:38:46 +0000 Subject: [PATCH 6/6] feat(ui): RGB Mode Signed-off-by: Collecting --- src/citron/configuration/configure_ui.ui | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/citron/configuration/configure_ui.ui b/src/citron/configuration/configure_ui.ui index 081ea5313..8416a7117 100644 --- a/src/citron/configuration/configure_ui.ui +++ b/src/citron/configuration/configure_ui.ui @@ -140,7 +140,10 @@ - Enable Rainbow Tab Buttons + Enable RGB Mode + + + May run slowly depending on hardware. Some tabs may not use RGB due to optimization purposes.