QStyledItemDelegate paint event on cell (not row) hover
-
I have a custom QStyledItemDelegate that paints a QPixmap in a particular column. When that cell is hovered over with the mouse, I would like to paint it differently.
Below is my paint event, which does paint the cell correctly when not State_MouseOver. However, it changes the color when I hover anywhere on the row. How can I make it change only when the mouse is hovering over the cell with the pixmap in it?
void myDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_ASSERT(index.isValid()); switch(index.column()) { case DAY_COLUMN: { QSize btnSize = QSize(option.rect.height() * .9, option.rect.height() * .9); QRect r = option.rect; int x = r.right() - btnSize.width() - 10; int y = r.top(); QRect btnRect = QRect(x, y, btnSize.width(), btnSize.height()); QPixmap pixmap(":/icons/edit.png"); // If hovered over, change color. if(option.state & QStyle::State_MouseOver) { auto mask = pixmap.createMaskFromColor(QColor("Black"), Qt::MaskOutColor); pixmap.fill(QColor("Red")); pixmap.setMask(mask); } painter->drawPixmap(btnRect, pixmap, pixmap.rect()); return; } /*.... draw other column(s) as appropriate ...*/ } }
I'm using this delegate on all rows withing a QTreeView.
Qt 5.12 -
I have not tested this, but you can probably get the styled widget like this:
const auto widget = qobject_cast<QWidget*>(option.styleObject);
Then you can probably check if mouse is inside of it using:
widget->rect().contains(mapFromGlobal(QCursor::pos()));
-
Thanks, that certainly seems to be on the right track. mapFromGlobal was not available within QStyledItemDelegate, so I used the created widget to call mapFromGlobal:
widget->rect().contains(widget->mapFromGlobal(QCursor::pos()));
However, the
widget->rect()
that is returned, is that of the treeView as a whole, not the cell. So it is always true, any time the cursor is inside the treeView. -
Hm, right. The delegates themselves are not QObjects, only QPainDevices. Well then, you can maybe get at this from the other side:
- map cursor position to widget coordinates (like you do above)
- then map
option.rect
to widget coordinates - then check if position is within the
rect
No idea if it will work, but maybe...
-
@sierdzio I was about to reply with something like that - I think this is close. The cell now changes color only when you hover over it, BUT it does not repaint if you move between cells within the same row. So you can move vertically between rows, and the cell will only change colors when you are over the pixmap - BUT if you move horizontally off of it, and back on, it does not repaint (and stays black).
Is there a way I can force a re-paint when moving between columns?
const auto widget = qobject_cast<QWidget*>(option.styleObject); auto cursorX = widget->mapFromGlobal(QCursor::pos()).x(); // If hovered over, change color. if(option.state & QStyle::State_MouseOver && btnRect.contains(cursorX, btnRect.y())) { auto mask = pixmap.createMaskFromColor(QColor("Black"), Qt::MaskOutColor); pixmap.fill(QColor("Red")); pixmap.setMask(mask); } painter->drawPixmap(btnRect, pixmap, pixmap.rect());
-
Is
paint()
not called at all when you move horizontally? Check with some debug messages.Also, print
btnRect
.cursorX
,btnRect.y()
, maybe it will offer some clues, perhapspaint()
is called, but always thinkscursorX
is within the rectangle?Is there a way I can force a re-paint when moving between columns?
There must be some way, yeah. I'm not sure how to do it yet, though.