Commit b62ae63f authored by Ulf Hermann's avatar Ulf Hermann

QmlProfiler: Rewrite "tooltip" window of flame graph view

Having the window "run away" from the mouse is pretty unintuitive. Use
the familiar dragging paradigm from the timeline view instead. Also,
show notes and source locations there.

Change-Id: I15c4aee2d6c08d5f20e101b436129abe66789d3b
Reviewed-by: default avatarJoerg Bornemann <joerg.bornemann@theqtcompany.com>
parent 0b7c63d6
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QtQuick 2.1
Item {
id: rangeDetails
property color titleBarColor: "#55a3b8"
property color titleBarTextColor: "white"
property color contentColor: "white"
property color contentTextColor: "black"
property color borderColor: "#a0a0a0"
property color noteTextColor: "orange"
property real titleBarHeight: 20
property real borderWidth: 1
property real outerMargin: 10
property real innerMargin: 5
property real minimumInnerWidth: 150
property real initialWidth: 300
property real minimumX
property real maximumX
property real minimumY
property real maximumY
property string dialogTitle
property var model
property string note
signal clearSelection
visible: dialogTitle.length > 0 || model.length > 0
width: dragHandle.x + dragHandle.width
height: contentArea.height + titleBar.height
onMinimumXChanged: x = Math.max(x, minimumX)
onMaximumXChanged: x = Math.min(x, Math.max(minimumX, maximumX - width))
onMinimumYChanged: y = Math.max(y, minimumY)
onMaximumYChanged: y = Math.min(y, Math.max(minimumY, maximumY - height))
MouseArea {
anchors.fill: parent
drag.target: parent
drag.minimumX: parent.minimumX
drag.maximumX: parent.maximumX - rangeDetails.width
drag.minimumY: parent.minimumY
drag.maximumY: parent.maximumY - rangeDetails.height
}
Rectangle {
id: titleBar
width: parent.width
height: titleBarHeight
color: titleBarColor
border.width: borderWidth
border.color: borderColor
FlameGraphText {
id: typeTitle
text: rangeDetails.dialogTitle
font.bold: true
verticalAlignment: Text.AlignVCenter
anchors.left: parent.left
anchors.right: closeIcon.left
anchors.leftMargin: outerMargin
anchors.rightMargin: innerMargin
anchors.top: parent.top
anchors.bottom: parent.bottom
color: titleBarTextColor
elide: Text.ElideRight
}
FlameGraphText {
id: closeIcon
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.rightMargin: innerMargin
verticalAlignment: Text.AlignVCenter
text: "X"
color: titleBarTextColor
MouseArea {
anchors.fill: parent
onClicked: rangeDetails.clearSelection()
}
}
}
Rectangle {
id: contentArea
color: contentColor
anchors.top: titleBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: dragHandle.bottom
border.width: borderWidth
border.color: borderColor
}
Grid {
id: col
anchors.left: parent.left
anchors.top: titleBar.bottom
anchors.topMargin: innerMargin
anchors.leftMargin: outerMargin
anchors.rightMargin: outerMargin
spacing: innerMargin
columns: 2
property int minimumWidth: {
var result = minimumInnerWidth;
for (var i = 0; i < children.length; ++i)
result = Math.max(children[i].x, result);
return result + 2 * outerMargin;
}
onMinimumWidthChanged: {
if (dragHandle.x < minimumWidth)
dragHandle.x = minimumWidth;
}
Repeater {
model: rangeDetails.model
FlameGraphText {
property bool isLabel: index % 2 === 0
font.bold: isLabel
elide: Text.ElideRight
width: text === "" ? 0 : (isLabel ? implicitWidth :
(dragHandle.x - x - innerMargin))
text: isLabel ? (modelData + ":") : modelData
color: contentTextColor
}
}
}
TextEdit {
id: noteEdit
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: outerMargin
anchors.rightMargin: outerMargin
anchors.topMargin: visible ? innerMargin : 0
anchors.top: col.bottom
height: visible ? implicitHeight : 0
readOnly: true
visible: text.length > 0
text: note
wrapMode: Text.Wrap
color: noteTextColor
font.italic: true
font.pixelSize: typeTitle.font.pixelSize
font.family: typeTitle.font.family
renderType: typeTitle.renderType
selectByMouse: true
}
Item {
id: dragHandle
width: outerMargin
height: outerMargin
x: initialWidth
anchors.top: noteEdit.bottom
clip: true
MouseArea {
anchors.fill: parent
drag.target: parent
drag.minimumX: col.minimumWidth
drag.axis: Drag.XAxis
cursorShape: Qt.SizeHorCursor
}
Rectangle {
color: titleBarColor
rotation: 45
width: parent.width * Math.SQRT2
height: parent.height * Math.SQRT2
x: parent.width - width / 2
y: parent.height - height / 2
}
}
}
......@@ -91,10 +91,14 @@ ScrollView {
}
}
// Functions, not properties to limit the initial overhead when creating the nodes,
// and because FlameGraph.data(...) cannot be notified anyway.
function title() { return FlameGraph.data(FlameGraphModel.Type) || ""; }
function note() { return FlameGraph.data(FlameGraphModel.Note) || ""; }
function details() {
var model = [];
function addDetail(name, index, format) {
model.push(name + ":");
model.push(name);
model.push(format(FlameGraph.data(index)));
}
......@@ -121,7 +125,7 @@ ScrollView {
}
if (!FlameGraph.dataValid) {
model.push(qsTr("Details") + ":");
model.push(qsTr("Details"));
model.push(qsTr("Various Events"));
} else {
addDetail(qsTr("Details"), FlameGraphModel.Details, noop);
......@@ -131,7 +135,7 @@ ScrollView {
addDetail(qsTr("Mean Time"), FlameGraphModel.TimePerCall, printTime);
addDetail(qsTr("In Percent"), FlameGraphModel.TimeInPercent,
addPercent);
addDetail(qsTr("Location"), FlameGraphModel.Location, noop);
}
return model;
}
......@@ -221,59 +225,56 @@ ScrollView {
}
}
Rectangle {
color: "white"
border.width: 1
border.color: flamegraph.grey2
width: tooltip.model.length > 0 ? tooltip.width + 10 : 0
height: tooltip.model.length > 0 ? tooltip.height + 10 : 0
y: flickable.contentY
x: anchorRight ? parent.width - width : 0
property bool anchorRight: true
Grid {
id: tooltip
anchors.margins: 5
anchors.top: parent.top
anchors.left: parent.left
spacing: 5
columns: 2
property var hoveredNode: null;
property var selectedNode: null;
property var model: {
if (flameGraphModel.rowCount() === 0)
return [ qsTr("No data available") ];
else if (hoveredNode !== null)
return hoveredNode.details();
else if (selectedNode !== null)
return selectedNode.details();
else
return [];
}
FlameGraphDetails {
id: tooltip
Connections {
target: flameGraphModel
onModelReset: {
tooltip.hoveredNode = null;
tooltip.selectedNode = null;
}
}
minimumX: 0
maximumX: flickable.width
minimumY: flickable.contentY
maximumY: flickable.contentY + flickable.height
Repeater {
model: parent.model
FlameGraphText {
text: modelData
font.bold: (index % 2) === 0
width: Math.min(implicitWidth, 200)
elide: Text.ElideRight
}
}
property var hoveredNode: null;
property var selectedNode: null;
property var currentNode: {
if (hoveredNode !== null)
return hoveredNode;
else if (selectedNode !== null)
return selectedNode;
else
return null;
}
onClearSelection: {
selectedTypeId = -1;
selectedNode = null;
root.typeSelected(-1);
}
dialogTitle: {
if (currentNode)
return currentNode.title();
else if (flameGraphModel.rowCount() === 0)
return qsTr("No data available");
else
return "";
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.anchorRight = !parent.anchorRight
model: currentNode ? currentNode.details() : []
note: currentNode ? currentNode.note() : ""
Connections {
target: flameGraphModel
onModelReset: {
tooltip.hoveredNode = null;
tooltip.selectedNode = null;
}
onDataChanged: {
// refresh to trigger reevaluation of note
var selectedNode = tooltip.selectedNode;
tooltip.selectedNode = null;
tooltip.selectedNode = selectedNode;
}
}
}
}
......
......@@ -2,5 +2,6 @@
<qresource prefix="/">
<file>FlameGraphView.qml</file>
<file>FlameGraphText.qml</file>
<file>FlameGraphDetails.qml</file>
</qresource>
</RCC>
......@@ -213,6 +213,7 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const
case RangeType: return type.rangeType;
case Details: return type.data.isEmpty() ?
FlameGraphModel::tr("Source code not available") : type.data;
case Location: return type.displayName;
default: return QVariant();
}
} else {
......
......@@ -68,6 +68,7 @@ public:
TimePerCall,
TimeInPercent,
RangeType,
Location,
MaxRole
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment