diff --git a/src/libs/flamegraph/flamegraph.cpp b/src/libs/flamegraph/flamegraph.cpp index eb7431d179d..a08c8ee53d5 100644 --- a/src/libs/flamegraph/flamegraph.cpp +++ b/src/libs/flamegraph/flamegraph.cpp @@ -30,6 +30,9 @@ namespace FlameGraph { FlameGraph::FlameGraph(QQuickItem *parent) : QQuickItem(parent) { + // Queue the rebuild in this case so that a delegate can set the root without getting deleted + // during the call. + connect(this, &FlameGraph::rootChanged, this, &FlameGraph::rebuild, Qt::QueuedConnection); } QQmlComponent *FlameGraph::delegate() const @@ -142,7 +145,7 @@ int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject, for (int row = 0; row < rowCount; ++row) { QModelIndex childIndex = m_model->index(row, 0, parentIndex); qreal size = m_model->data(childIndex, m_sizeRole).toReal(); - if (size / m_model->data(QModelIndex(), m_sizeRole).toReal() < m_sizeThreshold) { + if (size / m_model->data(m_root, m_sizeRole).toReal() < m_sizeThreshold) { skipped += size; continue; } @@ -155,7 +158,7 @@ int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject, } } - // Root object: attribute all remaining width to "others" + // Invisible Root object: attribute all remaining width to "others" if (!parentIndex.isValid()) skipped = parentSize - position; @@ -179,7 +182,13 @@ void FlameGraph::rebuild() return; } - m_depth = buildNode(QModelIndex(), this, 0, m_maximumDepth); + if (m_root.isValid()) { + QObject *parentObject = appendChild(this, this, qmlContext(this), m_root, 0, 1); + m_depth = buildNode(m_root, parentObject, 1, m_maximumDepth); + } else { + m_depth = buildNode(m_root, this, 0, m_maximumDepth); + } + emit depthChanged(m_depth); } diff --git a/src/libs/flamegraph/flamegraph.h b/src/libs/flamegraph/flamegraph.h index 9a25a6bb109..8b694cb0649 100644 --- a/src/libs/flamegraph/flamegraph.h +++ b/src/libs/flamegraph/flamegraph.h @@ -44,6 +44,7 @@ class FLAMEGRAPH_EXPORT FlameGraph : public QQuickItem Q_PROPERTY(int maximumDepth READ maximumDepth WRITE setMaximumDepth NOTIFY maximumDepthChanged) Q_PROPERTY(int depth READ depth NOTIFY depthChanged) + Q_PROPERTY(QPersistentModelIndex root READ root WRITE setRoot NOTIFY rootChanged) public: FlameGraph(QQuickItem *parent = 0); @@ -75,6 +76,24 @@ public: } } + QPersistentModelIndex root() const + { + return m_root; + } + + void setRoot(const QPersistentModelIndex &root) + { + if (root != m_root) { + m_root = root; + emit rootChanged(root); + } + } + + Q_INVOKABLE void resetRoot() + { + setRoot(QModelIndex()); + } + static FlameGraphAttached *qmlAttachedProperties(QObject *object); signals: @@ -84,12 +103,14 @@ signals: void sizeThresholdChanged(qreal threshold); void depthChanged(int depth); void maximumDepthChanged(); + void rootChanged(const QPersistentModelIndex &root); private: void rebuild(); QQmlComponent *m_delegate = nullptr; QAbstractItemModel *m_model = nullptr; + QPersistentModelIndex m_root; int m_sizeRole = 0; int m_depth = 0; qreal m_sizeThreshold = 0; diff --git a/src/libs/flamegraph/flamegraphattached.h b/src/libs/flamegraph/flamegraphattached.h index 00d5302b0b4..d087e54d7c0 100644 --- a/src/libs/flamegraph/flamegraphattached.h +++ b/src/libs/flamegraph/flamegraphattached.h @@ -40,11 +40,17 @@ class FLAMEGRAPH_EXPORT FlameGraphAttached : public QObject Q_PROPERTY(qreal relativePosition READ relativePosition WRITE setRelativePosition NOTIFY relativePositionChanged) Q_PROPERTY(bool dataValid READ isDataValid NOTIFY dataValidChanged) + Q_PROPERTY(QModelIndex modelIndex READ modelIndex WRITE setModelIndex NOTIFY modelIndexChanged) public: FlameGraphAttached(QObject *parent = 0) : QObject(parent), m_relativeSize(0), m_relativePosition(0) {} + QModelIndex modelIndex() const + { + return m_data; + } + Q_INVOKABLE QVariant data(int role) const { return m_data.isValid() ? m_data.data(role) : QVariant(); @@ -88,13 +94,13 @@ public: m_data = data; if (validChanged) emit dataValidChanged(); - emit dataChanged(); + emit modelIndexChanged(); } } signals: - void dataChanged(); void dataValidChanged(); + void modelIndexChanged(); void relativeSizeChanged(); void relativePositionChanged(); diff --git a/src/libs/flamegraph/qml/FlameGraphDelegate.qml b/src/libs/flamegraph/qml/FlameGraphDelegate.qml index 384ec73bfe5..8a8f90c99f6 100644 --- a/src/libs/flamegraph/qml/FlameGraphDelegate.qml +++ b/src/libs/flamegraph/qml/FlameGraphDelegate.qml @@ -37,6 +37,7 @@ Item { signal mouseEntered signal mouseExited signal clicked + signal doubleClicked property bool textVisible: width > 20 || isSelected property int level: parent.level !== undefined ? parent.level + 1 : 0 @@ -76,7 +77,7 @@ Item { onEntered: flamegraphItem.mouseEntered() onExited: flamegraphItem.mouseExited() onClicked: flamegraphItem.clicked() - + onDoubleClicked: flamegraphItem.doubleClicked() } } } diff --git a/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml b/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml index b49c16f562e..62c3ff47d91 100644 --- a/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml +++ b/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml @@ -58,8 +58,21 @@ ScrollView { contentHeight: flamegraph.height boundsBehavior: Flickable.StopAtBounds + MouseArea { + anchors.fill: parent + onClicked: { + tooltip.selectedNode = null; + flameGraphModel.typeSelected(-1); + } + onDoubleClicked: { + tooltip.selectedNode = null; + flameGraphModel.typeSelected(-1); + flamegraph.resetRoot(); + } + } + FlameGraph { - property int delegateHeight: Math.max(30, flickable.height / depth) + property int delegateHeight: Math.min(60, Math.max(30, flickable.height / depth)) property color blue: "blue" property color blue1: Qt.lighter(blue) property color blue2: Qt.rgba(0.375, 0, 1, 1) @@ -71,7 +84,7 @@ ScrollView { id: flamegraph width: parent.width - height: depth * delegateHeight + height: Math.max(depth * delegateHeight, flickable.height) model: flameGraphModel sizeRole: root.sizeRole sizeThreshold: 0.002 @@ -135,7 +148,7 @@ ScrollView { + Math.floor(width / flamegraph.width * 1000) / 10 + "%)"; } text: textVisible ? buildText() : "" - FlameGraph.onDataChanged: if (textVisible) text = buildText(); + FlameGraph.onModelIndexChanged: if (textVisible) text = buildText(); onMouseEntered: { tooltip.hoveredNode = flamegraphItem; @@ -152,7 +165,7 @@ ScrollView { } } - onClicked: { + function selectClicked() { if (flamegraphItem.FlameGraph.dataValid) { tooltip.selectedNode = flamegraphItem; flameGraphModel.typeSelected(flamegraphItem.FlameGraph.data( @@ -167,6 +180,12 @@ ScrollView { } } + onClicked: selectClicked() + onDoubleClicked: { + selectClicked(); + flamegraph.root = FlameGraph.modelIndex; + } + // 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(QmlProfilerFlameGraphModel.TypeRole) || ""; }