diff --git a/meshroom/core/attribute.py b/meshroom/core/attribute.py
index ec408bca..1257b81d 100644
--- a/meshroom/core/attribute.py
+++ b/meshroom/core/attribute.py
@@ -137,7 +137,7 @@ class Attribute(BaseObject):
self.valueChanged.emit()
def resetValue(self):
- self._value = ""
+ self._value = self.attributeDesc.value
def requestGraphUpdate(self):
if self.node.graph:
diff --git a/meshroom/ui/qml/GraphEditor/GraphEditor.qml b/meshroom/ui/qml/GraphEditor/GraphEditor.qml
index 8acbc718..9b6c2e11 100755
--- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml
+++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml
@@ -236,10 +236,10 @@ Item {
model: nodeRepeater.loaded && root.graph ? root.graph.edges : undefined
delegate: Edge {
- property var src: edge ? root._attributeToDelegate[edge.src] : undefined
- property var dst: edge ? root._attributeToDelegate[edge.dst] : undefined
- property var srcAnchor: src.nodeItem.mapFromItem(src, src.outputAnchorPos.x, src.outputAnchorPos.y)
- property var dstAnchor: dst.nodeItem.mapFromItem(dst, dst.inputAnchorPos.x, dst.inputAnchorPos.y)
+ property var src: root._attributeToDelegate[edge.src]
+ property var dst: root._attributeToDelegate[edge.dst]
+ property bool isValidEdge: src != undefined && dst != undefined
+ visible: isValidEdge
property bool inFocus: containsMouse || (edgeMenu.opened && edgeMenu.currentEdge == edge)
@@ -247,10 +247,10 @@ Item {
color: inFocus ? activePalette.highlight : activePalette.text
thickness: inFocus ? 2 : 1
opacity: 0.7
- point1x: src.nodeItem.x + srcAnchor.x
- point1y: src.nodeItem.y + srcAnchor.y
- point2x: dst.nodeItem.x + dstAnchor.x
- point2y: dst.nodeItem.y + dstAnchor.y
+ point1x: isValidEdge ? src.globalX + src.outputAnchorPos.x : 0
+ point1y: isValidEdge ? src.globalY + src.outputAnchorPos.y : 0
+ point2x: isValidEdge ? dst.globalX + dst.inputAnchorPos.x : 0
+ point2y: isValidEdge ? dst.globalY + dst.inputAnchorPos.y : 0
onPressed: {
const canEdit = !edge.dst.node.locked
diff --git a/meshroom/ui/qml/GraphEditor/Node.qml b/meshroom/ui/qml/GraphEditor/Node.qml
index fb281659..ace25034 100755
--- a/meshroom/ui/qml/GraphEditor/Node.qml
+++ b/meshroom/ui/qml/GraphEditor/Node.qml
@@ -27,6 +27,11 @@ Item {
readonly property color defaultColor: isCompatibilityNode ? "#444" : activePalette.base
property color baseColor: defaultColor
+ Item {
+ id: m
+ property bool displayParams: false
+ }
+
// Mouse interaction related signals
signal pressed(var mouse)
signal doubleClicked(var mouse)
@@ -60,7 +65,7 @@ Item {
}
// Whether an attribute can be displayed as an attribute pin on the node
- function isDisplayableAsPin(attribute) {
+ function isFileAttributeBaseType(attribute) {
// ATM, only File attributes are meant to be connected
// TODO: review this if we want to connect something else
return attribute.type == "File"
@@ -110,7 +115,7 @@ Item {
// Selection border
Rectangle {
- anchors.fill: parent
+ anchors.fill: nodeContent
anchors.margins: -border.width
visible: root.selected || root.hovered
border.width: 2.5
@@ -120,10 +125,9 @@ Item {
color: "transparent"
}
- // Background
Rectangle {
id: background
- anchors.fill: parent
+ anchors.fill: nodeContent
color: Qt.lighter(activePalette.base, 1.4)
layer.enabled: true
layer.effect: DropShadow { radius: 3; color: shadowColor }
@@ -131,192 +135,284 @@ Item {
opacity: 0.7
}
- // Data Layout
- Column {
- id: body
+ Rectangle {
+ id: nodeContent
width: parent.width
+ height: childrenRect.height
+ color: "transparent"
- // Header
- Rectangle {
- id: header
+ // Data Layout
+ Column {
+ id: body
width: parent.width
- height: headerLayout.height
- color: root.selected ? activePalette.highlight : root.baseColor
- radius: background.radius
- // Fill header's bottom radius
+ // Header
Rectangle {
+ id: header
width: parent.width
- height: parent.radius
- anchors.bottom: parent.bottom
- color: parent.color
- z: -1
- }
+ height: headerLayout.height
+ color: root.selected ? activePalette.highlight : root.baseColor
+ radius: background.radius
- // Header Layout
- RowLayout {
- id: headerLayout
- width: parent.width
- spacing: 0
-
- // Node Name
- Label {
- Layout.fillWidth: true
- text: node ? node.label : ""
- padding: 4
- color: root.selected ? "white" : activePalette.text
- elide: Text.ElideMiddle
- font.pointSize: 8
+ // Fill header's bottom radius
+ Rectangle {
+ width: parent.width
+ height: parent.radius
+ anchors.bottom: parent.bottom
+ color: parent.color
+ z: -1
}
- // Node State icons
+ // Header Layout
RowLayout {
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignRight
- Layout.rightMargin: 2
- spacing: 2
+ id: headerLayout
+ width: parent.width
+ spacing: 0
- // CompatibilityBadge icon for CompatibilityNodes
- Loader {
- active: root.isCompatibilityNode
- sourceComponent: CompatibilityBadge {
- sourceComponent: iconDelegate
- canUpgrade: root.node.canUpgrade
- issueDetails: root.node.issueDetails
+ // Node Name
+ Label {
+ Layout.fillWidth: true
+ text: node ? node.label : ""
+ padding: 4
+ color: root.selected ? "white" : activePalette.text
+ elide: Text.ElideMiddle
+ font.pointSize: 8
+ }
+
+ // Node State icons
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 2
+ spacing: 2
+
+ // CompatibilityBadge icon for CompatibilityNodes
+ Loader {
+ active: root.isCompatibilityNode
+ sourceComponent: CompatibilityBadge {
+ sourceComponent: iconDelegate
+ canUpgrade: root.node.canUpgrade
+ issueDetails: root.node.issueDetails
+ }
}
- }
- // Data sharing indicator
- // Note: for an unknown reason, there are some performance issues with the UI refresh.
- // Example: a node duplicated 40 times will be slow while creating another identical node
- // (sharing the same uid) will not be as slow. If save, quit and reload, it will become slow.
- MaterialToolButton {
- property string baseText: "Shares internal folder (data) with other node(s). Hold click for details."
- property string toolTipText: visible ? baseText : ""
- visible: node.hasDuplicates
- text: MaterialIcons.layers
- font.pointSize: 7
- padding: 2
- palette.text: Colors.sysPalette.text
- ToolTip.text: toolTipText
+ // Data sharing indicator
+ // Note: for an unknown reason, there are some performance issues with the UI refresh.
+ // Example: a node duplicated 40 times will be slow while creating another identical node
+ // (sharing the same uid) will not be as slow. If save, quit and reload, it will become slow.
+ MaterialToolButton {
+ property string baseText: "Shares internal folder (data) with other node(s). Hold click for details."
+ property string toolTipText: visible ? baseText : ""
+ visible: node.hasDuplicates
+ text: MaterialIcons.layers
+ font.pointSize: 7
+ padding: 2
+ palette.text: Colors.sysPalette.text
+ ToolTip.text: toolTipText
- onPressed: { offsetReleased.running = false; toolTipText = visible ? generateDuplicateList() : "" }
- onReleased: { toolTipText = "" ; offsetReleased.running = true }
- onCanceled: released()
+ onPressed: { offsetReleased.running = false; toolTipText = visible ? generateDuplicateList() : "" }
+ onReleased: { toolTipText = "" ; offsetReleased.running = true }
+ onCanceled: released()
- // Used for a better user experience with the button
- // Avoid to change the text too quickly
- Timer {
- id: offsetReleased
- interval: 750; running: false; repeat: false
- onTriggered: parent.toolTipText = visible ? parent.baseText : ""
+ // Used for a better user experience with the button
+ // Avoid to change the text too quickly
+ Timer {
+ id: offsetReleased
+ interval: 750; running: false; repeat: false
+ onTriggered: parent.toolTipText = visible ? parent.baseText : ""
+ }
}
- }
- // Submitted externally indicator
- MaterialLabel {
- visible: ["SUBMITTED", "RUNNING"].includes(node.globalStatus) && node.chunks.count > 0 && node.globalExecMode === "EXTERN"
- text: MaterialIcons.cloud
- padding: 2
- font.pointSize: 7
- palette.text: Colors.sysPalette.text
- ToolTip.text: "Computed Externally"
- }
+ // Submitted externally indicator
+ MaterialLabel {
+ visible: ["SUBMITTED", "RUNNING"].includes(node.globalStatus) && node.chunks.count > 0 && node.globalExecMode === "EXTERN"
+ text: MaterialIcons.cloud
+ padding: 2
+ font.pointSize: 7
+ palette.text: Colors.sysPalette.text
+ ToolTip.text: "Computed Externally"
+ }
- // Lock indicator
- MaterialLabel {
- visible: root.readOnly
- text: MaterialIcons.lock
- padding: 2
- font.pointSize: 7
- palette.text: "red"
- ToolTip.text: "Locked"
+ // Lock indicator
+ MaterialLabel {
+ visible: root.readOnly
+ text: MaterialIcons.lock
+ padding: 2
+ font.pointSize: 7
+ palette.text: "red"
+ ToolTip.text: "Locked"
+ }
}
}
}
- }
- // Node Chunks
- NodeChunks {
- defaultColor: Colors.sysPalette.mid
- implicitHeight: 3
- width: parent.width
- model: node ? node.chunks : undefined
+ // Node Chunks
+ NodeChunks {
+ defaultColor: Colors.sysPalette.mid
+ implicitHeight: 3
+ width: parent.width
+ model: node ? node.chunks : undefined
- Rectangle {
- anchors.fill: parent
- color: Colors.sysPalette.mid
- z: -1
+ Rectangle {
+ anchors.fill: parent
+ color: Colors.sysPalette.mid
+ z: -1
+ }
}
- }
- // Vertical Spacer
- Item { width: parent.width; height: 2 }
+ // Vertical Spacer
+ Item { width: parent.width; height: 2 }
- // Input/Output Attributes
- Item {
- id: nodeAttributes
- width: parent.width - 2
- height: childrenRect.height
- anchors.horizontalCenter: parent.horizontalCenter
+ // Input/Output Attributes
+ Item {
+ id: nodeAttributes
+ width: parent.width - 2
+ height: childrenRect.height
+ anchors.horizontalCenter: parent.horizontalCenter
- enabled: !root.readOnly && !root.isCompatibilityNode
-
- Column {
- width: parent.width
- spacing: 5
- bottomPadding: 2
+ enabled: !root.readOnly && !root.isCompatibilityNode
Column {
- id: outputs
+ id: attributesColumn
width: parent.width
- spacing: 3
- Repeater {
- model: node ? node.attributes : undefined
+ spacing: 5
+ bottomPadding: 2
- delegate: Loader {
- id: outputLoader
- active: object.isOutput && isDisplayableAsPin(object)
- anchors.right: parent.right
- width: outputs.width
+ Column {
+ id: outputs
+ width: parent.width
+ spacing: 3
+ Repeater {
+ model: node ? node.attributes : undefined
- sourceComponent: AttributePin {
- id: outPin
- nodeItem: root
- attribute: object
+ delegate: Loader {
+ id: outputLoader
+ active: object.isOutput && isFileAttributeBaseType(object)
+ anchors.right: parent.right
+ width: outputs.width
- readOnly: root.readOnly
- onPressed: root.pressed(mouse)
- Component.onCompleted: attributePinCreated(object, outPin)
- Component.onDestruction: attributePinDeleted(attribute, outPin)
+ sourceComponent: AttributePin {
+ id: outPin
+ nodeItem: root
+ attribute: object
+
+ property real globalX: root.x + nodeAttributes.x + outputs.x + outputLoader.x + outPin.x
+ property real globalY: root.y + nodeAttributes.y + outputs.y + outputLoader.y + outPin.y
+
+ readOnly: root.readOnly
+ onPressed: root.pressed(mouse)
+ Component.onCompleted: attributePinCreated(object, outPin)
+ Component.onDestruction: attributePinDeleted(attribute, outPin)
+ }
}
}
}
- }
- Column {
- id: inputs
- width: parent.width
- spacing: 3
- Repeater {
- model: node ? node.attributes : undefined
- delegate: Loader {
- active: !object.isOutput && isDisplayableAsPin(object)
- width: inputs.width
+ Column {
+ id: inputs
+ width: parent.width
+ spacing: 3
+ Repeater {
+ model: node ? node.attributes : undefined
+ delegate: Loader {
+ id: inputLoader
+ active: !object.isOutput && isFileAttributeBaseType(object)
+ width: inputs.width
- sourceComponent: AttributePin {
- id: inPin
- nodeItem: root
- attribute: object
- readOnly: root.readOnly
- Component.onCompleted: attributePinCreated(attribute, inPin)
- Component.onDestruction: attributePinDeleted(attribute, inPin)
- onPressed: root.pressed(mouse)
- onChildPinCreated: attributePinCreated(childAttribute, inPin)
- onChildPinDeleted: attributePinDeleted(childAttribute, inPin)
+ sourceComponent: AttributePin {
+ id: inPin
+ nodeItem: root
+ attribute: object
+
+ property real globalX: root.x + nodeAttributes.x + inputs.x + inputLoader.x + inPin.x
+ property real globalY: root.y + nodeAttributes.y + inputs.y + inputLoader.y + inPin.y
+
+ readOnly: root.readOnly
+ Component.onCompleted: attributePinCreated(attribute, inPin)
+ Component.onDestruction: attributePinDeleted(attribute, inPin)
+ onPressed: root.pressed(mouse)
+ onChildPinCreated: attributePinCreated(childAttribute, inPin)
+ onChildPinDeleted: attributePinDeleted(childAttribute, inPin)
+ }
}
}
}
+
+ // Vertical Spacer
+ Rectangle {
+ height: inputParams.height > 0 ? 3 : 0
+ visible: (height == 3)
+ Behavior on height { PropertyAnimation {easing.type: Easing.Linear} }
+ width: parent.width
+ color: Colors.sysPalette.mid
+ MaterialToolButton {
+ text: " "
+ width: parent.width
+ height: parent.height
+ padding: 0
+ spacing: 0
+ anchors.margins: 0
+ font.pointSize: 6
+ onClicked: {
+ m.displayParams = ! m.displayParams
+ }
+ }
+ }
+
+ Rectangle {
+ id: inputParamsRect
+ width: parent.width
+ height: childrenRect.height
+ color: "transparent"
+
+ Column {
+ id: inputParams
+ width: parent.width
+ spacing: 3
+ Repeater {
+ id: inputParamsRepeater
+ model: node ? node.attributes : undefined
+ delegate: Loader {
+ id: paramLoader
+ active: !object.isOutput && !isFileAttributeBaseType(object)
+ property bool isFullyActive: (m.displayParams || object.isLink || object.hasOutputConnections)
+ width: parent.width
+
+ sourceComponent: AttributePin {
+ id: inPin
+ nodeItem: root
+ property real globalX: root.x + nodeAttributes.x + inputParamsRect.x + paramLoader.x + inPin.x
+ property real globalY: root.y + nodeAttributes.y + inputParamsRect.y + paramLoader.y + inPin.y
+
+ height: isFullyActive ? childrenRect.height : 0
+ Behavior on height { PropertyAnimation {easing.type: Easing.Linear} }
+ visible: (height == childrenRect.height)
+ attribute: object
+ readOnly: root.readOnly
+ Component.onCompleted: attributePinCreated(attribute, inPin)
+ Component.onDestruction: attributePinDeleted(attribute, inPin)
+ onPressed: root.pressed(mouse)
+ onChildPinCreated: attributePinCreated(childAttribute, inPin)
+ onChildPinDeleted: attributePinDeleted(childAttribute, inPin)
+ }
+ }
+ }
+ }
+ }
+
+ MaterialToolButton {
+ text: root.hovered ? (m.displayParams ? MaterialIcons.arrow_drop_up : MaterialIcons.arrow_drop_down) : " "
+ Layout.alignment: Qt.AlignBottom
+ width: parent.width
+ height: 5
+ padding: 0
+ spacing: 0
+ anchors.margins: 0
+ font.pointSize: 10
+ onClicked: {
+ m.displayParams = ! m.displayParams
+ }
+ }
}
}
}