diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index 7cfb5031e..8cd55e22d 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -365,6 +365,130 @@ bool RichResourceTextMouseEvent(const QWidget *owner, const QVariant &var, QRect return false; } +RichTextViewDelegate::RichTextViewDelegate(QAbstractItemView *parent) + : m_widget(parent), ForwardingDelegate(parent) +{ +} + +RichTextViewDelegate::~RichTextViewDelegate() +{ +} + +void RichTextViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if(index.isValid()) + { + QVariant v = index.data(); + + if(RichResourceTextCheck(v)) + { + // draw the item without text, so we get the proper background/selection/etc. + // we'd like to be able to use the parent delegate's paint here, but either it calls to + // QStyledItemDelegate which will re-fetch the text (bleh), or it calls to the manual + // delegate which could do anything. So for this case we just use the style and skip the + // delegate and hope it works out. + QStyleOptionViewItem opt = option; + QStyledItemDelegate::initStyleOption(&opt, index); + opt.text.clear(); + m_widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, m_widget); + + painter->save(); + + QRect rect = option.rect; + if(!opt.icon.isNull()) + { + QIcon::Mode mode; + if((opt.state & QStyle::State_Enabled) == 0) + mode = QIcon::Disabled; + else if(opt.state & QStyle::State_Selected) + mode = QIcon::Selected; + else + mode = QIcon::Normal; + QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; + rect.setX(rect.x() + opt.icon.actualSize(opt.decorationSize, mode, state).width() + 4); + } + + RichResourceTextPaint(m_widget, painter, rect, option.font, option.palette, + option.state & QStyle::State_MouseOver, + m_widget->viewport()->mapFromGlobal(QCursor::pos()), v); + + painter->restore(); + return; + } + } + + return ForwardingDelegate::paint(painter, option, index); +} + +QSize RichTextViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if(index.isValid()) + { + QVariant v = index.data(); + + if(RichResourceTextCheck(v)) + return QSize(RichResourceTextWidthHint(m_widget, v), option.fontMetrics.height()); + } + + return ForwardingDelegate::sizeHint(option, index); +} + +bool RichTextViewDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + if(event->type() == QEvent::MouseButtonRelease && index.isValid()) + { + QVariant v = index.data(); + + if(RichResourceTextCheck(v)) + { + QRect rect = option.rect; + + QIcon icon = index.data(Qt::DecorationRole).value(); + + if(!icon.isNull()) + { + rect.setX(rect.x() + + icon.actualSize(option.decorationSize, QIcon::Normal, QIcon::On).width() + 4); + } + + // ignore the return value, we always consume clicks on this cell + RichResourceTextMouseEvent(m_widget, v, rect, (QMouseEvent *)event); + return true; + } + } + + return ForwardingDelegate::editorEvent(event, model, option, index); +} + +bool RichTextViewDelegate::linkHover(QMouseEvent *e, const QModelIndex &index) +{ + if(index.isValid()) + { + QVariant v = index.data(); + + if(RichResourceTextCheck(v)) + { + QRect rect = m_widget->visualRect(index); + + QIcon icon = index.data(Qt::DecorationRole).value(); + + if(!icon.isNull()) + { + rect.setX( + rect.x() + + icon.actualSize(QSize(rect.height(), rect.height()), QIcon::Normal, QIcon::On).width() + + 4); + } + + return RichResourceTextMouseEvent(m_widget, v, rect, e); + } + } + + return false; +} + #include "renderdoc_tostr.inl" QString ToQStr(const ResourceUsage usage, const GraphicsAPI apitype) diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 36612867a..b9dc02dc7 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -467,6 +467,28 @@ private: QAbstractItemDelegate *m_delegate = NULL; }; +// delegate that will handle painting, hovering and clicking on rich text items. +// owning view needs to call linkHover, and adjust its cursor and repaint as necessary. +class RichTextViewDelegate : public ForwardingDelegate +{ + Q_OBJECT +public: + explicit RichTextViewDelegate(QAbstractItemView *parent); + ~RichTextViewDelegate(); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, + const QModelIndex &index) override; + + bool linkHover(QMouseEvent *e, const QModelIndex &index); + +private: + QAbstractItemView *m_widget; +}; + class QMenu; // helper for doing a manual blocking invoke of a dialog diff --git a/qrenderdoc/Widgets/Extended/RDTableView.cpp b/qrenderdoc/Widgets/Extended/RDTableView.cpp index bc36790f9..54eb1cbe6 100644 --- a/qrenderdoc/Widgets/Extended/RDTableView.cpp +++ b/qrenderdoc/Widgets/Extended/RDTableView.cpp @@ -39,10 +39,24 @@ RDTableView::RDTableView(QWidget *parent) : QTableView(parent) m_horizontalHeader = new RDHeaderView(Qt::Horizontal, this); setHorizontalHeader(m_horizontalHeader); + m_delegate = new RichTextViewDelegate(this); + QTableView::setItemDelegate(m_delegate); + QObject::connect(m_horizontalHeader, &QHeaderView::sectionResized, [this](int, int, int) { viewport()->update(); }); } +void RDTableView::setItemDelegate(QAbstractItemDelegate *delegate) +{ + m_userDelegate = delegate; + m_delegate->setForwardDelegate(m_userDelegate); +} + +QAbstractItemDelegate *RDTableView::itemDelegate() const +{ + return m_userDelegate; +} + int RDTableView::columnViewportPosition(int column) const { return horizontalHeader()->sectionViewportPosition(column); @@ -306,9 +320,40 @@ void RDTableView::paintCell(QPainter *painter, const QModelIndex &index, if(selectionModel() && selectionModel()->isSelected(index)) cellopt.state |= QStyle::State_Selected; + if(cellopt.rect.contains(viewport()->mapFromGlobal(QCursor::pos()))) + cellopt.state |= QStyle::State_MouseOver; + + if(index.row() == 1 && index.column() == 5) + qInfo() << cellopt.state; + // draw the background, then the cell style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &cellopt, painter, this); - itemDelegate(index)->paint(painter, cellopt, index); + QTableView::itemDelegate(index)->paint(painter, cellopt, index); +} + +void RDTableView::mouseMoveEvent(QMouseEvent *e) +{ + QModelIndex oldHover = m_currentHoverIndex; + + QTableView::mouseMoveEvent(e); + + QModelIndex newHover = m_currentHoverIndex = indexAt(e->pos()); + + update(newHover); + + if(m_delegate && m_delegate->linkHover(e, newHover)) + { + setCursor(QCursor(Qt::PointingHandCursor)); + } + else + { + unsetCursor(); + } + + if(oldHover == newHover) + return; + + update(oldHover); } void RDTableView::scrollTo(const QModelIndex &index, ScrollHint hint) diff --git a/qrenderdoc/Widgets/Extended/RDTableView.h b/qrenderdoc/Widgets/Extended/RDTableView.h index e647dbbdb..3767094e6 100644 --- a/qrenderdoc/Widgets/Extended/RDTableView.h +++ b/qrenderdoc/Widgets/Extended/RDTableView.h @@ -27,6 +27,8 @@ #include #include "RDHeaderView.h" +class RichTextViewDelegate; + class RDTableView : public QTableView { Q_OBJECT @@ -44,6 +46,9 @@ public: void resizeColumnsToContents(); void setCustomHeaderSizing(bool sizing) { m_horizontalHeader->setCustomSizing(sizing); } + void setItemDelegate(QAbstractItemDelegate *delegate); + QAbstractItemDelegate *itemDelegate() const; + // these ones we CAN override, so even though the implementation is identical to QTableView we // reimplement so it can pick up the above functions QRect visualRect(const QModelIndex &index) const override; @@ -57,6 +62,7 @@ public: void setPinnedColumns(int numColumns); int pinnedColumns() const { return m_pinnedColumns; } protected: + void mouseMoveEvent(QMouseEvent *e) override; void keyPressEvent(QKeyEvent *e) override; void paintEvent(QPaintEvent *e) override; void updateGeometries() override; @@ -69,4 +75,11 @@ private: int m_columnGroupRole = 0; RDHeaderView *m_horizontalHeader; + + QModelIndex m_currentHoverIndex; + + QAbstractItemDelegate *m_userDelegate = NULL; + RichTextViewDelegate *m_delegate; + + friend class RichTextViewDelegate; }; diff --git a/qrenderdoc/Widgets/Extended/RDTreeView.cpp b/qrenderdoc/Widgets/Extended/RDTreeView.cpp index ea68ed60d..f41aa0e36 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeView.cpp +++ b/qrenderdoc/Widgets/Extended/RDTreeView.cpp @@ -37,36 +37,23 @@ RDTreeViewDelegate::RDTreeViewDelegate(RDTreeView *view) : ForwardingDelegate(vi QSize RDTreeViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { - m_suppressIcon = m_View->ignoreIconSize(); QSize ret = ForwardingDelegate::sizeHint(option, index); - m_suppressIcon = false; + + if(m_View->ignoreIconSize()) + ret.setHeight(qMax(option.decorationSize.height() + 2, ret.height())); // expand a pixel for the grid lines if(m_View->visibleGridLines()) - { ret.setWidth(ret.width() + 1); - ret.setHeight(ret.height() + 1); - } // ensure we have at least the margin on top of font size. If the style applied more, don't add to // it. - ret.setHeight(qMax(ret.height(), option.fontMetrics.height() + m_View->verticalItemMargin())); + int minHeight = option.fontMetrics.height(); + ret.setHeight(qMax(ret.height(), minHeight + m_View->verticalItemMargin())); return ret; } -void RDTreeViewDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const -{ - QStyledItemDelegate::initStyleOption(option, index); - - if(m_suppressIcon) - { - option->icon = QIcon(); - option->features &= ~QStyleOptionViewItem::HasDecoration; - option->decorationSize = QSize(); - } -} - RDTipLabel::RDTipLabel(QWidget *listener) : QLabel(NULL), mouseListener(listener) { int margin = style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, NULL, this); diff --git a/qrenderdoc/Widgets/Extended/RDTreeView.h b/qrenderdoc/Widgets/Extended/RDTreeView.h index 9f125ccbc..1dbfd2a43 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeView.h +++ b/qrenderdoc/Widgets/Extended/RDTreeView.h @@ -38,19 +38,11 @@ class RDTreeViewDelegate : public ForwardingDelegate private: Q_OBJECT - // I hate the mutable keyword, but it's necessary to modify it inside sizeHint to access in - // initStyleOption. There's no other way to only remove the icon while in sizeHint, and not in - // other places that call initStyleOption. We don't want to try and manually init, since if - // there's a forwarded delegate it might do something different. - mutable bool m_suppressIcon = false; RDTreeView *m_View; public: RDTreeViewDelegate(RDTreeView *view); QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - -protected: - void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override; }; class RDTipLabel : public QLabel diff --git a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp index 240ed3a6f..c2a582a52 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp +++ b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp @@ -36,6 +36,7 @@ #include "Code/Interface/QRDInterface.h" #include "Code/QRDUtils.h" #include "Code/Resources.h" +#include "Widgets/Extended/RDTableView.h" class RDTreeWidgetModel : public QAbstractItemModel { @@ -568,163 +569,12 @@ RDTreeWidgetItemIterator &RDTreeWidgetItemIterator::operator++() return *this; } -RDTreeWidgetDelegate::RDTreeWidgetDelegate(RDTreeWidget *parent) - : m_widget(parent), RDTreeViewDelegate(parent) -{ -} - -RDTreeWidgetDelegate::~RDTreeWidgetDelegate() -{ -} - -void RDTreeWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - RDTreeWidgetModel *model = m_widget->m_model; - if(index.isValid() && model == index.model()) - { - RDTreeWidgetItem *item = model->itemForIndex(index); - - if(index.column() < item->m_text.count()) - { - QVariant v = item->m_text[index.column()]; - - if(RichResourceTextCheck(v)) - { - // draw the item without text, so we get the proper background/selection/etc. - // we'd like to be able to use the parent delegate's paint here, but either it calls to - // QStyledItemDelegate which will re-fetch the text (bleh), or it calls to the manual - // delegate which could do anything. So for this case we just use the style and skip the - // delegate and hope it works out. - QStyleOptionViewItem opt = option; - QStyledItemDelegate::initStyleOption(&opt, index); - opt.text.clear(); - m_widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, m_widget); - - painter->save(); - - QRect rect = option.rect; - if(!opt.icon.isNull()) - { - QIcon::Mode mode; - if((opt.state & QStyle::State_Enabled) == 0) - mode = QIcon::Disabled; - else if(opt.state & QStyle::State_Selected) - mode = QIcon::Selected; - else - mode = QIcon::Normal; - QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; - rect.setX(rect.x() + opt.icon.actualSize(opt.decorationSize, mode, state).width()); - } - - RichResourceTextPaint(m_widget, painter, rect, option.font, option.palette, - option.state & QStyle::State_MouseOver, - m_widget->viewport()->mapFromGlobal(QCursor::pos()), v); - - painter->restore(); - return; - } - } - } - - return RDTreeViewDelegate::paint(painter, option, index); -} - -QSize RDTreeWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - RDTreeWidgetModel *model = m_widget->m_model; - if(index.isValid() && model == index.model()) - { - RDTreeWidgetItem *item = model->itemForIndex(index); - - if(index.column() < item->m_text.count()) - { - QVariant v = item->m_text[index.column()]; - - if(RichResourceTextCheck(v)) - return QSize(RichResourceTextWidthHint(m_widget, v), option.fontMetrics.height()); - } - } - - return RDTreeViewDelegate::sizeHint(option, index); -} - -bool RDTreeWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, const QModelIndex &index) -{ - RDTreeWidgetModel *rdmodel = m_widget->m_model; - if(event->type() == QEvent::MouseButtonRelease && index.isValid() && rdmodel == model) - { - RDTreeWidgetItem *item = rdmodel->itemForIndex(index); - - if(index.column() < item->m_text.count()) - { - QVariant v = item->m_text[index.column()]; - - if(RichResourceTextCheck(v)) - { - QRect rect = option.rect; - - if(!item->m_icons[index.column()].isNull()) - { - QIcon::Mode mode; - if((option.state & QStyle::State_Enabled) == 0) - mode = QIcon::Disabled; - else if(option.state & QStyle::State_Selected) - mode = QIcon::Selected; - else - mode = QIcon::Normal; - QIcon::State state = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off; - rect.setX(rect.x() + option.icon.actualSize(option.decorationSize, mode, state).width()); - } - - // ignore the return value, we always consume clicks on this cell - RichResourceTextMouseEvent(m_widget, v, rect, (QMouseEvent *)event); - return true; - } - } - } - - return RDTreeViewDelegate::editorEvent(event, model, option, index); -} - -bool RDTreeWidgetDelegate::linkHover(QMouseEvent *e, const QModelIndex &index) -{ - if(index.isValid()) - { - RDTreeWidgetItem *item = m_widget->m_model->itemForIndex(index); - - if(index.column() < item->m_text.count()) - { - QVariant v = item->m_text[index.column()]; - - if(RichResourceTextCheck(v)) - { - QRect rect = m_widget->visualRect(index); - - QIcon icon = item->m_icons[index.column()]; - - if(!icon.isNull()) - { - rect.setX( - rect.x() + - icon.actualSize(QSize(rect.height(), rect.height()), QIcon::Normal, QIcon::On).width()); - } - - return RichResourceTextMouseEvent(m_widget, v, rect, e); - } - } - } - - return false; -} - RDTreeWidget::RDTreeWidget(QWidget *parent) : RDTreeView(parent) { // we'll call this ourselves in drawBranches() RDTreeView::enableBranchRectFill(false); - m_delegate = new RDTreeWidgetDelegate(this); + m_delegate = new RichTextViewDelegate(this); RDTreeView::setItemDelegate(m_delegate); header()->setSectionsMovable(false); diff --git a/qrenderdoc/Widgets/Extended/RDTreeWidget.h b/qrenderdoc/Widgets/Extended/RDTreeWidget.h index bbf828a36..0ac244976 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeWidget.h +++ b/qrenderdoc/Widgets/Extended/RDTreeWidget.h @@ -198,25 +198,7 @@ private: RDTreeWidgetItem *m_Current; }; -class RDTreeWidgetDelegate : public RDTreeViewDelegate -{ - Q_OBJECT -public: - explicit RDTreeWidgetDelegate(RDTreeWidget *parent); - ~RDTreeWidgetDelegate(); - - void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - - bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, - const QModelIndex &index) override; - - bool linkHover(QMouseEvent *e, const QModelIndex &index); - -private: - RDTreeWidget *m_widget; -}; +class RichTextViewDelegate; class RDTreeWidget : public RDTreeView { @@ -313,7 +295,7 @@ private: RDTreeWidgetModel *m_model; QAbstractItemDelegate *m_userDelegate = NULL; - RDTreeWidgetDelegate *m_delegate; + RichTextViewDelegate *m_delegate; bool m_clearing = false; diff --git a/qrenderdoc/Windows/DebugMessageView.cpp b/qrenderdoc/Windows/DebugMessageView.cpp index 61f21c2bc..909961d04 100644 --- a/qrenderdoc/Windows/DebugMessageView.cpp +++ b/qrenderdoc/Windows/DebugMessageView.cpp @@ -106,7 +106,12 @@ public: case 2: return sort ? QVariant((uint32_t)msg.severity) : QVariant(ToQStr(msg.severity)); case 3: return ToQStr(msg.category); case 4: return msg.messageID; - case 5: return msg.description; + case 5: + { + QVariant desc = msg.description; + RichResourceTextInitialise(desc); + return desc; + } default: break; } } @@ -208,6 +213,11 @@ DebugMessageView::DebugMessageView(ICaptureContext &ctx, QWidget *parent) ui->messages->setSortingEnabled(true); ui->messages->sortByColumn(0, Qt::AscendingOrder); + ui->messages->setMouseTracking(true); + ui->messages->setAutoScroll(false); + + ui->messages->horizontalHeader()->setStretchLastSection(false); + ui->messages->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(ui->messages, &QWidget::customContextMenuRequested, this, &DebugMessageView::messages_contextMenu); diff --git a/qrenderdoc/Windows/DebugMessageView.ui b/qrenderdoc/Windows/DebugMessageView.ui index 598cf588c..d3fb6f9f5 100644 --- a/qrenderdoc/Windows/DebugMessageView.ui +++ b/qrenderdoc/Windows/DebugMessageView.ui @@ -27,7 +27,10 @@ 3 - + + + Qt::ScrollBarAlwaysOn + QAbstractItemView::SingleSelection @@ -53,6 +56,13 @@ + + + RDTableView + QTableView +
Widgets/Extended/RDTableView.h
+
+
diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index d7de76539..ec62c0722 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -111,7 +111,7 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent) ui->events->header()->setCascadingSectionResizes(false); - ui->events->setItemVerticalMargin(4); + ui->events->setItemVerticalMargin(0); ui->events->setIgnoreIconSize(true); // set up default section layout. This will be overridden in restoreState()