Switch RDTreeWidget to inherit from QTreeView instead of QTreeWidget

* We add our own custom item model to handle the cases we need. We can
  also make a few assumptions and optimisations around things we can
  safely assume like e.g. nodel columns won't really change after init.
* This lets us have full control over batching updates, which was the
  main motivation, but it makes it easier to extend in future (e.g.
  adding single per-item tags instead of the heavyweight Qt::UserRole
  data elements).
This commit is contained in:
baldurk
2017-04-21 18:44:51 +01:00
parent 5848d00091
commit d699e89fd2
2 changed files with 716 additions and 90 deletions
+543 -75
View File
@@ -27,72 +27,517 @@
#include <QDebug>
#include <QMouseEvent>
RDTreeWidget::RDTreeWidget(QWidget *parent) : QTreeWidget(parent)
class RDTreeWidgetModel : public QAbstractItemModel
{
public:
RDTreeWidgetModel(RDTreeWidget *parent) : QAbstractItemModel(parent), widget(parent) {}
QModelIndex indexForItem(RDTreeWidgetItem *item, int column) const
{
if(item->m_parent == NULL)
return QModelIndex();
int row = item->m_parent->indexOfChild(item);
return createIndex(row, column, item);
}
RDTreeWidgetItem *itemForIndex(QModelIndex idx) const
{
if(!idx.isValid())
return widget->m_root;
return (RDTreeWidgetItem *)idx.internalPointer();
}
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
{
if(row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
return QModelIndex();
RDTreeWidgetItem *par = itemForIndex(parent);
if(par == NULL)
par = widget->m_root;
return createIndex(row, column, par->m_children[row]);
}
void beginAddChild(RDTreeWidgetItem *item)
{
QModelIndex index = indexForItem(item, 0);
beginInsertRows(index, item->childCount(), item->childCount());
}
void endAddChild(RDTreeWidgetItem *item) { endInsertRows(); }
void beginRemoveChildren(RDTreeWidgetItem *parent, int first, int last)
{
beginRemoveRows(indexForItem(parent, 0), first, last);
}
void endRemoveChildren() { endRemoveRows(); }
void itemChanged(RDTreeWidgetItem *item, const QVector<int> &roles)
{
QModelIndex topLeft = indexForItem(item, 0);
QModelIndex bottomRight = indexForItem(item, columnCount() - 1);
emit dataChanged(topLeft, bottomRight, roles);
}
void refresh()
{
emit beginResetModel();
emit endResetModel();
}
void headerRefresh() { emit headerDataChanged(Qt::Horizontal, 0, columnCount() - 1); }
void itemsChanged(RDTreeWidgetItem *p, QPair<int, int> minRowColumn, QPair<int, int> maxRowColumn,
const QVector<int> &roles)
{
QModelIndex topLeft = createIndex(minRowColumn.first, minRowColumn.second, p);
QModelIndex bottomRight = createIndex(maxRowColumn.first, maxRowColumn.second, p);
emit dataChanged(topLeft, bottomRight, roles);
}
QModelIndex parent(const QModelIndex &index) const override
{
if(index.internalPointer() == NULL)
return QModelIndex();
RDTreeWidgetItem *item = itemForIndex(index);
if(item)
return indexForItem(item->m_parent, 0);
return QModelIndex();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
if(!parent.isValid())
return widget->m_root->childCount();
RDTreeWidgetItem *parentItem = itemForIndex(parent);
if(parentItem)
return parentItem->childCount();
return 0;
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
return widget->m_headers.count();
}
bool hasChildren(const QModelIndex &parent) const
{
if(!parent.isValid())
return widget->m_root->childCount() > 0;
RDTreeWidgetItem *parentItem = itemForIndex(parent);
if(parentItem)
return parentItem->childCount() > 0;
return false;
}
Qt::ItemFlags flags(const QModelIndex &index) const override
{
if(!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
}
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole && section < widget->m_headers.count())
return widget->m_headers[section];
return QVariant();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
RDTreeWidgetItem *item = itemForIndex(index);
// invisible root element has no data
if(!item->m_parent)
return QVariant();
if(role == Qt::DisplayRole)
{
if(index.column() < item->m_text.count())
return item->m_text[index.column()];
}
else if(role == Qt::DecorationRole)
{
if(widget->m_hoverColumn == index.column())
{
if(widget->m_currentHoverItem == item)
return widget->m_activeHoverIcon;
else
return widget->m_normalHoverIcon;
}
if(index.column() < item->m_icons.count())
return item->m_icons[index.column()];
}
else if(role == Qt::BackgroundRole)
{
// item's background color takes priority
if(item->m_back != QBrush())
return item->m_back;
// otherwise if we're hover-highlighting, use the window color
if(widget->m_currentHoverItem == item)
return widget->palette().brush(QPalette::Window);
// otherwise, no special background
return QBrush();
}
else if(role == Qt::ForegroundRole)
{
// same priority order as background role above
if(item->m_fore != QBrush())
return item->m_fore;
if(widget->m_currentHoverItem == item)
return widget->palette().brush(QPalette::WindowText);
return QBrush();
}
else if(role == Qt::ToolTipRole)
{
return item->m_tooltip;
}
else if(role == Qt::FontRole)
{
QFont font; // TODO should this come from some default?
font.setItalic(item->m_italic);
font.setBold(item->m_bold);
return font;
}
else if(role < 64 && item->m_customData & 1ULL << role)
{
return item->data(index.column(), role);
}
return QVariant();
}
private:
RDTreeWidget *widget;
};
RDTreeWidgetItem::RDTreeWidgetItem(const std::initializer_list<QVariant> &values)
{
m_text = values;
m_icons.resize(m_text.size());
}
RDTreeWidgetItem::~RDTreeWidgetItem()
{
if(m_parent)
m_parent->removeChild(this);
clear();
delete m_data;
}
QVariant RDTreeWidgetItem::data(int column, int role) const
{
if(column >= m_data->count())
return QVariant();
const QVector<RoleData> &dataVec = (*m_data)[column];
for(const RoleData &d : dataVec)
{
if(d.role == role)
return d.data;
}
return QVariant();
}
void RDTreeWidgetItem::setData(int column, int role, const QVariant &value)
{
// we lazy allocate
if(!m_data)
{
m_data = new QVector<QVector<RoleData>>;
m_data->resize(qMax(m_text.count(), column + 1));
}
if(role < Qt::UserRole)
{
Q_ASSERT(role < 64);
m_customData |= 1ULL << role;
}
// data is allowed to resize above the column count in the widget
if(m_data->count() <= column)
m_data->resize(column + 1);
QVector<RoleData> &dataVec = (*m_data)[column];
for(RoleData &d : dataVec)
{
if(d.role == role)
{
bool different = (d.data != value);
d.data = value;
if(different && role < Qt::UserRole)
m_widget->m_model->itemChanged(this, {role});
return;
}
}
dataVec.push_back(RoleData(role, value));
if(role < Qt::UserRole)
m_widget->m_model->itemChanged(this, {role});
}
void RDTreeWidgetItem::addChild(RDTreeWidgetItem *item)
{
int colCount = item->m_text.count();
// remove it from any previous parent
if(item->m_parent)
item->m_parent->removeChild(this);
// set up its new parent to us
item->m_parent = this;
// set the widget in case this changed
item->setWidget(m_widget);
// resize per-column vectors to column count
item->m_text.resize(colCount);
item->m_icons.resize(colCount);
// data can resize up, but we don't resize it down.
if(item->m_data)
item->m_data->resize(qMax(item->m_data->count(), colCount));
if(m_widget)
m_widget->m_model->beginAddChild(this);
// add to our list of children
m_children.push_back(item);
if(m_widget)
m_widget->m_model->endAddChild(this);
}
void RDTreeWidgetItem::setWidget(RDTreeWidget *widget)
{
if(widget == m_widget)
return;
// if the widget is different, we need to recurse to children
m_widget = widget;
for(RDTreeWidgetItem *item : m_children)
item->setWidget(widget);
}
void RDTreeWidgetItem::dataChanged(int role)
{
if(m_widget)
m_widget->dataChanged(this, role);
}
RDTreeWidgetItem *RDTreeWidgetItem::takeChild(int index)
{
if(m_widget)
m_widget->m_model->beginRemoveChildren(this, index, index);
m_children[index]->m_parent = NULL;
RDTreeWidgetItem *ret = m_children.takeAt(index);
if(m_widget)
m_widget->m_model->endRemoveChildren();
return ret;
}
void RDTreeWidgetItem::removeChild(RDTreeWidgetItem *child)
{
if(m_widget)
{
int row = m_children.indexOf(child);
m_widget->m_model->beginRemoveChildren(this, row, row);
}
m_children.removeOne(child);
if(m_widget)
m_widget->m_model->endRemoveChildren();
}
void RDTreeWidgetItem::clear()
{
if(!childCount())
return;
if(m_widget)
m_widget->m_model->beginRemoveChildren(this, 0, childCount() - 1);
while(childCount() > 0)
{
RDTreeWidgetItem *child = takeChild(0);
child->clear();
delete child;
}
if(m_widget)
m_widget->m_model->endRemoveChildren();
}
RDTreeWidget::RDTreeWidget(QWidget *parent) : QTreeView(parent)
{
setMouseTracking(true);
m_root = new RDTreeWidgetItem;
m_root->m_widget = this;
m_model = new RDTreeWidgetModel(this);
QTreeView::setModel(m_model);
QObject::connect(this, &RDTreeWidget::activated, [this](const QModelIndex &idx) {
emit itemActivated(m_model->itemForIndex(idx), idx.column());
});
QObject::connect(this, &RDTreeWidget::clicked, [this](const QModelIndex &idx) {
emit itemClicked(m_model->itemForIndex(idx), idx.column());
});
QObject::connect(this, &RDTreeWidget::doubleClicked, [this](const QModelIndex &idx) {
emit itemDoubleClicked(m_model->itemForIndex(idx), idx.column());
});
}
void RDTreeWidget::setHoverIcons(QTreeWidgetItem *item, QIcon normal, QIcon hover)
RDTreeWidget::~RDTreeWidget()
{
item->setData(m_hoverColumn, Qt::DecorationRole, normal);
item->setData(m_hoverColumn, hoverIconRole, hover);
delete m_root;
delete m_model;
}
void RDTreeWidget::setHoverColour(QTreeWidgetItem *item, QColor col)
void RDTreeWidget::beginUpdate()
{
item->setData(m_hoverColumn, hoverBackColourRole, col);
m_queueUpdates = true;
m_queuedItem = NULL;
m_lowestIndex = m_highestIndex = qMakePair<int, int>(-1, -1);
m_queuedRoles = 0;
}
void RDTreeWidget::endUpdate()
{
m_queueUpdates = false;
if(m_queuedRoles)
{
// if we updated multiple different trees we can't issue a single dataChanged for everything
// under a parent. Refresh the whole model.
if(m_queuedItem == NULL)
{
m_model->refresh();
}
else
{
QVector<int> roles;
for(int r = 0; r < 64; r++)
{
if(m_queuedRoles & (1ULL << r))
roles.push_back(r);
}
m_model->itemsChanged(m_queuedItem, m_lowestIndex, m_highestIndex, roles);
}
}
}
void RDTreeWidget::setColumns(const QStringList &columns)
{
m_headers = columns;
m_model->refresh();
}
void RDTreeWidget::setHeaderText(int column, const QString &text)
{
m_headers[column] = text;
m_model->headerRefresh();
}
RDTreeWidgetItem *RDTreeWidget::selectedItem() const
{
QModelIndexList sel = selectionModel()->selectedIndexes();
if(sel.isEmpty())
return NULL;
return m_model->itemForIndex(sel[0]);
}
RDTreeWidgetItem *RDTreeWidget::currentItem() const
{
return m_model->itemForIndex(currentIndex());
}
void RDTreeWidget::setSelectedItem(RDTreeWidgetItem *node)
{
selectionModel()->select(m_model->indexForItem(node, 0),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
void RDTreeWidget::setCurrentItem(RDTreeWidgetItem *node)
{
setCurrentIndex(m_model->indexForItem(node, 0));
}
RDTreeWidgetItem *RDTreeWidget::itemAt(const QPoint &p) const
{
return m_model->itemForIndex(indexAt(p));
}
void RDTreeWidget::expandItem(RDTreeWidgetItem *item)
{
expand(m_model->indexForItem(item, 0));
}
void RDTreeWidget::scrollToItem(RDTreeWidgetItem *node)
{
scrollTo(m_model->indexForItem(node, 0));
}
void RDTreeWidget::clear()
{
m_root->clear();
}
void RDTreeWidget::mouseMoveEvent(QMouseEvent *e)
{
QTreeWidgetItem *item = itemAt(e->pos());
QModelIndex idx = indexAt(e->pos());
RDTreeWidgetItem *item = m_model->itemForIndex(idx);
clearHovers(invisibleRootItem(), item);
if(idx.column() == m_hoverColumn && m_hoverHandCursor)
setCursor(QCursor(Qt::PointingHandCursor));
else
unsetCursor();
if(item)
{
QVariant normalIcon = item->data(m_hoverColumn, Qt::DecorationRole);
QVariant hoverIcon = item->data(m_hoverColumn, hoverIconRole);
if(m_currentHoverItem == item || m_hoverColumn < 0)
return;
if(normalIcon.isValid() && hoverIcon.isValid())
{
item->setData(m_hoverColumn, hoverIconRole, QVariant());
item->setData(m_hoverColumn, Qt::DecorationRole, hoverIcon);
item->setData(m_hoverColumn, backupNormalIconRole, normalIcon);
}
RDTreeWidgetItem *old = m_currentHoverItem;
m_currentHoverItem = item;
if(!item->data(m_hoverColumn, backupBackColourRole).isValid())
{
QVariant backHoverColor = item->data(m_hoverColumn, hoverBackColourRole);
QColor col = item->data(m_hoverColumn, hoverBackColourRole).value<QColor>();
if(!col.isValid())
col = m_defaultHoverColour;
if(col.isValid())
{
item->setData(m_hoverColumn, backupBackColourRole, item->background(m_hoverColumn));
for(int c = 0; c < item->columnCount(); c++)
item->setData(c, Qt::BackgroundRole, QBrush(col));
}
}
QModelIndex idx = indexAt(e->pos());
if(idx.column() == m_hoverColumn && item->data(m_hoverColumn, Qt::DecorationRole).isValid() &&
m_hoverHandCursor)
{
setCursor(QCursor(Qt::PointingHandCursor));
}
else
{
unsetCursor();
}
}
// it's only two items, don't try and make a range but just change them both
QVector<int> roles = {Qt::DecorationRole, Qt::BackgroundRole, Qt::ForegroundRole};
if(old)
m_model->itemChanged(old, roles);
m_model->itemChanged(item, roles);
emit mouseMove(e);
QTreeWidget::mouseMoveEvent(e);
QTreeView::mouseMoveEvent(e);
}
void RDTreeWidget::mouseReleaseEvent(QMouseEvent *e)
@@ -104,18 +549,23 @@ void RDTreeWidget::mouseReleaseEvent(QMouseEvent *e)
emit itemActivated(itemAt(e->pos()), idx.column());
}
QTreeWidget::mouseReleaseEvent(e);
QTreeView::mouseReleaseEvent(e);
}
void RDTreeWidget::leaveEvent(QEvent *e)
{
unsetCursor();
clearHovers(invisibleRootItem(), NULL);
if(m_currentHoverItem)
{
RDTreeWidgetItem *item = m_currentHoverItem;
m_currentHoverItem = NULL;
m_model->itemChanged(item, {Qt::DecorationRole, Qt::BackgroundRole, Qt::ForegroundRole});
}
emit leave(e);
QTreeWidget::leaveEvent(e);
QTreeView::leaveEvent(e);
}
void RDTreeWidget::focusOutEvent(QFocusEvent *event)
@@ -123,45 +573,63 @@ void RDTreeWidget::focusOutEvent(QFocusEvent *event)
if(m_clearSelectionOnFocusLoss)
clearSelection();
QTreeWidget::focusOutEvent(event);
QTreeView::focusOutEvent(event);
}
void RDTreeWidget::keyPressEvent(QKeyEvent *e)
{
emit(keyPress(e));
QTreeWidget::keyPressEvent(e);
QTreeView::keyPressEvent(e);
}
void RDTreeWidget::clearHovers(QTreeWidgetItem *root, QTreeWidgetItem *exception)
void RDTreeWidget::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
for(int i = 0; i < root->childCount(); i++)
emit itemSelectionChanged();
QTreeView::selectionChanged(selected, deselected);
}
void RDTreeWidget::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
emit currentItemChanged(m_model->itemForIndex(current), m_model->itemForIndex(previous));
QTreeView::currentChanged(current, previous);
}
void RDTreeWidget::dataChanged(RDTreeWidgetItem *item, int role)
{
if(m_queueUpdates)
{
QTreeWidgetItem *n = root->child(i);
m_queuedRoles |= (1ULL << role);
if(n == exception)
continue;
// for now we only support updating the whole row, with all columns, even if only one column
// changed.
int row = item->m_parent->indexOfChild(item);
clearHovers(n, exception);
QVariant original = n->data(m_hoverColumn, backupNormalIconRole);
if(original.isValid())
// no queued updates yet, set up this one
if(m_lowestIndex.first == -1)
{
QVariant icon = n->data(m_hoverColumn, Qt::DecorationRole);
n->setData(m_hoverColumn, hoverIconRole, icon);
n->setData(m_hoverColumn, Qt::DecorationRole, original);
n->setData(m_hoverColumn, backupNormalIconRole, QVariant());
m_queuedItem = item;
m_lowestIndex = qMakePair<int, int>(row, 0);
m_highestIndex = qMakePair<int, int>(m_lowestIndex.first, m_headers.count() - 1);
}
original = n->data(m_hoverColumn, backupBackColourRole);
if(original.isValid())
else
{
QBrush orig = original.value<QBrush>();
for(int c = 0; c < n->columnCount(); c++)
n->setBackground(c, orig);
n->setData(m_hoverColumn, backupBackColourRole, QVariant());
// there's already an update. Check if we can expand it
if(m_queuedItem == item)
{
m_lowestIndex.first = qMin(m_lowestIndex.first, row);
m_highestIndex.first = qMax(m_highestIndex.first, row);
}
else
{
// can't batch updates across multiple parents, so we just fallback to full model refresh
m_queuedItem = NULL;
}
}
}
else
{
m_model->itemChanged(item, {role});
}
}
+173 -15
View File
@@ -24,31 +24,170 @@
#pragma once
#include <QTreeWidget>
#include <QTreeView>
class RDTreeWidget : public QTreeWidget
class RDTreeWidget;
class RDTreeWidgetModel;
class RDTreeWidgetItem
{
public:
RDTreeWidgetItem() = default;
RDTreeWidgetItem(const std::initializer_list<QVariant> &values);
~RDTreeWidgetItem();
QVariant data(int column, int role) const;
void setData(int column, int role, const QVariant &value);
void addChild(RDTreeWidgetItem *item);
// the data above requires allocating a bunch of vectors since it's stored per-column. Where
// possible, just use this single per-item tag
inline QVariant tag() const { return m_tag; }
inline void setTag(const QVariant &value) { m_tag = value; }
// inline accessors to the private data
inline void setIcon(int column, const QIcon &icon)
{
if(column >= m_icons.size())
return;
m_icons[column] = icon;
dataChanged(Qt::DecorationRole);
}
inline RDTreeWidgetItem *child(int index) const { return m_children[index]; }
inline int indexOfChild(RDTreeWidgetItem *child) const { return m_children.indexOf(child); }
RDTreeWidgetItem *takeChild(int index);
void removeChild(RDTreeWidgetItem *child);
void clear();
inline int childCount() const { return m_children.count(); }
inline RDTreeWidgetItem *parent() const { return m_parent; }
inline RDTreeWidget *treeWidget() const { return m_widget; }
inline void setBold(bool bold)
{
m_bold = bold;
dataChanged(Qt::FontRole);
}
inline void setItalic(bool italic)
{
m_italic = italic;
dataChanged(Qt::FontRole);
}
inline void setBackgroundColor(QColor background) { setBackground(QBrush(background)); }
inline void setForegroundColor(QColor foreground) { setForeground(QBrush(foreground)); }
inline void setBackground(QBrush background)
{
m_back = background;
dataChanged(Qt::BackgroundRole);
}
inline void setForeground(QBrush foreground)
{
m_fore = foreground;
dataChanged(Qt::ForegroundRole);
}
inline QString text(int column) const { return m_text[column].toString(); }
inline void setText(int column, const QVariant &value)
{
if(column >= m_text.size())
return;
m_text[column] = value;
dataChanged(Qt::DisplayRole);
}
inline void setToolTip(const QString &value)
{
m_tooltip = value;
dataChanged(Qt::ToolTipRole);
}
private:
friend class RDTreeWidget;
friend class RDTreeWidgetModel;
void setWidget(RDTreeWidget *widget);
RDTreeWidget *m_widget = NULL;
RDTreeWidgetItem *m_parent = NULL;
QVector<RDTreeWidgetItem *> m_children;
struct RoleData
{
RoleData() : role(0), data() {}
RoleData(int r, const QVariant &d) : role(r), data(d) {}
int role;
QVariant data;
};
void dataChanged(int role);
// per-column properties
QVector<QVariant> m_text;
QVector<QIcon> m_icons;
// each element, per-column, is a list of other data values
// we allocate this lazily only if it's really needed
QVector<QVector<RoleData>> *m_data = NULL;
// bitmask of which special Qt roles have data set
uint64_t m_customData = 0;
// per-item properties
QString m_tooltip;
bool m_bold = false;
bool m_italic = false;
QBrush m_back;
QBrush m_fore;
QVariant m_tag;
};
class RDTreeWidget : public QTreeView
{
Q_OBJECT
public:
explicit RDTreeWidget(QWidget *parent = 0);
~RDTreeWidget();
void setDefaultHoverColor(QColor col) { m_defaultHoverColour = col; }
void setHoverIconColumn(int column)
void setHoverIconColumn(int column, QIcon normal, QIcon hover)
{
m_hoverColumn = column;
m_normalHoverIcon = normal;
m_activeHoverIcon = hover;
m_hoverHandCursor = true;
m_activateOnClick = true;
}
void setHoverHandCursor(bool hand) { m_hoverHandCursor = hand; }
void setHoverClickActivate(bool click) { m_activateOnClick = click; }
void setClearSelectionOnFocusLoss(bool clear) { m_clearSelectionOnFocusLoss = clear; }
void setHoverIcons(QTreeWidgetItem *item, QIcon normal, QIcon hover);
void setHoverColour(QTreeWidgetItem *item, QColor col);
RDTreeWidgetItem *invisibleRootItem() { return m_root; }
void addTopLevelItem(RDTreeWidgetItem *item) { m_root->addChild(item); }
RDTreeWidgetItem *topLevelItem(int index) const { return m_root->child(index); }
int indexOfTopLevelItem(RDTreeWidgetItem *item) const { return m_root->indexOfChild(item); }
RDTreeWidgetItem *takeTopLevelItem(int index) { return m_root->takeChild(index); }
int topLevelItemCount() const { return m_root->childCount(); }
void beginUpdate();
void endUpdate();
void setColumns(const QStringList &columns);
QString headerText(int column) const { return m_headers[column]; }
void setHeaderText(int column, const QString &text);
RDTreeWidgetItem *selectedItem() const;
RDTreeWidgetItem *currentItem() const;
void setSelectedItem(RDTreeWidgetItem *node);
void setCurrentItem(RDTreeWidgetItem *node);
RDTreeWidgetItem *itemAt(const QPoint &p) const;
RDTreeWidgetItem *itemAt(int x, int y) const { return itemAt(QPoint(x, y)); }
void expandItem(RDTreeWidgetItem *item);
void scrollToItem(RDTreeWidgetItem *node);
void clear();
signals:
void mouseMove(QMouseEvent *e);
void leave(QEvent *e);
void keyPress(QKeyEvent *e);
void itemClicked(RDTreeWidgetItem *item, int column);
void itemDoubleClicked(RDTreeWidgetItem *item, int column);
void itemActivated(RDTreeWidgetItem *item, int column);
void currentItemChanged(RDTreeWidgetItem *current, RDTreeWidgetItem *previous);
void itemSelectionChanged();
public slots:
@@ -59,16 +198,35 @@ private:
void focusOutEvent(QFocusEvent *event) override;
void keyPressEvent(QKeyEvent *e) override;
void clearHovers(QTreeWidgetItem *root, QTreeWidgetItem *exception);
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
QColor m_defaultHoverColour;
int m_hoverColumn = 0;
void setModel(QAbstractItemModel *model) override {}
void dataChanged(RDTreeWidgetItem *item, int role);
friend class RDTreeWidgetModel;
friend class RDTreeWidgetItem;
// invisible root item, used to simplify recursion by even top-level items having a parent
RDTreeWidgetItem *m_root;
RDTreeWidgetModel *m_model;
QStringList m_headers;
bool m_queueUpdates;
RDTreeWidgetItem *m_queuedItem;
QPair<int, int> m_lowestIndex;
QPair<int, int> m_highestIndex;
uint64_t m_queuedRoles;
RDTreeWidgetItem *m_currentHoverItem = NULL;
int m_hoverColumn = -1;
QIcon m_normalHoverIcon;
QIcon m_activeHoverIcon;
bool m_hoverHandCursor = false;
bool m_clearSelectionOnFocusLoss = true;
bool m_clearSelectionOnFocusLoss = false;
bool m_activateOnClick = false;
static const Qt::ItemDataRole hoverIconRole = Qt::ItemDataRole(Qt::UserRole + 10000);
static const Qt::ItemDataRole backupNormalIconRole = Qt::ItemDataRole(Qt::UserRole + 10001);
static const Qt::ItemDataRole hoverBackColourRole = Qt::ItemDataRole(Qt::UserRole + 10002);
static const Qt::ItemDataRole backupBackColourRole = Qt::ItemDataRole(Qt::UserRole + 10002);
};