QmlProfiler: Put height of rows in the timeline into the model

Like that we can provide functionality to interactively resize rows in
the model so that one can zoom in to more interesting parts.

Change-Id: I31f14cd8aa37703240ebec744ca2e77188fb0f27
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
This commit is contained in:
Ulf Hermann
2014-06-18 11:20:41 +02:00
parent d1cd1a490c
commit 63ca524069
10 changed files with 180 additions and 65 deletions

View File

@@ -32,6 +32,7 @@
namespace QmlProfiler {
static const int DefaultRowHeight = 30;
AbstractTimelineModel::AbstractTimelineModel(AbstractTimelineModelPrivate *dd,
const QString &name, const QString &label, QmlDebug::Message message,
@@ -122,6 +123,63 @@ bool AbstractTimelineModel::isEmpty() const
return count() == 0;
}
int AbstractTimelineModel::rowHeight(int rowNumber) const
{
Q_D(const AbstractTimelineModel);
if (!expanded())
return DefaultRowHeight;
if (d->rowOffsets.length() > rowNumber)
return d->rowOffsets[rowNumber] - (rowNumber > 0 ? d->rowOffsets[rowNumber - 1] : 0);
return DefaultRowHeight;
}
int AbstractTimelineModel::rowOffset(int rowNumber) const
{
Q_D(const AbstractTimelineModel);
if (rowNumber == 0)
return 0;
if (!expanded())
return DefaultRowHeight * rowNumber;
if (d->rowOffsets.length() >= rowNumber)
return d->rowOffsets[rowNumber - 1];
if (!d->rowOffsets.empty())
return d->rowOffsets.last() + (rowNumber - 1 - d->rowOffsets.length()) * DefaultRowHeight;
return rowNumber * DefaultRowHeight;
}
void AbstractTimelineModel::setRowHeight(int rowNumber, int height)
{
Q_D(AbstractTimelineModel);
if (!expanded())
return;
if (height < DefaultRowHeight)
height = DefaultRowHeight;
int nextOffset = d->rowOffsets.empty() ? 0 : d->rowOffsets.last();
while (d->rowOffsets.length() <= rowNumber)
d->rowOffsets << (nextOffset += DefaultRowHeight);
int difference = height - d->rowOffsets[rowNumber] +
(rowNumber > 0 ? d->rowOffsets[rowNumber - 1] : 0);
if (difference != 0) {
for (; rowNumber < d->rowOffsets.length(); ++rowNumber) {
d->rowOffsets[rowNumber] += difference;
}
emit rowHeightChanged();
}
}
int AbstractTimelineModel::height() const
{
Q_D(const AbstractTimelineModel);
int depth = rowCount();
if (!expanded() || d->rowOffsets.empty())
return depth * DefaultRowHeight;
return d->rowOffsets.last() + (depth - d->rowOffsets.length()) * DefaultRowHeight;
}
qint64 AbstractTimelineModel::traceStartTime() const
{
Q_D(const AbstractTimelineModel);
@@ -192,6 +250,8 @@ void AbstractTimelineModel::dataChanged()
default:
break;
}
d->rowOffsets.clear();
}
bool AbstractTimelineModel::eventAccepted(const QmlProfilerDataModel::QmlEventTypeData &event) const

View File

@@ -54,6 +54,11 @@ public:
bool isEmpty() const;
// Methods are directly passed on to the private model and relying on its virtual methods.
int rowHeight(int rowNumber) const;
int rowOffset(int rowNumber) const;
void setRowHeight(int rowNumber, int height);
int height() const;
Q_INVOKABLE qint64 lastTimeMark() const;
Q_INVOKABLE qint64 traceStartTime() const;
Q_INVOKABLE qint64 traceEndTime() const;
@@ -91,6 +96,7 @@ public:
signals:
void expandedChanged();
void rowHeightChanged();
protected:
enum BoxColorProperties {

View File

@@ -47,6 +47,7 @@ public:
virtual int findFirstIndexNoParents(qint64 startTime) const = 0;
virtual int findLastIndex(qint64 endTime) const = 0;
QVector<int> rowOffsets;
QString name;
QmlProfilerModelManager *modelManager;
int modelId;

View File

@@ -32,30 +32,25 @@ import QtQuick 2.1
Item {
id: labelContainer
property string text: qmlProfilerModelProxy.title(modelIndex)
property bool expanded: false
property int modelIndex: index;
property bool expanded: trigger(qmlProfilerModelProxy.expanded(modelIndex))
property int modelIndex: index
property int bindingTrigger: 1
property var descriptions: []
property var extdescriptions: []
property var eventIds: []
visible: qmlProfilerModelProxy.rowCount(modelIndex) > 0;
function trigger(i) {
return i * bindingTrigger * bindingTrigger;
}
height: root.singleRowHeight
visible: trigger(qmlProfilerModelProxy.rowCount(modelIndex)) > 0
height: trigger(qmlProfilerModelProxy.height(modelIndex))
width: 150
Component.onCompleted: {
updateHeight();
}
function updateHeight() {
height = root.singleRowHeight * qmlProfilerModelProxy.rowCount(modelIndex);
}
function getDescriptions() {
expanded = qmlProfilerModelProxy.expanded(modelIndex);
bindingTrigger = -bindingTrigger;
backgroundMarks.requestPaint();
visible = qmlProfilerModelProxy.rowCount(modelIndex) > 0;
if (!visible)
return;
@@ -71,18 +66,13 @@ Item {
descriptions = desc;
eventIds = ids;
extdescriptions = extdesc;
updateHeight();
}
Connections {
target: qmlProfilerModelProxy
onExpandedChanged: {
getDescriptions();
}
onStateChanged: {
getDescriptions();
}
onExpandedChanged: getDescriptions();
onStateChanged: getDescriptions()
onRowHeightChanged: getDescriptions()
}
Text {
@@ -91,7 +81,7 @@ Item {
font.pixelSize: 12
text: labelContainer.text
color: "#232323"
height: root.singleRowHeight
height: trigger(qmlProfilerModelProxy.rowHeight(modelIndex, 0))
width: 140
verticalAlignment: Text.AlignVCenter
}
@@ -105,20 +95,21 @@ Item {
}
Column {
y: root.singleRowHeight
anchors.top: txt.bottom
visible: expanded
Repeater {
model: descriptions.length
Rectangle {
width: labelContainer.width
height: root.singleRowHeight
height: trigger(qmlProfilerModelProxy.rowHeight(modelIndex, index + 1))
color: "#eaeaea"
border.width: 1
border.color:"#c8c8c8"
Text {
height: root.singleRowHeight
x: 5
width: 140
anchors.fill: parent
anchors.leftMargin: 5
anchors.rightMargin: 5
text: descriptions[index]
textFormat: Text.PlainText
elide: Text.ElideRight
@@ -143,7 +134,7 @@ Item {
Image {
source: expanded ? "arrow_down.png" : "arrow_right.png"
x: parent.width - 12
y: Math.floor((root.singleRowHeight - height) / 2)
y: 9
smooth: false
MouseArea {
anchors.fill: parent

View File

@@ -36,8 +36,6 @@ Rectangle {
// ***** properties
property int singleRowHeight: 30
property alias selectionLocked : view.selectionLocked
property bool lockItemSelection : false

View File

@@ -102,21 +102,30 @@ Canvas {
function drawBackgroundBars( context, region ) {
var colorIndex = true;
// row background
var backgroundOffset = y < 0 ? -y : -(y % (2 * root.singleRowHeight));
for (var currentY= backgroundOffset; currentY < Math.min(height, labels.height - y); currentY += root.singleRowHeight) {
context.fillStyle = colorIndex ? "#f0f0f0" : "white";
context.strokeStyle = colorIndex ? "#f0f0f0" : "white";
context.fillRect(0, currentY, width, root.singleRowHeight);
colorIndex = !colorIndex;
}
// separators
var cumulatedHeight = 0;
for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount() && cumulatedHeight < y + height; modelIndex++) {
cumulatedHeight += root.singleRowHeight * qmlProfilerModelProxy.rowCount(modelIndex);
if (cumulatedHeight < y)
var cumulatedHeight = y < 0 ? -y : 0;
for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
var modelHeight = qmlProfilerModelProxy.height(modelIndex);
if (cumulatedHeight + modelHeight < y) {
cumulatedHeight += modelHeight;
if (qmlProfilerModelProxy.rowCount(modelIndex) % 2 !== 0)
colorIndex = !colorIndex;
continue;
}
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 (cumulatedHeight > y + height)
return;
}
context.strokeStyle = "#B0B0B0";
context.beginPath();

View File

@@ -91,6 +91,7 @@ void TimelineModelAggregator::addModel(AbstractTimelineModel *m)
{
d->modelList << m;
connect(m,SIGNAL(expandedChanged()),this,SIGNAL(expandedChanged()));
connect(m,SIGNAL(rowHeightChanged()),this,SIGNAL(rowHeightChanged()));
}
QStringList TimelineModelAggregator::categoryTitles() const
@@ -141,6 +142,26 @@ qint64 TimelineModelAggregator::lastTimeMark() const
return mark;
}
int TimelineModelAggregator::height(int modelIndex) const
{
return d->modelList[modelIndex]->height();
}
int TimelineModelAggregator::rowHeight(int modelIndex, int row) const
{
return d->modelList[modelIndex]->rowHeight(row);
}
int TimelineModelAggregator::rowOffset(int modelIndex, int row) const
{
return d->modelList[modelIndex]->rowOffset(row);
}
void TimelineModelAggregator::setRowHeight(int modelIndex, int row, int height)
{
d->modelList[modelIndex]->setRowHeight(row, height);
}
bool TimelineModelAggregator::expanded(int modelIndex) const
{
return d->modelList[modelIndex]->expanded();

View File

@@ -62,6 +62,10 @@ public:
Q_INVOKABLE qint64 lastTimeMark() const;
Q_INVOKABLE int height(int modelIndex) const;
Q_INVOKABLE int rowHeight(int modelIndex, int row) const;
Q_INVOKABLE void setRowHeight(int modelIndex, int row, int height);
Q_INVOKABLE int rowOffset(int modelIndex, int row) const;
Q_INVOKABLE bool expanded(int modelIndex) const;
Q_INVOKABLE void setExpanded(int modelIndex, bool expanded);
Q_INVOKABLE int rowCount(int modelIndex) const;
@@ -93,6 +97,7 @@ signals:
void dataAvailable();
void stateChanged();
void expandedChanged();
void rowHeightChanged();
protected slots:
void dataChanged();

View File

@@ -41,8 +41,6 @@
using namespace QmlProfiler::Internal;
const int DefaultRowHeight = 30;
TimelineRenderer::TimelineRenderer(QQuickPaintedItem *parent) :
QQuickPaintedItem(parent), m_startTime(0), m_endTime(0), m_spacing(0), m_spacedDuration(0),
m_lastStartTime(0), m_lastEndTime(0)
@@ -130,18 +128,20 @@ void TimelineRenderer::drawItemsToPainter(QPainter *p, int modelIndex, int fromI
p->setPen(Qt::transparent);
int modelRowStart = 0;
for (int mi = 0; mi < modelIndex; mi++)
modelRowStart += m_profilerModelProxy->rowCount(mi);
modelRowStart += m_profilerModelProxy->height(mi);
for (int i = fromIndex; i <= toIndex; i++) {
int currentX, currentY, itemWidth, itemHeight;
int rowNumber = m_profilerModelProxy->getEventRow(modelIndex, i);
currentY = (modelRowStart + rowNumber) * DefaultRowHeight - y();
currentY = modelRowStart + m_profilerModelProxy->rowOffset(modelIndex, rowNumber) - y();
if (currentY >= height())
continue;
itemHeight = DefaultRowHeight * m_profilerModelProxy->getHeight(modelIndex, i);
currentY += DefaultRowHeight - itemHeight;
itemHeight = m_profilerModelProxy->rowHeight(modelIndex, rowNumber) *
m_profilerModelProxy->getHeight(modelIndex, i);
currentY += m_profilerModelProxy->rowHeight(modelIndex, rowNumber) - itemHeight;
if (currentY + itemHeight < 0)
continue;
@@ -164,7 +164,7 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromI
int modelRowStart = 0;
for (int mi = 0; mi < modelIndex; mi++)
modelRowStart += m_profilerModelProxy->rowCount(mi);
modelRowStart += m_profilerModelProxy->height(mi);
p->save();
@@ -183,16 +183,19 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromI
if (m_profilerModelProxy->getEventId(modelIndex, i) != id)
continue;
currentY = (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, i)) * DefaultRowHeight - y();
if (currentY + DefaultRowHeight < 0 || height() < currentY)
int row = m_profilerModelProxy->getEventRow(modelIndex, i);
int rowHeight = m_profilerModelProxy->rowHeight(modelIndex, row);
currentY = modelRowStart + m_profilerModelProxy->rowOffset(modelIndex, row) - y();
if (currentY + rowHeight < 0 || height() < currentY)
continue;
getItemXExtent(modelIndex, i, currentX, itemWidth);
if (i == m_selectedItem)
selectedItemRect = QRect(currentX, currentY - 1, itemWidth, DefaultRowHeight + 1);
selectedItemRect = QRect(currentX, currentY - 1, itemWidth, rowHeight + 1);
else
p->drawRect(currentX, currentY, itemWidth, DefaultRowHeight);
p->drawRect(currentX, currentY, itemWidth, rowHeight);
}
// draw the selected item rectangle the last, so that it's overlayed
@@ -209,7 +212,7 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int f
int destindex;
int xfrom, xto, width;
int yfrom, yto;
int radius = DefaultRowHeight / 3;
int radius = 10;
QPen shadowPen = QPen(QColor("grey"),2);
QPen markerPen = QPen(QColor("orange"),2);
QBrush shadowBrush = QBrush(QColor("grey"));
@@ -222,12 +225,14 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int f
// to
getItemXExtent(modelIndex, destindex, xto, width);
xto += width / 2;
yto = getYPosition(modelIndex, destindex) + DefaultRowHeight / 2 - y();
yto = getYPosition(modelIndex, destindex) +
m_profilerModelProxy->rowHeight(modelIndex, destindex) / 2 - y();
// from
getItemXExtent(modelIndex, i, xfrom, width);
xfrom += width / 2;
yfrom = getYPosition(modelIndex, i) + DefaultRowHeight / 2 - y();
yfrom = getYPosition(modelIndex, i) +
m_profilerModelProxy->rowHeight(modelIndex, i) / 2 - y();
// radius (derived from width of origin event)
radius = 5;
@@ -260,11 +265,29 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int f
p->restore();
}
int TimelineRenderer::rowFromPosition(int y)
{
int ret = 0;
for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
int modelHeight = m_profilerModelProxy->height(modelIndex);
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);
}
}
return ret;
}
int TimelineRenderer::modelFromPosition(int y)
{
y = y / DefaultRowHeight;
for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
y -= m_profilerModelProxy->rowCount(modelIndex);
y -= m_profilerModelProxy->height(modelIndex);
if (y < 0)
return modelIndex;
}
@@ -328,7 +351,7 @@ void TimelineRenderer::manageHovered(int mouseX, int mouseY)
// Make the "selected" area 3 pixels wide by adding/subtracting 1 to catch very narrow events.
qint64 startTime = (mouseX - 1) * (m_endTime - m_startTime) / width() + m_startTime;
qint64 endTime = (mouseX + 1) * (m_endTime - m_startTime) / width() + m_startTime;
int row = (mouseY + y()) / DefaultRowHeight;
int row = rowFromPosition(mouseY + y());
int modelIndex = modelFromPosition(mouseY + y());
// already covered? nothing to do
@@ -404,10 +427,10 @@ int TimelineRenderer::getYPosition(int modelIndex, int index) const
int modelRowStart = 0;
for (int mi = 0; mi < modelIndex; mi++)
modelRowStart += m_profilerModelProxy->rowCount(mi);
modelRowStart += m_profilerModelProxy->height(mi);
int y = DefaultRowHeight * (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, index));
return y;
return modelRowStart + m_profilerModelProxy->rowOffset(modelIndex,
m_profilerModelProxy->getEventRow(modelIndex, index));
}
void TimelineRenderer::selectNext()

View File

@@ -172,6 +172,7 @@ private:
void drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex);
void drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex);
int modelFromPosition(int y);
int rowFromPosition(int y);
void manageClicked();
void manageHovered(int mouseX, int mouseY);