diff --git a/src/libs/tracing/qml/FlameGraphDelegate.qml b/src/libs/tracing/qml/FlameGraphDelegate.qml index 8a8f90c99f6..79441d0267b 100644 --- a/src/libs/tracing/qml/FlameGraphDelegate.qml +++ b/src/libs/tracing/qml/FlameGraphDelegate.qml @@ -57,7 +57,7 @@ Item { anchors.right: flamegraphItem.right anchors.bottom: flamegraphItem.bottom - FlameGraphText { + TimelineText { id: text visible: textVisible anchors.fill: parent diff --git a/src/libs/tracing/qml/FlameGraphDetails.qml b/src/libs/tracing/qml/FlameGraphDetails.qml deleted file mode 100644 index 99ff3989b6e..00000000000 --- a/src/libs/tracing/qml/FlameGraphDetails.qml +++ /dev/null @@ -1,234 +0,0 @@ -/**************************************************************************** -** -** 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 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -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 color buttonSelectedColor: titleBarColor - property color buttonHoveredColor: titleBarColor - - 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 - } - - ToolButton { - id: closeIcon - - implicitWidth: 30 - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - onClicked: rangeDetails.clearSelection() - - Image { - id: image - source: "image://icons/close_window" + (parent.enabled ? "" : "/disabled") - width: 16 - height: 16 - anchors.centerIn: parent - } - - style: ButtonStyle { - background: Rectangle { - color: (control.checked || control.pressed) - ? buttonSelectedColor - : control.hovered - ? buttonHoveredColor - : "#00000000" - } - } - } - } - - 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: { - // max(width of longest label * 2, minimumInnerWidth) - var result = minimumInnerWidth; - for (var i = 0; i < children.length; ++i) { - if (children[i].isLabel) - result = Math.max(children[i].implicitWidth * 2 + innerMargin, result); - } - - return result + 2 * outerMargin; - } - - property int labelWidth: (minimumWidth - innerMargin) / 2 - outerMargin - property int valueWidth: dragHandle.x - labelWidth - innerMargin - outerMargin - - onMinimumWidthChanged: { - if (dragHandle.x < minimumWidth - outerMargin) - dragHandle.x = minimumWidth - outerMargin; - } - - Repeater { - model: rangeDetails.model - FlameGraphText { - property bool isLabel: index % 2 === 0 - font.bold: isLabel - elide: Text.ElideRight - width: isLabel ? col.labelWidth : col.valueWidth - 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 - outerMargin - 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 - } - } -} diff --git a/src/libs/tracing/qml/FlameGraphText.qml b/src/libs/tracing/qml/FlameGraphText.qml deleted file mode 100644 index a989b393556..00000000000 --- a/src/libs/tracing/qml/FlameGraphText.qml +++ /dev/null @@ -1,34 +0,0 @@ -/**************************************************************************** -** -** 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.0 - -Text { - font.pixelSize: 12 - font.family: "sans-serif" - textFormat: Text.PlainText - renderType: Text.NativeRendering -} - diff --git a/src/libs/tracing/qml/FlameGraphView.qml b/src/libs/tracing/qml/FlameGraphView.qml index 4410f38b47e..956b312ef9c 100644 --- a/src/libs/tracing/qml/FlameGraphView.qml +++ b/src/libs/tracing/qml/FlameGraphView.qml @@ -265,21 +265,15 @@ ScrollView { } } - FlameGraphDetails { + RangeDetails { id: tooltip minimumX: 0 maximumX: flickable.width minimumY: flickable.contentY maximumY: flickable.contentY + flickable.height + noteReadonly: true - titleBarColor: Theme.color(Theme.Timeline_PanelHeaderColor) - titleBarTextColor: Theme.color(Theme.PanelTextColorLight) - contentColor: Theme.color(Theme.Timeline_PanelBackgroundColor) - contentTextColor: Theme.color(Theme.Timeline_TextColor) - noteTextColor: Theme.color(Theme.Timeline_HighlightColor) - buttonHoveredColor: Theme.color(Theme.FancyToolButtonHoverColor) - buttonSelectedColor: Theme.color(Theme.FancyToolButtonSelectedColor) borderWidth: 0 property var hoveredNode: null; @@ -310,7 +304,7 @@ ScrollView { } model: currentNode ? currentNode.details() : [] - note: currentNode ? currentNode.note() : "" + noteText: currentNode ? currentNode.note() : "" Connections { target: root.model diff --git a/src/libs/tracing/qml/MainView.qml b/src/libs/tracing/qml/MainView.qml index 2c0aa1258e5..654b544442e 100644 --- a/src/libs/tracing/qml/MainView.qml +++ b/src/libs/tracing/qml/MainView.qml @@ -224,11 +224,12 @@ Rectangle { if (selectedModel !== -1 && selectedModel !== newModel) select(selectedModel, -1); + rangeDetails.saveNote(); selectedItem = newItem selectedModel = newModel if (selectedItem !== -1) { // display details - rangeDetails.showInfo(selectedModel, selectedItem); + rangeDetails.showInfo(); // update in other views var model = timelineModelAggregator.models[selectedModel]; @@ -362,20 +363,73 @@ Rectangle { x: 200 y: 25 + noteReadonly: false clip: true locked: content.selectionLocked - models: timelineModelAggregator.models - notes: timelineModelAggregator.notes - hasContents: false + onRecenterOnItem: { content.select(selectedModel, selectedItem) } + onToggleSelectionLocked: { content.selectionLocked = !content.selectionLocked; } + onClearSelection: { content.propagateSelection(-1, -1); } + + onUpdateNote: { + if (timelineModelAggregator.notes && selectedModel != -1 && selectedItem != -1) { + timelineModelAggregator.notes.setText( + timelineModelAggregator.models[selectedModel].modelId, + selectedItem, text); + } + } + + function hide() { + model = []; + file = ""; + line = -1; + column = 0; + noteText = ""; + dialogTitle = ""; + } + + function saveNote() { + noteFocus = false; + } + + function showInfo() { + var timelineModel = timelineModelAggregator.models[selectedModel]; + var eventData = timelineModel.details(selectedItem) + var content = []; + for (var k in eventData) { + if (k === "displayName") { + dialogTitle = eventData[k]; + } else { + content.push(k); + content.push(eventData[k]); + } + } + rangeDetails.model = content; + + var location = timelineModel.location(selectedItem) + if (location.hasOwnProperty("file")) { // not empty + rangeDetails.file = location.file; + rangeDetails.line = location.line; + rangeDetails.column = location.column; + } else { + // reset to default values + rangeDetails.file = ""; + rangeDetails.line = 0; + rangeDetails.column = -1; + } + + var notes = timelineModelAggregator.notes; + var noteId = notes ? notes.get(timelineModel.modelId, selectedItem) : -1; + rangeDetails.noteText = (noteId !== -1) ? notes.text(noteId) : ""; + } } Rectangle { diff --git a/src/libs/tracing/qml/RangeDetails.qml b/src/libs/tracing/qml/RangeDetails.qml index c6b057cc6c5..9913f36bdf3 100644 --- a/src/libs/tracing/qml/RangeDetails.qml +++ b/src/libs/tracing/qml/RangeDetails.qml @@ -29,272 +29,213 @@ import TimelineTheme 1.0 Item { id: rangeDetails - property string duration - property string label - property string dialogTitle - property string file - property int line - property int column - property bool isBindingLoop - property bool hasContents + 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 int selectedModel: -1 - property int selectedItem: -1 + property real minimumX: 0 + property real maximumX: parent.width + property real minimumY: 0 + property real maximumY: parent.height - property bool locked + property string dialogTitle: "" + property string file: "" + property int line: -1 + property int column: -1 - property var models - property var notes + property bool locked: false + property var model: [] + + property alias noteText: noteEdit.text + property alias noteFocus: noteEdit.focus + property alias noteReadonly: noteEdit.readOnly signal recenterOnItem signal toggleSelectionLocked signal clearSelection + signal updateNote(string text) - width: col.width + 20 - height: hasContents ? contentArea.height + titleBar.height : 0 + visible: dialogTitle.length > 0 || model.length > 0 - function hide() { - noteEdit.focus = false; - hasContents = false; - selectedModel = selectedItem = -1; - noteEdit.text = ""; - duration = ""; - label = ""; - file = ""; - line = -1; - column = 0; - isBindingLoop = false; - } + width: dragHandle.x + dragHandle.width + height: contentArea.height + titleBar.height - Connections { - target: rangeDetails.parent - // keep inside view - onWidthChanged: fitInView(); - onHeightChanged: fitInView(); - } + 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)) - QtObject { - id: eventInfo - property bool ready: false - property var content: [] - } - - function showInfo(model, item) { - eventInfo.ready = false; - // make sure we don't accidentally save the old text for the new event - noteEdit.focus = false; - - selectedModel = model; - selectedItem = item; - var timelineModel = models[selectedModel]; - var eventData = timelineModel.details(selectedItem) - eventInfo.content = []; - for (var k in eventData) { - if (k === "displayName") { - dialogTitle = eventData[k]; - } else { - eventInfo.content.push(k); - eventInfo.content.push(eventData[k]); - } - } - eventInfo.ready = true; - hasContents = eventInfo.content.length > 0; - - var location = timelineModel.location(selectedItem) - if (location.hasOwnProperty("file")) { // not empty - file = location.file; - line = location.line; - column = location.column; - } else { - // reset to default values - file = ""; - line = 0; - column = -1; - } - - noteEdit.focus = false; - var noteId = notes ? notes.get(timelineModel.modelId, selectedItem) : -1; - noteEdit.text = (noteId !== -1) ? notes.text(noteId) : ""; - } - - function fitInView() { - // don't reposition if it does not fit - if (parent.width < width || parent.height < height) - return; - - if (x + width > parent.width) - x = parent.width - width; - if (x < 0) - x = 0; - if (y + height > parent.height) - y = parent.height - height; - if (y < 0) - y = 0; + 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 + onClicked: rangeDetails.recenterOnItem() } Rectangle { id: titleBar width: parent.width - height: 20 + height: titleBarHeight color: Theme.color(Theme.Timeline_PanelHeaderColor) - } - Item { - width: parent.width+1 - height: 11 - y: 10 - clip: true - Rectangle { - width: parent.width-1 - height: 15 - y: -5 - color: Theme.color(Theme.Timeline_PanelHeaderColor) + border.width: borderWidth + border.color: Theme.color(Theme.PanelTextColorMid) + + TimelineText { + 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: Theme.color(Theme.PanelTextColorLight) + elide: Text.ElideRight + } + + ImageToolButton { + id: editIcon + imageSource: "image://icons/edit" + anchors.top: parent.top + anchors.right: lockIcon.left + implicitHeight: typeTitle.height + visible: !rangeDetails.noteReadonly + onClicked: noteEdit.focus = true + } + + ImageToolButton { + id: lockIcon + imageSource: "image://icons/lock_" + (locked ? "closed" : "open") + anchors.top: closeIcon.top + anchors.right: closeIcon.left + implicitHeight: typeTitle.height + onClicked: rangeDetails.toggleSelectionLocked() + } + + ImageToolButton { + id: closeIcon + anchors.right: parent.right + anchors.top: parent.top + implicitHeight: typeTitle.height + imageSource: "image://icons/close_window" + onClicked: rangeDetails.clearSelection() } } - //title - TimelineText { - id: typeTitle - text: " "+rangeDetails.dialogTitle - font.bold: true - height: 20 - verticalAlignment: Text.AlignVCenter - anchors.left: parent.left - anchors.right: editIcon.left - elide: Text.ElideRight - color: Theme.color(Theme.PanelTextColorLight) - } - - // Details area Rectangle { id: contentArea color: Theme.color(Theme.Timeline_PanelBackgroundColor) - width: parent.width - height: 10 + col.height + (noteEdit.visible ? (noteEdit.height + 5) : 0) - y: 20 - //details - Grid { - property int outerMargin: 10 - property int minimumWidth: 150 - property int labelWidth: (minimumWidth - spacing) / 2 - outerMargin - property int valueWidth: dragHandle.x - labelWidth - spacing - outerMargin - - id: col - x: outerMargin - y: 5 - spacing: 5 - columns: 2 - - onChildrenChanged: { - // max(width of longest label * 2, 150) - var result = 150; - for (var i = 0; i < children.length; ++i) { - if (children[i].isLabel) - result = Math.max(children[i].implicitWidth * 2 + spacing, result); - } - - minimumWidth = result + 2 * outerMargin; - if (dragHandle.x < minimumWidth - outerMargin) - dragHandle.x = minimumWidth - outerMargin; - } - - Repeater { - model: eventInfo.ready ? eventInfo.content : 0 - Detail { - labelWidth: col.labelWidth - valueWidth: col.valueWidth - isLabel: index % 2 === 0 - text: isLabel ? (modelData + ":") : modelData - } - } - } - - - TextEdit { - id: noteEdit - x: 10 - anchors.topMargin: 5 - anchors.bottomMargin: 5 - anchors.top: col.bottom - - visible: notes && (text.length > 0 || focus) - width: col.width - wrapMode: Text.Wrap - color: Theme.color(Theme.Timeline_HighlightColor) - font.italic: true - font.pixelSize: typeTitle.font.pixelSize - font.family: typeTitle.font.family - renderType: typeTitle.renderType - selectByMouse: true - onTextChanged: saveTimer.restart() - onFocusChanged: { - if (!focus && selectedModel != -1 && selectedItem != -1) { - saveTimer.stop(); - if (notes) - notes.setText(models[selectedModel].modelId, selectedItem, text); - } - } - - Timer { - id: saveTimer - onTriggered: { - if (notes && selectedModel != -1 && selectedItem != -1) - notes.setText(models[selectedModel].modelId, selectedItem, noteEdit.text); - } - interval: 1000 - } - } - } - - MouseArea { - anchors.fill: parent - drag.target: parent - drag.minimumX: 0 - drag.maximumX: rangeDetails.parent.width - rangeDetails.width - drag.minimumY: 0 - drag.maximumY: rangeDetails.parent.height - rangeDetails.height - onClicked: rangeDetails.recenterOnItem() - } - - ImageToolButton { - id: editIcon - imageSource: "image://icons/edit" - anchors.top: closeIcon.top - anchors.right: lockIcon.left - implicitHeight: typeTitle.height - visible: notes - onClicked: noteEdit.focus = true - } - - ImageToolButton { - id: lockIcon - imageSource: "image://icons/lock_" + (locked ? "closed" : "open") - anchors.top: closeIcon.top - anchors.right: closeIcon.left - implicitHeight: typeTitle.height - onClicked: rangeDetails.toggleSelectionLocked() - } - - ImageToolButton { - id: closeIcon + anchors.top: titleBar.bottom + anchors.left: parent.left anchors.right: parent.right - anchors.top: parent.top - implicitHeight: typeTitle.height - imageSource: "image://icons/close_window" - onClicked: rangeDetails.clearSelection() + anchors.bottom: dragHandle.bottom + + border.width: borderWidth + border.color: Theme.color(Theme.PanelTextColorMid) + } + + 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: { + // max(width of longest label * 2, minimumInnerWidth) + var result = minimumInnerWidth; + for (var i = 0; i < children.length; ++i) { + if (children[i].isLabel) + result = Math.max(children[i].implicitWidth * 2 + innerMargin, result); + } + + return result + 2 * outerMargin; + } + + property int labelWidth: (minimumWidth - innerMargin) / 2 - outerMargin + property int valueWidth: dragHandle.x - labelWidth - innerMargin - outerMargin + + onMinimumWidthChanged: { + if (dragHandle.x < minimumWidth - outerMargin) + dragHandle.x = minimumWidth - outerMargin; + } + + Repeater { + model: rangeDetails.model + Detail { + labelWidth: col.labelWidth + valueWidth: col.valueWidth + isLabel: index % 2 === 0 + text: isLabel ? (modelData + ":") : modelData + } + } + } + + + 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: rangeDetails.noteReadonly + visible: (text.length > 0 || focus) + wrapMode: Text.Wrap + color: Theme.color(Theme.Timeline_HighlightColor) + font.italic: true + font.pixelSize: typeTitle.font.pixelSize + font.family: typeTitle.font.family + renderType: typeTitle.renderType + selectByMouse: true + onTextChanged: saveTimer.restart() + onFocusChanged: { + if (!focus) { + saveTimer.stop(); + if (!readOnly) + rangeDetails.updateNote(text); + } + } + + Timer { + id: saveTimer + onTriggered: { + if (!rangeDetails.readOnly) + rangeDetails.updateNote(noteEdit.text); + } + interval: 1000 + } + } Item { id: dragHandle - width: 10 - height: 10 - x: 300 - anchors.bottom: parent.bottom + width: outerMargin + height: outerMargin + x: initialWidth + anchors.top: noteEdit.bottom clip: true MouseArea { anchors.fill: parent drag.target: parent - drag.minimumX: col.minimumWidth - col.outerMargin + drag.minimumX: col.minimumWidth - outerMargin drag.axis: Drag.XAxis cursorShape: Qt.SizeHorCursor } diff --git a/src/libs/tracing/qml/tracing.qrc b/src/libs/tracing/qml/tracing.qrc index a76ad2609ab..4c4ec100f85 100644 --- a/src/libs/tracing/qml/tracing.qrc +++ b/src/libs/tracing/qml/tracing.qrc @@ -4,8 +4,6 @@ CategoryLabel.qml Detail.qml FlameGraphDelegate.qml - FlameGraphDetails.qml - FlameGraphText.qml FlameGraphView.qml ico_edit.png ico_edit@2x.png