diff --git a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp index 53842f014..a0a7f1b77 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp +++ b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp @@ -27,72 +27,517 @@ #include #include -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 &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 minRowColumn, QPair maxRowColumn, + const QVector &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 &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 &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>; + 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 &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(-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 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(); - 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 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 ¤t, 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(row, 0); + m_highestIndex = qMakePair(m_lowestIndex.first, m_headers.count() - 1); } - - original = n->data(m_hoverColumn, backupBackColourRole); - - if(original.isValid()) + else { - QBrush orig = original.value(); - 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}); + } } diff --git a/qrenderdoc/Widgets/Extended/RDTreeWidget.h b/qrenderdoc/Widgets/Extended/RDTreeWidget.h index 64cedb075..cda74ede8 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeWidget.h +++ b/qrenderdoc/Widgets/Extended/RDTreeWidget.h @@ -24,31 +24,170 @@ #pragma once -#include +#include -class RDTreeWidget : public QTreeWidget +class RDTreeWidget; +class RDTreeWidgetModel; + +class RDTreeWidgetItem +{ +public: + RDTreeWidgetItem() = default; + RDTreeWidgetItem(const std::initializer_list &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 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 m_text; + QVector m_icons; + // each element, per-column, is a list of other data values + // we allocate this lazily only if it's really needed + QVector> *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 ¤t, 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 m_lowestIndex; + QPair 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); };