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