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