From aaa04b4d40060db4614e9549dfd3e477da5e4185 Mon Sep 17 00:00:00 2001 From: chenyuanbo Date: Tue, 8 Sep 2026 16:26:37 +0800 Subject: [PATCH] fix(palette): remove palette.windowText writes to break binding loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Remove `palette.windowText: D.ColorSelector.*` bindings from Button, MenuItem and ItemDelegate, the three controls reporting "Binding loop detected for property windowText" 2. Let contentItem text read the state foreground color directly from `D.ColorSelector.textColor`; ItemDelegate exposes a new readonly `resolvedTextColor` for its checked text 3. Add `makeIconPaletteWithForeground(QQuickPalette*, QColor)` and switch the state-dependent icons (Button/MenuItem/ItemDelegate plus the inherited IconButton/WindowButton/ButtonIndicator) to it, passing their foreground explicitly 4. Keep other controls' `palette.windowText` bindings unchanged (scope limited to the three reported loops) Influence: 1. Open control center; no more "Binding loop detected for property windowText" from the three controls 2. Verify checked/highlighted text and icon foreground still turn white (HighlightedText) in light theme 3. Verify other controls (ToolButton/TitleBar/ActionButton...) unchanged 4. Verify light/dark theme switching fix(palette): 移除 palette.windowText 写入以断开绑定环 1. 移除 Button、MenuItem、ItemDelegate 三处 `palette.windowText: D.ColorSelector.*` 绑定,这三者报告 "Binding loop detected for property windowText" 2. contentItem 文字改直接读 `D.ColorSelector.textColor`;ItemDelegate 新增只读 `resolvedTextColor` 表达选中文字色 3. 新增 `makeIconPaletteWithForeground(QQuickPalette*, QColor)`,让三者及连带控件 (IconButton / WindowButton / ButtonIndicator,继承或关联 Button)的图标显式 传入前景色 4. 其余控件(ToolButton/TitleBar/ActionButton 等)的 `palette.windowText` 绑定 保持不变(范围限定到三处报告的环) Influence: 1. 打开控制中心,三个控件不再出现 "Binding loop detected for property windowText" 2. 验证浅色主题下选中/高亮文字与图标前景仍为白色(HighlightedText) 3. 验证其余控件(ToolButton/TitleBar/ActionButton 等)行为不变 4. 验证亮/暗主题切换 PMS: Task-392413 --- qt6/src/qml/Button.qml | 3 +-- qt6/src/qml/ButtonIndicator.qml | 2 +- qt6/src/qml/IconButton.qml | 2 +- qt6/src/qml/ItemDelegate.qml | 14 +++++++------- qt6/src/qml/MenuItem.qml | 5 ++--- qt6/src/qml/WindowButton.qml | 2 +- src/private/dqmlglobalobject.cpp | 9 +++++++++ src/private/dqmlglobalobject_p.h | 1 + 8 files changed, 23 insertions(+), 15 deletions(-) diff --git a/qt6/src/qml/Button.qml b/qt6/src/qml/Button.qml index 076f950a..de0c7e31 100644 --- a/qt6/src/qml/Button.qml +++ b/qt6/src/qml/Button.qml @@ -23,8 +23,7 @@ T.Button { opacity: D.ColorSelector.controlState === D.DTK.DisabledState ? 0.4 : 1 D.DciIcon.mode: D.ColorSelector.controlState D.DciIcon.theme: D.ColorSelector.controlTheme - D.DciIcon.palette: D.DTK.makeIconPalette(palette) - palette.windowText: D.ColorSelector.textColor + D.DciIcon.palette: D.DTK.makeIconPaletteWithForeground(palette, D.ColorSelector.textColor) icon { width: DS.Style.button.iconSize height: DS.Style.button.iconSize diff --git a/qt6/src/qml/ButtonIndicator.qml b/qt6/src/qml/ButtonIndicator.qml index f021bf1c..54e96539 100644 --- a/qt6/src/qml/ButtonIndicator.qml +++ b/qt6/src/qml/ButtonIndicator.qml @@ -22,7 +22,7 @@ Rectangle { width: DS.Style.buttonIndicator.iconSize } name: "arrow_ordinary_down" - palette: control.D.DTK.makeIconPalette(control.palette) + palette: control.D.DTK.makeIconPaletteWithForeground(control.palette, control.D.ColorSelector.textColor) mode: control.D.ColorSelector.controlState theme: control.D.ColorSelector.controlTheme fallbackToQIcon: false diff --git a/qt6/src/qml/IconButton.qml b/qt6/src/qml/IconButton.qml index c7492d2c..717a0fb1 100644 --- a/qt6/src/qml/IconButton.qml +++ b/qt6/src/qml/IconButton.qml @@ -26,7 +26,7 @@ Button { contentItem: D.DciIcon { smooth: control.smooth name: control.icon.name - palette: D.DTK.makeIconPalette(control.palette) + palette: D.DTK.makeIconPaletteWithForeground(control.palette, control.D.ColorSelector.textColor) mode: control.D.ColorSelector.controlState theme: control.D.ColorSelector.controlTheme sourceSize: Qt.size(control.icon.width, control.icon.height) diff --git a/qt6/src/qml/ItemDelegate.qml b/qt6/src/qml/ItemDelegate.qml index ef530a6d..6517cc57 100644 --- a/qt6/src/qml/ItemDelegate.qml +++ b/qt6/src/qml/ItemDelegate.qml @@ -18,6 +18,10 @@ T.ItemDelegate { property bool contentFlow property Component content property D.Palette checkedTextColor: DS.Style.checkedButton.text + readonly property color resolvedTextColor: { + let undraged = D.DTK.hasAnimation ? control.backgroundVisible && !dragActive : true + return checked && !control.cascadeSelected && undraged ? D.ColorSelector.checkedTextColor : control.palette.windowText + } property int corners: D.RoundRectangle.TopLeftCorner | D.RoundRectangle.TopRightCorner | D.RoundRectangle.BottomLeftCorner | D.RoundRectangle.BottomRightCorner property bool dragActive: false // drag @@ -43,14 +47,10 @@ T.ItemDelegate { spacing: DS.Style.control.spacing checkable: true autoExclusive: true - palette.windowText: { - let undraged = D.DTK.hasAnimation ? control.backgroundVisible && !dragActive : true - return checked && !control.cascadeSelected && undraged ? D.ColorSelector.checkedTextColor : undefined - } D.DciIcon.mode: D.ColorSelector.controlState D.DciIcon.theme: D.ColorSelector.controlTheme - D.DciIcon.palette: D.DTK.makeIconPalette(palette) + D.DciIcon.palette: D.DTK.makeIconPaletteWithForeground(palette, control.resolvedTextColor) icon { width: DS.Style.itemDelegate.iconSize height: DS.Style.itemDelegate.iconSize @@ -65,7 +65,7 @@ T.ItemDelegate { sourceComponent: D.DciIcon { smooth: control.smooth - palette: D.DTK.makeIconPalette(control.palette) + palette: D.DTK.makeIconPaletteWithForeground(control.palette, control.resolvedTextColor) mode: control.D.ColorSelector.controlState theme: control.D.ColorSelector.controlTheme fallbackToQIcon: false @@ -84,7 +84,7 @@ T.ItemDelegate { ? Qt.AlignCenter : Qt.AlignLeft | Qt.AlignVCenter text: control.text font: control.font - color: control.palette.windowText + color: control.resolvedTextColor icon: D.DTK.makeIcon(control.icon, control.D.DciIcon) Layout.fillWidth: !control.contentFlow } diff --git a/qt6/src/qml/MenuItem.qml b/qt6/src/qml/MenuItem.qml index ed61474a..3f78c320 100644 --- a/qt6/src/qml/MenuItem.qml +++ b/qt6/src/qml/MenuItem.qml @@ -27,10 +27,9 @@ T.MenuItem { : DS.Style.menu.itemText property D.Palette subMenuBackgroundColor: DS.Style.menu.subMenuOpenedBackground - palette.windowText: D.ColorSelector.textColor D.DciIcon.mode: D.ColorSelector.controlState D.DciIcon.theme: D.ColorSelector.controlTheme - D.DciIcon.palette: D.DTK.makeIconPalette(palette) + D.DciIcon.palette: D.DTK.makeIconPaletteWithForeground(palette, D.ColorSelector.textColor) contentItem: D.IconLabel { readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0 readonly property real indicatorPadding: control.useIndicatorPadding && control.indicator ? control.indicator.width + control.spacing : 0 @@ -44,7 +43,7 @@ T.MenuItem { alignment: Qt.AlignLeft text: control.text font: control.font - color: control.palette.windowText + color: control.D.ColorSelector.textColor Behavior on color { enabled: D.DTK.hasAnimation ColorAnimation { duration: control.hovered ? 100 : 0; } diff --git a/qt6/src/qml/WindowButton.qml b/qt6/src/qml/WindowButton.qml index 81e0d82a..cc050b3b 100644 --- a/qt6/src/qml/WindowButton.qml +++ b/qt6/src/qml/WindowButton.qml @@ -33,7 +33,7 @@ D.IconButton { smooth: control.smooth name: control.icon.name asynchronous: false - palette: D.DTK.makeIconPalette(control.palette) + palette: D.DTK.makeIconPaletteWithForeground(control.palette, control.D.ColorSelector.textColor) mode: control.D.ColorSelector.controlState theme: control.D.ColorSelector.controlTheme sourceSize: Qt.size(control.icon.width, control.icon.height) diff --git a/src/private/dqmlglobalobject.cpp b/src/private/dqmlglobalobject.cpp index a63d5bd2..80053166 100644 --- a/src/private/dqmlglobalobject.cpp +++ b/src/private/dqmlglobalobject.cpp @@ -444,6 +444,15 @@ DDciIconPalette DQMLGlobalObject::makeIconPalette(const QQuickPalette *palette) iconPalette.setHighlightForeground(palette->highlightedText()); return iconPalette; } + +// 在 makeIconPalette 基础上,显式设置前景色(用于状态相关图标的颜色渲染) +DDciIconPalette DQMLGlobalObject::makeIconPaletteWithForeground(const QQuickPalette *palette, const QColor &foreground) +{ + DDciIconPalette iconPalette = makeIconPalette(palette); + if (foreground.isValid()) + iconPalette.setForeground(foreground); + return iconPalette; +} #endif bool DQMLGlobalObject::sendMessage(QObject *target, const QString &content, const QString &iconName, int duration, const QString &msgId) diff --git a/src/private/dqmlglobalobject_p.h b/src/private/dqmlglobalobject_p.h index e2d24d55..11d7ce5a 100644 --- a/src/private/dqmlglobalobject_p.h +++ b/src/private/dqmlglobalobject_p.h @@ -228,6 +228,7 @@ class DQMLGlobalObject : public QObject, public DTK_CORE_NAMESPACE::DObject Q_INVOKABLE DTK_GUI_NAMESPACE::DDciIconPalette makeIconPalette(const QPalette &palette); #else Q_INVOKABLE DTK_GUI_NAMESPACE::DDciIconPalette makeIconPalette(const QQuickPalette *palette); + Q_INVOKABLE DTK_GUI_NAMESPACE::DDciIconPalette makeIconPaletteWithForeground(const QQuickPalette *palette, const QColor &foreground); #endif Q_INVOKABLE bool sendMessage(QObject *target, const QString &content, const QString &iconName = QString(), int duration = 4000, const QString &msgId = QString());