Meshroom/meshroom/ui/qml/GraphEditor/NodeEditor.qml

425 lines
16 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0
/**
* NodeEditor allows to visualize and edit the parameters of a Node.
* It mainly provides an attribute editor and a log inspector.
*/
Panel {
id: root
property variant node
property string globalStatus : node !== null ? node.globalStatus : ""
property bool readOnly: false
property bool isCompatibilityNode: node && node.compatibilityIssue !== undefined
property string nodeStartDateTime: ""
signal attributeDoubleClicked(var mouse, var attribute)
signal upgradeRequest()
title: "Node" + (node !== null ? " - <b>" + node.label + "</b>" + (node.label !== node.defaultLabel ? " (" + node.defaultLabel + ")" : "") : "")
icon: MaterialLabel { text: MaterialIcons.tune }
onGlobalStatusChanged: {
nodeStartDateTime = ""
if (node !== null && node.isRunning()) {
timer.start()
}
else {
timer.stop()
if (node !== null && (node.isFinishedOrRunning() || globalStatus == "ERROR")) {
computationInfo.text = Format.sec2timeStr(node.elapsedTime)
}
else {
computationInfo.text = ""
}
}
}
headerBar: RowLayout {
Label {
id: computationInfo
color: node && node.isComputable ? Colors.statusColors[node.globalStatus] : palette.text
Timer {
id: timer
interval: 2500
triggeredOnStart: true
repeat: true
running: node !== null && node.isRunning()
onTriggered: {
if (nodeStartDateTime === "") {
nodeStartDateTime = new Date(node.getStartDateTime()).getTime()
}
var now = new Date().getTime()
parent.text = Format.sec2timeStr((now-nodeStartDateTime)/1000)
}
}
padding: 2
font.italic: true
visible: {
if (node !== null) {
if (node.isComputable && (node.isFinishedOrRunning() || node.isSubmittedOrRunning() || node.globalStatus=="ERROR")) {
return true
}
}
return false
}
ToolTip.text: {
if (node !== null && (node.isFinishedOrRunning() || (node.isSubmittedOrRunning() && node.elapsedTime > 0))) {
var longestChunkTime = getLongestChunkTime(node.chunks)
if (longestChunkTime > 0)
return "Longest chunk: " + Format.sec2timeStr(longestChunkTime) + " (" + node.chunks.count + " chunks)"
else
return ""
} else {
return ""
}
}
ToolTip.visible: ToolTip.text ? runningTimeMa.containsMouse : false
MouseArea {
id: runningTimeMa
anchors.fill: parent
hoverEnabled: true
}
function getLongestChunkTime(chunks) {
if (chunks.count <= 1)
return 0
var longestChunkTime = 0
for (var i = 0; i < chunks.count; i++) {
var elapsedTime = chunks.at(i).elapsedTime
longestChunkTime = elapsedTime > longestChunkTime ? elapsedTime : longestChunkTime
}
return longestChunkTime
}
}
SearchBar {
id: searchBar
toggle: true // Enable toggling the actual text field by the search button
Layout.minimumWidth: searchBar.width
maxWidth: 150
enabled: tabBar.currentIndex === 0 || tabBar.currentIndex === 5
}
MaterialToolButton {
text: MaterialIcons.more_vert
font.pointSize: 11
padding: 2
onClicked: settingsMenu.open()
checkable: true
checked: settingsMenu.visible
Menu {
id: settingsMenu
y: parent.height
Menu {
id: filterAttributesMenu
title: "Filter Attributes"
RowLayout {
CheckBox {
id: outputToggle
text: "Output"
checkable: true
checked: GraphEditorSettings.showOutputAttributes
onClicked: GraphEditorSettings.showOutputAttributes = !GraphEditorSettings.showOutputAttributes
enabled: tabBar.currentIndex === 0
}
CheckBox {
id: inputToggle
text: "Input"
checkable: true
checked: GraphEditorSettings.showInputAttributes
onClicked: GraphEditorSettings.showInputAttributes = !GraphEditorSettings.showInputAttributes
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
RowLayout {
CheckBox {
id: defaultToggle
text: "Default"
checkable: true
checked: GraphEditorSettings.showDefaultAttributes
onClicked: GraphEditorSettings.showDefaultAttributes = !GraphEditorSettings.showDefaultAttributes
enabled: tabBar.currentIndex === 0
}
CheckBox {
id: modifiedToggle
text: "Modified"
checkable: true
checked: GraphEditorSettings.showModifiedAttributes
onClicked: GraphEditorSettings.showModifiedAttributes = !GraphEditorSettings.showModifiedAttributes
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
RowLayout {
CheckBox {
id: linkToggle
text: "Link"
checkable: true
checked: GraphEditorSettings.showLinkAttributes
onClicked: GraphEditorSettings.showLinkAttributes = !GraphEditorSettings.showLinkAttributes
enabled: tabBar.currentIndex === 0
}
CheckBox {
id: notLinkToggle
text: "Not Link"
checkable: true
checked: GraphEditorSettings.showNotLinkAttributes
onClicked: GraphEditorSettings.showNotLinkAttributes = !GraphEditorSettings.showNotLinkAttributes
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
CheckBox {
id: advancedToggle
text: "Advanced"
MaterialLabel {
anchors.right: parent.right; anchors.rightMargin: parent.padding;
text: MaterialIcons.build
anchors.verticalCenter: parent.verticalCenter
font.pointSize: 8
}
checkable: true
checked: GraphEditorSettings.showAdvancedAttributes
onClicked: GraphEditorSettings.showAdvancedAttributes = !GraphEditorSettings.showAdvancedAttributes
}
}
MenuItem {
text: "Open Cache Folder"
enabled: root.node !== null
onClicked: Qt.openUrlExternally(Filepath.stringToUrl(root.node.internalFolder))
}
MenuSeparator {}
MenuItem {
enabled: root.node !== null
text: "Clear Pending Status"
onClicked: {
node.clearSubmittedChunks()
timer.stop()
}
}
}
}
}
ColumnLayout {
anchors.fill: parent
// CompatibilityBadge banner for CompatibilityNode
Loader {
active: root.isCompatibilityNode
Layout.fillWidth: true
visible: active // For layout update
sourceComponent: CompatibilityBadge {
canUpgrade: root.node.canUpgrade
issueDetails: root.node.issueDetails
onUpgradeRequest: root.upgradeRequest()
sourceComponent: bannerDelegate
}
}
Loader {
Layout.fillHeight: true
Layout.fillWidth: true
sourceComponent: root.node ? editor_component : placeholder_component
Component {
id: placeholder_component
Item {
Column {
anchors.centerIn: parent
MaterialLabel {
text: MaterialIcons.select_all
font.pointSize: 34
color: Qt.lighter(palette.mid, 1.2)
anchors.horizontalCenter: parent.horizontalCenter
}
Label {
color: Qt.lighter(palette.mid, 1.2)
text: "Select a Node to access its Details"
}
}
}
}
Component {
id: editor_component
MSplitView {
anchors.fill: parent
// The list of chunks
ChunksListView {
id: chunksLV
visible: (tabBar.currentIndex >= 1 && tabBar.currentIndex <= 3)
chunks: root.node.chunks
SplitView.preferredWidth: 55
SplitView.minimumWidth: 20
}
StackLayout {
SplitView.fillWidth: true
currentIndex: tabBar.currentIndex
AttributeEditor {
id: inOutAttr
objectsHideable: true
Layout.fillHeight: true
Layout.fillWidth: true
model: root.node.attributes
readOnly: root.readOnly || root.isCompatibilityNode
onAttributeDoubleClicked: function(mouse, attribute) { root.attributeDoubleClicked(mouse, attribute) }
onUpgradeRequest: root.upgradeRequest()
filterText: searchBar.text
}
Loader {
active: (tabBar.currentIndex === 1)
Layout.fillHeight: true
Layout.fillWidth: true
sourceComponent: NodeLog {
// anchors.fill: parent
Layout.fillHeight: true
Layout.fillWidth: true
width: parent.width
height: parent.height
id: nodeLog
node: root.node
currentChunkIndex: chunksLV.currentIndex
currentChunk: chunksLV.currentChunk
}
}
Loader {
active: (tabBar.currentIndex === 2)
Layout.fillHeight: true
Layout.fillWidth: true
sourceComponent: NodeStatistics {
id: nodeStatistics
Layout.fillHeight: true
Layout.fillWidth: true
node: root.node
currentChunkIndex: chunksLV.currentIndex
currentChunk: chunksLV.currentChunk
}
}
Loader {
active: (tabBar.currentIndex === 3)
Layout.fillHeight: true
Layout.fillWidth: true
sourceComponent: NodeStatus {
id: nodeStatus
Layout.fillHeight: true
Layout.fillWidth: true
node: root.node
currentChunkIndex: chunksLV.currentIndex
currentChunk: chunksLV.currentChunk
}
}
NodeDocumentation {
id: nodeDocumentation
Layout.fillHeight: true
Layout.fillWidth: true
node: root.node
}
AttributeEditor {
id: nodeInternalAttr
objectsHideable: false
Layout.fillHeight: true
Layout.fillWidth: true
model: root.node.internalAttributes
readOnly: root.readOnly || root.isCompatibilityNode
onAttributeDoubleClicked: function(mouse, attribute) { root.attributeDoubleClicked(mouse, attribute) }
onUpgradeRequest: root.upgradeRequest()
filterText: searchBar.text
}
}
}
}
}
TabBar {
id: tabBar
visible: root.node !== null
property bool isComputable: root.node !== null && root.node.isComputable
Layout.fillWidth: true
width: childrenRect.width
position: TabBar.Footer
currentIndex: 0
TabButton {
text: "Attributes"
padding: 4
leftPadding: 8
rightPadding: leftPadding
}
TabButton {
visible: tabBar.isComputable
width: !visible ? 0 : tabBar.width / tabBar.count
text: "Log"
leftPadding: 8
rightPadding: leftPadding
}
TabButton {
visible: tabBar.isComputable
width: !visible ? 0 : tabBar.width / tabBar.count
text: "Statistics"
leftPadding: 8
rightPadding: leftPadding
}
TabButton {
visible: tabBar.isComputable
width: !visible ? 0 : tabBar.width / tabBar.count
text: "Status"
leftPadding: 8
rightPadding: leftPadding
}
TabButton {
text: "Documentation"
leftPadding: 8
rightPadding: leftPadding
}
TabButton {
text: "Notes"
padding: 4
leftPadding: 8
rightPadding: leftPadding
}
onIsComputableChanged: {
if (!isComputable) {
tabBar.currentIndex = 0
}
}
}
}
}