Commit 8c64d132 authored by Christiaan Janssen's avatar Christiaan Janssen

QmlProfiler: improved look of the timeline UI

Change-Id: I5d79394529f3ba303e7353a60df77f59bbe38a12
Reviewed-by: default avatarAurindam Jana <aurindam.jana@nokia.com>
parent 21124e92
......@@ -1184,13 +1184,13 @@ int QmlProfilerEventList::getEventId(int index) const {
int QmlProfilerEventList::uniqueEventsOfType(int type) const {
if (!d->m_typeCounts.contains(type))
return 1;
return 0;
return d->m_typeCounts[type]->eventIds.count();
}
int QmlProfilerEventList::maxNestingForType(int type) const {
if (!d->m_typeCounts.contains(type))
return 1;
return 0;
return d->m_typeCounts[type]->nestingCount;
}
......
......@@ -39,7 +39,7 @@ Item {
property string content
signal linkActivated(string url)
height: childrenRect.height
height: childrenRect.height+2
width: childrenRect.width
Item {
id: guideline
......@@ -47,6 +47,7 @@ Item {
width: 5
}
Text {
y: 1
id: lbl
text: label
font.pixelSize: 12
......
......@@ -33,11 +33,12 @@
import QtQuick 1.0
Item {
id: labelContainer
property alias text: txt.text
property bool expanded: false
property int typeIndex: index
property variant descriptions: [text]
property variant descriptions: []
height: root.singleRowHeight
width: 150
......@@ -56,8 +57,8 @@ Item {
}
function updateHeight() {
height = root.singleRowHeight *
(expanded ? qmlEventList.uniqueEventsOfType(typeIndex) : qmlEventList.maxNestingForType(typeIndex));
height = root.singleRowHeight * (1 +
(expanded ? qmlEventList.uniqueEventsOfType(typeIndex) : qmlEventList.maxNestingForType(typeIndex)));
}
Connections {
......@@ -66,55 +67,67 @@ Item {
var desc=[];
for (var i=0; i<qmlEventList.uniqueEventsOfType(typeIndex); i++)
desc[i] = qmlEventList.eventTextForType(typeIndex, i);
// special case: empty
if (desc.length == 1 && desc[0]=="")
desc[0] = text;
descriptions = desc;
updateHeight();
}
onDataClear: {
descriptions = [text];
descriptions = [];
updateHeight();
}
}
Text {
id: txt
visible: !expanded
x: 5
font.pixelSize: 12
color: "#232323"
anchors.verticalCenter: parent.verticalCenter
height: root.singleRowHeight
width: 140
verticalAlignment: Text.AlignVCenter
}
Rectangle {
height: 1
width: parent.width
color: "#cccccc"
color: "#999999"
anchors.bottom: parent.bottom
z: 2
}
Column {
y: root.singleRowHeight
visible: expanded
Repeater {
model: descriptions.length
Text {
Rectangle {
width: labelContainer.width
height: root.singleRowHeight
x: 5
width: 140
text: descriptions[index]
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
color: "#eaeaea"
border.width: 1
border.color:"#c8c8c8"
Text {
height: root.singleRowHeight
x: 5
width: 140
text: descriptions[index]
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
}
}
}
Image {
visible: descriptions.length > 0
source: expanded ? "arrow_down.png" : "arrow_right.png"
x: parent.width - 12
y: 2
y: root.singleRowHeight / 2 - height / 2
MouseArea {
anchors.fill: parent
anchors.rightMargin: -10
anchors.leftMargin: -10
anchors.topMargin: -10
anchors.bottomMargin: -10
onClicked: {
expanded = !expanded;
}
......
......@@ -39,6 +39,7 @@ Rectangle {
// ***** properties
property int candidateHeight: 0
property int scrollY: 0
height: Math.max( candidateHeight, labels.height + 2 )
property int singleRowHeight: 30
......@@ -132,9 +133,12 @@ Rectangle {
function clearData() {
view.clearData();
root.dataAvailable = false;
root.eventCount = 0;
dataAvailable = false;
eventCount = 0;
hideRangeDetails();
selectionRangeMode = false;
updateRangeButton();
zoomControl.setRange(0,0);
}
function clearDisplay() {
......@@ -493,14 +497,6 @@ Rectangle {
}
}
}
//right border divider
Rectangle {
width: 1
height: parent.height
anchors.right: parent.right
color: "#cccccc"
}
}
Rectangle {
......@@ -516,4 +512,52 @@ Rectangle {
anchors.verticalCenter: labels.verticalCenter
z:3
}
// Gradient borders
Item {
anchors.left: labels.right
width: 6
anchors.top: root.top
anchors.bottom: root.bottom
Rectangle {
x: parent.width
transformOrigin: Item.TopLeft
rotation: 90
width: parent.height
height: parent.width
gradient: Gradient {
GradientStop { position: 0.0; color: "#00A0A0A0"; }
GradientStop { position: 1.0; color: "#FFA0A0A0"; }
}
}
}
Item {
anchors.right: root.right
width: 6
anchors.top: root.top
anchors.bottom: root.bottom
Rectangle {
x: parent.width
transformOrigin: Item.TopLeft
rotation: 90
width: parent.height
height: parent.width
gradient: Gradient {
GradientStop { position: 0.0; color: "#FFA0A0A0"; }
GradientStop { position: 1.0; color: "#00A0A0A0"; }
}
}
}
Rectangle {
y: root.scrollY + root.candidateHeight - height
height: 6
width: root.width
x: 0
gradient: Gradient {
GradientStop { position: 0.0; color: "#00A0A0A0"; }
GradientStop { position: 1.0; color: "#FFA0A0A0"; }
}
}
}
......@@ -37,11 +37,7 @@ var qmlEventList = 0;
//draw background of the graph
function drawGraph(canvas, ctxt, region)
{
var grad = ctxt.createLinearGradient(0, 0, 0, canvas.height);
grad.addColorStop(0, '#fff');
grad.addColorStop(1, '#ccc');
ctxt.fillStyle = grad;
ctxt.fillStyle = "#eaeaea";
ctxt.fillRect(0, 0, canvas.width, canvas.height);
}
......@@ -51,40 +47,106 @@ function drawData(canvas, ctxt, region)
if ((!qmlEventList) || qmlEventList.count() == 0)
return;
var typeCount = 5;
var width = canvas.width;
var height = canvas.height;
var bump = 10;
var height = canvas.height - bump;
var blockHeight = height / typeCount;
var spacing = width / qmlEventList.traceDuration();
ctxt.fillStyle = "rgba(0,0,0,1)";
var highest = [0,0,0,0,0];
var highest = [0,0,0,0,0]; // note: change if typeCount changes
// todo: use "region" in the timemarks?
//### only draw those in range
for (var ii = 0; ii < qmlEventList.count(); ++ii) {
var xx = (qmlEventList.getStartTime(ii) - qmlEventList.traceStartTime()) * spacing;
if (xx > region.x + region.width)
continue;
var size = qmlEventList.getDuration(ii) * spacing;
if (xx + size < region.x)
var eventWidth = qmlEventList.getDuration(ii) * spacing;
if (xx + eventWidth < region.x)
continue;
if (size < 0.5)
size = 0.5;
if (eventWidth < 1)
eventWidth = 1;
xx = Math.round(xx);
var ty = qmlEventList.getType(ii);
if (xx + size > highest[ty]) {
ctxt.fillRect(xx, ty*10, size, 10);
highest[ty] = xx+size;
if (xx + eventWidth > highest[ty]) {
var hue = ( qmlEventList.getEventId(ii) * 25 ) % 360;
ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)";
ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight);
highest[ty] = xx+eventWidth;
}
}
}
function drawTimeBar(canvas, ctxt, region)
{
var width = canvas.width;
var height = 10;
var startTime = qmlEventList.traceStartTime();
var endTime = qmlEventList.traceEndTime();
var totalTime = qmlEventList.traceDuration();
var spacing = width / totalTime;
var initialBlockLength = 120;
var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width * initialBlockLength ) / Math.LN2 ) );
var pixelsPerBlock = timePerBlock * spacing;
var pixelsPerSection = pixelsPerBlock / 5;
var blockCount = width / pixelsPerBlock;
var realStartTime = Math.floor(startTime/timePerBlock) * timePerBlock;
var realStartPos = (startTime-realStartTime) * spacing;
var timePerPixel = timePerBlock/pixelsPerBlock;
ctxt.fillStyle = "#000000";
ctxt.font = "6px sans-serif";
ctxt.fillStyle = "#cccccc";
ctxt.fillRect(0, 0, width, height);
for (var ii = 0; ii < blockCount+1; ii++) {
var x = Math.floor(ii*pixelsPerBlock - realStartPos);
// block boundary
ctxt.strokeStyle = "#525252";
ctxt.beginPath();
ctxt.moveTo(x, height/2);
ctxt.lineTo(x, height);
ctxt.stroke();
// block time label
ctxt.fillStyle = "#000000";
var timeString = prettyPrintTime((ii+0.5)*timePerBlock + realStartTime);
ctxt.textAlign = "center";
ctxt.fillText(timeString, x + pixelsPerBlock/2, height/2 + 3);
}
ctxt.fillStyle = "#808080";
ctxt.fillRect(0, height-1, width, 1);
}
function prettyPrintTime( t )
{
if (t <= 0) return "0";
if (t<1000) return t+" ns";
t = t/1000;
if (t<1000) return t+" μs";
t = Math.floor(t/100)/10;
if (t<1000) return t+" ms";
t = Math.floor(t)/1000;
if (t<60) return t+" s";
var m = Math.floor(t/60);
t = Math.floor(t - m*60);
return m+"m"+t+"s";
}
function plot(canvas, ctxt, region)
{
drawGraph(canvas, ctxt, region);
drawData(canvas, ctxt, region);
drawTimeBar(canvas, ctxt, region);
}
......@@ -124,6 +124,6 @@ Canvas2D {
Rectangle {
height: 1
width: parent.width
color: "#cccccc"
color: "#858585"
}
}
......@@ -33,7 +33,7 @@
import QtQuick 1.0
import Monitor 1.0
BorderImage {
Item {
id: rangeDetails
property string duration
......@@ -44,72 +44,115 @@ BorderImage {
property bool locked: view.selectionLocked
source: "popup_green.png"
border {
left: 10; top: 10
right: 20; bottom: 20
}
width: col.width + 45
height: childrenRect.height
height: col.height + 30
z: 1
visible: false
x: 200
y: 25
// shadow
BorderImage {
property int px: 4
source: "dialog_shadow.png"
border {
left: px; top: px
right: px; bottom: px
}
width: parent.width + 2*px - 1
height: parent.height
x: -px + 1
y: px + 1
}
// title bar
Rectangle {
width: parent.width
height: 20
color: "#55a3b8"
radius: 5
border.width: 1
border.color: "#a0a0a0"
}
Item {
width: parent.width+1
height: 11
y: 10
clip: true
Rectangle {
width: parent.width-1
height: 15
y: -5
color: "#55a3b8"
border.width: 1
border.color: "#a0a0a0"
}
}
//title
Text {
id: typeTitle
text: rangeDetails.type
text: " "+rangeDetails.type
font.bold: true
y: 10
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: -5
height: 18
y: 2
verticalAlignment: Text.AlignVCenter
width: parent.width
color: "white"
}
//details
Column {
id: col
anchors.top: typeTitle.bottom
x: 2
Detail {
label: qsTr("Duration")
content: rangeDetails.duration < 1000 ?
rangeDetails.duration + "μs" :
Math.floor(rangeDetails.duration/10)/100 + "ms"
}
Detail {
opacity: content.length !== 0 ? 1 : 0
label: qsTr("Details")
content: {
var inputString = rangeDetails.label;
if (inputString.length > 7 && inputString.substring(0,7) == "file://") {
var pos = inputString.lastIndexOf("/");
return inputString.substr(pos+1);
}
var maxLen = 40;
if (inputString.length > maxLen)
inputString = inputString.substring(0,maxLen)+"...";
// Details area
Rectangle {
color: "white"
width: parent.width
height: col.height + 10
y: 20
border.width: 1
border.color: "#a0a0a0"
return inputString;
//details
Column {
id: col
x: 10
y: 5
Detail {
label: qsTr("Duration:")
content: rangeDetails.duration < 1000 ? rangeDetails.duration + "μs" : Math.floor(rangeDetails.duration/10)/100 + "ms"
}
}
Detail {
opacity: content.length !== 0 ? 1 : 0
label: qsTr("Location")
content: {
var file = rangeDetails.file;
var pos = file.lastIndexOf("/");
if (pos != -1)
file = file.substr(pos+1);
return (file.length !== 0) ? (file + ":" + rangeDetails.line) : "";
Detail {
opacity: content.length !== 0 ? 1 : 0
label: qsTr("Details:")
content: {
var inputString = rangeDetails.label;
if (inputString.length > 7 && inputString.substring(0,7) == "file://") {
var pos = inputString.lastIndexOf("/");
return inputString.substr(pos+1);
}
var maxLen = 40;
if (inputString.length > maxLen)
inputString = inputString.substring(0,maxLen)+"...";
return inputString;
}
}
Detail {
opacity: content.length !== 0 ? 1 : 0
label: qsTr("Location:")
content: {
var file = rangeDetails.file;
var pos = file.lastIndexOf("/");
if (pos != -1)
file = file.substr(pos+1);
return (file.length !== 0) ? (file + ":" + rangeDetails.line) : "";
}
}
}
}
MouseArea {
width: col.width + 30
height: col.height + typeTitle.height + 30
width: col.width + 45
height: col.height + 30
drag.target: parent
onClicked: {
root.gotoSourceLocation(file, line);
......@@ -133,11 +176,13 @@ BorderImage {
}
}
Text {
id: closeIcon
x: col.width + 20
y: 10
x: col.width + 30
y: 4
text:"X"
color: "white"
MouseArea {
anchors.fill: parent
onClicked: {
......@@ -147,5 +192,4 @@ BorderImage {
}
}
}
......@@ -36,14 +36,13 @@ Rectangle {
id: rangeMover
property color lighterColor:"#cc80b2f6"
property color darkerColor:"#cc6da1e8"
property color gapColor: "#666da1e8"
property color hardBorderColor: "blue"
property color rangeColor:"#444a64b8"
property color borderColor:"#cc4a64b8"
property color dragMarkerColor: "#4a64b8"
width: 20
height: 50
color: lighterColor
color: rangeColor
property bool dragStarted: false
onXChanged: {
......@@ -74,16 +73,30 @@ Rectangle {
x: 0
height: parent.height
width: 1