diff --git a/src/controller.cpp b/src/controller.cpp index 5438159..2cb89c3 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -1,6 +1,7 @@ #include "controller.h" #include "typeselectorpopup.h" #include "providerregistry.h" +#include "themes/thememanager.h" #include #include #include @@ -1726,6 +1727,9 @@ void RcxController::updateCommandRow() { TypeSelectorPopup* RcxController::ensurePopup(RcxEditor* editor) { if (!m_cachedPopup) { m_cachedPopup = new TypeSelectorPopup(editor); + // Keep popup colors in sync when theme changes + connect(&ThemeManager::instance(), &ThemeManager::themeChanged, + m_cachedPopup, &TypeSelectorPopup::applyTheme); // Pre-warm: force native window creation so first visible show is fast m_cachedPopup->warmUp(); } diff --git a/src/typeselectorpopup.cpp b/src/typeselectorpopup.cpp index 620e410..b64f9c7 100644 --- a/src/typeselectorpopup.cpp +++ b/src/typeselectorpopup.cpp @@ -279,14 +279,14 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent) // Separator { - auto* sep = new QFrame; - sep->setFrameShape(QFrame::HLine); - sep->setFrameShadow(QFrame::Plain); + m_separator = new QFrame; + m_separator->setFrameShape(QFrame::HLine); + m_separator->setFrameShadow(QFrame::Plain); QPalette sepPal = pal; sepPal.setColor(QPalette::WindowText, theme.border); - sep->setPalette(sepPal); - sep->setFixedHeight(1); - layout->addWidget(sep); + m_separator->setPalette(sepPal); + m_separator->setFixedHeight(1); + layout->addWidget(m_separator); } // Row 3: Modifier toggles [ plain ] [ * ] [ ** ] [ [n] ] @@ -456,6 +456,60 @@ void TypeSelectorPopup::setFont(const QFont& font) { delegate->setFont(font); } +void TypeSelectorPopup::applyTheme(const Theme& theme) { + QPalette pal; + pal.setColor(QPalette::Window, theme.backgroundAlt); + pal.setColor(QPalette::WindowText, theme.text); + pal.setColor(QPalette::Base, theme.background); + pal.setColor(QPalette::AlternateBase, theme.surface); + pal.setColor(QPalette::Text, theme.text); + pal.setColor(QPalette::Button, theme.button); + pal.setColor(QPalette::ButtonText, theme.text); + pal.setColor(QPalette::Highlight, theme.hover); + pal.setColor(QPalette::HighlightedText, theme.text); + setPalette(pal); + + m_titleLabel->setPalette(pal); + m_filterEdit->setPalette(pal); + m_listView->setPalette(pal); + m_previewLabel->setPalette(pal); + m_arrayCountEdit->setPalette(pal); + + // Separator + QPalette sepPal = pal; + sepPal.setColor(QPalette::WindowText, theme.border); + m_separator->setPalette(sepPal); + + // Esc button + m_escLabel->setStyleSheet(QStringLiteral( + "QToolButton { color: %1; border: none; padding: 2px 6px; }" + "QToolButton:hover { color: %2; }") + .arg(theme.textDim.name(), theme.indHoverSpan.name())); + + // Create button + m_createBtn->setStyleSheet(QStringLiteral( + "QToolButton { color: %1; border: none; padding: 3px 6px; }" + "QToolButton:hover { color: %2; background: %3; }") + .arg(theme.textMuted.name(), theme.text.name(), theme.hover.name())); + + // Modifier toggle buttons + QString btnStyle = QStringLiteral( + "QToolButton { color: %1; background: %2; border: 1px solid %3;" + " padding: 2px 8px; border-radius: 3px; }" + "QToolButton:checked { color: %4; background: %5; border-color: %5; }" + "QToolButton:hover:!checked { background: %6; }") + .arg(theme.textDim.name(), theme.background.name(), theme.border.name(), + theme.text.name(), theme.selected.name(), theme.hover.name()); + m_btnPlain->setStyleSheet(btnStyle); + m_btnPtr->setStyleSheet(btnStyle); + m_btnDblPtr->setStyleSheet(btnStyle); + m_btnArray->setStyleSheet(btnStyle); + + // Preview label + m_previewLabel->setStyleSheet(QStringLiteral( + "QLabel { color: %1; padding: 1px 6px; }").arg(theme.syntaxType.name())); +} + void TypeSelectorPopup::setTitle(const QString& title) { m_titleLabel->setText(title); } diff --git a/src/typeselectorpopup.h b/src/typeselectorpopup.h index 1de9e94..b2011fb 100644 --- a/src/typeselectorpopup.h +++ b/src/typeselectorpopup.h @@ -16,6 +16,8 @@ class QWidget; namespace rcx { +struct Theme; + // ── Popup mode ── enum class TypePopupMode { Root, FieldType, ArrayElement, PointerTarget }; @@ -53,6 +55,7 @@ public: void setFont(const QFont& font); void setTitle(const QString& title); void setMode(TypePopupMode mode); + void applyTheme(const Theme& theme); void setCurrentNodeSize(int bytes); void setTypes(const QVector& types, const TypeEntry* current = nullptr); void popup(const QPoint& globalPos); @@ -77,6 +80,7 @@ private: QLabel* m_previewLabel = nullptr; QListView* m_listView = nullptr; QStringListModel* m_model = nullptr; + QFrame* m_separator = nullptr; // Modifier toggles QWidget* m_modRow = nullptr; diff --git a/tests/test_type_selector.cpp b/tests/test_type_selector.cpp index ad030f1..fba7da7 100644 --- a/tests/test_type_selector.cpp +++ b/tests/test_type_selector.cpp @@ -887,6 +887,79 @@ private slots: qPrintable(QString("Large popup width %1 should be > small %2") .arg(largeW).arg(smallW))); } + // ── Test: popup updates colors when theme changes ── + + void testPopupUpdatesOnThemeChange() { + auto& tm = ThemeManager::instance(); + int origIdx = tm.currentIndex(); + + // Ensure at least two themes exist + QVERIFY2(tm.themes().size() >= 2, + "Need at least 2 themes to test theme switching"); + + // Create popup with current theme + TypeSelectorPopup popup; + TypeEntry prim; + prim.entryKind = TypeEntry::Primitive; + prim.primitiveKind = NodeKind::Int32; + prim.displayName = QStringLiteral("int32_t"); + popup.setTypes({prim}); + + QColor bgBefore = popup.palette().color(QPalette::Window); + + // Switch to a different theme + int otherIdx = (origIdx == 0) ? 1 : 0; + tm.setCurrent(otherIdx); + QApplication::processEvents(); + + // The popup should have applyTheme connected to themeChanged + popup.applyTheme(tm.current()); + QColor bgAfter = popup.palette().color(QPalette::Window); + + // If the two themes have different background colors, verify the change + // (some themes may coincidentally share colors, so we just verify the + // method doesn't crash and the palette is set to the new theme's color) + QCOMPARE(bgAfter, tm.current().backgroundAlt); + + // Also verify child widgets got updated + auto* filterEdit = popup.findChild(); + QVERIFY(filterEdit); + QCOMPARE(filterEdit->palette().color(QPalette::Base), + tm.current().background); + + auto* listView = popup.findChild(); + QVERIFY(listView); + QCOMPARE(listView->palette().color(QPalette::Base), + tm.current().background); + + // Restore original theme + tm.setCurrent(origIdx); + } + + void testPopupAutoConnectsThemeChange() { + auto& tm = ThemeManager::instance(); + int origIdx = tm.currentIndex(); + QVERIFY2(tm.themes().size() >= 2, "Need >= 2 themes"); + + TypeSelectorPopup popup; + + // applyTheme is a public slot — verify it can be connected + connect(&tm, &ThemeManager::themeChanged, + &popup, &TypeSelectorPopup::applyTheme); + + QColor bgBefore = popup.palette().color(QPalette::Window); + + int otherIdx = (origIdx == 0) ? 1 : 0; + tm.setCurrent(otherIdx); + QApplication::processEvents(); + + // After theme change + signal, popup palette should match new theme + QCOMPARE(popup.palette().color(QPalette::Window), + tm.current().backgroundAlt); + + // Restore + tm.setCurrent(origIdx); + } }; QTEST_MAIN(TestTypeSelector)