forked from qt-creator/qt-creator
QmlProfiler: Reorganize timeline into rows
This potentially allows us to use different views for different categories and reduces the reliance on TimelineModelAggregator. Change-Id: I486481599d1517abc0087c565358f27405e4108b Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
@@ -34,42 +34,41 @@ import QtQuick.Controls.Styles 1.2
|
||||
|
||||
Item {
|
||||
id: labelContainer
|
||||
property string text: trigger(1) ? qmlProfilerModelProxy.displayName(modelIndex) : ""
|
||||
property bool expanded: trigger(qmlProfilerModelProxy.expanded(modelIndex))
|
||||
property int modelIndex: index
|
||||
property int bindingTrigger: 1
|
||||
|
||||
property QtObject model
|
||||
property bool mockup
|
||||
property string text: model ? model.displayName : ""
|
||||
property bool expanded: model && model.expanded
|
||||
property var descriptions: []
|
||||
property var extdescriptions: []
|
||||
property var selectionIds: []
|
||||
property bool dragging
|
||||
property int visualIndex
|
||||
property int dragOffset
|
||||
property Item draggerParent
|
||||
|
||||
signal dragStarted;
|
||||
signal dragStopped;
|
||||
signal dropped(int sourceIndex, int targetIndex)
|
||||
|
||||
signal selectById(int eventId)
|
||||
signal selectNextBySelectionId(int selectionId)
|
||||
signal selectPrevBySelectionId(int selectionId)
|
||||
|
||||
readonly property int dragHeight: 5
|
||||
|
||||
function trigger(i) {
|
||||
return i * bindingTrigger * bindingTrigger;
|
||||
}
|
||||
|
||||
property bool reverseSelect: false
|
||||
|
||||
visible: trigger(!qmlProfilerModelProxy.models[modelIndex].hidden &&
|
||||
!qmlProfilerModelProxy.models[modelIndex].empty)
|
||||
visible: model && (mockup || (!model.hidden && !model.empty))
|
||||
|
||||
height: trigger(qmlProfilerModelProxy.models[modelIndex].height)
|
||||
height: model ? Math.max(txt.height, model.height) : 0
|
||||
width: 150
|
||||
|
||||
function updateDescriptions() {
|
||||
bindingTrigger = -bindingTrigger;
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
var desc=[];
|
||||
var ids=[];
|
||||
var extdesc=[];
|
||||
var labelList = qmlProfilerModelProxy.labels(modelIndex);
|
||||
var labelList = model.labels;
|
||||
for (var i = 0; i < labelList.length; i++ ) {
|
||||
extdesc[i] = desc[i] = (labelList[i].description || qsTr("<bytecode>"));
|
||||
ids[i] = labelList[i].id;
|
||||
@@ -82,16 +81,8 @@ Item {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: qmlProfilerModelProxy.models[modelIndex]
|
||||
onExpandedChanged: updateDescriptions()
|
||||
onRowHeightChanged: updateDescriptions()
|
||||
onHiddenChanged: updateDescriptions()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: qmlProfilerModelProxy
|
||||
onStateChanged: updateDescriptions()
|
||||
onModelsChanged: updateDescriptions()
|
||||
target: model
|
||||
onLabelsChanged: updateDescriptions()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -99,20 +90,22 @@ Item {
|
||||
anchors.fill: txt
|
||||
drag.target: dragger
|
||||
cursorShape: dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
||||
drag.minimumY: dragging ? 0 : -dragOffset // Account for parent change below
|
||||
drag.maximumY: draggerParent.height - (dragging ? 0 : dragOffset)
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: dropArea
|
||||
|
||||
onPositionChanged: {
|
||||
if ((drag.source.modelIndex > labelContainer.modelIndex &&
|
||||
drag.source.y < labelContainer.y + drag.source.height) ||
|
||||
(drag.source.modelIndex < labelContainer.modelIndex &&
|
||||
drag.source.y > labelContainer.y + labelContainer.height -
|
||||
drag.source.height)) {
|
||||
qmlProfilerModelProxy.swapModels(drag.source.modelIndex,
|
||||
labelContainer.modelIndex);
|
||||
drag.source.modelIndex = labelContainer.modelIndex;
|
||||
var sourceIndex = drag.source.visualIndex;
|
||||
if (drag.source.y + drag.source.height > dragOffset + labelContainer.height &&
|
||||
sourceIndex !== visualIndex && sourceIndex !== visualIndex + 1) {
|
||||
var moveTo = sourceIndex > visualIndex ? visualIndex + 1 : visualIndex;
|
||||
labelContainer.dropped(sourceIndex, moveTo);
|
||||
} else if (drag.source.y === 0) {
|
||||
// special case for first position.
|
||||
labelContainer.dropped(sourceIndex, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +118,7 @@ Item {
|
||||
font.pixelSize: 12
|
||||
text: labelContainer.text
|
||||
color: "#232323"
|
||||
height: trigger(qmlProfilerModelProxy.rowHeight(modelIndex, 0))
|
||||
height: model ? model.defaultRowHeight : 0
|
||||
width: 140
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
@@ -140,19 +133,21 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
property QtObject parentModel: model
|
||||
anchors.top: txt.bottom
|
||||
visible: expanded
|
||||
Repeater {
|
||||
model: descriptions.length
|
||||
Button {
|
||||
width: labelContainer.width
|
||||
height: trigger(qmlProfilerModelProxy.rowHeight(modelIndex, index + 1))
|
||||
height: column.parentModel ? column.parentModel.rowHeight(index + 1) : 0
|
||||
action: Action {
|
||||
onTriggered: {
|
||||
if (reverseSelect)
|
||||
view.selectPrevFromSelectionId(modelIndex,selectionIds[index]);
|
||||
labelContainer.selectPrevBySelectionId(selectionIds[index]);
|
||||
else
|
||||
view.selectNextFromSelectionId(modelIndex,selectionIds[index]);
|
||||
labelContainer.selectNextBySelectionId(selectionIds[index]);
|
||||
}
|
||||
|
||||
tooltip: extdescriptions[index]
|
||||
@@ -186,8 +181,10 @@ Item {
|
||||
cursorShape: Qt.SizeVerCursor
|
||||
|
||||
onMouseYChanged: {
|
||||
if (resizing)
|
||||
qmlProfilerModelProxy.setRowHeight(modelIndex, index + 1, y + mouseY);
|
||||
if (resizing) {
|
||||
column.parentModel.setRowHeight(index + 1, y + mouseY);
|
||||
parent.height = column.parentModel.rowHeight(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,22 +200,22 @@ Item {
|
||||
property var eventIds: []
|
||||
property var texts: []
|
||||
property int currentNote: -1
|
||||
property var notesModel: qmlProfilerModelProxy.notes
|
||||
Connections {
|
||||
target: qmlProfilerModelProxy
|
||||
onModelsChanged: notesButton.updateNotes()
|
||||
onNotesChanged: {
|
||||
if (arguments[1] === -1 || arguments[1] === modelIndex)
|
||||
target: notesButton.notesModel
|
||||
onChanged: {
|
||||
if (arguments[1] === -1 || arguments[1] === model.modelId)
|
||||
notesButton.updateNotes();
|
||||
}
|
||||
}
|
||||
|
||||
function updateNotes() {
|
||||
var notes = qmlProfilerModelProxy.notesByTimelineModel(modelIndex);
|
||||
var notes = notesModel.byTimelineModel(model.modelId);
|
||||
var newTexts = [];
|
||||
var newEventIds = [];
|
||||
for (var i in notes) {
|
||||
newTexts.push(qmlProfilerModelProxy.noteText(notes[i]))
|
||||
newEventIds.push(qmlProfilerModelProxy.noteTimelineIndex(notes[i]));
|
||||
newTexts.push(notesModel.text(notes[i]))
|
||||
newEventIds.push(notesModel.timelineIndex(notes[i]));
|
||||
}
|
||||
|
||||
// Bindings are only triggered when assigning the whole array.
|
||||
@@ -232,7 +229,7 @@ Item {
|
||||
onClicked: {
|
||||
if (++currentNote >= eventIds.length)
|
||||
currentNote = 0;
|
||||
view.selectFromEventIndex(modelIndex, eventIds[currentNote]);
|
||||
labelContainer.selectById(eventIds[currentNote]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,15 +239,15 @@ Item {
|
||||
anchors.right: parent.right
|
||||
implicitWidth: 17
|
||||
implicitHeight: txt.height - 1
|
||||
enabled: expanded || trigger(qmlProfilerModelProxy.count(modelIndex)) > 0
|
||||
enabled: expanded || (model && !model.empty)
|
||||
iconSource: expanded ? "arrow_down.png" : "arrow_right.png"
|
||||
tooltip: expanded ? qsTr("Collapse category") : qsTr("Expand category.")
|
||||
onClicked: qmlProfilerModelProxy.setExpanded(modelIndex, !expanded);
|
||||
onClicked: model.expanded = !expanded
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dragger
|
||||
property int modelIndex
|
||||
property int visualIndex: labelContainer.visualIndex
|
||||
width: labelContainer.width
|
||||
height: 0
|
||||
color: "black"
|
||||
@@ -262,10 +259,9 @@ Item {
|
||||
|
||||
Drag.active: dragArea.drag.active
|
||||
Drag.onActiveChanged: {
|
||||
// We don't want height, text, or modelIndex to be changed when reordering occurs, so we
|
||||
// don't make them properties.
|
||||
// We don't want height or text to be changed when reordering occurs, so we don't make
|
||||
// them properties.
|
||||
draggerText.text = txt.text;
|
||||
modelIndex = labelContainer.modelIndex;
|
||||
if (Drag.active) {
|
||||
height = labelContainer.height;
|
||||
labelContainer.dragStarted();
|
||||
|
||||
@@ -31,13 +31,14 @@
|
||||
import QtQuick 2.1
|
||||
import Monitor 1.0
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQml.Models 2.1
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
// ***** properties
|
||||
|
||||
property alias selectionLocked : view.selectionLocked
|
||||
property bool selectionLocked : true
|
||||
property bool lockItemSelection : false
|
||||
|
||||
property real mainviewTimePerPixel: zoomControl.rangeDuration / root.width
|
||||
@@ -47,6 +48,8 @@ Rectangle {
|
||||
property int lineNumber: -1
|
||||
property int columnNumber: 0
|
||||
property int typeId: -1
|
||||
property int selectedModel: -1
|
||||
property int selectedItem: -1
|
||||
|
||||
property bool selectionRangeMode: false
|
||||
|
||||
@@ -60,12 +63,11 @@ Rectangle {
|
||||
Connections {
|
||||
target: zoomControl
|
||||
onRangeChanged: {
|
||||
backgroundMarks.updateMarks(zoomControl.rangeStart, zoomControl.rangeEnd);
|
||||
zoomSliderToolBar.updateZoomLevel();
|
||||
view.updateWindow();
|
||||
flick.updateWindow();
|
||||
}
|
||||
onWindowChanged: {
|
||||
view.updateWindow();
|
||||
flick.updateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,15 +75,10 @@ Rectangle {
|
||||
Connections {
|
||||
target: qmlProfilerModelProxy
|
||||
onDataAvailable: {
|
||||
view.clearData();
|
||||
timelineView.clearChildren();
|
||||
zoomControl.setRange(zoomControl.traceStart,
|
||||
zoomControl.traceStart + zoomControl.traceDuration / 10);
|
||||
view.requestPaint();
|
||||
}
|
||||
onStateChanged: backgroundMarks.requestPaint()
|
||||
onModelsChanged: backgroundMarks.requestPaint()
|
||||
onExpandedChanged: backgroundMarks.requestPaint()
|
||||
onRowHeightChanged: backgroundMarks.requestPaint()
|
||||
}
|
||||
|
||||
|
||||
@@ -94,11 +91,15 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
function recenterOnItem() {
|
||||
timelineView.select(selectedModel, selectedItem);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
flick.contentY = 0;
|
||||
flick.contentX = 0;
|
||||
flick.contentWidth = 0;
|
||||
view.clearData();
|
||||
timelineView.clearChildren();
|
||||
rangeDetails.hide();
|
||||
selectionRangeMode = false;
|
||||
zoomSlider.externalUpdate = true;
|
||||
@@ -107,34 +108,35 @@ Rectangle {
|
||||
timeDisplay.clear();
|
||||
}
|
||||
|
||||
function enableButtonsBar(enable) {
|
||||
buttonsBar.enabled = enable;
|
||||
}
|
||||
|
||||
function recenter( centerPoint ) {
|
||||
var newStart = Math.floor(centerPoint - zoomControl.rangeDuration / 2);
|
||||
zoomControl.setRange(Math.max(newStart, zoomControl.traceStart),
|
||||
Math.min(newStart + zoomControl.rangeDuration, zoomControl.traceEnd));
|
||||
}
|
||||
|
||||
function recenterOnItem(modelIndex, itemIndex)
|
||||
{
|
||||
if (itemIndex === -1)
|
||||
function propagateSelection(newModel, newItem) {
|
||||
if (lockItemSelection || (newModel === selectedModel && newItem === selectedItem))
|
||||
return;
|
||||
|
||||
// if item is outside of the view, jump back to its position
|
||||
if (qmlProfilerModelProxy.endTime(modelIndex, itemIndex) < zoomControl.rangeStart ||
|
||||
qmlProfilerModelProxy.startTime(modelIndex, itemIndex) > zoomControl.rangeEnd) {
|
||||
recenter((qmlProfilerModelProxy.startTime(modelIndex, itemIndex) +
|
||||
qmlProfilerModelProxy.endTime(modelIndex, itemIndex)) / 2);
|
||||
lockItemSelection = true;
|
||||
if (selectedModel !== -1 && selectedModel !== newModel)
|
||||
timelineView.select(selectedModel, -1);
|
||||
|
||||
selectedItem = newItem
|
||||
selectedModel = newModel
|
||||
if (selectedItem !== -1) {
|
||||
// display details
|
||||
rangeDetails.showInfo(selectedModel, selectedItem);
|
||||
|
||||
// update in other views
|
||||
var model = qmlProfilerModelProxy.models[selectedModel];
|
||||
var eventLocation = model.location(selectedItem);
|
||||
gotoSourceLocation(eventLocation.file, eventLocation.line,
|
||||
eventLocation.column);
|
||||
typeId = model.typeId(selectedItem);
|
||||
updateCursorPosition();
|
||||
} else {
|
||||
rangeDetails.hide();
|
||||
}
|
||||
var row = qmlProfilerModelProxy.row(modelIndex, itemIndex);
|
||||
var totalRowOffset = qmlProfilerModelProxy.modelOffset(modelIndex) +
|
||||
qmlProfilerModelProxy.rowOffset(modelIndex, row);
|
||||
if (totalRowOffset > flick.contentY + flick.height ||
|
||||
totalRowOffset + qmlProfilerModelProxy.rowHeight(modelIndex, row) < flick.contentY)
|
||||
flick.contentY = Math.min(flick.contentHeight - flick.height,
|
||||
Math.max(0, totalRowOffset - flick.height / 2));
|
||||
lockItemSelection = false;
|
||||
}
|
||||
|
||||
function enableButtonsBar(enable) {
|
||||
buttonsBar.enabled = enable;
|
||||
}
|
||||
|
||||
function selectByTypeId(typeId)
|
||||
@@ -142,8 +144,6 @@ Rectangle {
|
||||
if (lockItemSelection || typeId === -1)
|
||||
return;
|
||||
|
||||
lockItemSelection = true;
|
||||
|
||||
var itemIndex = -1;
|
||||
var modelIndex = -1;
|
||||
var notes = qmlProfilerModelProxy.notesByTypeId(typeId);
|
||||
@@ -151,15 +151,16 @@ Rectangle {
|
||||
modelIndex = qmlProfilerModelProxy.noteTimelineModel(notes[0]);
|
||||
itemIndex = qmlProfilerModelProxy.noteTimelineIndex(notes[0]);
|
||||
} else {
|
||||
for (modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
|
||||
if (modelIndex === view.selectedModel && view.selectedItem !== -1 &&
|
||||
typeId === qmlProfilerModelProxy.typeId(modelIndex, view.selectedItem))
|
||||
for (modelIndex = 0; modelIndex < qmlProfilerModelProxy.models.length; ++modelIndex) {
|
||||
if (modelIndex === selectedModel && selectedItem !== -1 &&
|
||||
typeId === qmlProfilerModelProxy.models[modelIndex].typeId(selectedItem))
|
||||
break;
|
||||
|
||||
if (!qmlProfilerModelProxy.handlesTypeId(modelIndex, typeId))
|
||||
if (!qmlProfilerModelProxy.models[modelIndex].handlesTypeId(typeId))
|
||||
continue;
|
||||
|
||||
itemIndex = view.nextItemFromTypeId(modelIndex, typeId);
|
||||
itemIndex = qmlProfilerModelProxy.models[modelIndex].nextItemByTypeId(typeId,
|
||||
zoomControl.rangeStart, selectedItem);
|
||||
if (itemIndex !== -1)
|
||||
break;
|
||||
}
|
||||
@@ -167,11 +168,9 @@ Rectangle {
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
// select an item, lock to it, and recenter if necessary
|
||||
view.selectFromEventIndex(modelIndex, itemIndex); // triggers recentering
|
||||
view.selectionLocked = true;
|
||||
timelineView.select(modelIndex, itemIndex);
|
||||
root.selectionLocked = true;
|
||||
}
|
||||
|
||||
lockItemSelection = false;
|
||||
}
|
||||
|
||||
// ***** slots
|
||||
@@ -210,8 +209,6 @@ Rectangle {
|
||||
color: root.color
|
||||
height: col.height
|
||||
|
||||
property int rowCount: qmlProfilerModelProxy.modelCount();
|
||||
|
||||
Column {
|
||||
id: col
|
||||
|
||||
@@ -220,15 +217,47 @@ Rectangle {
|
||||
// events, so we can't use the drag events to determine the cursor shape.
|
||||
property bool dragging: false
|
||||
|
||||
Repeater {
|
||||
model: labels.rowCount
|
||||
DelegateModel {
|
||||
id: labelsModel
|
||||
model: qmlProfilerModelProxy.models
|
||||
delegate: CategoryLabel {
|
||||
model: modelData
|
||||
mockup: qmlProfilerModelProxy.height == 0
|
||||
visualIndex: DelegateModel.itemsIndex
|
||||
dragging: col.dragging
|
||||
reverseSelect: root.shiftPressed
|
||||
onDragStarted: col.dragging = true
|
||||
onDragStopped: col.dragging = false
|
||||
draggerParent: labels
|
||||
dragOffset: y
|
||||
|
||||
onDropped: {
|
||||
timelineModel.items.move(sourceIndex, targetIndex);
|
||||
labelsModel.items.move(sourceIndex, targetIndex);
|
||||
}
|
||||
|
||||
onSelectById: {
|
||||
timelineView.select(index, eventId)
|
||||
}
|
||||
|
||||
onSelectNextBySelectionId: {
|
||||
timelineView.select(index, modelData.nextItemBySelectionId(selectionId,
|
||||
zoomControl.rangeStart,
|
||||
root.selectedModel === index ? root.selectedItem : -1));
|
||||
}
|
||||
|
||||
onSelectPrevBySelectionId: {
|
||||
timelineView.select(index, modelData.prevItemBySelectionId(selectionId,
|
||||
zoomControl.rangeStart,
|
||||
root.selectedModel === index ? root.selectedItem : -1));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: labelsModel
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,15 +283,16 @@ Rectangle {
|
||||
onZoomControlChanged: zoomSliderToolBar.visible = !zoomSliderToolBar.visible
|
||||
onFilterMenuChanged: filterMenu.visible = !filterMenu.visible
|
||||
onJumpToNext: {
|
||||
var next = qmlProfilerModelProxy.nextItem(view.selectedModel, view.selectedItem,
|
||||
var next = qmlProfilerModelProxy.nextItem(root.selectedModel, root.selectedItem,
|
||||
zoomControl.rangeStart);
|
||||
view.selectFromEventIndex(next.model, next.item);
|
||||
timelineView.select(next.model, next.item);
|
||||
}
|
||||
onJumpToPrev: {
|
||||
var prev = qmlProfilerModelProxy.prevItem(view.selectedModel, view.selectedItem,
|
||||
var prev = qmlProfilerModelProxy.prevItem(root.selectedModel, root.selectedItem,
|
||||
zoomControl.rangeEnd);
|
||||
view.selectFromEventIndex(prev.model, prev.item);
|
||||
timelineView.select(prev.model, prev.item);
|
||||
}
|
||||
|
||||
onRangeSelectChanged: selectionRangeMode = rangeButtonChecked();
|
||||
onLockChanged: selectionLocked = !lockButtonChecked();
|
||||
}
|
||||
@@ -290,40 +320,11 @@ Rectangle {
|
||||
onInteractiveChanged: interactive = stayInteractive
|
||||
onStayInteractiveChanged: interactive = stayInteractive
|
||||
|
||||
// ***** child items
|
||||
TimeMarks {
|
||||
id: backgroundMarks
|
||||
y: flick.contentY
|
||||
x: flick.contentX
|
||||
height: flick.height
|
||||
width: scroller.width
|
||||
}
|
||||
|
||||
SelectionRange {
|
||||
id: selectionRange
|
||||
visible: root.selectionRangeMode && creationState !== 0
|
||||
z: 2
|
||||
}
|
||||
|
||||
TimelineRenderer {
|
||||
id: view
|
||||
|
||||
profilerModelProxy: qmlProfilerModelProxy
|
||||
zoomer: zoomControl
|
||||
|
||||
x: flick.contentX
|
||||
y: flick.contentY
|
||||
|
||||
// paint "under" the vertical scrollbar, so that it always matches with the timemarks
|
||||
width: scroller.width
|
||||
height: flick.height
|
||||
|
||||
onYChanged: requestPaint()
|
||||
onHeightChanged: requestPaint()
|
||||
property bool recursionGuard: false
|
||||
property int intX: contentX
|
||||
property int intWidth: scroller.width
|
||||
|
||||
property int intX: x
|
||||
property int intWidth: width
|
||||
// Update the zoom control on srolling.
|
||||
onIntXChanged: {
|
||||
if (recursionGuard)
|
||||
return;
|
||||
@@ -340,6 +341,7 @@ Rectangle {
|
||||
recursionGuard = false;
|
||||
}
|
||||
|
||||
// Scroll when the zoom control is updated
|
||||
function updateWindow() {
|
||||
if (recursionGuard || zoomControl.rangeDuration <= 0)
|
||||
return;
|
||||
@@ -360,41 +362,158 @@ Rectangle {
|
||||
recursionGuard = false;
|
||||
}
|
||||
|
||||
onSelectionChanged: {
|
||||
if (selectedItem !== -1) {
|
||||
// display details
|
||||
rangeDetails.showInfo(selectedModel, selectedItem);
|
||||
// ***** child items
|
||||
SelectionRange {
|
||||
id: selectionRange
|
||||
visible: root.selectionRangeMode && creationState !== 0
|
||||
z: 2
|
||||
}
|
||||
|
||||
// center view (horizontally)
|
||||
recenterOnItem(selectedModel, selectedItem);
|
||||
if (!lockItemSelection) {
|
||||
lockItemSelection = true;
|
||||
// update in other views
|
||||
var eventLocation = qmlProfilerModelProxy.location(view.selectedModel,
|
||||
view.selectedItem);
|
||||
gotoSourceLocation(eventLocation.file, eventLocation.line,
|
||||
eventLocation.column);
|
||||
root.typeId = qmlProfilerModelProxy.typeId(view.selectedModel,
|
||||
view.selectedItem);
|
||||
root.updateCursorPosition();
|
||||
lockItemSelection = false;
|
||||
Column {
|
||||
id: timelineView
|
||||
|
||||
signal clearChildren
|
||||
signal select(int modelIndex, int eventIndex)
|
||||
|
||||
// As we cannot retrieve items by visible index we keep an array of row counts here,
|
||||
// for the time marks to draw the row backgrounds in the right colors.
|
||||
property var rowCounts: new Array(qmlProfilerModelProxy.models.length)
|
||||
|
||||
DelegateModel {
|
||||
id: timelineModel
|
||||
model: qmlProfilerModelProxy.models
|
||||
delegate: Item {
|
||||
id: spacer
|
||||
height: modelData.height
|
||||
width: flick.contentWidth
|
||||
property int rowCount: (modelData.empty || modelData.hidden) ?
|
||||
0 : modelData.rowCount
|
||||
property int visualIndex: DelegateModel.itemsIndex
|
||||
|
||||
function updateRowParentCount() {
|
||||
if (timelineView.rowCounts[visualIndex] !== rowCount) {
|
||||
timelineView.rowCounts[visualIndex] = rowCount;
|
||||
// Array don't "change" if entries change. We have to signal manually.
|
||||
timelineView.rowCountsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
onRowCountChanged: updateRowParentCount()
|
||||
onVisualIndexChanged: updateRowParentCount()
|
||||
|
||||
TimeMarks {
|
||||
model: modelData
|
||||
id: backgroundMarks
|
||||
anchors.fill: renderer
|
||||
startTime: zoomControl.rangeStart
|
||||
endTime: zoomControl.rangeEnd
|
||||
|
||||
// Quite a mouthful, but works fine: Add up all the row counts up to the one
|
||||
// for this visual index and check if the result is even or odd.
|
||||
startOdd: (timelineView.rowCounts.slice(0, spacer.visualIndex).reduce(
|
||||
function(prev, rows) {return prev + rows}, 0) % 2) === 0
|
||||
onStartOddChanged: requestPaint()
|
||||
}
|
||||
|
||||
TimelineRenderer {
|
||||
id: renderer
|
||||
model: modelData
|
||||
notes: qmlProfilerModelProxy.notes
|
||||
zoomer: zoomControl
|
||||
selectionLocked: root.selectionLocked
|
||||
x: flick.contentX
|
||||
|
||||
// paint "under" the vertical scrollbar, so that it always matches with the
|
||||
// timemarks
|
||||
width: scroller.width
|
||||
property int yScrollStartDiff: flick.contentY - parent.y
|
||||
property int yScrollEndDiff: flick.height - parent.height + yScrollStartDiff
|
||||
y: Math.min(parent.height, Math.max(0, yScrollStartDiff))
|
||||
height: {
|
||||
if (yScrollStartDiff > 0) {
|
||||
return Math.max(0, Math.min(flick.height,
|
||||
parent.height - yScrollStartDiff));
|
||||
} else if (yScrollEndDiff < 0) {
|
||||
return Math.max(0, Math.min(flick.height,
|
||||
parent.height + yScrollEndDiff));
|
||||
} else {
|
||||
rangeDetails.hide();
|
||||
return parent.height;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: timelineView
|
||||
onClearChildren: renderer.clearData()
|
||||
onSelect: {
|
||||
if (modelIndex === index || modelIndex === -1) {
|
||||
renderer.selectedItem = eventIndex;
|
||||
if (eventIndex !== -1)
|
||||
renderer.recenter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
onSelectionLockedChanged: {
|
||||
renderer.selectionLocked = root.selectionLocked;
|
||||
}
|
||||
}
|
||||
|
||||
onSelectionLockedChanged: {
|
||||
root.selectionLocked = renderer.selectionLocked;
|
||||
}
|
||||
|
||||
function recenter() {
|
||||
if (modelData.endTime(selectedItem) < zoomer.rangeStart ||
|
||||
modelData.startTime(selectedItem) > zoomer.rangeEnd) {
|
||||
|
||||
var newStart = (modelData.startTime(selectedItem) +
|
||||
modelData.endTime(selectedItem) -
|
||||
zoomer.rangeDuration) / 2;
|
||||
zoomer.setRange(Math.max(newStart, zoomer.traceStart),
|
||||
Math.min(newStart + zoomer.rangeDuration,
|
||||
zoomer.traceEnd));
|
||||
}
|
||||
|
||||
if (spacer.y + spacer.height < flick.contentY)
|
||||
flick.contentY = spacer.y + spacer.height;
|
||||
else if (spacer.y - flick.height > flick.contentY)
|
||||
flick.contentY = spacer.y - flick.height;
|
||||
|
||||
var row = modelData.row(selectedItem);
|
||||
var rowStart = modelData.rowOffset(row);
|
||||
var rowEnd = rowStart + modelData.rowHeight(row);
|
||||
if (rowStart < y)
|
||||
flick.contentY -= y - rowStart;
|
||||
else if (rowEnd > y + height)
|
||||
flick.contentY += rowEnd - y - height;
|
||||
}
|
||||
|
||||
onSelectedItemChanged: {
|
||||
root.propagateSelection(index, selectedItem);
|
||||
}
|
||||
|
||||
onItemPressed: {
|
||||
var location = qmlProfilerModelProxy.location(modelIndex, pressedItem);
|
||||
if (pressedItem === -1) {
|
||||
// User clicked on empty space. Remove selection.
|
||||
root.propagateSelection(-1, -1);
|
||||
} else {
|
||||
var location = model.location(pressedItem);
|
||||
if (location.hasOwnProperty("file")) // not empty
|
||||
root.gotoSourceLocation(location.file, location.line, location.column);
|
||||
root.typeId = qmlProfilerModelProxy.typeId(modelIndex, pressedItem);
|
||||
root.typeId = model.typeId(pressedItem);
|
||||
root.updateCursorPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hack to pass mouse events to the other mousearea if enabled
|
||||
startDragArea: selectionRange.ready ? selectionRange.rangeLeft : -x
|
||||
endDragArea: selectionRange.ready ? selectionRange.rangeRight : -x - 1
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: timelineModel
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: selectionRangeControl
|
||||
@@ -441,6 +560,9 @@ Rectangle {
|
||||
|
||||
RangeDetails {
|
||||
id: rangeDetails
|
||||
property alias locked: root.selectionLocked
|
||||
models: qmlProfilerModelProxy.models
|
||||
notes: qmlProfilerModelProxy.notes
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -451,7 +573,7 @@ Rectangle {
|
||||
width: labels.width
|
||||
anchors.left: parent.left
|
||||
anchors.top: buttonsBar.bottom
|
||||
height: qmlProfilerModelProxy.modelCount() * buttonsBar.height
|
||||
height: qmlProfilerModelProxy.models.length * buttonsBar.height
|
||||
|
||||
Repeater {
|
||||
id: filterMenuInner
|
||||
@@ -461,10 +583,10 @@ Rectangle {
|
||||
anchors.right: filterMenu.right
|
||||
height: buttonsBar.height
|
||||
y: index * height
|
||||
text: qmlProfilerModelProxy.models[index].displayName
|
||||
enabled: !qmlProfilerModelProxy.models[index].empty
|
||||
checked: enabled && !qmlProfilerModelProxy.models[index].hidden
|
||||
onCheckedChanged: qmlProfilerModelProxy.models[index].hidden = !checked
|
||||
text: modelData.displayName
|
||||
enabled: !modelData.empty
|
||||
checked: enabled && !modelData.hidden
|
||||
onCheckedChanged: modelData.hidden = !checked
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,9 +636,11 @@ Rectangle {
|
||||
minWindowLength);
|
||||
|
||||
var fixedPoint = (zoomControl.rangeStart + zoomControl.rangeEnd) / 2;
|
||||
if (view.selectedItem !== -1) {
|
||||
if (root.selectedItem !== -1) {
|
||||
// center on selected item if it's inside the current screen
|
||||
var newFixedPoint = qmlProfilerModelProxy.startTime(view.selectedModel, view.selectedItem);
|
||||
var model = qmlProfilerModelProxy.models[root.selectedModel]
|
||||
var newFixedPoint = (model.startTime(root.selectedItem) +
|
||||
model.endTime(root.selectedItem)) / 2;
|
||||
if (newFixedPoint >= zoomControl.rangeStart &&
|
||||
newFixedPoint < zoomControl.rangeEnd)
|
||||
fixedPoint = newFixedPoint;
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
.pragma library
|
||||
|
||||
var qmlProfilerModelProxy = 0;
|
||||
var models = 0;
|
||||
var notes = 0;
|
||||
var zoomControl = 0;
|
||||
|
||||
//draw background of the graph
|
||||
@@ -43,28 +44,25 @@ function drawGraph(canvas, ctxt)
|
||||
//draw the actual data to be graphed
|
||||
function drawData(canvas, ctxt)
|
||||
{
|
||||
if (!zoomControl || !qmlProfilerModelProxy || qmlProfilerModelProxy.isEmpty())
|
||||
if (!zoomControl || !models)
|
||||
return;
|
||||
|
||||
for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
|
||||
for (var ii = canvas.offset; ii < qmlProfilerModelProxy.count(modelIndex);
|
||||
ii += canvas.increment) {
|
||||
for (var modelIndex = 0; modelIndex < models.length; ++modelIndex) {
|
||||
var model = models[modelIndex];
|
||||
for (var ii = canvas.offset; ii < model.count; ii += canvas.increment) {
|
||||
var xx = (model.startTime(ii) - zoomControl.traceStart) * canvas.spacing;
|
||||
|
||||
var xx = (qmlProfilerModelProxy.startTime(modelIndex,ii) - zoomControl.traceStart) *
|
||||
canvas.spacing;
|
||||
|
||||
var eventWidth = qmlProfilerModelProxy.duration(modelIndex,ii) * canvas.spacing;
|
||||
var eventWidth = model.duration(ii) * canvas.spacing;
|
||||
|
||||
if (eventWidth < 1)
|
||||
eventWidth = 1;
|
||||
|
||||
xx = Math.round(xx);
|
||||
|
||||
var itemHeight = qmlProfilerModelProxy.relativeHeight(modelIndex, ii) *
|
||||
canvas.blockHeight;
|
||||
var itemHeight = model.relativeHeight(ii) * canvas.blockHeight;
|
||||
var yy = (modelIndex + 1) * canvas.blockHeight - itemHeight ;
|
||||
|
||||
ctxt.fillStyle = qmlProfilerModelProxy.color(modelIndex, ii);
|
||||
ctxt.fillStyle = model.color(ii);
|
||||
ctxt.fillRect(xx, canvas.bump + yy, eventWidth, itemHeight);
|
||||
}
|
||||
}
|
||||
@@ -74,12 +72,11 @@ function drawBindingLoops(canvas, ctxt) {
|
||||
ctxt.strokeStyle = "orange";
|
||||
ctxt.lineWidth = 2;
|
||||
var radius = 1;
|
||||
for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
|
||||
for (var ii = canvas.offset - canvas.increment; ii < qmlProfilerModelProxy.count(modelIndex);
|
||||
ii += canvas.increment) {
|
||||
if (qmlProfilerModelProxy.bindingLoopDest(modelIndex,ii) >= 0) {
|
||||
var xcenter = Math.round(qmlProfilerModelProxy.startTime(modelIndex,ii) +
|
||||
qmlProfilerModelProxy.duration(modelIndex,ii) / 2 -
|
||||
for (var modelIndex = 0; modelIndex < models.length; ++modelIndex) {
|
||||
var model = models[modelIndex];
|
||||
for (var ii = canvas.offset - canvas.increment; ii < model.count; ii += canvas.increment) {
|
||||
if (model.bindingLoopDest(ii) >= 0) {
|
||||
var xcenter = Math.round(model.startTime(ii) + model.duration(ii) / 2 -
|
||||
zoomControl.traceStart) * canvas.spacing;
|
||||
var ycenter = Math.round(canvas.bump + canvas.blockHeight * modelIndex +
|
||||
canvas.blockHeight / 2);
|
||||
@@ -93,25 +90,30 @@ function drawBindingLoops(canvas, ctxt) {
|
||||
|
||||
function drawNotes(canvas, ctxt)
|
||||
{
|
||||
if (!zoomControl || !qmlProfilerModelProxy || qmlProfilerModelProxy.noteCount() === 0)
|
||||
if (!zoomControl || !models || !notes || notes.count === 0)
|
||||
return;
|
||||
|
||||
var spacing = canvas.width / zoomControl.traceDuration;
|
||||
var modelsById = models.reduce(function(prev, model) {
|
||||
prev[model.modelId] = model;
|
||||
return prev;
|
||||
}, {});
|
||||
|
||||
// divide canvas height in 7 parts: margin, 3*line, space, dot, margin
|
||||
var vertSpace = (canvas.height - canvas.bump) / 7;
|
||||
|
||||
ctxt.strokeStyle = "orange";
|
||||
ctxt.lineWidth = 2;
|
||||
for (var i = 0; i < qmlProfilerModelProxy.noteCount(); ++i) {
|
||||
var timelineModel = qmlProfilerModelProxy.noteTimelineModel(i);
|
||||
var timelineIndex = qmlProfilerModelProxy.noteTimelineIndex(i);
|
||||
for (var i = 0; i < notes.count; ++i) {
|
||||
var timelineModel = notes.timelineModel(i);
|
||||
var timelineIndex = notes.timelineIndex(i);
|
||||
if (timelineIndex === -1)
|
||||
continue;
|
||||
var start = Math.max(qmlProfilerModelProxy.startTime(timelineModel, timelineIndex),
|
||||
var start = Math.max(modelsById[timelineModel].startTime(timelineIndex),
|
||||
zoomControl.traceStart);
|
||||
var end = Math.min(qmlProfilerModelProxy.endTime(timelineModel, timelineIndex),
|
||||
zoomControl.traceStart);
|
||||
var annoX = Math.round(((start + end) / 2 - zoomControl.traceStart) * spacing);
|
||||
var end = Math.min(modelsById[timelineModel].endTime(timelineIndex),
|
||||
zoomControl.traceEnd);
|
||||
|
||||
var annoX = Math.round(((start + end) / 2 - zoomControl.traceStart) * canvas.spacing);
|
||||
|
||||
ctxt.moveTo(annoX, canvas.bump + vertSpace)
|
||||
ctxt.lineTo(annoX, canvas.bump + vertSpace * 4)
|
||||
|
||||
@@ -98,8 +98,9 @@ Canvas {
|
||||
onDataAvailable: {
|
||||
dataReady = true;
|
||||
increment = 0;
|
||||
for (var i = 0; i < qmlProfilerModelProxy.modelCount(); ++i)
|
||||
increment += qmlProfilerModelProxy.count(i);
|
||||
var models = qmlProfilerModelProxy.models;
|
||||
for (var i = 0; i < models.length; ++i)
|
||||
increment += models[i].count;
|
||||
increment = Math.ceil(increment / eventsPerPass);
|
||||
offset = -1;
|
||||
requestPaint();
|
||||
@@ -119,8 +120,9 @@ Canvas {
|
||||
onPaint: {
|
||||
var context = (canvas.context === null) ? getContext("2d") : canvas.context;
|
||||
|
||||
Plotter.qmlProfilerModelProxy = qmlProfilerModelProxy;
|
||||
Plotter.models = qmlProfilerModelProxy.models;
|
||||
Plotter.zoomControl = zoomControl;
|
||||
Plotter.notes = qmlProfilerModelProxy.notes;
|
||||
|
||||
if (offset < 0) {
|
||||
context.reset();
|
||||
@@ -143,6 +145,7 @@ Canvas {
|
||||
|
||||
Canvas {
|
||||
property alias bump: canvas.bump
|
||||
property alias spacing: canvas.spacing
|
||||
property bool doPaint: false
|
||||
onDoPaintChanged: {
|
||||
if (doPaint)
|
||||
|
||||
@@ -45,7 +45,10 @@ Item {
|
||||
property int selectedModel: -1
|
||||
property int selectedItem: -1
|
||||
|
||||
property bool locked: view.selectionLocked
|
||||
property bool locked
|
||||
|
||||
property var models
|
||||
property var notes
|
||||
|
||||
width: col.width + 25
|
||||
height: col.height + 30
|
||||
@@ -84,7 +87,8 @@ Item {
|
||||
|
||||
selectedModel = model;
|
||||
selectedItem = item;
|
||||
var eventData = qmlProfilerModelProxy.details(selectedModel, selectedItem)
|
||||
var timelineModel = models[selectedModel];
|
||||
var eventData = timelineModel.details(selectedItem)
|
||||
eventInfo.clear();
|
||||
for (var k in eventData) {
|
||||
if (k === "displayName") {
|
||||
@@ -96,7 +100,7 @@ Item {
|
||||
}
|
||||
rangeDetails.visible = true;
|
||||
|
||||
var location = qmlProfilerModelProxy.location(selectedModel, selectedItem)
|
||||
var location = timelineModel.location(selectedItem)
|
||||
if (location.hasOwnProperty("file")) { // not empty
|
||||
file = location.file;
|
||||
line = location.line;
|
||||
@@ -109,7 +113,8 @@ Item {
|
||||
}
|
||||
|
||||
noteEdit.focus = false;
|
||||
noteEdit.text = qmlProfilerModelProxy.noteText(selectedModel, selectedItem);
|
||||
var noteId = notes.get(timelineModel.modelId, selectedItem);
|
||||
noteEdit.text = (noteId !== -1) ? notes.text(noteId) : "";
|
||||
}
|
||||
|
||||
function fitInView() {
|
||||
@@ -225,7 +230,7 @@ Item {
|
||||
onFocusChanged: {
|
||||
if (!focus && selectedModel != -1 && selectedItem != -1) {
|
||||
saveTimer.stop();
|
||||
qmlProfilerModelProxy.setNoteText(selectedModel, selectedItem, text);
|
||||
notes.setText(models[selectedModel].modelId, selectedItem, text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,8 +238,7 @@ Item {
|
||||
id: saveTimer
|
||||
onTriggered: {
|
||||
if (selectedModel != -1 && selectedItem != -1)
|
||||
qmlProfilerModelProxy.setNoteText(selectedModel, selectedItem,
|
||||
noteEdit.text);
|
||||
notes.setText(models[selectedModel].modelId, selectedItem, noteEdit.text);
|
||||
}
|
||||
interval: 1000
|
||||
}
|
||||
@@ -250,7 +254,7 @@ Item {
|
||||
drag.maximumY: root.height - parent.height
|
||||
onClicked: {
|
||||
root.gotoSourceLocation(file, line, column);
|
||||
root.recenterOnItem(view.selectedModel, view.selectedItem);
|
||||
root.recenterOnItem();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,10 +298,7 @@ Item {
|
||||
renderType: Text.NativeRendering
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
rangeDetails.hide();
|
||||
view.selectFromEventIndex(view.selectedModel, -1);
|
||||
}
|
||||
onClicked: root.propagateSelection(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ RangeMover {
|
||||
target: zoomControl
|
||||
onRangeChanged: {
|
||||
var oldTimePerPixel = selectionRange.viewTimePerPixel;
|
||||
selectionRange.viewTimePerPixel = zoomControl.rangeDuration / view.intWidth;
|
||||
selectionRange.viewTimePerPixel = zoomControl.rangeDuration / flick.intWidth;
|
||||
if (creationState === 3 && oldTimePerPixel != selectionRange.viewTimePerPixel) {
|
||||
var newWidth = rangeWidth * oldTimePerPixel / viewTimePerPixel;
|
||||
rangeLeft = rangeLeft * oldTimePerPixel / viewTimePerPixel;
|
||||
|
||||
@@ -36,6 +36,9 @@ Canvas {
|
||||
objectName: "TimeMarks"
|
||||
contextType: "2d"
|
||||
|
||||
property QtObject model
|
||||
property bool startOdd
|
||||
|
||||
readonly property int scaleMinHeight: 60
|
||||
readonly property int scaleStepping: 30
|
||||
readonly property string units: " kMGT"
|
||||
@@ -45,11 +48,14 @@ Canvas {
|
||||
property real timePerPixel
|
||||
|
||||
Connections {
|
||||
target: labels
|
||||
target: model
|
||||
onHeightChanged: requestPaint()
|
||||
}
|
||||
|
||||
onStartTimeChanged: requestPaint()
|
||||
onEndTimeChanged: requestPaint()
|
||||
onYChanged: requestPaint()
|
||||
onHeightChanged: requestPaint()
|
||||
|
||||
onPaint: {
|
||||
var context = (timeMarks.context === null) ? getContext("2d") : timeMarks.context;
|
||||
@@ -70,36 +76,26 @@ Canvas {
|
||||
|
||||
timePerPixel = timePerBlock/pixelsPerBlock;
|
||||
|
||||
var lineStart = y < 0 ? -y : 0;
|
||||
var lineEnd = Math.min(height, labels.height - y);
|
||||
|
||||
for (var ii = 0; ii < blockCount+1; ii++) {
|
||||
var x = Math.floor(ii*pixelsPerBlock - realStartPos);
|
||||
context.strokeStyle = "#B0B0B0";
|
||||
context.beginPath();
|
||||
context.moveTo(x, lineStart);
|
||||
context.lineTo(x, lineEnd);
|
||||
context.moveTo(x, 0);
|
||||
context.lineTo(x, height);
|
||||
context.stroke();
|
||||
|
||||
context.strokeStyle = "#CCCCCC";
|
||||
for (var jj=1; jj < 5; jj++) {
|
||||
var xx = Math.floor(ii*pixelsPerBlock + jj*pixelsPerSection - realStartPos);
|
||||
context.beginPath();
|
||||
context.moveTo(xx, lineStart);
|
||||
context.lineTo(xx, lineEnd);
|
||||
context.moveTo(xx, 0);
|
||||
context.lineTo(xx, height);
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateMarks(start, end) {
|
||||
if (startTime !== start || endTime !== end) {
|
||||
startTime = start;
|
||||
endTime = end;
|
||||
requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
function prettyPrintScale(amount) {
|
||||
var unitOffset = 0;
|
||||
for (unitOffset = 0; amount > (1 << ((unitOffset + 1) * 10)); ++unitOffset) {}
|
||||
@@ -117,26 +113,16 @@ Canvas {
|
||||
}
|
||||
|
||||
function drawBackgroundBars( context, region ) {
|
||||
var colorIndex = true;
|
||||
var colorIndex = startOdd;
|
||||
|
||||
context.font = "8px sans-serif";
|
||||
|
||||
// separators
|
||||
var cumulatedHeight = y < 0 ? -y : 0;
|
||||
for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
|
||||
var modelHeight = qmlProfilerModelProxy.models[modelIndex].height;
|
||||
if (modelHeight === 0)
|
||||
continue;
|
||||
if (cumulatedHeight + modelHeight < y) {
|
||||
cumulatedHeight += modelHeight;
|
||||
if (qmlProfilerModelProxy.rowCount(modelIndex) % 2 !== 0)
|
||||
colorIndex = !colorIndex;
|
||||
continue;
|
||||
}
|
||||
var cumulatedHeight = 0;
|
||||
|
||||
for (var row = 0; row < qmlProfilerModelProxy.rowCount(modelIndex); ++row) {
|
||||
for (var row = 0; row < model.rowCount; ++row) {
|
||||
// row background
|
||||
var rowHeight = qmlProfilerModelProxy.rowHeight(modelIndex, row)
|
||||
var rowHeight = model.rowHeight(row)
|
||||
cumulatedHeight += rowHeight;
|
||||
colorIndex = !colorIndex;
|
||||
if (cumulatedHeight < y - rowHeight)
|
||||
@@ -145,8 +131,8 @@ Canvas {
|
||||
context.fillRect(0, cumulatedHeight - rowHeight - y, width, rowHeight);
|
||||
|
||||
if (rowHeight >= scaleMinHeight) {
|
||||
var minVal = qmlProfilerModelProxy.rowMinValue(modelIndex, row);
|
||||
var maxVal = qmlProfilerModelProxy.rowMaxValue(modelIndex, row);
|
||||
var minVal = model.rowMinValue(row);
|
||||
var maxVal = model.rowMaxValue(row);
|
||||
if (minVal !== maxVal) {
|
||||
context.strokeStyle = context.fillStyle = "#B0B0B0";
|
||||
|
||||
@@ -188,11 +174,4 @@ Canvas {
|
||||
context.lineTo(width, cumulatedHeight - y);
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
// bottom
|
||||
if (height > labels.height - y) {
|
||||
context.fillStyle = "#f5f5f5";
|
||||
context.fillRect(0, labels.height - y, width, height - labels.height + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerT
|
||||
groupLayout->setContentsMargins(0, 0, 0, 0);
|
||||
groupLayout->setSpacing(0);
|
||||
|
||||
qmlRegisterType<TimelineZoomControl>();
|
||||
qmlRegisterType<QmlProfilerTimelineModel>();
|
||||
qmlRegisterType<QmlProfilerNotesModel>();
|
||||
|
||||
d->m_mainView = new QmlProfilerQuickView(this);
|
||||
@@ -195,7 +197,8 @@ void QmlProfilerTraceView::selectBySourceLocation(const QString &filename, int l
|
||||
return;
|
||||
|
||||
for (int modelIndex = 0; modelIndex < d->m_modelProxy->modelCount(); ++modelIndex) {
|
||||
int typeId = d->m_modelProxy->selectionIdForLocation(modelIndex, filename, line, column);
|
||||
int typeId = d->m_modelProxy->model(modelIndex)->selectionIdForLocation(filename, line,
|
||||
column);
|
||||
if (typeId != -1) {
|
||||
QMetaObject::invokeMethod(rootObject, "selectBySelectionId",
|
||||
Q_ARG(QVariant,QVariant(modelIndex)),
|
||||
@@ -264,7 +267,7 @@ void QmlProfilerTraceView::showContextMenu(QPoint position)
|
||||
if (d->m_viewContainer->hasGlobalStats())
|
||||
getGlobalStatsAction->setEnabled(false);
|
||||
|
||||
if (!d->m_modelProxy->isEmpty()) {
|
||||
if (d->m_zoomControl->traceDuration() > 0) {
|
||||
menu.addSeparator();
|
||||
viewAllAction = menu.addAction(tr("Reset Zoom"));
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class QMLPROFILER_EXPORT TimelineModel : public QObject
|
||||
Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged)
|
||||
Q_PROPERTY(QVariantList labels READ labels NOTIFY labelsChanged)
|
||||
Q_PROPERTY(int count READ count NOTIFY emptyChanged)
|
||||
Q_PROPERTY(int defaultRowHeight READ defaultRowHeight CONSTANT)
|
||||
|
||||
public:
|
||||
class TimelineModelPrivate;
|
||||
|
||||
@@ -41,45 +41,47 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
using namespace QmlProfiler;
|
||||
using namespace QmlProfiler::Internal;
|
||||
|
||||
TimelineRenderer::TimelineRenderer(QQuickPaintedItem *parent) :
|
||||
QQuickPaintedItem(parent), m_spacing(0), m_spacedDuration(0), m_profilerModelProxy(0),
|
||||
m_zoomer(0), m_selectedItem(-1), m_selectedModel(-1), m_selectionLocked(true),
|
||||
m_startDragArea(-1), m_endDragArea(-1)
|
||||
QQuickPaintedItem(parent), m_spacing(0), m_spacedDuration(0),
|
||||
m_model(0), m_zoomer(0), m_notes(0), m_selectedItem(-1), m_selectionLocked(true)
|
||||
{
|
||||
resetCurrentSelection();
|
||||
setAcceptedMouseButtons(Qt::LeftButton);
|
||||
setAcceptHoverEvents(true);
|
||||
|
||||
connect(this, &QQuickItem::yChanged, this, &TimelineRenderer::requestPaint);
|
||||
connect(this, &QQuickItem::xChanged, this, &TimelineRenderer::requestPaint);
|
||||
connect(this, &QQuickItem::widthChanged, this, &TimelineRenderer::requestPaint);
|
||||
connect(this, &QQuickItem::heightChanged, this, &TimelineRenderer::requestPaint);
|
||||
}
|
||||
|
||||
void TimelineRenderer::setProfilerModelProxy(QObject *profilerModelProxy)
|
||||
void TimelineRenderer::setModel(QmlProfilerTimelineModel *model)
|
||||
{
|
||||
if (m_profilerModelProxy) {
|
||||
disconnect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
|
||||
disconnect(m_profilerModelProxy, SIGNAL(hiddenChanged()), this, SLOT(requestPaint()));
|
||||
disconnect(m_profilerModelProxy, SIGNAL(rowHeightChanged()), this, SLOT(requestPaint()));
|
||||
disconnect(m_profilerModelProxy, SIGNAL(modelsChanged(int,int)),
|
||||
this, SLOT(swapSelections(int,int)));
|
||||
disconnect(m_profilerModelProxy, SIGNAL(notesChanged()), this, SLOT(requestPaint()));
|
||||
}
|
||||
m_profilerModelProxy = qobject_cast<TimelineModelAggregator *>(profilerModelProxy);
|
||||
if (m_model == model)
|
||||
return;
|
||||
|
||||
if (m_profilerModelProxy) {
|
||||
connect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
|
||||
connect(m_profilerModelProxy, SIGNAL(hiddenChanged()), this, SLOT(requestPaint()));
|
||||
connect(m_profilerModelProxy, SIGNAL(rowHeightChanged()), this, SLOT(requestPaint()));
|
||||
connect(m_profilerModelProxy, SIGNAL(modelsChanged(int,int)),
|
||||
this, SLOT(swapSelections(int,int)));
|
||||
connect(m_profilerModelProxy, SIGNAL(notesChanged(int,int,int)),
|
||||
this, SLOT(requestPaint()));
|
||||
}
|
||||
emit profilerModelProxyChanged(m_profilerModelProxy);
|
||||
if (m_model) {
|
||||
disconnect(m_model, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
|
||||
disconnect(m_model, SIGNAL(hiddenChanged()), this, SLOT(requestPaint()));
|
||||
disconnect(m_model, SIGNAL(rowHeightChanged()), this, SLOT(requestPaint()));
|
||||
}
|
||||
|
||||
void TimelineRenderer::setZoomer(QObject *zoomControl)
|
||||
m_model = model;
|
||||
if (m_model) {
|
||||
connect(m_model, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
|
||||
connect(m_model, SIGNAL(hiddenChanged()), this, SLOT(requestPaint()));
|
||||
connect(m_model, SIGNAL(rowHeightChanged()), this, SLOT(requestPaint()));
|
||||
}
|
||||
|
||||
emit modelChanged(m_model);
|
||||
update();
|
||||
}
|
||||
|
||||
void TimelineRenderer::setZoomer(TimelineZoomControl *zoomer)
|
||||
{
|
||||
TimelineZoomControl *zoomer = qobject_cast<TimelineZoomControl *>(zoomControl);
|
||||
if (zoomer != m_zoomer) {
|
||||
if (m_zoomer != 0)
|
||||
disconnect(m_zoomer, SIGNAL(rangeChanged(qint64,qint64)), this, SLOT(requestPaint()));
|
||||
@@ -91,17 +93,20 @@ void TimelineRenderer::setZoomer(QObject *zoomControl)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRenderer::componentComplete()
|
||||
void TimelineRenderer::setNotes(QmlProfilerNotesModel *notes)
|
||||
{
|
||||
const QMetaObject *metaObject = this->metaObject();
|
||||
int propertyCount = metaObject->propertyCount();
|
||||
int requestPaintMethod = metaObject->indexOfMethod("requestPaint()");
|
||||
for (int ii = TimelineRenderer::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
|
||||
QMetaProperty p = metaObject->property(ii);
|
||||
if (p.hasNotifySignal())
|
||||
QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0);
|
||||
}
|
||||
QQuickItem::componentComplete();
|
||||
if (m_notes == notes)
|
||||
return;
|
||||
|
||||
if (m_notes)
|
||||
disconnect(m_notes, &QmlProfilerNotesModel::changed, this, &TimelineRenderer::requestPaint);
|
||||
|
||||
m_notes = notes;
|
||||
if (m_notes)
|
||||
connect(m_notes, &QmlProfilerNotesModel::changed, this, &TimelineRenderer::requestPaint);
|
||||
|
||||
emit notesChanged(m_notes);
|
||||
update();
|
||||
}
|
||||
|
||||
void TimelineRenderer::requestPaint()
|
||||
@@ -109,36 +114,22 @@ void TimelineRenderer::requestPaint()
|
||||
update();
|
||||
}
|
||||
|
||||
void TimelineRenderer::swapSelections(int modelIndex1, int modelIndex2)
|
||||
inline void TimelineRenderer::getItemXExtent(int i, int ¤tX, int &itemWidth)
|
||||
{
|
||||
// Any hovered event is most likely useless now. Reset it.
|
||||
resetCurrentSelection();
|
||||
|
||||
// Explicitly selected events can be tracked in a useful way.
|
||||
if (m_selectedModel == modelIndex1)
|
||||
setSelectedModel(modelIndex2);
|
||||
else if (m_selectedModel == modelIndex2)
|
||||
setSelectedModel(modelIndex1);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
inline void TimelineRenderer::getItemXExtent(int modelIndex, int i, int ¤tX, int &itemWidth)
|
||||
{
|
||||
qint64 start = m_profilerModelProxy->startTime(modelIndex, i) - m_zoomer->rangeStart();
|
||||
qint64 start = m_model->startTime(i) - m_zoomer->rangeStart();
|
||||
|
||||
// avoid integer overflows by using floating point for width calculations. m_spacing is qreal,
|
||||
// too, so for some intermediate calculations we have to use floats anyway.
|
||||
qreal rawWidth;
|
||||
if (start > 0) {
|
||||
currentX = static_cast<int>(start * m_spacing);
|
||||
rawWidth = m_profilerModelProxy->duration(modelIndex, i) * m_spacing;
|
||||
rawWidth = m_model->duration(i) * m_spacing;
|
||||
} else {
|
||||
currentX = -OutOfScreenMargin;
|
||||
// Explicitly round the "start" part down, away from 0, to match the implicit rounding of
|
||||
// currentX in the > 0 case. If we don't do that we get glitches where a pixel is added if
|
||||
// the element starts outside the screen and subtracted if it starts inside the screen.
|
||||
rawWidth = m_profilerModelProxy->duration(modelIndex, i) * m_spacing +
|
||||
rawWidth = m_model->duration(i) * m_spacing +
|
||||
floor(start * m_spacing) + OutOfScreenMargin;
|
||||
}
|
||||
if (rawWidth < MinimumItemWidth) {
|
||||
@@ -157,12 +148,11 @@ void TimelineRenderer::resetCurrentSelection()
|
||||
m_currentSelection.endTime = -1;
|
||||
m_currentSelection.row = -1;
|
||||
m_currentSelection.eventIndex = -1;
|
||||
m_currentSelection.modelIndex = -1;
|
||||
}
|
||||
|
||||
void TimelineRenderer::paint(QPainter *p)
|
||||
{
|
||||
if (m_zoomer->rangeDuration() <= 0)
|
||||
if (height() <= 0 || m_zoomer->rangeDuration() <= 0)
|
||||
return;
|
||||
|
||||
m_spacing = width() / m_zoomer->rangeDuration();
|
||||
@@ -170,56 +160,52 @@ void TimelineRenderer::paint(QPainter *p)
|
||||
|
||||
p->setPen(Qt::transparent);
|
||||
|
||||
for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
|
||||
if (m_profilerModelProxy->hidden(modelIndex))
|
||||
continue;
|
||||
int lastIndex = m_profilerModelProxy->lastIndex(modelIndex, m_zoomer->rangeEnd());
|
||||
if (lastIndex >= 0 && lastIndex < m_profilerModelProxy->count(modelIndex)) {
|
||||
int firstIndex = m_profilerModelProxy->firstIndex(modelIndex, m_zoomer->rangeStart());
|
||||
int lastIndex = m_model->lastIndex(m_zoomer->rangeEnd());
|
||||
if (lastIndex >= 0 && lastIndex < m_model->count()) {
|
||||
int firstIndex = m_model->firstIndex(m_zoomer->rangeStart());
|
||||
if (firstIndex >= 0) {
|
||||
drawItemsToPainter(p, modelIndex, firstIndex, lastIndex);
|
||||
if (m_selectedModel == modelIndex)
|
||||
drawSelectionBoxes(p, modelIndex, firstIndex, lastIndex);
|
||||
drawBindingLoopMarkers(p, modelIndex, firstIndex, lastIndex);
|
||||
}
|
||||
drawItemsToPainter(p, firstIndex, lastIndex);
|
||||
drawSelectionBoxes(p, firstIndex, lastIndex);
|
||||
drawBindingLoopMarkers(p, firstIndex, lastIndex);
|
||||
}
|
||||
}
|
||||
drawNotes(p);
|
||||
}
|
||||
|
||||
void TimelineRenderer::drawItemsToPainter(QPainter *p, int modelIndex, int fromIndex, int toIndex)
|
||||
void TimelineRenderer::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
}
|
||||
|
||||
void TimelineRenderer::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
|
||||
{
|
||||
p->save();
|
||||
p->setPen(Qt::transparent);
|
||||
int modelRowStart = 0;
|
||||
for (int mi = 0; mi < modelIndex; mi++)
|
||||
modelRowStart += m_profilerModelProxy->model(mi)->height();
|
||||
|
||||
for (int i = fromIndex; i <= toIndex; i++) {
|
||||
int currentX, currentY, itemWidth, itemHeight;
|
||||
|
||||
int rowNumber = m_profilerModelProxy->row(modelIndex, i);
|
||||
currentY = modelRowStart + m_profilerModelProxy->rowOffset(modelIndex, rowNumber) - y();
|
||||
int rowNumber = m_model->row(i);
|
||||
currentY = m_model->rowOffset(rowNumber) - y();
|
||||
if (currentY >= height())
|
||||
continue;
|
||||
|
||||
itemHeight = m_profilerModelProxy->rowHeight(modelIndex, rowNumber) *
|
||||
m_profilerModelProxy->relativeHeight(modelIndex, i);
|
||||
int rowHeight = m_model->rowHeight(rowNumber);
|
||||
itemHeight = rowHeight * m_model->relativeHeight(i);
|
||||
|
||||
currentY += m_profilerModelProxy->rowHeight(modelIndex, rowNumber) - itemHeight;
|
||||
currentY += rowHeight - itemHeight;
|
||||
if (currentY + itemHeight < 0)
|
||||
continue;
|
||||
|
||||
getItemXExtent(modelIndex, i, currentX, itemWidth);
|
||||
getItemXExtent(i, currentX, itemWidth);
|
||||
|
||||
// normal events
|
||||
p->setBrush(m_profilerModelProxy->color(modelIndex, i));
|
||||
p->setBrush(m_model->color(i));
|
||||
p->drawRect(currentX, currentY, itemWidth, itemHeight);
|
||||
}
|
||||
p->restore();
|
||||
}
|
||||
|
||||
void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex)
|
||||
void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
|
||||
{
|
||||
const uint strongLineWidth = 3;
|
||||
const uint lightLineWidth = 2;
|
||||
@@ -231,12 +217,7 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromI
|
||||
if (m_selectedItem == -1)
|
||||
return;
|
||||
|
||||
|
||||
int id = m_profilerModelProxy->selectionId(modelIndex, m_selectedItem);
|
||||
|
||||
int modelRowStart = 0;
|
||||
for (int mi = 0; mi < modelIndex; mi++)
|
||||
modelRowStart += m_profilerModelProxy->model(mi)->height();
|
||||
int id = m_model->selectionId(m_selectedItem);
|
||||
|
||||
p->save();
|
||||
|
||||
@@ -249,19 +230,18 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromI
|
||||
|
||||
int currentX, currentY, itemWidth;
|
||||
for (int i = fromIndex; i <= toIndex; i++) {
|
||||
if (m_profilerModelProxy->selectionId(modelIndex, i) != id)
|
||||
if (m_model->selectionId(i) != id)
|
||||
continue;
|
||||
|
||||
int row = m_profilerModelProxy->row(modelIndex, i);
|
||||
int rowHeight = m_profilerModelProxy->rowHeight(modelIndex, row);
|
||||
int itemHeight = rowHeight * m_profilerModelProxy->relativeHeight(modelIndex, i);
|
||||
int row = m_model->row(i);
|
||||
int rowHeight = m_model->rowHeight(row);
|
||||
int itemHeight = rowHeight * m_model->relativeHeight(i);
|
||||
|
||||
currentY = modelRowStart + m_profilerModelProxy->rowOffset(modelIndex, row) + rowHeight -
|
||||
itemHeight - y();
|
||||
currentY = m_model->rowOffset(row) + rowHeight - itemHeight - y();
|
||||
if (currentY + itemHeight < 0 || height() < currentY)
|
||||
continue;
|
||||
|
||||
getItemXExtent(modelIndex, i, currentX, itemWidth);
|
||||
getItemXExtent(i, currentX, itemWidth);
|
||||
|
||||
if (i == m_selectedItem)
|
||||
p->setPen(strongPen);
|
||||
@@ -297,7 +277,7 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromI
|
||||
p->restore();
|
||||
}
|
||||
|
||||
void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex)
|
||||
void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
|
||||
{
|
||||
int destindex;
|
||||
int xfrom, xto, width;
|
||||
@@ -310,19 +290,17 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int f
|
||||
|
||||
p->save();
|
||||
for (int i = fromIndex; i <= toIndex; i++) {
|
||||
destindex = m_profilerModelProxy->bindingLoopDest(modelIndex, i);
|
||||
destindex = m_model->bindingLoopDest(i);
|
||||
if (destindex >= 0) {
|
||||
// to
|
||||
getItemXExtent(modelIndex, destindex, xto, width);
|
||||
getItemXExtent(destindex, xto, width);
|
||||
xto += width / 2;
|
||||
yto = getYPosition(modelIndex, destindex) + m_profilerModelProxy->rowHeight(modelIndex,
|
||||
m_profilerModelProxy->row(modelIndex, destindex)) / 2 - y();
|
||||
yto = getYPosition(destindex) + m_model->rowHeight(m_model->row(destindex)) / 2 - y();
|
||||
|
||||
// from
|
||||
getItemXExtent(modelIndex, i, xfrom, width);
|
||||
getItemXExtent(i, xfrom, width);
|
||||
xfrom += width / 2;
|
||||
yfrom = getYPosition(modelIndex, i) + m_profilerModelProxy->rowHeight(modelIndex,
|
||||
m_profilerModelProxy->row(modelIndex, i)) / 2 - y();
|
||||
yfrom = getYPosition(i) + m_model->rowHeight(m_model->row(i)) / 2 - y();
|
||||
|
||||
// radius (derived from width of origin event)
|
||||
radius = 5;
|
||||
@@ -365,25 +343,19 @@ void TimelineRenderer::drawNotes(QPainter *p)
|
||||
static const int annotationSpace = 4;
|
||||
static const int shadowOffset = 2;
|
||||
|
||||
QmlProfilerNotesModel *notes = m_profilerModelProxy->notes();
|
||||
for (int i = 0; i < notes->count(); ++i) {
|
||||
int managerIndex = notes->timelineModel(i);
|
||||
if (managerIndex == -1)
|
||||
for (int i = 0; i < m_notes->count(); ++i) {
|
||||
int modelId = m_notes->timelineModel(i);
|
||||
if (modelId == -1 || modelId != m_model->modelId())
|
||||
continue;
|
||||
int modelIndex = m_profilerModelProxy->modelIndexFromManagerIndex(managerIndex);
|
||||
if (m_profilerModelProxy->hidden(modelIndex))
|
||||
continue;
|
||||
int eventIndex = notes->timelineIndex(i);
|
||||
int row = m_profilerModelProxy->row(modelIndex, eventIndex);
|
||||
int rowHeight = m_profilerModelProxy->rowHeight(modelIndex, row);
|
||||
int currentY = m_profilerModelProxy->rowOffset(modelIndex, row) - y();
|
||||
for (int mi = 0; mi < modelIndex; mi++)
|
||||
currentY += m_profilerModelProxy->model(mi)->height();
|
||||
int eventIndex = m_notes->timelineIndex(i);
|
||||
int row = m_model->row(eventIndex);
|
||||
int rowHeight = m_model->rowHeight(row);
|
||||
int currentY = m_model->rowOffset(row) - y();
|
||||
if (currentY + rowHeight < 0 || height() < currentY)
|
||||
continue;
|
||||
int currentX;
|
||||
int itemWidth;
|
||||
getItemXExtent(modelIndex, eventIndex, currentX, itemWidth);
|
||||
getItemXExtent(eventIndex, currentX, itemWidth);
|
||||
|
||||
// shadow
|
||||
int annoX = currentX + (itemWidth - annotationWidth) / 2;
|
||||
@@ -406,47 +378,20 @@ void TimelineRenderer::drawNotes(QPainter *p)
|
||||
int TimelineRenderer::rowFromPosition(int y)
|
||||
{
|
||||
int ret = 0;
|
||||
for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
|
||||
int modelHeight = m_profilerModelProxy->model(modelIndex)->height();
|
||||
if (y < modelHeight) {
|
||||
for (int row = 0; row < m_profilerModelProxy->rowCount(modelIndex); ++row) {
|
||||
y -= m_profilerModelProxy->rowHeight(modelIndex, row);
|
||||
|
||||
for (int row = 0; row < m_model->rowCount(); ++row) {
|
||||
y -= m_model->rowHeight(row);
|
||||
if (y < 0) return ret;
|
||||
++ret;
|
||||
}
|
||||
} else {
|
||||
y -= modelHeight;
|
||||
ret += m_profilerModelProxy->rowCount(modelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TimelineRenderer::modelFromPosition(int y)
|
||||
{
|
||||
for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
|
||||
y -= m_profilerModelProxy->model(modelIndex)->height();
|
||||
if (y < 0)
|
||||
return modelIndex;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TimelineRenderer::mousePressEvent(QMouseEvent *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 TimelineRenderer::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
if (!m_profilerModelProxy->isEmpty())
|
||||
if (!m_model->isEmpty())
|
||||
manageClicked();
|
||||
}
|
||||
|
||||
@@ -467,18 +412,19 @@ void TimelineRenderer::hoverMoveEvent(QHoverEvent *event)
|
||||
void TimelineRenderer::manageClicked()
|
||||
{
|
||||
if (m_currentSelection.eventIndex != -1) {
|
||||
if (m_currentSelection.eventIndex == m_selectedItem && m_currentSelection.modelIndex == m_selectedModel)
|
||||
if (m_currentSelection.eventIndex == m_selectedItem)
|
||||
setSelectionLocked(!m_selectionLocked);
|
||||
else
|
||||
setSelectionLocked(true);
|
||||
|
||||
// itemPressed() will trigger an update of the events and JavaScript views. Make sure the
|
||||
// correct event is already selected when that happens, to prevent confusion.
|
||||
selectFromEventIndex(m_currentSelection.modelIndex, m_currentSelection.eventIndex);
|
||||
emit itemPressed(m_currentSelection.modelIndex, m_currentSelection.eventIndex);
|
||||
setSelectedItem(m_currentSelection.eventIndex);
|
||||
emit itemPressed(m_currentSelection.eventIndex);
|
||||
} else {
|
||||
setSelectionLocked(false);
|
||||
selectFromEventIndex(m_currentSelection.modelIndex, m_currentSelection.eventIndex);
|
||||
setSelectedItem(-1);
|
||||
emit itemPressed(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,49 +438,41 @@ void TimelineRenderer::manageHovered(int mouseX, int mouseY)
|
||||
qint64 startTime = (mouseX - 1) * duration / width() + m_zoomer->rangeStart();
|
||||
qint64 endTime = (mouseX + 1) * duration / width() + m_zoomer->rangeStart();
|
||||
int row = rowFromPosition(mouseY + y());
|
||||
int modelIndex = modelFromPosition(mouseY + y());
|
||||
|
||||
// already covered? nothing to do
|
||||
// already covered? Only recheck selectionLocked and make sure m_selectedItem is correct.
|
||||
if (m_currentSelection.eventIndex != -1 &&
|
||||
endTime >= m_currentSelection.startTime &&
|
||||
startTime <= m_currentSelection.endTime &&
|
||||
row == m_currentSelection.row) {
|
||||
if (!m_selectionLocked)
|
||||
setSelectedItem(m_currentSelection.eventIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// find if there's items in the time range
|
||||
int eventFrom = m_profilerModelProxy->firstIndex(modelIndex, startTime);
|
||||
int eventTo = m_profilerModelProxy->lastIndex(modelIndex, endTime);
|
||||
if (eventFrom == -1 ||
|
||||
eventTo < eventFrom || eventTo >= m_profilerModelProxy->count(modelIndex)) {
|
||||
int eventFrom = m_model->firstIndex(startTime);
|
||||
int eventTo = m_model->lastIndex(endTime);
|
||||
if (eventFrom == -1 || eventTo < eventFrom || eventTo >= m_model->count()) {
|
||||
m_currentSelection.eventIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
int modelRowStart = 0;
|
||||
for (int mi = 0; mi < modelIndex; mi++)
|
||||
modelRowStart += m_profilerModelProxy->rowCount(mi);
|
||||
|
||||
// find if we are in the right column
|
||||
int itemRow;
|
||||
for (int i=eventTo; i>=eventFrom; --i) {
|
||||
itemRow = modelRowStart + m_profilerModelProxy->row(modelIndex, i);
|
||||
|
||||
if (itemRow == row) {
|
||||
if ( m_model->row(i) == row) {
|
||||
// There can be small events that don't reach the cursor position after large events
|
||||
// that do but are in a different row.
|
||||
qint64 itemEnd = m_profilerModelProxy->endTime(modelIndex, i);
|
||||
qint64 itemEnd = m_model->endTime(i);
|
||||
if (itemEnd < startTime)
|
||||
continue;
|
||||
|
||||
// match
|
||||
m_currentSelection.eventIndex = i;
|
||||
m_currentSelection.startTime = m_profilerModelProxy->startTime(modelIndex, i);
|
||||
m_currentSelection.startTime = m_model->startTime(i);
|
||||
m_currentSelection.endTime = itemEnd;
|
||||
m_currentSelection.row = row;
|
||||
m_currentSelection.modelIndex = modelIndex;
|
||||
if (!m_selectionLocked)
|
||||
selectFromEventIndex(modelIndex, i);
|
||||
setSelectedItem(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -549,43 +487,26 @@ void TimelineRenderer::clearData()
|
||||
m_spacedDuration = 0;
|
||||
resetCurrentSelection();
|
||||
setSelectedItem(-1);
|
||||
setSelectedModel(-1);
|
||||
setSelectionLocked(true);
|
||||
setStartDragArea(-1);
|
||||
setEndDragArea(-1);
|
||||
}
|
||||
|
||||
int TimelineRenderer::getYPosition(int modelIndex, int index) const
|
||||
int TimelineRenderer::getYPosition(int index) const
|
||||
{
|
||||
Q_ASSERT(m_profilerModelProxy);
|
||||
if (index >= m_profilerModelProxy->count(modelIndex))
|
||||
Q_ASSERT(m_model);
|
||||
if (index >= m_model->count())
|
||||
return 0;
|
||||
|
||||
int modelRowStart = 0;
|
||||
for (int mi = 0; mi < modelIndex; mi++)
|
||||
modelRowStart += m_profilerModelProxy->model(mi)->height();
|
||||
|
||||
return modelRowStart + m_profilerModelProxy->rowOffset(modelIndex,
|
||||
m_profilerModelProxy->row(modelIndex, index));
|
||||
return m_model->rowOffset(m_model->row(index));
|
||||
}
|
||||
|
||||
void TimelineRenderer::selectFromEventIndex(int modelIndex, int eventIndex)
|
||||
void TimelineRenderer::selectNextFromSelectionId(int selectionId)
|
||||
{
|
||||
if (modelIndex != m_selectedModel || eventIndex != m_selectedItem) {
|
||||
setSelectedModel(modelIndex);
|
||||
setSelectedItem(eventIndex);
|
||||
emit selectionChanged(modelIndex, eventIndex);
|
||||
}
|
||||
setSelectedItem(m_model->nextItemBySelectionId(selectionId, m_zoomer->rangeStart(),
|
||||
m_selectedItem));
|
||||
}
|
||||
|
||||
void TimelineRenderer::selectNextFromSelectionId(int modelIndex, int selectionId)
|
||||
void TimelineRenderer::selectPrevFromSelectionId(int selectionId)
|
||||
{
|
||||
selectFromEventIndex(modelIndex, m_profilerModelProxy->model(modelIndex)->nextItemBySelectionId(
|
||||
selectionId, m_zoomer->rangeStart(), m_selectedItem));
|
||||
}
|
||||
|
||||
void TimelineRenderer::selectPrevFromSelectionId(int modelIndex, int selectionId)
|
||||
{
|
||||
selectFromEventIndex(modelIndex, m_profilerModelProxy->model(modelIndex)->prevItemBySelectionId(
|
||||
selectionId, m_zoomer->rangeStart(), m_selectedItem));
|
||||
setSelectedItem(m_model->prevItemBySelectionId(selectionId, m_zoomer->rangeStart(),
|
||||
m_selectedItem));
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
#include <QQuickPaintedItem>
|
||||
#include <QJSValue>
|
||||
#include "timelinezoomcontrol.h"
|
||||
#include "timelinemodelaggregator.h"
|
||||
#include "timelinemodel.h"
|
||||
#include "qmlprofilernotesmodel.h"
|
||||
|
||||
namespace QmlProfiler {
|
||||
namespace Internal {
|
||||
@@ -42,13 +43,11 @@ namespace Internal {
|
||||
class TimelineRenderer : public QQuickPaintedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QObject *profilerModelProxy READ profilerModelProxy WRITE setProfilerModelProxy NOTIFY profilerModelProxyChanged)
|
||||
Q_PROPERTY(QObject *zoomer READ zoomer WRITE setZoomer NOTIFY zoomerChanged)
|
||||
Q_PROPERTY(QmlProfiler::QmlProfilerTimelineModel *model READ model WRITE setModel NOTIFY modelChanged)
|
||||
Q_PROPERTY(QmlProfiler::TimelineZoomControl *zoomer READ zoomer WRITE setZoomer NOTIFY zoomerChanged)
|
||||
Q_PROPERTY(QmlProfiler::QmlProfilerNotesModel *notes READ notes WRITE setNotes NOTIFY notesChanged)
|
||||
Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged)
|
||||
Q_PROPERTY(int selectedItem READ selectedItem NOTIFY selectedItemChanged)
|
||||
Q_PROPERTY(int selectedModel READ selectedModel NOTIFY selectedModelChanged)
|
||||
Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged)
|
||||
Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged)
|
||||
Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged)
|
||||
|
||||
public:
|
||||
explicit TimelineRenderer(QQuickPaintedItem *parent = 0);
|
||||
@@ -63,93 +62,32 @@ public:
|
||||
return m_selectedItem;
|
||||
}
|
||||
|
||||
int selectedModel() const
|
||||
{
|
||||
return m_selectedModel;
|
||||
}
|
||||
|
||||
int startDragArea() const
|
||||
{
|
||||
return m_startDragArea;
|
||||
}
|
||||
|
||||
int endDragArea() const
|
||||
{
|
||||
return m_endDragArea;
|
||||
}
|
||||
|
||||
TimelineModelAggregator *profilerModelProxy() const { return m_profilerModelProxy; }
|
||||
void setProfilerModelProxy(QObject *profilerModelProxy);
|
||||
QmlProfilerTimelineModel *model() const { return m_model; }
|
||||
void setModel(QmlProfilerTimelineModel *model);
|
||||
|
||||
TimelineZoomControl *zoomer() const { return m_zoomer; }
|
||||
void setZoomer(QObject *zoomer);
|
||||
void setZoomer(TimelineZoomControl *zoomer);
|
||||
|
||||
Q_INVOKABLE int getYPosition(int modelIndex, int index) const;
|
||||
QmlProfilerNotesModel *notes() const { return m_notes; }
|
||||
void setNotes(QmlProfilerNotesModel *notes);
|
||||
|
||||
Q_INVOKABLE void selectFromEventIndex(int modelIndex, int index);
|
||||
Q_INVOKABLE void selectNextFromSelectionId(int modelIndex, int selectionId);
|
||||
Q_INVOKABLE void selectPrevFromSelectionId(int modelIndex, int selectionId);
|
||||
Q_INVOKABLE int getYPosition(int index) const;
|
||||
|
||||
Q_INVOKABLE void selectNextFromSelectionId(int selectionId);
|
||||
Q_INVOKABLE void selectPrevFromSelectionId(int selectionId);
|
||||
|
||||
signals:
|
||||
void profilerModelProxyChanged(TimelineModelAggregator *list);
|
||||
void modelChanged(TimelineModel *model);
|
||||
void zoomerChanged(TimelineZoomControl *zoomer);
|
||||
void notesChanged(QmlProfilerNotesModel *notes);
|
||||
|
||||
void selectionLockedChanged(bool locked);
|
||||
void selectedItemChanged(int itemIndex);
|
||||
void selectedModelChanged(int modelIndex);
|
||||
void selectionChanged(int modelIndex, int itemIndex);
|
||||
void startDragAreaChanged(int startDragArea);
|
||||
void endDragAreaChanged(int endDragArea);
|
||||
void itemPressed(int modelIndex, int pressedItem);
|
||||
void itemPressed(int pressedItem);
|
||||
|
||||
public slots:
|
||||
void clearData();
|
||||
void requestPaint();
|
||||
void swapSelections(int modelIndex1, int modelIndex2);
|
||||
|
||||
void setSelectionLocked(bool locked)
|
||||
{
|
||||
if (m_selectionLocked != locked) {
|
||||
m_selectionLocked = locked;
|
||||
update();
|
||||
emit selectionLockedChanged(locked);
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
virtual void paint(QPainter *);
|
||||
virtual void componentComplete();
|
||||
virtual void mousePressEvent(QMouseEvent *event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event);
|
||||
virtual void mouseMoveEvent(QMouseEvent *event);
|
||||
virtual void hoverMoveEvent(QHoverEvent *event);
|
||||
|
||||
private:
|
||||
void drawItemsToPainter(QPainter *p, int modelIndex, int fromIndex, int toIndex);
|
||||
void drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex);
|
||||
void drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex);
|
||||
void drawNotes(QPainter *p);
|
||||
|
||||
int modelFromPosition(int y);
|
||||
int rowFromPosition(int y);
|
||||
|
||||
void manageClicked();
|
||||
void manageHovered(int mouseX, int mouseY);
|
||||
|
||||
void setSelectedItem(int itemIndex)
|
||||
{
|
||||
@@ -160,43 +98,55 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void setSelectedModel(int modelIndex)
|
||||
void setSelectionLocked(bool locked)
|
||||
{
|
||||
if (m_selectedModel != modelIndex) {
|
||||
m_selectedModel = modelIndex;
|
||||
if (m_selectionLocked != locked) {
|
||||
m_selectionLocked = locked;
|
||||
update();
|
||||
emit selectedModelChanged(modelIndex);
|
||||
emit selectionLockedChanged(locked);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *);
|
||||
virtual void mousePressEvent(QMouseEvent *event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event);
|
||||
virtual void mouseMoveEvent(QMouseEvent *event);
|
||||
virtual void hoverMoveEvent(QHoverEvent *event);
|
||||
|
||||
private:
|
||||
enum IdType { SelectionId, TypeId };
|
||||
void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex);
|
||||
void drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex);
|
||||
void drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex);
|
||||
void drawNotes(QPainter *p);
|
||||
|
||||
int rowFromPosition(int y);
|
||||
|
||||
void manageClicked();
|
||||
void manageHovered(int mouseX, int mouseY);
|
||||
|
||||
static const int OutOfScreenMargin = 3; // margin to make sure the rectangles stay invisible
|
||||
static const int MinimumItemWidth = 3;
|
||||
|
||||
inline void getItemXExtent(int modelIndex, int i, int ¤tX, int &itemWidth);
|
||||
inline void getItemXExtent(int i, int ¤tX, int &itemWidth);
|
||||
void resetCurrentSelection();
|
||||
|
||||
qreal m_spacing;
|
||||
qreal m_spacedDuration;
|
||||
|
||||
TimelineModelAggregator *m_profilerModelProxy;
|
||||
QmlProfilerTimelineModel *m_model;
|
||||
TimelineZoomControl *m_zoomer;
|
||||
QmlProfilerNotesModel *m_notes;
|
||||
|
||||
struct {
|
||||
qint64 startTime;
|
||||
qint64 endTime;
|
||||
int row;
|
||||
int eventIndex;
|
||||
int modelIndex;
|
||||
} m_currentSelection;
|
||||
|
||||
int m_selectedItem;
|
||||
int m_selectedModel;
|
||||
bool m_selectionLocked;
|
||||
int m_startDragArea;
|
||||
int m_endDragArea;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
Reference in New Issue
Block a user