diff options
author | Christiaan Janssen <christiaan.janssen@nokia.com> | 2011-10-26 11:32:01 +0200 |
---|---|---|
committer | Christiaan Janssen <christiaan.janssen@nokia.com> | 2011-11-02 10:14:09 +0100 |
commit | bf4dfd5e74ed42af5bd6b4047813cb93682b5b10 (patch) | |
tree | 43e3db06f59460905eda7f523ca184c2d8b6f5e5 /src/plugins | |
parent | 38ad15a7728de7691087d255d76e50d6df2e9efd (diff) | |
download | qt-creator-bf4dfd5e74ed42af5bd6b4047813cb93682b5b10.tar.gz |
QmlProfiler: optimized timeline display
Change-Id: I0d7cf110356ef5f805b81a5fc39dca3870765ea3
Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/qmlprofiler/qml/Label.qml | 74 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/qml/MainView.qml | 295 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/qml/RangeDetails.qml | 29 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/qml/TimeMarks.qml | 14 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/qml/arrow_down.png | bin | 0 -> 208 bytes | |||
-rw-r--r-- | src/plugins/qmlprofiler/qml/arrow_right.png | bin | 0 -> 209 bytes | |||
-rw-r--r-- | src/plugins/qmlprofiler/qml/arrow_select.png | bin | 0 -> 609 bytes | |||
-rw-r--r-- | src/plugins/qmlprofiler/qml/qmlprofiler.qrc | 3 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/timelineview.cpp | 419 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/timelineview.h | 138 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/tracewindow.cpp | 26 | ||||
-rw-r--r-- | src/plugins/qmlprofiler/tracewindow.h | 4 |
12 files changed, 584 insertions, 418 deletions
diff --git a/src/plugins/qmlprofiler/qml/Label.qml b/src/plugins/qmlprofiler/qml/Label.qml index abe28391bb..a9f3594ad1 100644 --- a/src/plugins/qmlprofiler/qml/Label.qml +++ b/src/plugins/qmlprofiler/qml/Label.qml @@ -34,12 +34,53 @@ import QtQuick 1.0 Item { property alias text: txt.text + property bool expanded: false + property int typeIndex: index - height: 50 - width: 150 //### required, or ignored by positioner + property variant descriptions: [text] + + height: root.singleRowHeight + width: 150 + + onExpandedChanged: { + var rE = labels.rowExpanded; + rE[typeIndex] = expanded; + labels.rowExpanded = rE; + backgroundMarks.requestPaint(); + view.rowExpanded(typeIndex, expanded); + updateHeight(); + } + + Component.onCompleted: { + updateHeight(); + } + + function updateHeight() { + height = root.singleRowHeight * + (expanded ? qmlEventList.uniqueEventsOfType(typeIndex) : qmlEventList.maxNestingForType(typeIndex)); + } + + Connections { + target: qmlEventList + onDataReady: { + var desc=[]; + for (var i=0; i<qmlEventList.uniqueEventsOfType(typeIndex); i++) + desc[i] = qmlEventList.eventTextForType(typeIndex, i); + // special case: empty + if (desc.length == 1 && desc[0]=="") + desc[0] = text; + descriptions = desc; + updateHeight(); + } + onDataClear: { + descriptions = [text]; + updateHeight(); + } + } Text { - id: txt; + id: txt + visible: !expanded x: 5 font.pixelSize: 12 color: "#232323" @@ -52,4 +93,31 @@ Item { color: "#cccccc" anchors.bottom: parent.bottom } + + Column { + visible: expanded + Repeater { + model: descriptions.length + Text { + height: root.singleRowHeight + x: 5 + width: 140 + text: descriptions[index] + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + } + } + + Image { + source: expanded ? "arrow_down.png" : "arrow_right.png" + x: parent.width - 12 + y: 2 + MouseArea { + anchors.fill: parent + onClicked: { + expanded = !expanded; + } + } + } } diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml index cbdab441c4..1ba891631e 100644 --- a/src/plugins/qmlprofiler/qml/MainView.qml +++ b/src/plugins/qmlprofiler/qml/MainView.qml @@ -39,13 +39,17 @@ Rectangle { // ***** properties property int candidateHeight: 0 - height: Math.max( candidateHeight, labels.rowCount * 50 + 2 ) + height: Math.max( candidateHeight, labels.height + 2 ) + + property int singleRowHeight: 30 property bool dataAvailable: true property int eventCount: 0 property real progress: 0 - property bool mouseOverSelection : true + property alias selectionLocked : view.selectionLocked + signal updateLockButton + property alias selectedItem: view.selectedItem property variant names: [ qsTr("Painting"), qsTr("Compiling"), qsTr("Creating"), qsTr("Binding"), qsTr("Handling Signal")] property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ] @@ -56,9 +60,6 @@ Rectangle { property string fileName: "" property int lineNumber: -1 - property int selectedEventIndex : -1 - property bool mouseTracking: false - property real elapsedTime signal updateTimer @@ -107,7 +108,11 @@ Rectangle { onDataReady: { if (eventCount > 0) { view.clearData(); - view.rebuildCache(); + progress = 1.0; + dataAvailable = true; + view.visible = true; + view.requestPaint(); + zoomControl.setRange(0, qmlEventList.traceEndTime()/10); } } } @@ -128,7 +133,6 @@ Rectangle { function clearDisplay() { clearData(); - selectedEventIndex = -1; view.visible = false; } @@ -139,19 +143,11 @@ Rectangle { } function nextEvent() { - if (eventCount > 0) { - ++selectedEventIndex; - if (selectedEventIndex >= eventCount) - selectedEventIndex = 0; - } + view.selectNext(); } function prevEvent() { - if (eventCount > 0) { - --selectedEventIndex; - if (selectedEventIndex < 0) - selectedEventIndex = eventCount - 1; - } + view.selectPrev(); } function updateWindowLength(absoluteFactor) { @@ -179,9 +175,9 @@ Rectangle { } var fixedPoint = (view.startTime + view.endTime) / 2; - if (root.selectedEventIndex !== -1) { + if (view.selectedItem !== -1) { // center on selected item if it's inside the current screen - var newFixedPoint = qmlEventList.getStartTime(selectedEventIndex); + var newFixedPoint = qmlEventList.getStartTime(view.selectedItem); if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime) fixedPoint = newFixedPoint; } @@ -242,37 +238,18 @@ Rectangle { rangeDetails.type = ""; rangeDetails.file = ""; rangeDetails.line = -1; - - root.mouseOverSelection = true; - selectionHighlight.visible = false; } // ***** slots - onSelectedEventIndexChanged: { - if ((!mouseTracking) && eventCount > 0 - && selectedEventIndex > -1 && selectedEventIndex < eventCount) { - var windowLength = view.endTime - view.startTime; - - var eventStartTime = qmlEventList.getStartTime(selectedEventIndex); - var eventEndTime = eventStartTime + qmlEventList.getDuration(selectedEventIndex); - - if (eventEndTime < view.startTime || eventStartTime > view.endTime) { - var center = (eventStartTime + eventEndTime)/2; - var from = Math.min(qmlEventList.traceEndTime()-windowLength, - Math.max(0, Math.floor(center - windowLength/2))); - - zoomControl.setRange(from, from + windowLength); - } - } - if (selectedEventIndex === -1) - selectionHighlight.visible = false; - } - onSelectionRangeModeChanged: { selectionRangeControl.enabled = selectionRangeMode; selectionRange.reset(selectionRangeMode); } + onSelectionLockedChanged: { + updateLockButton(); + } + // ***** child items Timer { id: elapsedTimer @@ -312,7 +289,7 @@ Rectangle { anchors.left: labels.right height: root.height contentWidth: 0; - contentHeight: labels.rowCount * 50 + contentHeight: labels.height flickableDirection: Flickable.HorizontalFlick onContentXChanged: { @@ -322,13 +299,43 @@ Rectangle { clip:true + MouseArea { + id: selectionRangeDrag + enabled: selectionRange.ready + anchors.fill: selectionRange + drag.target: selectionRange + drag.axis: "XAxis" + drag.minimumX: 0 + drag.maximumX: flick.contentWidth - selectionRange.width + onPressed: { + selectionRange.isDragging = true; + } + onReleased: { + selectionRange.isDragging = false; + } + onDoubleClicked: { + zoomControl.setRange(selectionRange.startTime, selectionRange.startTime + selectionRange.duration); + root.selectionRangeMode = false; + root.updateRangeButton(); + } + } + + + SelectionRange { + id: selectionRange + visible: root.selectionRangeMode + height: root.height + z: 2 + } + TimelineView { id: view eventList: qmlEventList + x: flick.contentX width: flick.width - height: flick.contentHeight + height: root.height property variant startX: 0 onStartXChanged: { @@ -349,178 +356,65 @@ Rectangle { var newStartX = startTime * flick.width / (endTime-startTime); if (Math.abs(newStartX - startX) >= 1) startX = newStartX; - updateTimeline(); } } - property real timeSpan: endTime - startTime - onTimeSpanChanged: { - if (selectedEventIndex !== -1 && selectionHighlight.visible) { - var spacing = flick.width / timeSpan; - selectionHighlight.x = (qmlEventList.getStartTime(selectedEventIndex) - qmlEventList.traceStartTime()) * spacing; - selectionHighlight.width = qmlEventList.getDuration(selectedEventIndex) * spacing; - } - } - - onCachedProgressChanged: { - root.progress = 0.5 + cachedProgress * 0.5; - } - - onCacheReady: { - root.progress = 1.0; - root.dataAvailable = true; - if (root.eventCount > 0) { - view.visible = true; - view.updateTimeline(); - zoomControl.setRange(0, qmlEventList.traceEndTime()/10); - } - } - - delegate: Rectangle { - id: obj - property color myColor: root.colors[type] - - function conditionalHide() { - if (!mouseArea.containsMouse) - mouseArea.exited() - } - - property int baseY: type * view.height / labels.rowCount - property int baseHeight: view.height / labels.rowCount - y: baseY + (nestingLevel-1)*(baseHeight / nestingDepth) - height: baseHeight / nestingDepth - gradient: Gradient { - GradientStop { position: 0.0; color: myColor } - GradientStop { position: 0.5; color: Qt.darker(myColor, 1.1) } - GradientStop { position: 1.0; color: myColor } - } - smooth: true - - property bool componentIsCompleted: false - Component.onCompleted: { - componentIsCompleted = true; - updateDetails(); - } - - property bool isSelected: root.selectedEventIndex == index; - onIsSelectedChanged: { - updateDetails(); - } - - function updateDetails() { - if (!root.mouseTracking && componentIsCompleted) { - if (isSelected) { - enableSelected(0, 0); - } - } - } - - function enableSelected(x,y) { - rangeDetails.duration = qmlEventList.getDuration(index)/1000.0; - rangeDetails.label = qmlEventList.getDetails(index); - rangeDetails.file = qmlEventList.getFilename(index); - rangeDetails.line = qmlEventList.getLine(index); - rangeDetails.type = root.names[type]; + onSelectedItemChanged: { + if (selectedItem !== -1) { + // display details + rangeDetails.duration = qmlEventList.getDuration(selectedItem)/1000.0; + rangeDetails.label = qmlEventList.getDetails(selectedItem); + rangeDetails.file = qmlEventList.getFilename(selectedItem); + rangeDetails.line = qmlEventList.getLine(selectedItem); + rangeDetails.type = root.names[qmlEventList.getType(selectedItem)]; rangeDetails.visible = true; - selectionHighlight.x = obj.x; - selectionHighlight.y = obj.y; - selectionHighlight.width = width; - selectionHighlight.height = height; - selectionHighlight.visible = true; - } + // center view + var windowLength = view.endTime - view.startTime; + var eventStartTime = qmlEventList.getStartTime(selectedItem); + var eventEndTime = eventStartTime + qmlEventList.getDuration(selectedItem); - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onEntered: { - if (root.mouseOverSelection) { - root.mouseTracking = true; - root.selectedEventIndex = index; - enableSelected(mouseX, y); - root.mouseTracking = false; - } - } - - onPressed: { - root.mouseTracking = true; - root.selectedEventIndex = index; - enableSelected(mouseX, y); - root.mouseTracking = false; + if (eventEndTime < view.startTime || eventStartTime > view.endTime) { + var center = (eventStartTime + eventEndTime)/2; + var from = Math.min(qmlEventList.traceEndTime()-windowLength, + Math.max(0, Math.floor(center - windowLength/2))); - root.mouseOverSelection = false; - root.gotoSourceLocation(rangeDetails.file, rangeDetails.line); + zoomControl.setRange(from, from + windowLength); } - } - } - - Rectangle { - id: selectionHighlight - color:"transparent" - border.width: 2 - border.color: "blue" - z: 1 - radius: 2 - visible: false - } - - MouseArea { - width: parent.width - height: parent.height - x: flick.contentX - onClicked: { + } else { root.hideRangeDetails(); } } - MouseArea { - id: selectionRangeControl - enabled: false - width: flick.width - height: root.height - x: flick.contentX - hoverEnabled: enabled - z: 2 - - onReleased: { - selectionRange.releasedOnCreation(); - } - onPressed: { - selectionRange.pressedOnCreation(); - } - onMousePositionChanged: { - selectionRange.movedOnCreation(); + onItemPressed: { + if (pressedItem !== -1) { + root.gotoSourceLocation(qmlEventList.getFilename(pressedItem), qmlEventList.getLine(pressedItem)); } } - SelectionRange { - id: selectionRange - visible: root.selectionRangeMode - height: root.height - z: 2 - } + // hack to pass mouse events to the other mousearea if enabled + startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x : -flick.contentX + endDragArea: selectionRangeDrag.enabled ? + selectionRangeDrag.x + selectionRangeDrag.width : -flick.contentX-1 + } + MouseArea { + id: selectionRangeControl + enabled: false + width: flick.width + height: root.height + x: flick.contentX + hoverEnabled: enabled + z: 2 - MouseArea { - id: selectionRangeDrag - enabled: selectionRange.ready - anchors.fill: selectionRange - drag.target: selectionRange - drag.axis: "XAxis" - drag.minimumX: 0 - drag.maximumX: flick.contentWidth - selectionRange.width - onPressed: { - selectionRange.isDragging = true; - } - onReleased: { - selectionRange.isDragging = false; - } - onDoubleClicked: { - zoomControl.setRange(selectionRange.startTime, selectionRange.startTime + selectionRange.duration); - root.selectionRangeMode = false; - root.updateRangeButton(); - } + onReleased: { + selectionRange.releasedOnCreation(); + } + onPressed: { + selectionRange.pressedOnCreation(); + } + onMousePositionChanged: { + selectionRange.movedOnCreation(); } } } @@ -542,9 +436,10 @@ Rectangle { id: labels width: 150 color: "#dcdcdc" - height: flick.contentHeight + height: col.height property int rowCount: 5 + property variant rowExpanded: [false,false,false,false,false]; Column { id: col diff --git a/src/plugins/qmlprofiler/qml/RangeDetails.qml b/src/plugins/qmlprofiler/qml/RangeDetails.qml index d044fc0ed5..0b3b58d7f0 100644 --- a/src/plugins/qmlprofiler/qml/RangeDetails.qml +++ b/src/plugins/qmlprofiler/qml/RangeDetails.qml @@ -36,13 +36,13 @@ import Monitor 1.0 BorderImage { id: rangeDetails - property string duration //###int? + property string duration property string label property string type property string file property int line - property bool locked: !root.mouseOverSelection + property bool locked: view.selectionLocked source: "popup_green.png" border { @@ -107,6 +107,15 @@ BorderImage { } } + MouseArea { + width: col.width + 30 + height: col.height + typeTitle.height + 30 + drag.target: parent + onClicked: { + root.gotoSourceLocation(file, line); + } + } + Image { id: lockIcon source: locked?"lock_closed.png" : "lock_open.png" @@ -118,7 +127,7 @@ BorderImage { MouseArea { anchors.fill: parent onClicked: { - root.mouseOverSelection = !root.mouseOverSelection; + root.selectionLocked = !root.selectionLocked; } } } @@ -132,20 +141,10 @@ BorderImage { anchors.fill: parent onClicked: { root.hideRangeDetails(); + view.selectedItem = -1; } } } - MouseArea { - width: col.width - height: col.height + typeTitle.height + 30 - drag.target: parent - onClicked: { - // force reload of selected item - var selectedItem = root.selectedEventIndex; - root.selectedEventIndex = -1; - root.selectedEventIndex = selectedItem; - root.gotoSourceLocation(file, line); - } - } + } diff --git a/src/plugins/qmlprofiler/qml/TimeMarks.qml b/src/plugins/qmlprofiler/qml/TimeMarks.qml index 8639f4d85c..eb378a79aa 100644 --- a/src/plugins/qmlprofiler/qml/TimeMarks.qml +++ b/src/plugins/qmlprofiler/qml/TimeMarks.qml @@ -107,13 +107,17 @@ TiledCanvas { } function drawBackgroundBars( ctxt, region ) { - var barHeight = Math.round(labels.height / labels.rowCount); + var cumulatedHeight = 0; for (var i=0; i<labels.rowCount; i++) { - ctxt.fillStyle = i%2 ? "#f3f3f3" : "white" - ctxt.strokeStyle = i%2 ? "#f3f3f3" : "white" - ctxt.fillRect(0, i * barHeight, width, barHeight); + var barHeight = labels.rowExpanded[i] ? + qmlEventList.uniqueEventsOfType(i) * root.singleRowHeight : + qmlEventList.maxNestingForType(i) * root.singleRowHeight; + ctxt.fillStyle = i%2 ? "#f0f0f0" : "white" + ctxt.strokeStyle = i%2 ? "#f0f0f0" : "white" + ctxt.fillRect(0, cumulatedHeight, width, barHeight); + cumulatedHeight += barHeight; } - ctxt.fillStyle = "white"; + ctxt.fillStyle = "#f5f5f5"; ctxt.fillRect(0, labels.height, width, height - labels.height); } } diff --git a/src/plugins/qmlprofiler/qml/arrow_down.png b/src/plugins/qmlprofiler/qml/arrow_down.png Binary files differnew file mode 100644 index 0000000000..8d66b0a623 --- /dev/null +++ b/src/plugins/qmlprofiler/qml/arrow_down.png diff --git a/src/plugins/qmlprofiler/qml/arrow_right.png b/src/plugins/qmlprofiler/qml/arrow_right.png Binary files differnew file mode 100644 index 0000000000..228529f0ff --- /dev/null +++ b/src/plugins/qmlprofiler/qml/arrow_right.png diff --git a/src/plugins/qmlprofiler/qml/arrow_select.png b/src/plugins/qmlprofiler/qml/arrow_select.png Binary files differnew file mode 100644 index 0000000000..e1e2d4a5b1 --- /dev/null +++ b/src/plugins/qmlprofiler/qml/arrow_select.png diff --git a/src/plugins/qmlprofiler/qml/qmlprofiler.qrc b/src/plugins/qmlprofiler/qml/qmlprofiler.qrc index 0b1ffce54a..daf2b78b42 100644 --- a/src/plugins/qmlprofiler/qml/qmlprofiler.qrc +++ b/src/plugins/qmlprofiler/qml/qmlprofiler.qrc @@ -24,5 +24,8 @@ <file>SelectionRange.qml</file> <file>SelectionRangeDetails.qml</file> <file>magnifier.png</file> + <file>arrow_down.png</file> + <file>arrow_right.png</file> + <file>arrow_select.png</file> </qresource> </RCC> diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelineview.cpp index 7dc44045c9..56eaa70f3c 100644 --- a/src/plugins/qmlprofiler/timelineview.cpp +++ b/src/plugins/qmlprofiler/timelineview.cpp @@ -35,223 +35,274 @@ #include <qdeclarativecontext.h> #include <qdeclarativeproperty.h> #include <QtCore/QTimer> +#include <QtGui/QPixmap> +#include <QtGui/QPainter> +#include <QtGui/QGraphicsSceneMouseEvent> + +#include <math.h> using namespace QmlProfiler::Internal; -#define CACHE_ENABLED true -#define CACHE_UPDATEDELAY 10 -#define CACHE_STEP 200 +const int DefaultRowHeight = 30; TimelineView::TimelineView(QDeclarativeItem *parent) : - QDeclarativeItem(parent), m_delegate(0), m_itemCount(0), m_startTime(0), m_endTime(0), m_spacing(0), - prevMin(0), prevMax(0), m_eventList(0), m_totalWidth(0), m_lastCachedIndex(0), m_creatingCache(false), m_oldCacheSize(0) + QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0), + m_lastStartTime(0), m_lastEndTime(0), m_eventList(0) { + clearData(); + setFlag(QGraphicsItem::ItemHasNoContents, false); + setAcceptedMouseButtons(Qt::LeftButton); + setAcceptHoverEvents(true); + for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) + m_rowsExpanded << false; } void TimelineView::componentComplete() { + const QMetaObject *metaObject = this->metaObject(); + int propertyCount = metaObject->propertyCount(); + int requestPaintMethod = metaObject->indexOfMethod("requestPaint()"); + for (int ii = TimelineView::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) { + QMetaProperty p = metaObject->property(ii); + if (p.hasNotifySignal()) + QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0); + } QDeclarativeItem::componentComplete(); } -void TimelineView::clearData() +void TimelineView::requestPaint() { - if (CACHE_ENABLED) - foreach (QDeclarativeItem *item, m_items.values()) - item->setVisible(false); - else - foreach (QDeclarativeItem *item, m_items.values()) - delete m_items.take(m_items.key(item)); - - m_startTime = 0; - m_endTime = 0; - prevMin = 0; - prevMax = 0; - m_totalWidth = 0; - m_lastCachedIndex = 0; + update(); } -void TimelineView::updateTimeline() +void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) { - if (!m_delegate) - return; - - if (!m_eventList) + qint64 windowDuration = m_endTime - m_startTime; + if (windowDuration <= 0) return; - qreal totalRange = m_eventList->traceEndTime() - m_eventList->traceStartTime(); - qreal window = m_endTime - m_startTime; + m_spacing = qreal(width()) / windowDuration; - if (window == 0) //### - return; + m_rowWidths.clear(); + for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) { + m_rowWidths << (m_rowsExpanded[i] ? m_eventList->uniqueEventsOfType(i) : m_eventList->maxNestingForType(i)); + } - qreal newSpacing = width() / window; - bool spacingChanged = (newSpacing != m_spacing); - m_spacing = newSpacing; + // event rows + m_rowStarts.clear(); + int pos = 0; + for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) { + m_rowStarts << pos; + pos += DefaultRowHeight * m_rowWidths[i]; + } - qreal oldtw = m_totalWidth; - m_totalWidth = totalRange * m_spacing; + p->setPen(Qt::transparent); + // speedup: don't draw overlapping events, just skip them + m_rowLastX.clear(); + for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) + for (int j=0; j<m_rowWidths[i]; j++) + m_rowLastX << -m_startTime * m_spacing; - int minsample = m_eventList->findFirstIndex(m_startTime + m_eventList->traceStartTime()); - int maxsample = m_eventList->findLastIndex(m_endTime + m_eventList->traceStartTime()); + int firstIndex = m_eventList->findFirstIndex(m_startTime); + int lastIndex = m_eventList->findLastIndex(m_endTime); + drawItemsToPainter(p, firstIndex, lastIndex); - //### emitting this before startXChanged was causing issues - if (m_totalWidth != oldtw) - emit totalWidthChanged(m_totalWidth); + drawSelectionBoxes(p); + m_lastStartTime = m_startTime; + m_lastEndTime = m_endTime; +} - // the next loops have to be modified with the new implementation of the cache +QColor TimelineView::colorForItem(int itemIndex) +{ + int ndx = m_eventList->getHash(itemIndex); + return QColor::fromHsl((ndx*25)%360, 76, 166); +} - // hide items that are not visible any more - if (maxsample < prevMin || minsample > prevMax) { - for (int i = prevMin; i <= prevMax; ++i) - if (m_items.contains(i)) { - if (CACHE_ENABLED) - m_items.value(i)->setVisible(false); - else - delete m_items.take(i); - } - } else { - if (minsample > prevMin && minsample <= prevMax) - for (int i = prevMin; i < minsample; ++i) - if (m_items.contains(i)) { - if (CACHE_ENABLED) - m_items.value(i)->setVisible(false); - else - delete m_items.take(i); - } - - if (maxsample >= prevMin && maxsample < prevMax) - for (int i = maxsample + 1; i <= prevMax; ++i) - if (m_items.contains(i)) { - if (CACHE_ENABLED) - m_items.value(i)->setVisible(false); - else - delete m_items.take(i); - } +QLinearGradient *TimelineView::gradientForItem(int itemIndex) +{ + int ndx = m_eventList->getHash(itemIndex); + if (!m_hashedGradients.contains(ndx)) { + QLinearGradient *linearGrad = new QLinearGradient(0,0,0,DefaultRowHeight); + linearGrad->setColorAt(0, colorForItem(itemIndex)); + linearGrad->setColorAt(0.5, colorForItem(itemIndex).darker(115)); + linearGrad->setColorAt(1, colorForItem(itemIndex)); + m_hashedGradients[ndx] = linearGrad; } + return m_hashedGradients[ndx]; +} - // Update visible items - for (int i = minsample; i <= maxsample; ++i) { - if (!m_items.contains(i)) { - createItem(i); - m_items.value(i)->setVisible(true); - } +void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex) +{ + int x,y,width,rowNumber, eventType; + for (int i = fromIndex; i <= toIndex; i++) { + x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing; + eventType = m_eventList->getType(i); + if (m_rowsExpanded[eventType]) + y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->eventPosInType(i); else - if (spacingChanged || !m_items.value(i)->isVisible()) { - m_items.value(i)->setVisible(true); - updateItemPosition(i); - } - } + y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->getNestingLevel(i)-1); + + width = m_eventList->getDuration(i)*m_spacing; + if (width<1) + width = 1; - prevMin = minsample; - prevMax = maxsample; + rowNumber = y/DefaultRowHeight; + if (m_rowLastX[rowNumber] > x+width) + continue; + m_rowLastX[rowNumber] = x+width; + p->setBrush(*gradientForItem(i)); + p->drawRect(x,y,width,DefaultRowHeight); + } } -void TimelineView::createItem(int itemIndex) +void TimelineView::drawSelectionBoxes(QPainter *p) { - QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(this)); - QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(m_delegate->beginCreate(ctxt)); - m_items.insert(itemIndex, item); + if (m_selectedItem == -1) + return; - ctxt->setParent(item); //### QDeclarative_setParent_noEvent(ctxt, item); instead? - ctxt->setContextProperty("index", itemIndex); - ctxt->setContextProperty("type", m_eventList->getType(itemIndex)); - ctxt->setContextProperty("nestingLevel", m_eventList->getNestingLevel(itemIndex)); - ctxt->setContextProperty("nestingDepth", m_eventList->getNestingDepth(itemIndex)); + int fromIndex = m_eventList->findFirstIndex(m_startTime); + int toIndex = m_eventList->findLastIndex(m_endTime); + int id = m_eventList->getHash(m_selectedItem); - updateItemPosition(itemIndex); + p->setBrush(Qt::transparent); + QPen strongPen(QBrush(Qt::blue), 3); + QPen lightPen(QBrush(QColor(Qt::blue).lighter(130)), 2); + p->setPen(lightPen); + + int x,y,width,rowNumber,eventType; + for (int i = fromIndex; i <= toIndex; i++) { + if (m_eventList->getHash(i) != id) + continue; + + if (i == m_selectedItem) + p->setPen(strongPen); + else + p->setPen(lightPen); + + x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing; + eventType = m_eventList->getType(i); + if (m_rowsExpanded[eventType]) + y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->eventPosInType(i); + else + y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->getNestingLevel(i)-1); - item->setVisible(false); + width = m_eventList->getDuration(i)*m_spacing; + if (width<1) + width = 1; - item->setParentItem(this); - m_delegate->completeCreate(); - m_itemCount++; + rowNumber = y/DefaultRowHeight; + p->drawRect(x,y,width,DefaultRowHeight); + } } -void TimelineView::updateItemPosition(int itemIndex) +void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QDeclarativeItem *item = m_items.value(itemIndex); - if (item) { - qreal itemStartPos = (m_eventList->getStartTime(itemIndex) - m_eventList->traceStartTime()) * m_spacing; - item->setX(itemStartPos); - qreal width = (m_eventList->getEndTime(itemIndex) - m_eventList->getStartTime(itemIndex)) * m_spacing; - item->setWidth(width > 1 ? width : 1); - } + // special case: if there is a drag area below me, don't accept the + // events unless I'm actually clicking inside an item + if (m_currentSelection.eventIndex == -1 && + event->pos().x()+x() >= m_startDragArea && + event->pos().x()+x() <= m_endDragArea) + event->setAccepted(false); + } -void TimelineView::rebuildCache() +void TimelineView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - if (CACHE_ENABLED) { - m_lastCachedIndex = 0; - m_creatingCache = false; - m_oldCacheSize = m_items.count(); - emit cachedProgressChanged(); - QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(purgeCache())); - } else { - m_creatingCache = true; - m_lastCachedIndex = m_eventList->count(); - emit cacheReady(); - } + Q_UNUSED(event); + manageClicked(); } -qreal TimelineView::cachedProgress() const +void TimelineView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - qreal progress; - if (!m_creatingCache) { - if (m_oldCacheSize == 0) - progress = 0.5; - else - progress = (m_lastCachedIndex * 0.5) / m_oldCacheSize; - } - else - progress = 0.5 + (m_lastCachedIndex * 0.5) / m_eventList->count(); + event->setAccepted(false); +} - return progress; + +void TimelineView::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); + manageHovered(event->pos().x(), event->pos().y()); + if (m_currentSelection.eventIndex == -1) + event->setAccepted(false); } -void TimelineView::increaseCache() +void TimelineView::manageClicked() { - int totalCount = m_eventList->count(); - if (m_lastCachedIndex >= totalCount) { - emit cacheReady(); - return; + if (m_currentSelection.eventIndex != -1) { + if (m_currentSelection.eventIndex == m_selectedItem) + setSelectionLocked(!m_selectionLocked); + else + setSelectionLocked(true); + emit itemPressed(m_currentSelection.eventIndex); + } else { +// setSelectionLocked(false); } + setSelectedItem(m_currentSelection.eventIndex); +} - for (int i = 0; i < CACHE_STEP; i++) { - createItem(m_lastCachedIndex); - m_lastCachedIndex++; - if (m_lastCachedIndex >= totalCount) - break; - } +void TimelineView::manageHovered(int x, int y) +{ + if (m_endTime - m_startTime <=0) + return; - emit cachedProgressChanged(); + qint64 time = x * (m_endTime - m_startTime) / width() + m_startTime; + int row = y / DefaultRowHeight; - QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache())); -} + // already covered? nothing to do + if (m_currentSelection.eventIndex != -1 && time >= m_currentSelection.startTime && time <= m_currentSelection.endTime && row == m_currentSelection.row) { + return; + } -void TimelineView::purgeCache() -{ - if (m_items.isEmpty()) { - m_creatingCache = true; - m_lastCachedIndex = 0; - QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache())); + // find if there's items in the time range + int eventFrom = m_eventList->findFirstIndex(time); + int eventTo = m_eventList->findLastIndex(time); + if (eventTo < eventFrom) { + m_currentSelection.eventIndex = -1; return; } - for (int i=0; i < CACHE_STEP; i++) - { - if (m_items.contains(m_lastCachedIndex)) - delete m_items.take(m_lastCachedIndex); + // find if we are in the right column + int itemRow, eventType; + for (int i=eventTo; i>=eventFrom; --i) { + if (ceil(m_eventList->getEndTime(i)*m_spacing) < floor(time*m_spacing)) + continue; - m_lastCachedIndex++; - if (m_items.isEmpty()) - break; + eventType = m_eventList->getType(i); + if (m_rowsExpanded[eventType]) + itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->eventPosInType(i); + else + itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->getNestingLevel(i)-1; + if (itemRow == row) { + // match + m_currentSelection.eventIndex = i; + m_currentSelection.startTime = m_eventList->getStartTime(i); + m_currentSelection.endTime = m_eventList->getEndTime(i); + m_currentSelection.row = row; + if (!m_selectionLocked) + setSelectedItem(i); + return; + } } - emit cachedProgressChanged(); - QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(purgeCache())); + m_currentSelection.eventIndex = -1; + return; +} + +void TimelineView::clearData() +{ + m_startTime = 0; + m_endTime = 0; + m_lastStartTime = 0; + m_lastEndTime = 0; + m_currentSelection.startTime = -1; + m_currentSelection.endTime = -1; + m_currentSelection.row = -1; + m_currentSelection.eventIndex = -1; + m_selectedItem = -1; + m_selectionLocked = true; } qint64 TimelineView::getDuration(int index) const @@ -277,3 +328,65 @@ QString TimelineView::getDetails(int index) const Q_ASSERT(m_eventList); return m_eventList->getDetails(index); } + +void TimelineView::rowExpanded(int rowIndex, bool expanded) +{ + m_rowsExpanded[rowIndex] = expanded; + update(); +} + +void TimelineView::selectNext() +{ + if (m_eventList->count() == 0) + return; + + if (m_selectionLocked && m_selectedItem !=-1 ) { + // find next item with same hashId + int hashId = m_eventList->getHash(m_selectedItem); + int i = m_selectedItem+1; + while (i<m_eventList->count() && m_eventList->getHash(i) != hashId) + i++; + if (i == m_eventList->count()) { + i = 0; + while (i<m_selectedItem && m_eventList->getHash(i) != hashId) + i++; + } + setSelectedItem(i); + } else { + // select next in view or after + int newIndex = m_selectedItem+1; + if (newIndex >= m_eventList->count()) + newIndex = 0; + if (m_eventList->getEndTime(newIndex) < m_startTime) + newIndex = m_eventList->findFirstIndexNoParents(m_startTime); + setSelectedItem(newIndex); + } +} + +void TimelineView::selectPrev() +{ + if (m_eventList->count() == 0) + return; + + if (m_selectionLocked && m_selectedItem !=-1) { + // find previous item with same hashId + int hashId = m_eventList->getHash(m_selectedItem); + int i = m_selectedItem-1; + while (i>-1 && m_eventList->getHash(i) != hashId) + i--; + if (i == -1) { + i = m_eventList->count()-1; + while (i>m_selectedItem && m_eventList->getHash(i) != hashId) + i--; + } + setSelectedItem(i); + } else { + // select last in view or before + int newIndex = m_selectedItem-1; + if (newIndex < 0) + newIndex = m_eventList->count()-1; + if (m_eventList->getStartTime(newIndex) > m_endTime) + newIndex = m_eventList->findLastIndex(m_endTime); + setSelectedItem(newIndex); + } +} diff --git a/src/plugins/qmlprofiler/timelineview.h b/src/plugins/qmlprofiler/timelineview.h index 1979c4c1b9..2830b503f7 100644 --- a/src/plugins/qmlprofiler/timelineview.h +++ b/src/plugins/qmlprofiler/timelineview.h @@ -43,21 +43,17 @@ namespace Internal { class TimelineView : public QDeclarativeItem { Q_OBJECT - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged) - Q_PROPERTY(qreal totalWidth READ totalWidth NOTIFY totalWidthChanged) Q_PROPERTY(QObject* eventList READ eventList WRITE setEventList NOTIFY eventListChanged) - Q_PROPERTY(qreal cachedProgress READ cachedProgress NOTIFY cachedProgressChanged) + Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged) + Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged) + Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged) + Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged) public: explicit TimelineView(QDeclarativeItem *parent = 0); - QDeclarativeComponent * delegate() const - { - return m_delegate; - } - qint64 startTime() const { return m_startTime; @@ -68,12 +64,25 @@ public: return m_endTime; } - qreal totalWidth() const + bool selectionLocked() const + { + return m_selectionLocked; + } + + int selectedItem() const + { + return m_selectedItem; + } + + int startDragArea() const { - return m_totalWidth; + return m_startDragArea; } - qreal cachedProgress() const; + int endDragArea() const + { + return m_endDragArea; + } QmlJsDebugClient::QmlProfilerEventList *eventList() const { return m_eventList; } void setEventList(QObject *eventList) @@ -82,33 +91,30 @@ public: emit eventListChanged(m_eventList); } + Q_INVOKABLE void selectNext(); + Q_INVOKABLE void selectPrev(); + + Q_INVOKABLE void rowExpanded(int rowIndex, bool expanded); + Q_INVOKABLE qint64 getDuration(int index) const; Q_INVOKABLE QString getFilename(int index) const; Q_INVOKABLE int getLine(int index) const; Q_INVOKABLE QString getDetails(int index) const; - Q_INVOKABLE void rebuildCache(); signals: - void delegateChanged(QDeclarativeComponent * arg); void startTimeChanged(qint64 arg); void endTimeChanged(qint64 arg); - void totalWidthChanged(qreal arg); void eventListChanged(QmlJsDebugClient::QmlProfilerEventList *list); - - void cachedProgressChanged(); - void cacheReady(); + void selectionLockedChanged(bool locked); + void selectedItemChanged(int itemIndex); + void startDragAreaChanged(int startDragArea); + void endDragAreaChanged(int endDragArea); + void itemPressed(int pressedItem); public slots: void clearData(); - void updateTimeline(); + void requestPaint(); - void setDelegate(QDeclarativeComponent * arg) - { - if (m_delegate != arg) { - m_delegate = arg; - emit delegateChanged(arg); - } - } void setStartTime(qint64 arg) { @@ -126,34 +132,82 @@ public slots: } } + void setSelectionLocked(bool locked) + { + if (m_selectionLocked != locked) { + m_selectionLocked = locked; + emit selectionLockedChanged(locked); + } + } + + void setSelectedItem(int itemIndex) + { + if (m_selectedItem != itemIndex) { + m_selectedItem = itemIndex; + update(); + emit selectedItemChanged(itemIndex); + } + } + + void setStartDragArea(int startDragArea) + { + if (m_startDragArea != startDragArea) { + m_startDragArea = startDragArea; + emit startDragAreaChanged(startDragArea); + } + } + + void setEndDragArea(int endDragArea) + { + if (m_endDragArea != endDragArea) { + m_endDragArea = endDragArea; + emit endDragAreaChanged(endDragArea); + } + } + protected: - void componentComplete(); + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + virtual void componentComplete(); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event); private: - void createItem(int itemIndex); - void updateItemPosition(int itemIndex); + QColor colorForItem(int itemIndex); + QLinearGradient *gradientForItem(int itemIndex); + void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex); + void drawSelectionBoxes(QPainter *p); -public slots: - void increaseCache(); - void purgeCache(); + void manageClicked(); + void manageHovered(int x, int y); private: - QDeclarativeComponent * m_delegate; - QHash<int,QDeclarativeItem*> m_items; - qint64 m_itemCount; qint64 m_startTime; qint64 m_endTime; qreal m_spacing; - int prevMin; - int prevMax; + qint64 m_lastStartTime; + qint64 m_lastEndTime; QmlJsDebugClient::QmlProfilerEventList *m_eventList; - - qreal m_totalWidth; - int m_lastCachedIndex; - bool m_creatingCache; - int m_oldCacheSize; - + QHash<int, QLinearGradient*> m_hashedGradients; + + QList<int> m_rowLastX; + QList<int> m_rowStarts; + QList<int> m_rowWidths; + QList<bool> m_rowsExpanded; + + struct { + qint64 startTime; + qint64 endTime; + int row; + int eventIndex; + } m_currentSelection; + + int m_selectedItem; + bool m_selectionLocked; + int m_startDragArea; + int m_endDragArea; }; } // namespace Internal diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp index f201ffe069..406590a557 100644 --- a/src/plugins/qmlprofiler/tracewindow.cpp +++ b/src/plugins/qmlprofiler/tracewindow.cpp @@ -174,6 +174,16 @@ QWidget *TraceWindow::createToolbar() connect(this, SIGNAL(enableToolbar(bool)), m_buttonRange, SLOT(setEnabled(bool))); connect(this, SIGNAL(rangeModeChanged(bool)), m_buttonRange, SLOT(setChecked(bool))); + m_buttonLock = new QToolButton; + m_buttonLock->setIcon(QIcon(":/qmlprofiler/arrow_select.png")); + m_buttonLock->setToolTip(tr("View event information on mouseover")); + m_buttonLock->setCheckable(true); + m_buttonLock->setChecked(false); + connect(m_buttonLock, SIGNAL(clicked(bool)), this, SLOT(toggleLockMode(bool))); + connect(this, SIGNAL(enableToolbar(bool)), m_buttonLock, SLOT(setEnabled(bool))); + connect(this, SIGNAL(lockModeChanged(bool)), m_buttonLock, SLOT(setChecked(bool))); + + toolBarLayout->addWidget(m_buttonLock); toolBarLayout->addWidget(buttonPrev); toolBarLayout->addWidget(buttonNext); toolBarLayout->addWidget(buttonZoomControls); @@ -264,6 +274,7 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn) connect(m_mainView->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition())); connect(m_mainView->rootObject(), SIGNAL(updateTimer()), this, SLOT(updateTimer())); connect(m_mainView->rootObject(), SIGNAL(updateRangeButton()), this, SLOT(updateRangeButton())); + connect(m_mainView->rootObject(), SIGNAL(updateLockButton()), this, SLOT(updateLockButton())); connect(m_eventList, SIGNAL(countChanged()), this, SLOT(updateToolbar())); connect(this, SIGNAL(jumpToPrev()), m_mainView->rootObject(), SLOT(prevEvent())); connect(this, SIGNAL(jumpToNext()), m_mainView->rootObject(), SLOT(nextEvent())); @@ -340,6 +351,21 @@ void TraceWindow::updateRangeButton() emit rangeModeChanged(rangeMode); } +void TraceWindow::toggleLockMode(bool active) +{ + bool lockMode = !m_mainView->rootObject()->property("selectionLocked").toBool(); + if (active != lockMode) { + m_mainView->rootObject()->setProperty("selectionLocked", QVariant(!active)); + m_mainView->rootObject()->setProperty("selectedItem", QVariant(-1)); + } +} + +void TraceWindow::updateLockButton() +{ + bool lockMode = !m_mainView->rootObject()->property("selectionLocked").toBool(); + emit lockModeChanged(lockMode); +} + void TraceWindow::setRecording(bool recording) { if (recording) { diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/tracewindow.h index a0a9f74546..3ae9b017d2 100644 --- a/src/plugins/qmlprofiler/tracewindow.h +++ b/src/plugins/qmlprofiler/tracewindow.h @@ -103,7 +103,9 @@ public slots: void clearDisplay(); void updateToolbar(); void toggleRangeMode(bool); + void toggleLockMode(bool); void updateRangeButton(); + void updateLockButton(); void setZoomLevel(int zoomLevel); void updateRange(); void mouseWheelMoved(int x, int y, int delta); @@ -124,6 +126,7 @@ signals: void jumpToPrev(); void jumpToNext(); void rangeModeChanged(bool); + void lockModeChanged(bool); void enableToolbar(bool); void zoomLevelChanged(int); void updateViewZoom(QVariant zoomLevel); @@ -155,6 +158,7 @@ private: QWeakPointer<ZoomControl> m_zoomControl; QToolButton *m_buttonRange; + QToolButton *m_buttonLock; QWidget *m_zoomToolbar; int m_currentZoomLevel; }; |