mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-25 20:47:39 +02:00
[GraphEditor] Allow to display and connect attributes within GroupAttribute
This commit is contained in:
parent
b9dcc7f3bb
commit
a97778f2c2
2 changed files with 180 additions and 21 deletions
|
@ -13,6 +13,7 @@ RowLayout {
|
||||||
|
|
||||||
property var nodeItem
|
property var nodeItem
|
||||||
property var attribute
|
property var attribute
|
||||||
|
property bool expanded: false
|
||||||
property bool readOnly: false
|
property bool readOnly: false
|
||||||
/// Whether to display an output pin for input attribute
|
/// Whether to display an output pin for input attribute
|
||||||
property bool displayOutputPinForInput: true
|
property bool displayOutputPinForInput: true
|
||||||
|
@ -25,6 +26,8 @@ RowLayout {
|
||||||
outputAnchor.y + outputAnchor.height / 2)
|
outputAnchor.y + outputAnchor.height / 2)
|
||||||
|
|
||||||
readonly property bool isList: attribute && attribute.type === "ListAttribute"
|
readonly property bool isList: attribute && attribute.type === "ListAttribute"
|
||||||
|
readonly property bool isGroup: attribute && attribute.type === "GroupAttribute"
|
||||||
|
readonly property bool isChild: attribute && attribute.root
|
||||||
|
|
||||||
signal childPinCreated(var childAttribute, var pin)
|
signal childPinCreated(var childAttribute, var pin)
|
||||||
signal childPinDeleted(var childAttribute, var pin)
|
signal childPinDeleted(var childAttribute, var pin)
|
||||||
|
@ -32,6 +35,8 @@ RowLayout {
|
||||||
signal pressed(var mouse)
|
signal pressed(var mouse)
|
||||||
signal edgeAboutToBeRemoved(var input)
|
signal edgeAboutToBeRemoved(var input)
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
|
||||||
objectName: attribute ? attribute.name + "." : ""
|
objectName: attribute ? attribute.name + "." : ""
|
||||||
layoutDirection: Qt.LeftToRight
|
layoutDirection: Qt.LeftToRight
|
||||||
spacing: 3
|
spacing: 3
|
||||||
|
@ -52,6 +57,24 @@ RowLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateLabel() {
|
||||||
|
var label = ""
|
||||||
|
var expandedGroup = expanded ? "-" : "+"
|
||||||
|
if (attribute && attribute.label !== undefined) {
|
||||||
|
label = attribute.label
|
||||||
|
if (isGroup && attribute.isOutput) {
|
||||||
|
label = label + " " + expandedGroup
|
||||||
|
} else if (isGroup && !attribute.isOutput) {
|
||||||
|
label = expandedGroup + " " + label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpandedChanged: {
|
||||||
|
nameLabel.text = updateLabel()
|
||||||
|
}
|
||||||
|
|
||||||
// Instantiate empty Items for each child attribute
|
// Instantiate empty Items for each child attribute
|
||||||
Repeater {
|
Repeater {
|
||||||
id: childrenRepeater
|
id: childrenRepeater
|
||||||
|
@ -171,7 +194,8 @@ RowLayout {
|
||||||
onReleased: {
|
onReleased: {
|
||||||
inputDragTarget.Drag.drop()
|
inputDragTarget.Drag.drop()
|
||||||
}
|
}
|
||||||
hoverEnabled: true
|
onClicked: root.clicked()
|
||||||
|
hoverEnabled: root.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
Edge {
|
Edge {
|
||||||
|
@ -197,18 +221,22 @@ RowLayout {
|
||||||
id: nameLabel
|
id: nameLabel
|
||||||
|
|
||||||
enabled: !root.readOnly
|
enabled: !root.readOnly
|
||||||
|
visible: true
|
||||||
property bool hovered: (inputConnectMA.containsMouse || inputConnectMA.drag.active || inputDropArea.containsDrag || outputConnectMA.containsMouse || outputConnectMA.drag.active || outputDropArea.containsDrag)
|
property bool hovered: (inputConnectMA.containsMouse || inputConnectMA.drag.active || inputDropArea.containsDrag || outputConnectMA.containsMouse || outputConnectMA.drag.active || outputDropArea.containsDrag)
|
||||||
text: (attribute && attribute.label) !== undefined ? attribute.label : ""
|
text: root.updateLabel()
|
||||||
elide: hovered ? Text.ElideNone : Text.ElideMiddle
|
elide: hovered ? Text.ElideNone : Text.ElideMiddle
|
||||||
width: hovered ? contentWidth : parent.width
|
width: hovered ? contentWidth : parent.width
|
||||||
font.pointSize: 7
|
font.pointSize: 7
|
||||||
|
font.italic: isChild ? true : false
|
||||||
horizontalAlignment: attribute && attribute.isOutput ? Text.AlignRight : Text.AlignLeft
|
horizontalAlignment: attribute && attribute.isOutput ? Text.AlignRight : Text.AlignLeft
|
||||||
anchors.right: attribute && attribute.isOutput ? parent.right : undefined
|
anchors.right: attribute && attribute.isOutput ? parent.right : undefined
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
color: {
|
color: {
|
||||||
if ((object.hasOutputConnections || object.isLink) && !object.enabled)
|
if ((object.hasOutputConnections || object.isLink) && !object.enabled)
|
||||||
return Colors.lightgrey
|
return Colors.lightgrey
|
||||||
return hovered ? palette.highlight : palette.text
|
else if (hovered)
|
||||||
|
return palette.highlight
|
||||||
|
return palette.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,8 +262,8 @@ RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 2
|
anchors.margins: 2
|
||||||
color: {
|
color: {
|
||||||
if (object.enabled && (outputConnectMA.containsMouse || outputConnectMA.drag.active ||
|
if (modelData.enabled && (outputConnectMA.containsMouse || outputConnectMA.drag.active ||
|
||||||
(outputDropArea.containsDrag && outputDropArea.acceptableDrop)))
|
(outputDropArea.containsDrag && outputDropArea.acceptableDrop)))
|
||||||
return Colors.sysPalette.highlight
|
return Colors.sysPalette.highlight
|
||||||
return Colors.sysPalette.text
|
return Colors.sysPalette.text
|
||||||
}
|
}
|
||||||
|
@ -314,8 +342,9 @@ RowLayout {
|
||||||
|
|
||||||
onPressed: function(mouse) { root.pressed(mouse) }
|
onPressed: function(mouse) { root.pressed(mouse) }
|
||||||
onReleased: outputDragTarget.Drag.drop()
|
onReleased: outputDragTarget.Drag.drop()
|
||||||
|
onClicked: root.clicked()
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: root.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
Edge {
|
Edge {
|
||||||
|
|
|
@ -117,6 +117,54 @@ Item {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateChildPin(attribute, parentPins, pin) {
|
||||||
|
/*
|
||||||
|
* Update the pin of a child attribute: if the attribute is enabled and its parent is a GroupAttribute,
|
||||||
|
* the visibility is determined based on the parent pin's "expanded" state, using the "parentPins" map to
|
||||||
|
* access the status.
|
||||||
|
* If the current pin is also a GroupAttribute and is expanded while its newly "visible" state is false,
|
||||||
|
* it is reset.
|
||||||
|
*/
|
||||||
|
if (Boolean(attribute.enabled)) {
|
||||||
|
// If the parent's a GroupAttribute, use status of the parent's pin to determine visibility
|
||||||
|
if (attribute.root && attribute.root.type === "GroupAttribute") {
|
||||||
|
var visible = Boolean(parentPins.get(attribute.root.name))
|
||||||
|
if (!visible && parentPins.has(attribute.name) && parentPins.get(attribute.name) === true) {
|
||||||
|
parentPins.set(attribute.name, false)
|
||||||
|
pin.expanded = false
|
||||||
|
}
|
||||||
|
return visible
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateAttributesModel(isOutput, parentPins) {
|
||||||
|
if (!node)
|
||||||
|
return undefined
|
||||||
|
|
||||||
|
const attributes = []
|
||||||
|
for (let i = 0; i < node.attributes.count; ++i) {
|
||||||
|
let attr = node.attributes.at(i)
|
||||||
|
if (attr.isOutput == isOutput) {
|
||||||
|
attributes.push(attr)
|
||||||
|
if (attr.type === "GroupAttribute") {
|
||||||
|
parentPins.set(attr.name, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < attr.flattenedChildren.count; ++j) {
|
||||||
|
attributes.push(attr.flattenedChildren.at(j))
|
||||||
|
if (attr.flattenedChildren.at(j).type === "GroupAttribute") {
|
||||||
|
parentPins.set(attr.flattenedChildren.at(j).name, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes
|
||||||
|
}
|
||||||
|
|
||||||
// Main Layout
|
// Main Layout
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
@ -398,26 +446,53 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: 3
|
spacing: 3
|
||||||
|
|
||||||
|
property var parentPins: new Map()
|
||||||
|
signal parentPinsUpdated()
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: node ? node.attributes : undefined
|
model: generateAttributesModel(true, outputs.parentPins) // isOutput = true
|
||||||
|
|
||||||
delegate: Loader {
|
delegate: Loader {
|
||||||
id: outputLoader
|
id: outputLoader
|
||||||
active: Boolean(object.isOutput && object.desc.visible)
|
active: Boolean(modelData.isOutput && modelData.desc.visible)
|
||||||
visible: Boolean(object.enabled || object.hasOutputConnections)
|
|
||||||
|
visible: {
|
||||||
|
if (Boolean(modelData.enabled || modelData.hasOutputConnections)) {
|
||||||
|
if (modelData.root && modelData.root.type === "GroupAttribute") {
|
||||||
|
return Boolean(outputs.parentPins.get(modelData.root.name))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: outputs.width
|
width: outputs.width
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: outputs
|
||||||
|
|
||||||
|
function onParentPinsUpdated() {
|
||||||
|
visible = updateChildPin(modelData, outputs.parentPins, outputLoader.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceComponent: AttributePin {
|
sourceComponent: AttributePin {
|
||||||
id: outPin
|
id: outPin
|
||||||
nodeItem: root
|
nodeItem: root
|
||||||
attribute: object
|
attribute: modelData
|
||||||
|
|
||||||
property real globalX: root.x + nodeAttributes.x + outputs.x + outputLoader.x + outPin.x
|
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
|
property real globalY: root.y + nodeAttributes.y + outputs.y + outputLoader.y + outPin.y
|
||||||
|
|
||||||
onPressed: function(mouse) { root.pressed(mouse) }
|
onPressed: function(mouse) { root.pressed(mouse) }
|
||||||
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
|
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
|
||||||
|
onClicked: {
|
||||||
|
expanded = !expanded
|
||||||
|
if (outputs.parentPins.has(modelData.name)) {
|
||||||
|
outputs.parentPins.set(modelData.name, expanded)
|
||||||
|
outputs.parentPinsUpdated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: attributePinCreated(attribute, outPin)
|
Component.onCompleted: attributePinCreated(attribute, outPin)
|
||||||
onChildPinCreated: attributePinCreated(childAttribute, outPin)
|
onChildPinCreated: attributePinCreated(childAttribute, outPin)
|
||||||
|
@ -433,19 +508,38 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: 3
|
spacing: 3
|
||||||
|
|
||||||
|
property var parentPins: new Map()
|
||||||
|
signal parentPinsUpdated()
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: node ? node.attributes : undefined
|
model: generateAttributesModel(false, inputs.parentPins) // isOutput = false
|
||||||
|
|
||||||
delegate: Loader {
|
delegate: Loader {
|
||||||
id: inputLoader
|
id: inputLoader
|
||||||
active: !object.isOutput && object.exposed && object.desc.visible
|
active: !modelData.isOutput && modelData.exposed && modelData.desc.visible
|
||||||
visible: Boolean(object.enabled)
|
visible: {
|
||||||
|
if (Boolean(modelData.enabled)) {
|
||||||
|
if (modelData.root && modelData.root.type === "GroupAttribute") {
|
||||||
|
return Boolean(inputs.parentPins.get(modelData.root.name))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
width: inputs.width
|
width: inputs.width
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: inputs
|
||||||
|
|
||||||
|
function onParentPinsUpdated() {
|
||||||
|
visible = updateChildPin(modelData, inputs.parentPins, inputLoader.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceComponent: AttributePin {
|
sourceComponent: AttributePin {
|
||||||
id: inPin
|
id: inPin
|
||||||
nodeItem: root
|
nodeItem: root
|
||||||
attribute: object
|
attribute: modelData
|
||||||
|
|
||||||
property real globalX: root.x + nodeAttributes.x + inputs.x + inputLoader.x + inPin.x
|
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
|
property real globalY: root.y + nodeAttributes.y + inputs.y + inputLoader.y + inPin.y
|
||||||
|
@ -454,6 +548,13 @@ Item {
|
||||||
Component.onCompleted: attributePinCreated(attribute, inPin)
|
Component.onCompleted: attributePinCreated(attribute, inPin)
|
||||||
Component.onDestruction: attributePinDeleted(attribute, inPin)
|
Component.onDestruction: attributePinDeleted(attribute, inPin)
|
||||||
onPressed: function(mouse) { root.pressed(mouse) }
|
onPressed: function(mouse) { root.pressed(mouse) }
|
||||||
|
onClicked: {
|
||||||
|
expanded = !expanded
|
||||||
|
if (inputs.parentPins.has(modelData.name)) {
|
||||||
|
inputs.parentPins.set(modelData.name, expanded)
|
||||||
|
inputs.parentPinsUpdated()
|
||||||
|
}
|
||||||
|
}
|
||||||
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
|
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
|
||||||
onChildPinCreated: function(childAttribute, inPin) { attributePinCreated(childAttribute, inPin) }
|
onChildPinCreated: function(childAttribute, inPin) { attributePinCreated(childAttribute, inPin) }
|
||||||
onChildPinDeleted: function(childAttribute, inPin) { attributePinDeleted(childAttribute, inPin) }
|
onChildPinDeleted: function(childAttribute, inPin) { attributePinDeleted(childAttribute, inPin) }
|
||||||
|
@ -493,30 +594,59 @@ Item {
|
||||||
id: inputParams
|
id: inputParams
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: 3
|
spacing: 3
|
||||||
|
|
||||||
|
property var parentPins: new Map()
|
||||||
|
signal parentPinsUpdated()
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: inputParamsRepeater
|
model: generateAttributesModel(false, inputParams.parentPins) // isOutput = false
|
||||||
model: node ? node.attributes : undefined
|
|
||||||
delegate: Loader {
|
delegate: Loader {
|
||||||
id: paramLoader
|
id: paramLoader
|
||||||
active: !object.isOutput && !object.exposed && object.desc.visible
|
active: !modelData.isOutput && !modelData.exposed && modelData.desc.visible
|
||||||
visible: Boolean(object.enabled || object.isLinkNested || object.hasOutputConnections)
|
visible: {
|
||||||
property bool isFullyActive: Boolean(m.displayParams || object.isLinkNested || object.hasOutputConnections)
|
if (Boolean(modelData.enabled || modelData.isLinkNested || modelData.hasOutputConnections)) {
|
||||||
|
if (modelData.root && modelData.root.type === "GroupAttribute") {
|
||||||
|
return Boolean(inputParams.parentPins.get(modelData.root.name))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
property bool isFullyActive: Boolean(m.displayParams || modelData.isLinkNested || modelData.hasOutputConnections)
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: inputParams
|
||||||
|
|
||||||
|
function onParentPinsUpdated() {
|
||||||
|
visible = updateChildPin(modelData, inputParams.parentPins, paramLoader.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceComponent: AttributePin {
|
sourceComponent: AttributePin {
|
||||||
id: inParamsPin
|
id: inParamsPin
|
||||||
nodeItem: root
|
nodeItem: root
|
||||||
|
attribute: modelData
|
||||||
|
|
||||||
property real globalX: root.x + nodeAttributes.x + inputParamsRect.x + paramLoader.x + inParamsPin.x
|
property real globalX: root.x + nodeAttributes.x + inputParamsRect.x + paramLoader.x + inParamsPin.x
|
||||||
property real globalY: root.y + nodeAttributes.y + inputParamsRect.y + paramLoader.y + inParamsPin.y
|
property real globalY: root.y + nodeAttributes.y + inputParamsRect.y + paramLoader.y + inParamsPin.y
|
||||||
|
|
||||||
height: isFullyActive ? childrenRect.height : 0
|
height: isFullyActive ? childrenRect.height : 0
|
||||||
Behavior on height { PropertyAnimation {easing.type: Easing.Linear} }
|
Behavior on height { PropertyAnimation {easing.type: Easing.Linear} }
|
||||||
visible: (height == childrenRect.height)
|
visible: (height == childrenRect.height)
|
||||||
attribute: object
|
|
||||||
readOnly: Boolean(root.readOnly || object.isReadOnly)
|
readOnly: Boolean(root.readOnly || modelData.isReadOnly)
|
||||||
Component.onCompleted: attributePinCreated(attribute, inParamsPin)
|
Component.onCompleted: attributePinCreated(attribute, inParamsPin)
|
||||||
Component.onDestruction: attributePinDeleted(attribute, inParamsPin)
|
Component.onDestruction: attributePinDeleted(attribute, inParamsPin)
|
||||||
onPressed: function(mouse) { root.pressed(mouse) }
|
onPressed: function(mouse) { root.pressed(mouse) }
|
||||||
|
onClicked: {
|
||||||
|
expanded = !expanded
|
||||||
|
if (inputParams.parentPins.has(modelData.name)) {
|
||||||
|
inputParams.parentPins.set(modelData.name, expanded)
|
||||||
|
inputParams.parentPinsUpdated()
|
||||||
|
}
|
||||||
|
}
|
||||||
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
|
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
|
||||||
onChildPinCreated: function(childAttribute, inParamsPin) { attributePinCreated(childAttribute, inParamsPin) }
|
onChildPinCreated: function(childAttribute, inParamsPin) { attributePinCreated(childAttribute, inParamsPin) }
|
||||||
onChildPinDeleted: function(childAttribute, inParamsPin) { attributePinDeleted(childAttribute, inParamsPin) }
|
onChildPinDeleted: function(childAttribute, inParamsPin) { attributePinDeleted(childAttribute, inParamsPin) }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue