From 322e83fd36c059b0f0e535994c7fa7a0c4705278 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Thu, 26 Mar 2026 20:33:12 -0400 Subject: [PATCH 1/2] fix: respect custom combo delegates --- ...combobox-custom-delegate-replacement.patch | 71 +++++++++++++++++++ tests/test_style.py | 33 ++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 patches/162-fix-combobox-custom-delegate-replacement.patch diff --git a/patches/162-fix-combobox-custom-delegate-replacement.patch b/patches/162-fix-combobox-custom-delegate-replacement.patch new file mode 100644 index 0000000..eff1bd5 --- /dev/null +++ b/patches/162-fix-combobox-custom-delegate-replacement.patch @@ -0,0 +1,71 @@ +diff --git a/lib/src/style/QlementineStyle.cpp b/lib/src/style/QlementineStyle.cpp +index 07151b9..6fb46c1 100644 +--- a/lib/src/style/QlementineStyle.cpp ++++ b/lib/src/style/QlementineStyle.cpp +@@ -4856,10 +4856,12 @@ void QlementineStyle::polish(QWidget* w) { + if (auto* comboBox = qobject_cast(w)) { + comboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); + +- // Will define a delegate to stylize the QComboBox items, +- comboBox->setItemDelegate(new ComboBoxDelegate(comboBox, *this)); +- // Trigger the redefine when the QComboBox's view changes. +- new ComboboxFilter(comboBox); ++ // Only replace the delegate if the combobox doesn't already have a custom one. ++ // This preserves delegates set by third-party widgets (e.g. custom QStyledItemDelegate subclasses). ++ if (isDefaultItemDelegate(comboBox->itemDelegate())) { ++ comboBox->setItemDelegate(new ComboBoxDelegate(comboBox, *this)); ++ new ComboboxFilter(comboBox); ++ } + } else if (auto* tabBar = qobject_cast(w)) { + tabBar->installEventFilter(new TabBarEventFilter(tabBar)); + } else if (auto* label = qobject_cast(w)) { +diff --git a/lib/src/style/eventFilters/ComboboxItemViewFilter.hpp b/lib/src/style/eventFilters/ComboboxItemViewFilter.hpp +index 77eed32..76bbbd1 100644 +--- a/lib/src/style/eventFilters/ComboboxItemViewFilter.hpp ++++ b/lib/src/style/eventFilters/ComboboxItemViewFilter.hpp +@@ -7,6 +7,7 @@ + #include + #include + ++#include + #include + #include + #include +@@ -16,6 +17,15 @@ + #include + + namespace oclero::qlementine { ++ ++/// Returns true if the delegate is a default Qt delegate (not a custom subclass). ++inline bool isDefaultItemDelegate(QAbstractItemDelegate* delegate) { ++ if (!delegate) return true; ++ const auto* meta = delegate->metaObject(); ++ return meta == &QStyledItemDelegate::staticMetaObject ++ || meta == &QItemDelegate::staticMetaObject; ++} ++ + // Event filter for the item view in the QComboBox's popup. + class ComboboxItemViewFilter : public QObject { + public: +@@ -48,7 +58,9 @@ protected: + const auto* child = childEvent->child(); + if (child == _comboBox->view()) { + if (auto* qlementine = qobject_cast(_comboBox->style())) { +- _comboBox->setItemDelegate(new ComboBoxDelegate(_comboBox, *qlementine)); ++ if (isDefaultItemDelegate(_comboBox->itemDelegate())) { ++ _comboBox->setItemDelegate(new ComboBoxDelegate(_comboBox, *qlementine)); ++ } + } + } + } +@@ -166,7 +178,9 @@ public: + const auto* child = childEvent->child(); + if (child == _comboBox->view()) { + if (auto* qlementine = qobject_cast(_comboBox->style())) { +- _comboBox->setItemDelegate(new ComboBoxDelegate(_comboBox, *qlementine)); ++ if (isDefaultItemDelegate(_comboBox->itemDelegate())) { ++ _comboBox->setItemDelegate(new ComboBoxDelegate(_comboBox, *qlementine)); ++ } + } + + // if (const auto* treeView = qobject_cast(child)) { diff --git a/tests/test_style.py b/tests/test_style.py index 357bc73..88a7d51 100644 --- a/tests/test_style.py +++ b/tests/test_style.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING -from _qt_compat import QApplication, Qlementine +from _qt_compat import QApplication, Qlementine, QtWidgets if TYPE_CHECKING: from pytestqt.qtbot import QtBot @@ -211,3 +211,34 @@ def test_animations_enabled_changed_signal(qtbot: QtBot) -> None: style = QlementineStyle() with qtbot.waitSignal(style.animationsEnabledChanged): style.setAnimationsEnabled(False) + + +# ============================================================ +# Delegate preservation + + +def test_polish_preserves_custom_combobox_delegate( + qtbot: QtBot, +) -> None: + """QlementineStyle.polish() should not replace a custom item delegate.""" + + QComboBox = QtWidgets.QComboBox + QStyledItemDelegate = QtWidgets.QStyledItemDelegate + + class MyDelegate(QStyledItemDelegate): + pass + + style = QlementineStyle() + + # Combobox with custom delegate — should be preserved + combo = QComboBox() + custom = MyDelegate(combo) + combo.setItemDelegate(custom) + style.polish(combo) + assert combo.itemDelegate() is custom + + # Plain combobox — should get the qlementine delegate + combo2 = QComboBox() + style.polish(combo2) + delegate2 = combo2.itemDelegate() + assert type(delegate2).__name__ != "QStyledItemDelegate" From dbe9bcb3643b0471b9d564b44eb4f809f821e821 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Thu, 26 Mar 2026 20:47:35 -0400 Subject: [PATCH 2/2] refactor: remove unused custom combobox delegate test --- tests/test_style.py | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/tests/test_style.py b/tests/test_style.py index 88a7d51..357bc73 100644 --- a/tests/test_style.py +++ b/tests/test_style.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING -from _qt_compat import QApplication, Qlementine, QtWidgets +from _qt_compat import QApplication, Qlementine if TYPE_CHECKING: from pytestqt.qtbot import QtBot @@ -211,34 +211,3 @@ def test_animations_enabled_changed_signal(qtbot: QtBot) -> None: style = QlementineStyle() with qtbot.waitSignal(style.animationsEnabledChanged): style.setAnimationsEnabled(False) - - -# ============================================================ -# Delegate preservation - - -def test_polish_preserves_custom_combobox_delegate( - qtbot: QtBot, -) -> None: - """QlementineStyle.polish() should not replace a custom item delegate.""" - - QComboBox = QtWidgets.QComboBox - QStyledItemDelegate = QtWidgets.QStyledItemDelegate - - class MyDelegate(QStyledItemDelegate): - pass - - style = QlementineStyle() - - # Combobox with custom delegate — should be preserved - combo = QComboBox() - custom = MyDelegate(combo) - combo.setItemDelegate(custom) - style.polish(combo) - assert combo.itemDelegate() is custom - - # Plain combobox — should get the qlementine delegate - combo2 = QComboBox() - style.polish(combo2) - delegate2 = combo2.itemDelegate() - assert type(delegate2).__name__ != "QStyledItemDelegate"