I am using a QListView with a custom delegate that extends from QStyledItemDelegate. I reimplemented the paint method to custom paint each item in the list. In the paint method, I am drawing a border around selected items in the list view.
I want to be able to animate the item border as I select an item. For example, if the intended item border is 5 pixels, I want to have it "animate in" from 0 pixels to 5 pixels when the item is selected.
My original idea was to hook up a timer to go off every 50 milliseconds and have the delegate paint every time the timer goes off until the full border width has been painted. However, the delegate's reimplemented paint method is const, so I can't save or update a border width member variable during each pass through of the paint method.
What is the best way of accomplishing this?
A possible solution is to create a role that manages the border size of the item, and update it using a QVariantAnimation
:
#include <QApplication>
#include <QListView>
#include <QPainter>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QVariantAnimation>
int BorderSizeRole = Qt::UserRole+1;
class AnimationDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QStyledItemDelegate::paint(painter, option, index);
bool ok;
int borderSize = index.data(BorderSizeRole).toInt(&ok);
if(borderSize >0 && ok){
painter->save();
QPen pen(QBrush(Qt::red), borderSize);
painter->setPen(pen);
painter->drawRect(option.rect);
painter->restore();
}
}
};
class CustomAnimation: public QVariantAnimation{
QPersistentModelIndex m_index;
QAbstractItemModel *m_model;
public:
CustomAnimation(QAbstractItemModel *m_model, QPersistentModelIndex index, QObject *parent=nullptr)
: QVariantAnimation(parent),
m_index(index),
m_model(m_model)
{
setStartValue(0);
setEndValue(5);
setDuration(50*5);
connect(this, &CustomAnimation::valueChanged, this, &CustomAnimation::on_valueChanged);
// delete animation
start(QAbstractAnimation::DeleteWhenStopped);
}
private:
Q_SLOT void on_valueChanged(const QVariant & value){
if(m_model)
m_model->setData(m_index, value, BorderSizeRole);
else
stop();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListView view;
view.setItemDelegate(new AnimationDelegate(&view));
QStandardItemModel model;
for(int i=0; i<10; i++){
QStandardItem *item = new QStandardItem(QString("item %1").arg(i));
item->setData(-1, BorderSizeRole);
model.appendRow(item);
}
view.setModel(&model);
QObject::connect(view.selectionModel(), &QItemSelectionModel::selectionChanged,
[&model](const QItemSelection &selected, const QItemSelection & deselected){
for(const QModelIndex & index: selected.indexes()){
new CustomAnimation(&model, QPersistentModelIndex(index));
}
// remove border
for(const QModelIndex & index: deselected.indexes()){
model.setData(index, -1, BorderSizeRole);
}
});
view.show();
return a.exec();
}