QStyle::drawControl() not correctly painting enabled/disabled state
-
I have a custom delegate that is subclassed from
QStyledItemDelegate. In thepaint()method for this delegate, I'm usingQStyle::drawControl()to draw a check box in the current application's style. I would like to have this check box appear disabled (i.e. grayed out) if the current index is uneditable. However, I can't figure out how to get this to work.Here is what I have currently:
void MyCheckBoxDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { this->QStyledItemDelegate::paint(painter, option, index); QStyleOptionButton buttonOption; buttonOption.rect = MyCheckBoxDelegate::getCheckBoxRectangle(option); buttonOption.palette = option.palette; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.styleObject = option.styleObject; buttonOption.state = QStyle::State_None; buttonOption.state |= index.data().toBool() ? QStyle::State_On : QStyle::State_Off; if (index.flags().testFlag(Qt::ItemIsEditable)) buttonOption.state |= QStyle::State_Enabled; QApplication::style()->drawControl(QStyle::CE_CheckBox, &buttonOption, painter, option.widget); }Right now, the check box always appears to be enabled. For reference, here's a comparison with an actual check box widget in the application:
Enabled, On Enabled, Off Disabled, On Disabled, Off Widget 



Delegate 



Am I doing something wrong? Or does
QStyle->drawControl()not consider the enabled state when drawing? -
Some more information: I put a breakpoint on
drawControl()and followed it through the Qt source code. The application style is aQWindows11Style, but it eventually gets down toQCommonStyle::drawControl(). There's a large switch statement, and it gets to this case forCE_CheckBox. I don't see anything directly in that code that compares the state withState_Enabled.That code then calls
drawPrimitive()withPE_IndicatorCheckBox. That method also has a large switch statement, and it gets to this case forPE_IndicatorCheckBox. I don't see anything here that compares the state withState_Enabled.Going back up to the case in
QCommonStyle::drawControl(), it next recursively calls itself withCE_CheckBoxLabel. That takes us to this case forCE_CheckBoxLabel. I'm only seeing that it compares withState_Enabledto adjust how the icon or text are displayed, but both are null/empty in my case. -
Hi,
Might be a silly question but did you check the value of state in each of these cases ?
-
Hi,
Might be a silly question but did you check the value of state in each of these cases ?
@SGaist The incoming state for
optionisState_Enabled|State_Children|State_Sibling. The state onbuttonOptionis being set toState_On/State_Offwhen the index is not editable and set toState_On|State_Enabled/State_Off|State_Enabledwhen the index is editable. -
Some more information: I put a breakpoint on
drawControl()and followed it through the Qt source code. The application style is aQWindows11Style, but it eventually gets down toQCommonStyle::drawControl(). There's a large switch statement, and it gets to this case forCE_CheckBox. I don't see anything directly in that code that compares the state withState_Enabled.That code then calls
drawPrimitive()withPE_IndicatorCheckBox. That method also has a large switch statement, and it gets to this case forPE_IndicatorCheckBox. I don't see anything here that compares the state withState_Enabled.Going back up to the case in
QCommonStyle::drawControl(), it next recursively calls itself withCE_CheckBoxLabel. That takes us to this case forCE_CheckBoxLabel. I'm only seeing that it compares withState_Enabledto adjust how the icon or text are displayed, but both are null/empty in my case. -
Turns out it was the palette. You have to set the "current color group" for the palette to
QPalette::Disabled. In fact, setting the state doesn't seem to have any effect whatsoever. Setting the palette alone is enough to control the color (and make it appear disabled). But I think I'll keep setting the state just in case it has an effect in other styles.void MyCheckBoxDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { this->QStyledItemDelegate::paint(painter, option, index); QStyleOptionButton buttonOption; buttonOption.rect = MyCheckBoxDelegate::getCheckBoxRectangle(option); buttonOption.palette = option.palette; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.styleObject = option.styleObject; buttonOption.state = QStyle::State_None; buttonOption.state |= index.data().toBool() ? QStyle::State_On : QStyle::State_Off; if (index.flags().testFlag(Qt::ItemIsEditable)) { buttonOption.state |= QStyle::State_Enabled; buttonOption.palette.setCurrentColorGroup(QPalette::Normal); } else { buttonOption.palette.setCurrentColorGroup(QPalette::Disabled); } QApplication::style()->drawControl(QStyle::CE_CheckBox, &buttonOption, painter, option.widget); } -
P peter.thompson has marked this topic as solved
-
This problem is fixed with Qt6.10, maybe also with 6.9 but don't know exactly since there are a lot of changes in the windows11 style as it is not yet finished. This works fine for me with git HEAD:
class Delegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionButton buttonOption; buttonOption.rect = option.rect; buttonOption.palette = option.palette; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.styleObject = option.styleObject; buttonOption.state = QStyle::State_None; buttonOption.state |= index.column() % 2 == 0 ? QStyle::State_On : QStyle::State_Off; if (index.column() < 2) buttonOption.state |= QStyle::State_Enabled; option.widget->style()->drawControl(QStyle::CE_CheckBox, &buttonOption, painter, option.widget); } };