forked from qt-creator/qt-creator
QmlProfiler: optimized timeline display
Change-Id: I0d7cf110356ef5f805b81a5fc39dca3870765ea3 Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
This commit is contained in:
@@ -95,6 +95,11 @@ struct QmlEventStartTimeData {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct QmlEventTypeCount {
|
||||||
|
QList <int> eventIds;
|
||||||
|
int nestingCount;
|
||||||
|
};
|
||||||
|
|
||||||
// used by quicksort
|
// used by quicksort
|
||||||
bool compareEndTimes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
|
bool compareEndTimes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
|
||||||
{
|
{
|
||||||
@@ -173,6 +178,8 @@ public:
|
|||||||
QV8EventDescriptions m_v8EventList;
|
QV8EventDescriptions m_v8EventList;
|
||||||
QHash<int, QV8EventData *> m_v8parents;
|
QHash<int, QV8EventData *> m_v8parents;
|
||||||
|
|
||||||
|
QHash<int, QmlEventTypeCount *> m_typeCounts;
|
||||||
|
|
||||||
qint64 m_traceEndTime;
|
qint64 m_traceEndTime;
|
||||||
|
|
||||||
// file to load
|
// file to load
|
||||||
@@ -211,8 +218,13 @@ void QmlProfilerEventList::clear()
|
|||||||
d->m_v8EventList.clear();
|
d->m_v8EventList.clear();
|
||||||
d->m_v8parents.clear();
|
d->m_v8parents.clear();
|
||||||
|
|
||||||
|
foreach (QmlEventTypeCount *typeCount, d->m_typeCounts.values())
|
||||||
|
delete typeCount;
|
||||||
|
d->m_typeCounts.clear();
|
||||||
|
|
||||||
d->m_traceEndTime = 0;
|
d->m_traceEndTime = 0;
|
||||||
emit countChanged();
|
emit countChanged();
|
||||||
|
emit dataClear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
|
QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
|
||||||
@@ -429,6 +441,26 @@ void QmlProfilerEventList::compileStatistics()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate numeric ids
|
||||||
|
int ndx = 0;
|
||||||
|
foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
|
||||||
|
binding->numericHash = ndx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect type counts
|
||||||
|
foreach (const QmlEventStartTimeData &eventStartData, d->m_startTimeSortedList) {
|
||||||
|
int typeNumber = eventStartData.description->eventType;
|
||||||
|
if (!d->m_typeCounts.contains(typeNumber)) {
|
||||||
|
d->m_typeCounts[typeNumber] = new QmlEventTypeCount;
|
||||||
|
d->m_typeCounts[typeNumber]->nestingCount = 0;
|
||||||
|
}
|
||||||
|
if (eventStartData.nestingLevel > d->m_typeCounts[typeNumber]->nestingCount) {
|
||||||
|
d->m_typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
|
||||||
|
}
|
||||||
|
if (!d->m_typeCounts[typeNumber]->eventIds.contains(eventStartData.description->numericHash))
|
||||||
|
d->m_typeCounts[typeNumber]->eventIds << eventStartData.description->numericHash;
|
||||||
|
}
|
||||||
|
|
||||||
// continue postprocess
|
// continue postprocess
|
||||||
postProcess();
|
postProcess();
|
||||||
}
|
}
|
||||||
@@ -592,6 +624,7 @@ void QmlProfilerEventList::computeNestingDepth()
|
|||||||
|
|
||||||
void QmlProfilerEventList::postProcess()
|
void QmlProfilerEventList::postProcess()
|
||||||
{
|
{
|
||||||
|
// Todo: collapse all this
|
||||||
switch (d->m_parsingStatus) {
|
switch (d->m_parsingStatus) {
|
||||||
case GettingDataStatus: {
|
case GettingDataStatus: {
|
||||||
setParsingStatus(SortingListsStatus);
|
setParsingStatus(SortingListsStatus);
|
||||||
@@ -691,6 +724,37 @@ int QmlProfilerEventList::findFirstIndex(qint64 startTime) const
|
|||||||
return ndx;
|
return ndx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int QmlProfilerEventList::findFirstIndexNoParents(qint64 startTime) const
|
||||||
|
{
|
||||||
|
int candidate = -1;
|
||||||
|
// in the "endtime" list, find the first event that ends after startTime
|
||||||
|
if (d->m_endTimeSortedList.isEmpty())
|
||||||
|
return 0; // -1
|
||||||
|
if (d->m_endTimeSortedList.length() == 1 || d->m_endTimeSortedList.first().endTime >= startTime)
|
||||||
|
candidate = 0;
|
||||||
|
else
|
||||||
|
if (d->m_endTimeSortedList.last().endTime <= startTime)
|
||||||
|
return 0; // -1
|
||||||
|
|
||||||
|
if (candidate == -1) {
|
||||||
|
int fromIndex = 0;
|
||||||
|
int toIndex = d->m_endTimeSortedList.count()-1;
|
||||||
|
while (toIndex - fromIndex > 1) {
|
||||||
|
int midIndex = (fromIndex + toIndex)/2;
|
||||||
|
if (d->m_endTimeSortedList[midIndex].endTime < startTime)
|
||||||
|
fromIndex = midIndex;
|
||||||
|
else
|
||||||
|
toIndex = midIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate = toIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
|
||||||
|
|
||||||
|
return ndx;
|
||||||
|
}
|
||||||
|
|
||||||
int QmlProfilerEventList::findLastIndex(qint64 endTime) const
|
int QmlProfilerEventList::findLastIndex(qint64 endTime) const
|
||||||
{
|
{
|
||||||
// in the "starttime" list, find the last event that starts before endtime
|
// in the "starttime" list, find the last event that starts before endtime
|
||||||
@@ -1122,5 +1186,31 @@ QString QmlProfilerEventList::getDetails(int index) const {
|
|||||||
return d->m_startTimeSortedList[index].description->details;
|
return d->m_startTimeSortedList[index].description->details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int QmlProfilerEventList::getHash(int index) const {
|
||||||
|
return d->m_startTimeSortedList[index].description->numericHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QmlProfilerEventList::uniqueEventsOfType(int type) const {
|
||||||
|
if (!d->m_typeCounts.contains(type))
|
||||||
|
return 1;
|
||||||
|
return d->m_typeCounts[type]->eventIds.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
int QmlProfilerEventList::maxNestingForType(int type) const {
|
||||||
|
if (!d->m_typeCounts.contains(type))
|
||||||
|
return 1;
|
||||||
|
return d->m_typeCounts[type]->nestingCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QmlProfilerEventList::eventTextForType(int type, int index) const {
|
||||||
|
if (!d->m_typeCounts.contains(type))
|
||||||
|
return QString();
|
||||||
|
return d->m_eventDescriptions.values().at(d->m_typeCounts[type]->eventIds[index])->details;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QmlProfilerEventList::eventPosInType(int index) const {
|
||||||
|
int eventType = d->m_startTimeSortedList[index].description->eventType;
|
||||||
|
return d->m_typeCounts[eventType]->eventIds.indexOf(d->m_startTimeSortedList[index].description->numericHash);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlJsDebugClient
|
} // namespace QmlJsDebugClient
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ struct QMLJSDEBUGCLIENT_EXPORT QmlEventData
|
|||||||
double timePerCall;
|
double timePerCall;
|
||||||
double percentOfTime;
|
double percentOfTime;
|
||||||
qint64 medianTime;
|
qint64 medianTime;
|
||||||
|
int numericHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct QMLJSDEBUGCLIENT_EXPORT QV8EventData
|
struct QMLJSDEBUGCLIENT_EXPORT QV8EventData
|
||||||
@@ -99,6 +100,7 @@ public:
|
|||||||
const QV8EventDescriptions& getV8Events() const;
|
const QV8EventDescriptions& getV8Events() const;
|
||||||
|
|
||||||
int findFirstIndex(qint64 startTime) const;
|
int findFirstIndex(qint64 startTime) const;
|
||||||
|
int findFirstIndexNoParents(qint64 startTime) const;
|
||||||
int findLastIndex(qint64 endTime) const;
|
int findLastIndex(qint64 endTime) const;
|
||||||
Q_INVOKABLE qint64 firstTimeMark() const;
|
Q_INVOKABLE qint64 firstTimeMark() const;
|
||||||
Q_INVOKABLE qint64 lastTimeMark() const;
|
Q_INVOKABLE qint64 lastTimeMark() const;
|
||||||
@@ -117,6 +119,13 @@ public:
|
|||||||
Q_INVOKABLE QString getFilename(int index) const;
|
Q_INVOKABLE QString getFilename(int index) const;
|
||||||
Q_INVOKABLE int getLine(int index) const;
|
Q_INVOKABLE int getLine(int index) const;
|
||||||
Q_INVOKABLE QString getDetails(int index) const;
|
Q_INVOKABLE QString getDetails(int index) const;
|
||||||
|
Q_INVOKABLE int getHash(int index) const;
|
||||||
|
|
||||||
|
// per-type data
|
||||||
|
Q_INVOKABLE int uniqueEventsOfType(int type) const;
|
||||||
|
Q_INVOKABLE int maxNestingForType(int type) const;
|
||||||
|
Q_INVOKABLE QString eventTextForType(int type, int index) const;
|
||||||
|
Q_INVOKABLE int eventPosInType(int index) const;
|
||||||
|
|
||||||
Q_INVOKABLE qint64 traceStartTime() const;
|
Q_INVOKABLE qint64 traceStartTime() const;
|
||||||
Q_INVOKABLE qint64 traceEndTime() const;
|
Q_INVOKABLE qint64 traceEndTime() const;
|
||||||
@@ -127,6 +136,7 @@ signals:
|
|||||||
void countChanged();
|
void countChanged();
|
||||||
void parsingStatusChanged();
|
void parsingStatusChanged();
|
||||||
void error(const QString &error);
|
void error(const QString &error);
|
||||||
|
void dataClear();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void clear();
|
void clear();
|
||||||
|
|||||||
@@ -34,12 +34,53 @@ import QtQuick 1.0
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias text: txt.text
|
property alias text: txt.text
|
||||||
|
property bool expanded: false
|
||||||
|
property int typeIndex: index
|
||||||
|
|
||||||
height: 50
|
property variant descriptions: [text]
|
||||||
width: 150 //### required, or ignored by positioner
|
|
||||||
|
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 {
|
Text {
|
||||||
id: txt;
|
id: txt
|
||||||
|
visible: !expanded
|
||||||
x: 5
|
x: 5
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
color: "#232323"
|
color: "#232323"
|
||||||
@@ -52,4 +93,31 @@ Item {
|
|||||||
color: "#cccccc"
|
color: "#cccccc"
|
||||||
anchors.bottom: parent.bottom
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,13 +39,17 @@ Rectangle {
|
|||||||
// ***** properties
|
// ***** properties
|
||||||
|
|
||||||
property int candidateHeight: 0
|
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 bool dataAvailable: true
|
||||||
property int eventCount: 0
|
property int eventCount: 0
|
||||||
property real progress: 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 names: [ qsTr("Painting"), qsTr("Compiling"), qsTr("Creating"), qsTr("Binding"), qsTr("Handling Signal")]
|
||||||
property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
|
property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
|
||||||
@@ -56,9 +60,6 @@ Rectangle {
|
|||||||
property string fileName: ""
|
property string fileName: ""
|
||||||
property int lineNumber: -1
|
property int lineNumber: -1
|
||||||
|
|
||||||
property int selectedEventIndex : -1
|
|
||||||
property bool mouseTracking: false
|
|
||||||
|
|
||||||
property real elapsedTime
|
property real elapsedTime
|
||||||
signal updateTimer
|
signal updateTimer
|
||||||
|
|
||||||
@@ -107,7 +108,11 @@ Rectangle {
|
|||||||
onDataReady: {
|
onDataReady: {
|
||||||
if (eventCount > 0) {
|
if (eventCount > 0) {
|
||||||
view.clearData();
|
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() {
|
function clearDisplay() {
|
||||||
clearData();
|
clearData();
|
||||||
selectedEventIndex = -1;
|
|
||||||
view.visible = false;
|
view.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,19 +143,11 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function nextEvent() {
|
function nextEvent() {
|
||||||
if (eventCount > 0) {
|
view.selectNext();
|
||||||
++selectedEventIndex;
|
|
||||||
if (selectedEventIndex >= eventCount)
|
|
||||||
selectedEventIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevEvent() {
|
function prevEvent() {
|
||||||
if (eventCount > 0) {
|
view.selectPrev();
|
||||||
--selectedEventIndex;
|
|
||||||
if (selectedEventIndex < 0)
|
|
||||||
selectedEventIndex = eventCount - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateWindowLength(absoluteFactor) {
|
function updateWindowLength(absoluteFactor) {
|
||||||
@@ -179,9 +175,9 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fixedPoint = (view.startTime + view.endTime) / 2;
|
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
|
// 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)
|
if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
|
||||||
fixedPoint = newFixedPoint;
|
fixedPoint = newFixedPoint;
|
||||||
}
|
}
|
||||||
@@ -242,37 +238,18 @@ Rectangle {
|
|||||||
rangeDetails.type = "";
|
rangeDetails.type = "";
|
||||||
rangeDetails.file = "";
|
rangeDetails.file = "";
|
||||||
rangeDetails.line = -1;
|
rangeDetails.line = -1;
|
||||||
|
|
||||||
root.mouseOverSelection = true;
|
|
||||||
selectionHighlight.visible = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ***** slots
|
// ***** 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: {
|
onSelectionRangeModeChanged: {
|
||||||
selectionRangeControl.enabled = selectionRangeMode;
|
selectionRangeControl.enabled = selectionRangeMode;
|
||||||
selectionRange.reset(selectionRangeMode);
|
selectionRange.reset(selectionRangeMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSelectionLockedChanged: {
|
||||||
|
updateLockButton();
|
||||||
|
}
|
||||||
|
|
||||||
// ***** child items
|
// ***** child items
|
||||||
Timer {
|
Timer {
|
||||||
id: elapsedTimer
|
id: elapsedTimer
|
||||||
@@ -312,7 +289,7 @@ Rectangle {
|
|||||||
anchors.left: labels.right
|
anchors.left: labels.right
|
||||||
height: root.height
|
height: root.height
|
||||||
contentWidth: 0;
|
contentWidth: 0;
|
||||||
contentHeight: labels.rowCount * 50
|
contentHeight: labels.height
|
||||||
flickableDirection: Flickable.HorizontalFlick
|
flickableDirection: Flickable.HorizontalFlick
|
||||||
|
|
||||||
onContentXChanged: {
|
onContentXChanged: {
|
||||||
@@ -322,13 +299,43 @@ Rectangle {
|
|||||||
|
|
||||||
clip:true
|
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 {
|
TimelineView {
|
||||||
id: view
|
id: view
|
||||||
|
|
||||||
eventList: qmlEventList
|
eventList: qmlEventList
|
||||||
|
|
||||||
|
x: flick.contentX
|
||||||
width: flick.width
|
width: flick.width
|
||||||
height: flick.contentHeight
|
height: root.height
|
||||||
|
|
||||||
property variant startX: 0
|
property variant startX: 0
|
||||||
onStartXChanged: {
|
onStartXChanged: {
|
||||||
@@ -349,178 +356,65 @@ Rectangle {
|
|||||||
var newStartX = startTime * flick.width / (endTime-startTime);
|
var newStartX = startTime * flick.width / (endTime-startTime);
|
||||||
if (Math.abs(newStartX - startX) >= 1)
|
if (Math.abs(newStartX - startX) >= 1)
|
||||||
startX = newStartX;
|
startX = newStartX;
|
||||||
updateTimeline();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property real timeSpan: endTime - startTime
|
onSelectedItemChanged: {
|
||||||
onTimeSpanChanged: {
|
if (selectedItem !== -1) {
|
||||||
if (selectedEventIndex !== -1 && selectionHighlight.visible) {
|
// display details
|
||||||
var spacing = flick.width / timeSpan;
|
rangeDetails.duration = qmlEventList.getDuration(selectedItem)/1000.0;
|
||||||
selectionHighlight.x = (qmlEventList.getStartTime(selectedEventIndex) - qmlEventList.traceStartTime()) * spacing;
|
rangeDetails.label = qmlEventList.getDetails(selectedItem);
|
||||||
selectionHighlight.width = qmlEventList.getDuration(selectedEventIndex) * spacing;
|
rangeDetails.file = qmlEventList.getFilename(selectedItem);
|
||||||
}
|
rangeDetails.line = qmlEventList.getLine(selectedItem);
|
||||||
}
|
rangeDetails.type = root.names[qmlEventList.getType(selectedItem)];
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
rangeDetails.visible = true;
|
rangeDetails.visible = true;
|
||||||
|
|
||||||
selectionHighlight.x = obj.x;
|
// center view
|
||||||
selectionHighlight.y = obj.y;
|
var windowLength = view.endTime - view.startTime;
|
||||||
selectionHighlight.width = width;
|
var eventStartTime = qmlEventList.getStartTime(selectedItem);
|
||||||
selectionHighlight.height = height;
|
var eventEndTime = eventStartTime + qmlEventList.getDuration(selectedItem);
|
||||||
selectionHighlight.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
if (eventEndTime < view.startTime || eventStartTime > view.endTime) {
|
||||||
id: mouseArea
|
var center = (eventStartTime + eventEndTime)/2;
|
||||||
anchors.fill: parent
|
var from = Math.min(qmlEventList.traceEndTime()-windowLength,
|
||||||
hoverEnabled: true
|
Math.max(0, Math.floor(center - windowLength/2)));
|
||||||
onEntered: {
|
|
||||||
if (root.mouseOverSelection) {
|
zoomControl.setRange(from, from + windowLength);
|
||||||
root.mouseTracking = true;
|
|
||||||
root.selectedEventIndex = index;
|
|
||||||
enableSelected(mouseX, y);
|
|
||||||
root.mouseTracking = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
onPressed: {
|
|
||||||
root.mouseTracking = true;
|
|
||||||
root.selectedEventIndex = index;
|
|
||||||
enableSelected(mouseX, y);
|
|
||||||
root.mouseTracking = false;
|
|
||||||
|
|
||||||
root.mouseOverSelection = false;
|
|
||||||
root.gotoSourceLocation(rangeDetails.file, rangeDetails.line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: {
|
|
||||||
root.hideRangeDetails();
|
root.hideRangeDetails();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
onItemPressed: {
|
||||||
id: selectionRangeControl
|
if (pressedItem !== -1) {
|
||||||
enabled: false
|
root.gotoSourceLocation(qmlEventList.getFilename(pressedItem), qmlEventList.getLine(pressedItem));
|
||||||
width: flick.width
|
|
||||||
height: root.height
|
|
||||||
x: flick.contentX
|
|
||||||
hoverEnabled: enabled
|
|
||||||
z: 2
|
|
||||||
|
|
||||||
onReleased: {
|
|
||||||
selectionRange.releasedOnCreation();
|
|
||||||
}
|
|
||||||
onPressed: {
|
|
||||||
selectionRange.pressedOnCreation();
|
|
||||||
}
|
|
||||||
onMousePositionChanged: {
|
|
||||||
selectionRange.movedOnCreation();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionRange {
|
// hack to pass mouse events to the other mousearea if enabled
|
||||||
id: selectionRange
|
startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x : -flick.contentX
|
||||||
visible: root.selectionRangeMode
|
endDragArea: selectionRangeDrag.enabled ?
|
||||||
height: root.height
|
selectionRangeDrag.x + selectionRangeDrag.width : -flick.contentX-1
|
||||||
z: 2
|
}
|
||||||
}
|
MouseArea {
|
||||||
|
id: selectionRangeControl
|
||||||
|
enabled: false
|
||||||
|
width: flick.width
|
||||||
|
height: root.height
|
||||||
|
x: flick.contentX
|
||||||
|
hoverEnabled: enabled
|
||||||
|
z: 2
|
||||||
|
|
||||||
MouseArea {
|
onReleased: {
|
||||||
id: selectionRangeDrag
|
selectionRange.releasedOnCreation();
|
||||||
enabled: selectionRange.ready
|
}
|
||||||
anchors.fill: selectionRange
|
onPressed: {
|
||||||
drag.target: selectionRange
|
selectionRange.pressedOnCreation();
|
||||||
drag.axis: "XAxis"
|
}
|
||||||
drag.minimumX: 0
|
onMousePositionChanged: {
|
||||||
drag.maximumX: flick.contentWidth - selectionRange.width
|
selectionRange.movedOnCreation();
|
||||||
onPressed: {
|
|
||||||
selectionRange.isDragging = true;
|
|
||||||
}
|
|
||||||
onReleased: {
|
|
||||||
selectionRange.isDragging = false;
|
|
||||||
}
|
|
||||||
onDoubleClicked: {
|
|
||||||
zoomControl.setRange(selectionRange.startTime, selectionRange.startTime + selectionRange.duration);
|
|
||||||
root.selectionRangeMode = false;
|
|
||||||
root.updateRangeButton();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,9 +436,10 @@ Rectangle {
|
|||||||
id: labels
|
id: labels
|
||||||
width: 150
|
width: 150
|
||||||
color: "#dcdcdc"
|
color: "#dcdcdc"
|
||||||
height: flick.contentHeight
|
height: col.height
|
||||||
|
|
||||||
property int rowCount: 5
|
property int rowCount: 5
|
||||||
|
property variant rowExpanded: [false,false,false,false,false];
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: col
|
id: col
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ import Monitor 1.0
|
|||||||
BorderImage {
|
BorderImage {
|
||||||
id: rangeDetails
|
id: rangeDetails
|
||||||
|
|
||||||
property string duration //###int?
|
property string duration
|
||||||
property string label
|
property string label
|
||||||
property string type
|
property string type
|
||||||
property string file
|
property string file
|
||||||
property int line
|
property int line
|
||||||
|
|
||||||
property bool locked: !root.mouseOverSelection
|
property bool locked: view.selectionLocked
|
||||||
|
|
||||||
source: "popup_green.png"
|
source: "popup_green.png"
|
||||||
border {
|
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 {
|
Image {
|
||||||
id: lockIcon
|
id: lockIcon
|
||||||
source: locked?"lock_closed.png" : "lock_open.png"
|
source: locked?"lock_closed.png" : "lock_open.png"
|
||||||
@@ -118,7 +127,7 @@ BorderImage {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.mouseOverSelection = !root.mouseOverSelection;
|
root.selectionLocked = !root.selectionLocked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,20 +141,10 @@ BorderImage {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.hideRangeDetails();
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,13 +107,17 @@ TiledCanvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawBackgroundBars( ctxt, region ) {
|
function drawBackgroundBars( ctxt, region ) {
|
||||||
var barHeight = Math.round(labels.height / labels.rowCount);
|
var cumulatedHeight = 0;
|
||||||
for (var i=0; i<labels.rowCount; i++) {
|
for (var i=0; i<labels.rowCount; i++) {
|
||||||
ctxt.fillStyle = i%2 ? "#f3f3f3" : "white"
|
var barHeight = labels.rowExpanded[i] ?
|
||||||
ctxt.strokeStyle = i%2 ? "#f3f3f3" : "white"
|
qmlEventList.uniqueEventsOfType(i) * root.singleRowHeight :
|
||||||
ctxt.fillRect(0, i * barHeight, width, barHeight);
|
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);
|
ctxt.fillRect(0, labels.height, width, height - labels.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/plugins/qmlprofiler/qml/arrow_down.png
Normal file
BIN
src/plugins/qmlprofiler/qml/arrow_down.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 208 B |
BIN
src/plugins/qmlprofiler/qml/arrow_right.png
Normal file
BIN
src/plugins/qmlprofiler/qml/arrow_right.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 209 B |
BIN
src/plugins/qmlprofiler/qml/arrow_select.png
Normal file
BIN
src/plugins/qmlprofiler/qml/arrow_select.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 609 B |
@@ -24,5 +24,8 @@
|
|||||||
<file>SelectionRange.qml</file>
|
<file>SelectionRange.qml</file>
|
||||||
<file>SelectionRangeDetails.qml</file>
|
<file>SelectionRangeDetails.qml</file>
|
||||||
<file>magnifier.png</file>
|
<file>magnifier.png</file>
|
||||||
|
<file>arrow_down.png</file>
|
||||||
|
<file>arrow_right.png</file>
|
||||||
|
<file>arrow_select.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -35,223 +35,274 @@
|
|||||||
#include <qdeclarativecontext.h>
|
#include <qdeclarativecontext.h>
|
||||||
#include <qdeclarativeproperty.h>
|
#include <qdeclarativeproperty.h>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtGui/QPixmap>
|
||||||
|
#include <QtGui/QPainter>
|
||||||
|
#include <QtGui/QGraphicsSceneMouseEvent>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
using namespace QmlProfiler::Internal;
|
using namespace QmlProfiler::Internal;
|
||||||
|
|
||||||
#define CACHE_ENABLED true
|
const int DefaultRowHeight = 30;
|
||||||
#define CACHE_UPDATEDELAY 10
|
|
||||||
#define CACHE_STEP 200
|
|
||||||
|
|
||||||
TimelineView::TimelineView(QDeclarativeItem *parent) :
|
TimelineView::TimelineView(QDeclarativeItem *parent) :
|
||||||
QDeclarativeItem(parent), m_delegate(0), m_itemCount(0), m_startTime(0), m_endTime(0), m_spacing(0),
|
QDeclarativeItem(parent), 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)
|
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()
|
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();
|
QDeclarativeItem::componentComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TimelineView::requestPaint()
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
|
||||||
|
{
|
||||||
|
qint64 windowDuration = m_endTime - m_startTime;
|
||||||
|
if (windowDuration <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_spacing = qreal(width()) / windowDuration;
|
||||||
|
|
||||||
|
m_rowWidths.clear();
|
||||||
|
for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) {
|
||||||
|
m_rowWidths << (m_rowsExpanded[i] ? m_eventList->uniqueEventsOfType(i) : m_eventList->maxNestingForType(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// event rows
|
||||||
|
m_rowStarts.clear();
|
||||||
|
int pos = 0;
|
||||||
|
for (int i=0; i<QmlJsDebugClient::MaximumQmlEventType; i++) {
|
||||||
|
m_rowStarts << pos;
|
||||||
|
pos += DefaultRowHeight * m_rowWidths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 firstIndex = m_eventList->findFirstIndex(m_startTime);
|
||||||
|
int lastIndex = m_eventList->findLastIndex(m_endTime);
|
||||||
|
drawItemsToPainter(p, firstIndex, lastIndex);
|
||||||
|
|
||||||
|
drawSelectionBoxes(p);
|
||||||
|
|
||||||
|
m_lastStartTime = m_startTime;
|
||||||
|
m_lastEndTime = m_endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TimelineView::colorForItem(int itemIndex)
|
||||||
|
{
|
||||||
|
int ndx = m_eventList->getHash(itemIndex);
|
||||||
|
return QColor::fromHsl((ndx*25)%360, 76, 166);
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->getNestingLevel(i)-1);
|
||||||
|
|
||||||
|
width = m_eventList->getDuration(i)*m_spacing;
|
||||||
|
if (width<1)
|
||||||
|
width = 1;
|
||||||
|
|
||||||
|
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::drawSelectionBoxes(QPainter *p)
|
||||||
|
{
|
||||||
|
if (m_selectedItem == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int fromIndex = m_eventList->findFirstIndex(m_startTime);
|
||||||
|
int toIndex = m_eventList->findLastIndex(m_endTime);
|
||||||
|
int id = m_eventList->getHash(m_selectedItem);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
width = m_eventList->getDuration(i)*m_spacing;
|
||||||
|
if (width<1)
|
||||||
|
width = 1;
|
||||||
|
|
||||||
|
rowNumber = y/DefaultRowHeight;
|
||||||
|
p->drawRect(x,y,width,DefaultRowHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
// 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::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
manageClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
event->setAccepted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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::manageClicked()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineView::manageHovered(int x, int y)
|
||||||
|
{
|
||||||
|
if (m_endTime - m_startTime <=0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qint64 time = x * (m_endTime - m_startTime) / width() + m_startTime;
|
||||||
|
int row = y / DefaultRowHeight;
|
||||||
|
|
||||||
|
// already covered? nothing to do
|
||||||
|
if (m_currentSelection.eventIndex != -1 && time >= m_currentSelection.startTime && time <= m_currentSelection.endTime && row == m_currentSelection.row) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentSelection.eventIndex = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void TimelineView::clearData()
|
void TimelineView::clearData()
|
||||||
{
|
{
|
||||||
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_startTime = 0;
|
||||||
m_endTime = 0;
|
m_endTime = 0;
|
||||||
prevMin = 0;
|
m_lastStartTime = 0;
|
||||||
prevMax = 0;
|
m_lastEndTime = 0;
|
||||||
m_totalWidth = 0;
|
m_currentSelection.startTime = -1;
|
||||||
m_lastCachedIndex = 0;
|
m_currentSelection.endTime = -1;
|
||||||
}
|
m_currentSelection.row = -1;
|
||||||
|
m_currentSelection.eventIndex = -1;
|
||||||
void TimelineView::updateTimeline()
|
m_selectedItem = -1;
|
||||||
{
|
m_selectionLocked = true;
|
||||||
if (!m_delegate)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!m_eventList)
|
|
||||||
return;
|
|
||||||
|
|
||||||
qreal totalRange = m_eventList->traceEndTime() - m_eventList->traceStartTime();
|
|
||||||
qreal window = m_endTime - m_startTime;
|
|
||||||
|
|
||||||
if (window == 0) //###
|
|
||||||
return;
|
|
||||||
|
|
||||||
qreal newSpacing = width() / window;
|
|
||||||
bool spacingChanged = (newSpacing != m_spacing);
|
|
||||||
m_spacing = newSpacing;
|
|
||||||
|
|
||||||
qreal oldtw = m_totalWidth;
|
|
||||||
m_totalWidth = totalRange * m_spacing;
|
|
||||||
|
|
||||||
|
|
||||||
int minsample = m_eventList->findFirstIndex(m_startTime + m_eventList->traceStartTime());
|
|
||||||
int maxsample = m_eventList->findLastIndex(m_endTime + m_eventList->traceStartTime());
|
|
||||||
|
|
||||||
//### emitting this before startXChanged was causing issues
|
|
||||||
if (m_totalWidth != oldtw)
|
|
||||||
emit totalWidthChanged(m_totalWidth);
|
|
||||||
|
|
||||||
|
|
||||||
// the next loops have to be modified with the new implementation of the cache
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update visible items
|
|
||||||
for (int i = minsample; i <= maxsample; ++i) {
|
|
||||||
if (!m_items.contains(i)) {
|
|
||||||
createItem(i);
|
|
||||||
m_items.value(i)->setVisible(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (spacingChanged || !m_items.value(i)->isVisible()) {
|
|
||||||
m_items.value(i)->setVisible(true);
|
|
||||||
updateItemPosition(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prevMin = minsample;
|
|
||||||
prevMax = maxsample;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimelineView::createItem(int itemIndex)
|
|
||||||
{
|
|
||||||
QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(this));
|
|
||||||
QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(m_delegate->beginCreate(ctxt));
|
|
||||||
m_items.insert(itemIndex, item);
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
updateItemPosition(itemIndex);
|
|
||||||
|
|
||||||
item->setVisible(false);
|
|
||||||
|
|
||||||
item->setParentItem(this);
|
|
||||||
m_delegate->completeCreate();
|
|
||||||
m_itemCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimelineView::updateItemPosition(int itemIndex)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimelineView::rebuildCache()
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal TimelineView::cachedProgress() const
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
return progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimelineView::increaseCache()
|
|
||||||
{
|
|
||||||
int totalCount = m_eventList->count();
|
|
||||||
if (m_lastCachedIndex >= totalCount) {
|
|
||||||
emit cacheReady();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < CACHE_STEP; i++) {
|
|
||||||
createItem(m_lastCachedIndex);
|
|
||||||
m_lastCachedIndex++;
|
|
||||||
if (m_lastCachedIndex >= totalCount)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit cachedProgressChanged();
|
|
||||||
|
|
||||||
QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimelineView::purgeCache()
|
|
||||||
{
|
|
||||||
if (m_items.isEmpty()) {
|
|
||||||
m_creatingCache = true;
|
|
||||||
m_lastCachedIndex = 0;
|
|
||||||
QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i < CACHE_STEP; i++)
|
|
||||||
{
|
|
||||||
if (m_items.contains(m_lastCachedIndex))
|
|
||||||
delete m_items.take(m_lastCachedIndex);
|
|
||||||
|
|
||||||
m_lastCachedIndex++;
|
|
||||||
if (m_items.isEmpty())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit cachedProgressChanged();
|
|
||||||
QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(purgeCache()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 TimelineView::getDuration(int index) const
|
qint64 TimelineView::getDuration(int index) const
|
||||||
@@ -277,3 +328,65 @@ QString TimelineView::getDetails(int index) const
|
|||||||
Q_ASSERT(m_eventList);
|
Q_ASSERT(m_eventList);
|
||||||
return m_eventList->getDetails(index);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,21 +43,17 @@ namespace Internal {
|
|||||||
class TimelineView : public QDeclarativeItem
|
class TimelineView : public QDeclarativeItem
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
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 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
|
||||||
Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
|
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(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:
|
public:
|
||||||
explicit TimelineView(QDeclarativeItem *parent = 0);
|
explicit TimelineView(QDeclarativeItem *parent = 0);
|
||||||
|
|
||||||
QDeclarativeComponent * delegate() const
|
|
||||||
{
|
|
||||||
return m_delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
qint64 startTime() const
|
qint64 startTime() const
|
||||||
{
|
{
|
||||||
return m_startTime;
|
return m_startTime;
|
||||||
@@ -68,12 +64,25 @@ public:
|
|||||||
return m_endTime;
|
return m_endTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal totalWidth() const
|
bool selectionLocked() const
|
||||||
{
|
{
|
||||||
return m_totalWidth;
|
return m_selectionLocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal cachedProgress() const;
|
int selectedItem() const
|
||||||
|
{
|
||||||
|
return m_selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startDragArea() const
|
||||||
|
{
|
||||||
|
return m_startDragArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
int endDragArea() const
|
||||||
|
{
|
||||||
|
return m_endDragArea;
|
||||||
|
}
|
||||||
|
|
||||||
QmlJsDebugClient::QmlProfilerEventList *eventList() const { return m_eventList; }
|
QmlJsDebugClient::QmlProfilerEventList *eventList() const { return m_eventList; }
|
||||||
void setEventList(QObject *eventList)
|
void setEventList(QObject *eventList)
|
||||||
@@ -82,33 +91,30 @@ public:
|
|||||||
emit eventListChanged(m_eventList);
|
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 qint64 getDuration(int index) const;
|
||||||
Q_INVOKABLE QString getFilename(int index) const;
|
Q_INVOKABLE QString getFilename(int index) const;
|
||||||
Q_INVOKABLE int getLine(int index) const;
|
Q_INVOKABLE int getLine(int index) const;
|
||||||
Q_INVOKABLE QString getDetails(int index) const;
|
Q_INVOKABLE QString getDetails(int index) const;
|
||||||
Q_INVOKABLE void rebuildCache();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void delegateChanged(QDeclarativeComponent * arg);
|
|
||||||
void startTimeChanged(qint64 arg);
|
void startTimeChanged(qint64 arg);
|
||||||
void endTimeChanged(qint64 arg);
|
void endTimeChanged(qint64 arg);
|
||||||
void totalWidthChanged(qreal arg);
|
|
||||||
void eventListChanged(QmlJsDebugClient::QmlProfilerEventList *list);
|
void eventListChanged(QmlJsDebugClient::QmlProfilerEventList *list);
|
||||||
|
void selectionLockedChanged(bool locked);
|
||||||
void cachedProgressChanged();
|
void selectedItemChanged(int itemIndex);
|
||||||
void cacheReady();
|
void startDragAreaChanged(int startDragArea);
|
||||||
|
void endDragAreaChanged(int endDragArea);
|
||||||
|
void itemPressed(int pressedItem);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void clearData();
|
void clearData();
|
||||||
void updateTimeline();
|
void requestPaint();
|
||||||
|
|
||||||
void setDelegate(QDeclarativeComponent * arg)
|
|
||||||
{
|
|
||||||
if (m_delegate != arg) {
|
|
||||||
m_delegate = arg;
|
|
||||||
emit delegateChanged(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setStartTime(qint64 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:
|
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:
|
private:
|
||||||
void createItem(int itemIndex);
|
QColor colorForItem(int itemIndex);
|
||||||
void updateItemPosition(int itemIndex);
|
QLinearGradient *gradientForItem(int itemIndex);
|
||||||
|
void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex);
|
||||||
|
void drawSelectionBoxes(QPainter *p);
|
||||||
|
|
||||||
public slots:
|
void manageClicked();
|
||||||
void increaseCache();
|
void manageHovered(int x, int y);
|
||||||
void purgeCache();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDeclarativeComponent * m_delegate;
|
|
||||||
QHash<int,QDeclarativeItem*> m_items;
|
|
||||||
qint64 m_itemCount;
|
|
||||||
qint64 m_startTime;
|
qint64 m_startTime;
|
||||||
qint64 m_endTime;
|
qint64 m_endTime;
|
||||||
qreal m_spacing;
|
qreal m_spacing;
|
||||||
int prevMin;
|
qint64 m_lastStartTime;
|
||||||
int prevMax;
|
qint64 m_lastEndTime;
|
||||||
|
|
||||||
QmlJsDebugClient::QmlProfilerEventList *m_eventList;
|
QmlJsDebugClient::QmlProfilerEventList *m_eventList;
|
||||||
|
QHash<int, QLinearGradient*> m_hashedGradients;
|
||||||
|
|
||||||
qreal m_totalWidth;
|
QList<int> m_rowLastX;
|
||||||
int m_lastCachedIndex;
|
QList<int> m_rowStarts;
|
||||||
bool m_creatingCache;
|
QList<int> m_rowWidths;
|
||||||
int m_oldCacheSize;
|
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
|
} // namespace Internal
|
||||||
|
|||||||
@@ -174,6 +174,16 @@ QWidget *TraceWindow::createToolbar()
|
|||||||
connect(this, SIGNAL(enableToolbar(bool)), m_buttonRange, SLOT(setEnabled(bool)));
|
connect(this, SIGNAL(enableToolbar(bool)), m_buttonRange, SLOT(setEnabled(bool)));
|
||||||
connect(this, SIGNAL(rangeModeChanged(bool)), m_buttonRange, SLOT(setChecked(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(buttonPrev);
|
||||||
toolBarLayout->addWidget(buttonNext);
|
toolBarLayout->addWidget(buttonNext);
|
||||||
toolBarLayout->addWidget(buttonZoomControls);
|
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(updateCursorPosition()), this, SLOT(updateCursorPosition()));
|
||||||
connect(m_mainView->rootObject(), SIGNAL(updateTimer()), this, SLOT(updateTimer()));
|
connect(m_mainView->rootObject(), SIGNAL(updateTimer()), this, SLOT(updateTimer()));
|
||||||
connect(m_mainView->rootObject(), SIGNAL(updateRangeButton()), this, SLOT(updateRangeButton()));
|
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(m_eventList, SIGNAL(countChanged()), this, SLOT(updateToolbar()));
|
||||||
connect(this, SIGNAL(jumpToPrev()), m_mainView->rootObject(), SLOT(prevEvent()));
|
connect(this, SIGNAL(jumpToPrev()), m_mainView->rootObject(), SLOT(prevEvent()));
|
||||||
connect(this, SIGNAL(jumpToNext()), m_mainView->rootObject(), SLOT(nextEvent()));
|
connect(this, SIGNAL(jumpToNext()), m_mainView->rootObject(), SLOT(nextEvent()));
|
||||||
@@ -340,6 +351,21 @@ void TraceWindow::updateRangeButton()
|
|||||||
emit rangeModeChanged(rangeMode);
|
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)
|
void TraceWindow::setRecording(bool recording)
|
||||||
{
|
{
|
||||||
if (recording) {
|
if (recording) {
|
||||||
|
|||||||
@@ -103,7 +103,9 @@ public slots:
|
|||||||
void clearDisplay();
|
void clearDisplay();
|
||||||
void updateToolbar();
|
void updateToolbar();
|
||||||
void toggleRangeMode(bool);
|
void toggleRangeMode(bool);
|
||||||
|
void toggleLockMode(bool);
|
||||||
void updateRangeButton();
|
void updateRangeButton();
|
||||||
|
void updateLockButton();
|
||||||
void setZoomLevel(int zoomLevel);
|
void setZoomLevel(int zoomLevel);
|
||||||
void updateRange();
|
void updateRange();
|
||||||
void mouseWheelMoved(int x, int y, int delta);
|
void mouseWheelMoved(int x, int y, int delta);
|
||||||
@@ -124,6 +126,7 @@ signals:
|
|||||||
void jumpToPrev();
|
void jumpToPrev();
|
||||||
void jumpToNext();
|
void jumpToNext();
|
||||||
void rangeModeChanged(bool);
|
void rangeModeChanged(bool);
|
||||||
|
void lockModeChanged(bool);
|
||||||
void enableToolbar(bool);
|
void enableToolbar(bool);
|
||||||
void zoomLevelChanged(int);
|
void zoomLevelChanged(int);
|
||||||
void updateViewZoom(QVariant zoomLevel);
|
void updateViewZoom(QVariant zoomLevel);
|
||||||
@@ -155,6 +158,7 @@ private:
|
|||||||
QWeakPointer<ZoomControl> m_zoomControl;
|
QWeakPointer<ZoomControl> m_zoomControl;
|
||||||
|
|
||||||
QToolButton *m_buttonRange;
|
QToolButton *m_buttonRange;
|
||||||
|
QToolButton *m_buttonLock;
|
||||||
QWidget *m_zoomToolbar;
|
QWidget *m_zoomToolbar;
|
||||||
int m_currentZoomLevel;
|
int m_currentZoomLevel;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user