glvdebug: Timeline now supports ItemDelegates, and other rendering improvements.
authorPeter Lohrmann <peterl@valvesofware.com>
Thu, 5 Feb 2015 00:21:34 +0000 (16:21 -0800)
committerJon Ashburn <jon@lunarg.com>
Thu, 12 Feb 2015 15:08:28 +0000 (08:08 -0700)
* Timeline items are now drawn with a default QAbstractItemDelegate. Supporting delegates means we can have more interesting rendering options down the road (ie, colors based on things other than duration cost).
* Cleaned up extraneous (and confusing) calls to translate the painter, and also removed unnecessary calls save() and restore() the painter state.
* The timeline marker is now positioned centered under the item rect, which makes it look much cleaner than before.
* The timeline marker is now a little larger and has an antialiased dark gray border instead of an aliased black border.

tools/glave/src/glvdebug/glvdebug_qtimelineview.cpp
tools/glave/src/glvdebug/glvdebug_qtimelineview.h

index 1fdae79..376e411 100755 (executable)
@@ -72,18 +72,79 @@ float u64ToFloat(uint64_t value)
     return static_cast<float>(value);\r
 }\r
 \r
-//-----------------------------------------------------------------------------\r
+//=============================================================================\r
+glvdebug_QTimelineItemDelegate::glvdebug_QTimelineItemDelegate(QObject *parent)\r
+    : QAbstractItemDelegate(parent)\r
+{\r
+    assert(parent != NULL);\r
+}\r
+\r
+glvdebug_QTimelineItemDelegate::~glvdebug_QTimelineItemDelegate()\r
+{\r
+\r
+}\r
+\r
+void glvdebug_QTimelineItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const\r
+{\r
+    glv_trace_packet_header* pHeader = (glv_trace_packet_header*)index.internalPointer();\r
+\r
+    if (pHeader->entrypoint_end_time <= pHeader->entrypoint_begin_time)\r
+    {\r
+        return;\r
+    }\r
+\r
+    float duration = u64ToFloat(pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time);\r
+\r
+    painter->save();\r
+    {\r
+        glvdebug_QTimelineView* pTimeline = (glvdebug_QTimelineView*)parent();\r
+        if (pTimeline != NULL)\r
+        {\r
+            QRectF rect = pTimeline->itemRect(index);\r
+\r
+            if (rect.isValid())\r
+            {\r
+                float durationRatio = duration / pTimeline->getMaxItemDuration();\r
+                int intensity = std::min(255, (int)(durationRatio * 255.0f));\r
+                QColor color(intensity, 255-intensity, 0);\r
+                painter->setBrush(QBrush(color));\r
+                painter->setPen(color);\r
+\r
+                painter->drawRect(rect);\r
+            }\r
+        }\r
+    }\r
+\r
+    painter->restore();\r
+}\r
+\r
+QSize glvdebug_QTimelineItemDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index) const\r
+{\r
+    QSize size;\r
+\r
+    glvdebug_QTimelineView* pTimeline = (glvdebug_QTimelineView*)parent();\r
+    if (pTimeline != NULL)\r
+    {\r
+        QRectF rect = pTimeline->itemRect(index);\r
+\r
+        size = rect.toRect().size();\r
+    }\r
+    return QSize();\r
+}\r
+\r
+//=============================================================================\r
 glvdebug_QTimelineView::glvdebug_QTimelineView(QWidget *parent) :\r
     QAbstractItemView(parent),\r
     m_maxItemDuration(0),\r
     m_threadHeight(0),\r
-    m_pPixmap(NULL)\r
+    m_pPixmap(NULL),\r
+    m_itemDelegate(this)\r
 {\r
     horizontalScrollBar()->setRange(0,0);\r
     verticalScrollBar()->setRange(0,0);\r
 \r
     m_background = QBrush(QColor(200,200,200));\r
-    m_trianglePen = QPen(Qt::black);\r
+    m_trianglePen = QPen(Qt::darkGray);\r
     m_trianglePen.setWidth(1);\r
     m_textPen = QPen(Qt::white);\r
     m_textFont.setPixelSize(50);\r
@@ -117,6 +178,8 @@ void glvdebug_QTimelineView::setModel(QAbstractItemModel* pModel)
         return;\r
     }\r
 \r
+    setItemDelegate(&m_itemDelegate);\r
+\r
     int numRows = model()->rowCount();\r
     for (int i = 0; i < numRows; i++)\r
     {\r
@@ -181,6 +244,7 @@ QRectF glvdebug_QTimelineView::itemRect(const QModelIndex &item) const
     uint64_t duration = pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time;\r
 \r
     float leftOffset = scalePositionHorizontally(pHeader->entrypoint_begin_time);\r
+    leftOffset += 10; // leaves a small margin on the left side;\r
     float scaledWidth = scaleDurationHorizontally(duration);\r
 \r
     // Clamp the item so that it is 1 pixel wide.\r
@@ -251,8 +315,7 @@ QModelIndex glvdebug_QTimelineView::indexAt(const QPoint &point) const
         QModelIndex index = model()->index(r, glvdebug_QTraceFileModel::Column_EntrypointName);\r
         QRectF rectf = itemRect(index);\r
         QRect rect = rectf.toRect();\r
-        int gap = 10;\r
-        if (rect.contains(wx-gap, wy))\r
+        if (rect.contains(wx, wy))\r
         {\r
             return index;\r
         }\r
@@ -283,33 +346,19 @@ void glvdebug_QTimelineView::drawBaseTimelines(QPainter* painter, const QRect& r
 {\r
     int numThreads = threadList.count();\r
 \r
-    //int left = rect.x();\r
-    //int top = rect.y();\r
-    int width = rect.width();\r
     int height = (numThreads > 0) ? rect.height() / numThreads : rect.height();\r
 \r
     for (int i = 0; i < numThreads; i++)\r
     {\r
         int threadTop = (i*height);\r
 \r
-        painter->save();\r
-\r
-        // move painter to top corner for this thread\r
-        painter->translate(0, threadTop);\r
-\r
-        painter->drawText(0, 15, QString("Thread %1").arg(threadList[i]));\r
-\r
-        // translate drawing to vertical center of rect\r
-        painter->translate(0, height/2);\r
-\r
-        // everything will have a small gap on the left and right sides\r
-        painter->translate(gap, 0);\r
-\r
-        // draw the actual timeline\r
-        int lineLength = width-2*gap;\r
-        painter->drawLine(0,0, lineLength, 0);\r
+        painter->drawText(0, threadTop + 15, QString("Thread %1").arg(threadList[i]));\r
 \r
-        painter->restore();\r
+        // draw the timeline in the middle of this thread's area\r
+        int lineStart = gap;\r
+        int lineEnd = m_lineLength + lineStart;\r
+        int lineY = threadTop + height/2;\r
+        painter->drawLine(lineStart, lineY, lineEnd, lineY);\r
     }\r
 }\r
 \r
@@ -329,9 +378,9 @@ void glvdebug_QTimelineView::paint(QPainter *painter, QPaintEvent *event)
     }\r
 \r
     int gap = 10;\r
-    int arrowHeight = 10;\r
-    int arrowTop = m_threadHeight-gap-arrowHeight;\r
-    int arrowHalfWidth = 3;\r
+    int arrowHeight = 12;\r
+    int arrowTop = 2;\r
+    int arrowHalfWidth = 4;\r
     m_lineLength = event->rect().width()-2*gap;\r
 \r
     QPolygon triangle(3);\r
@@ -381,9 +430,6 @@ void glvdebug_QTimelineView::paint(QPainter *painter, QPaintEvent *event)
         pixmapPainter.fillRect(event->rect(), m_background);\r
         drawBaseTimelines(&pixmapPainter, event->rect(), threadList, gap);\r
 \r
-        // translate sideways to insert a small gap on the left side\r
-        pixmapPainter.translate(gap, 0);\r
-\r
         m_horizontalScale = (float)m_lineLength / u64ToFloat(m_rawEndTime - m_rawStartTime);\r
 \r
         if (model() != NULL)\r
@@ -407,56 +453,39 @@ void glvdebug_QTimelineView::paint(QPainter *painter, QPaintEvent *event)
         return;\r
     }\r
 \r
-    painter->save();\r
+    // draw current api call marker\r
+    int currentIndexRow = currentIndex().row();\r
+    if (currentIndexRow >= 0)\r
     {\r
-        // translate to leave a small gap on the left\r
-        painter->translate(gap, 0);\r
-\r
-        int currentIndexRow = currentIndex().row();\r
-        if (currentIndexRow >= 0)\r
-        {\r
-            // draw current api call marker\r
-            QModelIndex index = model()->index(currentIndexRow, glvdebug_QTraceFileModel::Column_EntrypointName);\r
-            glv_trace_packet_header* pHeader = (glv_trace_packet_header*)index.internalPointer();\r
-\r
-            // Overlay a black rectangle around the current item.\r
-            // For more information on how rects are drawn as outlines,\r
-            // see here: http://qt-project.org/doc/qt-4.8/qrectf.html#rendering\r
-            int penWidth = 2;\r
-            int penWidthHalf = 1;\r
-            QPen blackPen(Qt::black);\r
-            blackPen.setWidth(penWidth);\r
-            blackPen.setJoinStyle(Qt::MiterJoin);\r
-            painter->setPen(blackPen);\r
-\r
-            // Don't fill the rectangle\r
-            painter->setBrush(Qt::NoBrush);\r
-\r
-            QRectF rect = itemRect(index);\r
-            rect.adjust(-penWidthHalf, -penWidthHalf, penWidthHalf-1, penWidthHalf+1);\r
-            painter->drawRect(rect);\r
-\r
-            // translate down to the proper thread\r
-            int threadIndex = m_threadIdList.indexOf(pHeader->thread_id);\r
-            painter->translate(0, m_threadHeight * threadIndex);\r
-\r
-            // Draw marker at midpoint of call duration.\r
-            uint64_t halfDuration = (pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time) / 2;\r
-            drawCurrentApiCallMarker(painter, triangle, pHeader->entrypoint_begin_time + halfDuration);\r
-        }\r
+        // Overlay a black rectangle around the current item.\r
+        // For more information on how rects are drawn as outlines,\r
+        // see here: http://qt-project.org/doc/qt-4.8/qrectf.html#rendering\r
+        int penWidth = 2;\r
+        int penWidthHalf = 1;\r
+        QPen blackPen(Qt::black);\r
+        blackPen.setWidth(penWidth);\r
+        blackPen.setJoinStyle(Qt::MiterJoin);\r
+        painter->setPen(blackPen);\r
+\r
+        // Don't fill the rectangle\r
+        painter->setBrush(Qt::NoBrush);\r
+\r
+        QModelIndex index = model()->index(currentIndexRow, glvdebug_QTraceFileModel::Column_EntrypointName);\r
+        QRectF rect = itemRect(index);\r
+        rect.adjust(-penWidthHalf, -penWidthHalf, penWidthHalf-1, penWidthHalf+1);\r
+        painter->drawRect(rect);\r
+\r
+        // Draw marker underneath the current rect\r
+        painter->save();\r
+        QPainter::RenderHints hints = painter->renderHints();\r
+        painter->setRenderHints(QPainter::Antialiasing);\r
+        painter->setPen(m_trianglePen);\r
+        painter->setBrush(QColor(Qt::yellow));\r
+        painter->translate(rect.center().x(), rect.bottom());\r
+        painter->drawPolygon(triangle);\r
+        painter->setRenderHints(hints, false);\r
+        painter->restore();\r
     }\r
-    painter->restore();\r
-}\r
-\r
-//-----------------------------------------------------------------------------\r
-void glvdebug_QTimelineView::drawCurrentApiCallMarker(QPainter* painter, QPolygon& triangle, uint64_t rawTime)\r
-{\r
-    painter->save();\r
-    painter->setPen(m_trianglePen);\r
-    painter->setBrush(QColor(Qt::yellow));\r
-    painter->translate(scalePositionHorizontally(rawTime), 0);\r
-    painter->drawPolygon(triangle);\r
-    painter->restore();\r
 }\r
 \r
 //-----------------------------------------------------------------------------\r
@@ -485,39 +514,31 @@ float glvdebug_QTimelineView::scalePositionHorizontally(uint64_t value) const
 void glvdebug_QTimelineView::drawTimelineItem(QPainter* painter, const QModelIndex &index, int height)\r
 {\r
     glv_trace_packet_header* pHeader = (glv_trace_packet_header*)index.internalPointer();\r
-\r
-    float duration = u64ToFloat(pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time);\r
-    if (duration < 0)\r
+    if (pHeader == NULL)\r
     {\r
         return;\r
     }\r
 \r
-    painter->save();\r
+    if (pHeader->entrypoint_end_time <= pHeader->entrypoint_begin_time)\r
     {\r
-        int threadIndex = m_threadIdList.indexOf(pHeader->thread_id);\r
+        return;\r
+    }\r
 \r
-        // only draw if the item will extend beyond the minimum offset\r
-//        float leftOffset = scalePositionHorizontally(pHeader->entrypoint_begin_time);\r
-//        float scaledWidth = scaleDurationHorizontally(duration);\r
-//        if (m_threadIdMinOffset[threadIndex] < leftOffset + scaledWidth)\r
-        {\r
-            QRectF rect = itemRect(index);\r
 \r
-            if (rect.isValid())\r
-            {\r
-                float durationRatio = duration / m_maxItemDuration;\r
-                int intensity = std::min(255, (int)(durationRatio * 255.0f));\r
-                QColor color(intensity, 255-intensity, 0);\r
-                painter->setBrush(QBrush(color));\r
-                painter->setPen(color);\r
+    QRectF rect = itemRect(index);\r
+    if (!rect.isValid() || rect.bottom() < 0 ||\r
+        rect.y() > viewport()->height())\r
+    {\r
+        return;\r
+    }\r
 \r
-                // update minimum offset\r
-                m_threadIdMinOffset[threadIndex] = rect.left() + rect.width();\r
+    QStyleOptionViewItem option = viewOptions();\r
+    option.rect = rect.toRect();\r
+    if (selectionModel()->isSelected(index))\r
+        option.state |= QStyle::State_Selected;\r
+    if (currentIndex() == index)\r
+        option.state |= QStyle::State_HasFocus;\r
 \r
-                painter->drawRect(rect);\r
-            }\r
-        }\r
-    }\r
 \r
-    painter->restore();\r
+    itemDelegate()->paint(painter, option, index);\r
 }\r
index 682a3bd..9a821d1 100755 (executable)
@@ -42,6 +42,17 @@ QT_END_NAMESPACE
 #include <QPen>\r
 #include <QScrollBar>\r
 \r
+class glvdebug_QTimelineItemDelegate : public QAbstractItemDelegate\r
+{\r
+    Q_OBJECT\r
+public:\r
+    glvdebug_QTimelineItemDelegate(QObject *parent = 0);\r
+    virtual ~glvdebug_QTimelineItemDelegate();\r
+\r
+    virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;\r
+    virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;\r
+};\r
+\r
 class glvdebug_QTimelineView : public QAbstractItemView\r
 {\r
     Q_OBJECT\r
@@ -57,6 +68,13 @@ public:
     virtual QModelIndex indexAt(const QPoint &point) const;\r
     // End public virtual functions of QAbstractItemView\r
 \r
+    QList<int> getModelThreadList() const;\r
+    QRectF itemRect(const QModelIndex &item) const;\r
+    float getMaxItemDuration() const\r
+    {\r
+        return m_maxItemDuration;\r
+    }\r
+\r
     void deletePixmap()\r
     {\r
         if (m_pPixmap != NULL)\r
@@ -83,16 +101,14 @@ private:
     int m_threadHeight;\r
 \r
     QPixmap *m_pPixmap;\r
+    glvdebug_QTimelineItemDelegate m_itemDelegate;\r
 \r
-    QList<int> getModelThreadList() const;\r
     void drawBaseTimelines(QPainter *painter, const QRect &rect, const QList<int> &threadList, int gap);\r
     void drawTimelineItem(QPainter* painter, const QModelIndex &index, int height);\r
-    void drawCurrentApiCallMarker(QPainter *painter, QPolygon &triangle, uint64_t rawTime);\r
 \r
     float scaleDurationHorizontally(uint64_t value) const;\r
     float scalePositionHorizontally(uint64_t value) const;\r
 \r
-    QRectF itemRect(const QModelIndex &item) const;\r
     // Begin Private...\r
     virtual QRegion itemRegion(const QModelIndex &index) const;\r
     // End private...\r