Qt6: Disable drawing of default background for QTreeView items
-
Prior to Qt 6.5 background for items was rendered only when alternating background for items was specified, allowing
QStyledItemDelegate
to render background independently. This however was changed by the qt/qtbase@16e0e7c commit. Another commit that is part of this regression: qt/qtbase@483ae6c. This one translatesPanelItemViewRow
s intoPanelItemViewItem
s to "properly" render background because it is non-styleable from qss.One solution would be to override
PE_PanelItemViewRow
usingQProxyStyle
but this does not work with stylesheets which is hard requirement as the app allows custom styling.Another possibility would be to set
::item
in qss to have no background and somehow render it inQStyledItemDelegate
but I don't know how to do it in a way that it would be possible to control it using qss. Maybe create artificial widget that will be the source for styles?Incorrect behavior:
Expected one:
-
@kadet1090 I'm not sure if it's supported but try
QTreeView::item:selected { background: transparent; }
-
@medyakovvit This does not work as it disables background entirely for selected items which is not the goal.
I managed to find a hacky solution to this problem.
To disable rendering of the background I can abuse this if statement in Qt:
// For compatibility reasons, QTreeView draws different parts of // the background of an item row separately, before calling the // delegate to draw the item. The row background of an item is // however not separately styleable through a style sheet, but // only indirectly through the background of the item. To get the // same background for all parts drawn by QTreeView, we have to // use the background rule for the item here. if (renderRule(w, opt, PseudoElement_ViewItem).hasBackground()) pseudoElement = PseudoElement_ViewItem;
So disabling background for
::item
disables rendering of branches and not-wanted background (I'm using qtsass):QTreeView::item { &, &:selected, &:hover { background: none; border: none; } }
This however denies me from defining background in qss. But as a workarond I create artificial QTreeView widget inside the delegate with some well defined object name,
DocumentTreeItems
in my case:TreeWidgetItemDelegate::TreeWidgetItemDelegate(QObject* parent) : QStyledItemDelegate(parent) { artificial = new QTreeView(qobject_cast<QWidget*>(parent)); artificial->setObjectName("DocumentTreeItems"); artificial->setFixedSize(0, 0); }
Thanks to that I can attach styles to this widget inside qss:
#DocumentTreeItems::item { border: 1px solid transparent; border-radius: 2px; background-color: $primaryBackground; &:hover { border: 1px solid $contrastBorder; background-color: $controlWidgetBackground; } &:selected { border: 1px solid $accentBorder; background-color: $accentBackground; } }
Then I can repaint the background using style and referring to my artificial widget in the
paint
method of delegate:style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, artificial);
This gives the expected result:
I'm however deeply dissatisfied with this solution, so if anyone knows a better one I'd be glad to hear suggestions.
-
@kadet1090 What if to disable QStyle::State_Selected in the delegate paint function
TreeWidgetItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QStyleOptionViewItem modifiedOption.state = option & ~QStyle::State_Selected; QStyledItemDelegate::paint(painter, modifiedOption, index); }
-
@medyakovvit Delegate is used after problematic background is drawn, so fiddling with paint method won't help much. I can remove entire body of the paint method and the background will still be drawn. Not to say that it has nothing to do with selected state, as non selected items are also drawn incorrectly.
-
Seems I misunderstood the assignment)