From ef57737108fbd19117a8a21adf25c86832c37742 Mon Sep 17 00:00:00 2001 From: nicolas lambert Date: Fri, 2 May 2025 09:12:16 +0200 Subject: [PATCH 01/20] [ui] AttributeEditor: Add navigation buttons for linked attributes --- meshroom/core/attribute.py | 34 +++++++++++++++++++ meshroom/ui/qml/Application.qml | 28 +++++++++++++++ .../ui/qml/GraphEditor/AttributeEditor.qml | 10 ++++++ .../qml/GraphEditor/AttributeItemDelegate.qml | 26 ++++++++++++++ meshroom/ui/qml/GraphEditor/NodeEditor.qml | 9 +++++ 5 files changed, 107 insertions(+) diff --git a/meshroom/core/attribute.py b/meshroom/core/attribute.py index 59856e4290..44edef5683 100644 --- a/meshroom/core/attribute.py +++ b/meshroom/core/attribute.py @@ -12,6 +12,11 @@ from meshroom.common import BaseObject, Property, Variant, Signal, ListModel, DictModel, Slot from meshroom.core import desc, hashValue +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from meshroom.core.graph import Edge + def attributeFactory(description, value, isOutput, node, root=None, parent=None): """ @@ -326,6 +331,33 @@ def hasOutputConnections(self): any(attr.hasOutputConnections for attr in self._value if hasattr(attr, 'hasOutputConnections')) return next((edge for edge in self.node.graph.edges.values() if edge.src == self), None) is not None + def getInputConnections(self) -> list["Edge"]: + """ Retrieve the upstreams connected edges """ + + if not self.node.graph or not self.node.graph.edges: + return [] + + return [edge for edge in self.node.graph.edges.values() if edge.dst == self] + + def getOutputConnections(self) -> list["Edge"]: + """ Retrieve all the edges connected to this attribute """ + + if not self.node.graph or not self.node.graph.edges: + return [] + + return [edge for edge in self.node.graph.edges.values() if edge.src == self] + + def getInputAttributes(self) -> list["Edge"]: + """ Return the upstreams connected attributes """ + + return [edge.src for edge in self.getInputConnections()] + + + def getOutputAttributes(self): + """ Return the downstreams connected attributes """ + + return [edge.dst for edge in self.getOutputConnections()] + def _applyExpr(self): """ For string parameters with an expression (when loaded from file), @@ -449,6 +481,8 @@ def updateInternals(self): isLinkNested = isLink hasOutputConnectionsChanged = Signal() hasOutputConnections = Property(bool, hasOutputConnections.fget, notify=hasOutputConnectionsChanged) + inputAttributes = Property(Variant, getInputAttributes) + outputAttributes = Property(Variant, getOutputAttributes) isDefault = Property(bool, _isDefault, notify=valueChanged) linkParam = Property(BaseObject, getLinkParam, notify=isLinkChanged) rootLinkParam = Property(BaseObject, lambda self: self.getLinkParam(recursive=True), notify=isLinkChanged) diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml index 59e04d1288..bdf175603c 100644 --- a/meshroom/ui/qml/Application.qml +++ b/meshroom/ui/qml/Application.qml @@ -1342,7 +1342,35 @@ Page { onUpgradeRequest: { var n = _reconstruction.upgradeNode(node) _reconstruction.selectedNode = n + } + + onInAttributeClicked: function(mouse, inAttributes) { + selectNodesFromAttributes(inAttributes) + } + + onOutAttributeClicked: function(mouse, outAttributes) { + selectNodesFromAttributes(outAttributes) } + + function selectNodesFromAttributes(attributes) { + /* + Retrieve the nodes from givn attributes, and select its + */ + + console.log("attributes", attributes) + + if ( !attributes || attributes.length == 0) { return } + + graphEditor.uigraph.clearNodeSelection() + + const nodes = attributes.map( attr => attr.node) + + if (attributes.length == 1) { + _reconstruction.selectedNode = attributes[0].node + } + graphEditor.uigraph.selectNodes(nodes) + } + } } } diff --git a/meshroom/ui/qml/GraphEditor/AttributeEditor.qml b/meshroom/ui/qml/GraphEditor/AttributeEditor.qml index 940df7a1a2..2702c0994d 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeEditor.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeEditor.qml @@ -16,6 +16,8 @@ ListView { signal upgradeRequest() signal attributeDoubleClicked(var mouse, var attribute) + signal inAttributeClicked(var mouse, var inAttributes) + signal outAttributeClicked(var mouse, var outAttributes) implicitHeight: contentHeight @@ -40,9 +42,17 @@ ListView { filterText: root.filterText objectsHideable: root.objectsHideable attribute: object + onDoubleClicked: function(mouse, attr) { root.attributeDoubleClicked(mouse, attr) } + onInAttributeClicked: function(mouse, inAttributes) { + root.inAttributeClicked(mouse, inAttributes) + } + onOutAttributeClicked: function(mouse, outAttributes) { + root.outAttributeClicked(mouse, outAttributes) + } + } onActiveChanged: height = active ? item.implicitHeight : -spacing diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 48ce84505e..6258bdf692 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -26,6 +26,8 @@ RowLayout { readonly property bool editable: !attribute.isOutput && !attribute.isLink && !readOnly signal doubleClicked(var mouse, var attr) + signal inAttributeClicked(var mouse, var inAttributes) + signal outAttributeClicked(var mouse, var outAttributes) spacing: 2 @@ -56,6 +58,18 @@ RowLayout { width: parent.width height: parent.height + // In connection + MaterialToolButton { + text: (object != undefined && object.isLink) ? MaterialIcons.login : " " + enabled: (object != undefined && object.isLink) + font.pointSize: 8 + ToolTip.text: (object != undefined && object.isLink) ? object.linkParam.label : "" + onClicked: function(mouse) { + root.inAttributeClicked(mouse, object.inputAttributes) + } + + } + Label { id: parameterLabel @@ -167,6 +181,18 @@ RowLayout { } } } + + MaterialToolButton { + text: (attribute != undefined && attribute.hasOutputConnections) ? MaterialIcons.logout : "" + font.pointSize: 8 + enabled: (attribute != undefined && attribute.hasOutputConnections) + + onClicked: function(mouse) { + root.outAttributeClicked(mouse, attribute.outputAttributes) + } + + } + MaterialLabel { visible: attribute.desc.advanced text: MaterialIcons.build diff --git a/meshroom/ui/qml/GraphEditor/NodeEditor.qml b/meshroom/ui/qml/GraphEditor/NodeEditor.qml index c6ddffbafa..8500317714 100644 --- a/meshroom/ui/qml/GraphEditor/NodeEditor.qml +++ b/meshroom/ui/qml/GraphEditor/NodeEditor.qml @@ -21,6 +21,8 @@ Panel { property string nodeStartDateTime: "" signal attributeDoubleClicked(var mouse, var attribute) + signal inAttributeClicked(var mouse, var inAttributes) + signal outAttributeClicked(var mouse, var outAttributes) signal upgradeRequest() title: "Node" + (node !== null ? " - " + node.label + "" + (node.label !== node.defaultLabel ? " (" + node.defaultLabel + ")" : "") : "") @@ -301,6 +303,13 @@ Panel { onAttributeDoubleClicked: function(mouse, attribute) { root.attributeDoubleClicked(mouse, attribute) } onUpgradeRequest: root.upgradeRequest() filterText: searchBar.text + + onInAttributeClicked: function(mouse, inAttributes) { + root.inAttributeClicked(mouse, inAttributes) + } + onOutAttributeClicked: function(mouse, outAttributes) { + root.outAttributeClicked(mouse, outAttributes) + } } Loader { From a26870cd875b1779e63609d1e77146e34865b320 Mon Sep 17 00:00:00 2001 From: nicolas lambert Date: Fri, 2 May 2025 09:31:43 +0200 Subject: [PATCH 02/20] [ui] AttributeEditor: Add missing singal connections to allow attribute navigation in list items --- meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 6258bdf692..dbb5ac272a 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -692,6 +692,8 @@ RowLayout { obj.label.horizontalAlignment = Text.AlignHCenter obj.label.verticalAlignment = Text.AlignVCenter obj.doubleClicked.connect(function(attr) { root.doubleClicked(attr) }) + obj.inAttributeClicked.connect(function(mouse, inAttributes) { root.inAttributeClicked(mouse, inAttributes) }) + obj.outAttributeClicked.connect(function(mouse, outAttributes) { root.outAttributeClicked(mouse, outAttributes) }) } ToolButton { enabled: root.editable From 3a9565f4e20aa8e85b3973f6fdb6913eaf2a625c Mon Sep 17 00:00:00 2001 From: nicolas lambert Date: Fri, 2 May 2025 10:30:09 +0200 Subject: [PATCH 03/20] [ui] AttributeEditor: The attribute nav buttons are now aligned with the label --- meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index dbb5ac272a..4ba54409f8 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -63,6 +63,9 @@ RowLayout { text: (object != undefined && object.isLink) ? MaterialIcons.login : " " enabled: (object != undefined && object.isLink) font.pointSize: 8 + anchors.top: parent.top + anchors.left: parent.left + topPadding: 7 ToolTip.text: (object != undefined && object.isLink) ? object.linkParam.label : "" onClicked: function(mouse) { root.inAttributeClicked(mouse, object.inputAttributes) @@ -186,7 +189,10 @@ RowLayout { text: (attribute != undefined && attribute.hasOutputConnections) ? MaterialIcons.logout : "" font.pointSize: 8 enabled: (attribute != undefined && attribute.hasOutputConnections) - + anchors.top: parent.top + anchors.right: parent.right + topPadding: 7 + onClicked: function(mouse) { root.outAttributeClicked(mouse, attribute.outputAttributes) } From 97196d99411d103551b810a14ca07b7e024d4cd8 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 11:33:03 +0200 Subject: [PATCH 04/20] [ui] Attribute: Add tests to check retrieving the linked input/output attributes --- tests/test_attributes.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/test_attributes.py diff --git a/tests/test_attributes.py b/tests/test_attributes.py new file mode 100644 index 0000000000..5784a1ff50 --- /dev/null +++ b/tests/test_attributes.py @@ -0,0 +1,29 @@ +from meshroom.core.graph import Graph + + +def test_attribute_retrieve_linked_input_and_output_attributes(): + """ + Check that an attribute can retrieve the linked input and output attributes + """ + + # n0 -- n1 -- n2 + # \ \ + # ---------- n3 + + g = Graph('') + n0 = g.addNewNode('Ls', input='/tmp') + n1 = g.addNewNode('Ls', input=n0.output) + n2 = g.addNewNode('Ls', input=n1.output) + n3 = g.addNewNode('AppendFiles', input=n1.output, input2=n2.output) + + # check that the attribute can retrieve its linked input attributes + assert len(n0.input.getInputAttributes()) == 0 + assert len(n1.input.getInputAttributes()) == 1 + assert n1.input.getInputAttributes()[0] == n0.output + + assert len(n1.output.getOutputAttributes()) == 2 + + assert n1.output.getOutputAttributes()[0] == n2.input + assert n1.output.getOutputAttributes()[1] == n3.input + + \ No newline at end of file From 2f39d41b1426460d932af84a1e02fd90babac34a Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 11:38:15 +0200 Subject: [PATCH 05/20] [ui] Attribute: Refacto the test to avoid the coverage complaining about filesystem security --- tests/test_attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 5784a1ff50..25b9bd1fb8 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -11,7 +11,7 @@ def test_attribute_retrieve_linked_input_and_output_attributes(): # ---------- n3 g = Graph('') - n0 = g.addNewNode('Ls', input='/tmp') + n0 = g.addNewNode('Ls', input='') n1 = g.addNewNode('Ls', input=n0.output) n2 = g.addNewNode('Ls', input=n1.output) n3 = g.addNewNode('AppendFiles', input=n1.output, input2=n2.output) From ea1adcd494c3c8185ead73c4910ba777f04a9fbe Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 11:45:54 +0200 Subject: [PATCH 06/20] [ui] AttributeEditor: Remove console.log --- meshroom/ui/qml/Application.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml index bdf175603c..f05252ed7c 100644 --- a/meshroom/ui/qml/Application.qml +++ b/meshroom/ui/qml/Application.qml @@ -1354,11 +1354,9 @@ Page { function selectNodesFromAttributes(attributes) { /* - Retrieve the nodes from givn attributes, and select its + Retrieve the nodes from given attributes, and select its */ - console.log("attributes", attributes) - if ( !attributes || attributes.length == 0) { return } graphEditor.uigraph.clearNodeSelection() From a3056c828c40855797180d7f52f0758592e92c8f Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 11:48:17 +0200 Subject: [PATCH 07/20] [ui] AttributeEditor: Factorize the visibility conditions in nav buttons --- .../ui/qml/GraphEditor/AttributeItemDelegate.qml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 4ba54409f8..404f254fbc 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -60,13 +60,16 @@ RowLayout { // In connection MaterialToolButton { - text: (object != undefined && object.isLink) ? MaterialIcons.login : " " - enabled: (object != undefined && object.isLink) + property var shouldBeVisible: (object != undefined && object.isLink) + + text: shouldBeVisible ? MaterialIcons.login : " " + enabled: shouldBeVisible font.pointSize: 8 anchors.top: parent.top anchors.left: parent.left topPadding: 7 - ToolTip.text: (object != undefined && object.isLink) ? object.linkParam.label : "" + ToolTip.text: shouldBeVisible ? object.linkParam.label : "" + onClicked: function(mouse) { root.inAttributeClicked(mouse, object.inputAttributes) } @@ -186,9 +189,11 @@ RowLayout { } MaterialToolButton { - text: (attribute != undefined && attribute.hasOutputConnections) ? MaterialIcons.logout : "" + property var shouldBeVisible: (attribute != undefined && attribute.hasOutputConnections) + + text: shouldBeVisible ? MaterialIcons.logout : "" font.pointSize: 8 - enabled: (attribute != undefined && attribute.hasOutputConnections) + enabled: shouldBeVisible anchors.top: parent.top anchors.right: parent.right topPadding: 7 From 96a09fe0c494c02e7569ee21908a03753588fb8d Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 12:00:16 +0200 Subject: [PATCH 08/20] [ui,core] Attribute: Refacto the linked attributes method's name for clarity --- meshroom/core/attribute.py | 9 ++++----- .../ui/qml/GraphEditor/AttributeItemDelegate.qml | 4 ++-- tests/test_attributes.py | 12 ++++++------ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/meshroom/core/attribute.py b/meshroom/core/attribute.py index 44edef5683..ee7adc1c0b 100644 --- a/meshroom/core/attribute.py +++ b/meshroom/core/attribute.py @@ -347,13 +347,12 @@ def getOutputConnections(self) -> list["Edge"]: return [edge for edge in self.node.graph.edges.values() if edge.src == self] - def getInputAttributes(self) -> list["Edge"]: + def getLinkedInAttributes(self) -> list["Attribute"]: """ Return the upstreams connected attributes """ return [edge.src for edge in self.getInputConnections()] - - def getOutputAttributes(self): + def getLinkedOutAttributes(self) -> list["Attribute"]: """ Return the downstreams connected attributes """ return [edge.dst for edge in self.getOutputConnections()] @@ -481,8 +480,8 @@ def updateInternals(self): isLinkNested = isLink hasOutputConnectionsChanged = Signal() hasOutputConnections = Property(bool, hasOutputConnections.fget, notify=hasOutputConnectionsChanged) - inputAttributes = Property(Variant, getInputAttributes) - outputAttributes = Property(Variant, getOutputAttributes) + linkedInAttributes = Property(Variant, getLinkedInAttributes) + linkedOutAttributes = Property(Variant, getLinkedOutAttributes) isDefault = Property(bool, _isDefault, notify=valueChanged) linkParam = Property(BaseObject, getLinkParam, notify=isLinkChanged) rootLinkParam = Property(BaseObject, lambda self: self.getLinkParam(recursive=True), notify=isLinkChanged) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 404f254fbc..bcf7a5780b 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -71,7 +71,7 @@ RowLayout { ToolTip.text: shouldBeVisible ? object.linkParam.label : "" onClicked: function(mouse) { - root.inAttributeClicked(mouse, object.inputAttributes) + root.inAttributeClicked(mouse, object.linkedInAttributes) } } @@ -199,7 +199,7 @@ RowLayout { topPadding: 7 onClicked: function(mouse) { - root.outAttributeClicked(mouse, attribute.outputAttributes) + root.outAttributeClicked(mouse, attribute.linkedOutAttributes) } } diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 25b9bd1fb8..c4648c2007 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -17,13 +17,13 @@ def test_attribute_retrieve_linked_input_and_output_attributes(): n3 = g.addNewNode('AppendFiles', input=n1.output, input2=n2.output) # check that the attribute can retrieve its linked input attributes - assert len(n0.input.getInputAttributes()) == 0 - assert len(n1.input.getInputAttributes()) == 1 - assert n1.input.getInputAttributes()[0] == n0.output + assert len(n0.input.getLinkedInAttributes()) == 0 + assert len(n1.input.getLinkedInAttributes()) == 1 + assert n1.input.getLinkedInAttributes()[0] == n0.output - assert len(n1.output.getOutputAttributes()) == 2 + assert len(n1.output.getLinkedOutAttributes()) == 2 - assert n1.output.getOutputAttributes()[0] == n2.input - assert n1.output.getOutputAttributes()[1] == n3.input + assert n1.output.getLinkedOutAttributes()[0] == n2.input + assert n1.output.getLinkedOutAttributes()[1] == n3.input \ No newline at end of file From 1de383443e311af2fc62401a16b7de8a55874d76 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 12:22:05 +0200 Subject: [PATCH 09/20] [ui] AttributeDelegate: Use correct alignment strategy to avoid warnings --- meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index bcf7a5780b..173ad60a0a 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -65,8 +65,7 @@ RowLayout { text: shouldBeVisible ? MaterialIcons.login : " " enabled: shouldBeVisible font.pointSize: 8 - anchors.top: parent.top - anchors.left: parent.left + Layout.alignment: Qt.AlignTop | Qt.AlignLeft topPadding: 7 ToolTip.text: shouldBeVisible ? object.linkParam.label : "" @@ -194,8 +193,7 @@ RowLayout { text: shouldBeVisible ? MaterialIcons.logout : "" font.pointSize: 8 enabled: shouldBeVisible - anchors.top: parent.top - anchors.right: parent.right + Layout.alignment: Qt.AlignTop | Qt.AlignRight topPadding: 7 onClicked: function(mouse) { From 194347493680e2c81124ca78221f3d9a24f0e2b3 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 12:24:25 +0200 Subject: [PATCH 10/20] [ui] AttributeEditor: Now, middle click on nav button fit the view to the selected node --- meshroom/ui/qml/Application.qml | 10 ++++++++++ .../qml/GraphEditor/AttributeItemDelegate.qml | 19 +++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml index f05252ed7c..545dc115f4 100644 --- a/meshroom/ui/qml/Application.qml +++ b/meshroom/ui/qml/Application.qml @@ -1346,10 +1346,20 @@ Page { onInAttributeClicked: function(mouse, inAttributes) { selectNodesFromAttributes(inAttributes) + + if (mouse.button === Qt.MiddleButton) { + graphEditor.fit() + } + } onOutAttributeClicked: function(mouse, outAttributes) { selectNodesFromAttributes(outAttributes) + + if (mouse.button === Qt.MiddleButton) { + graphEditor.fit() + } + } function selectNodesFromAttributes(attributes) { diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 173ad60a0a..cc2a9e08ae 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -69,8 +69,13 @@ RowLayout { topPadding: 7 ToolTip.text: shouldBeVisible ? object.linkParam.label : "" - onClicked: function(mouse) { - root.inAttributeClicked(mouse, object.linkedInAttributes) + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.MiddleButton + + onClicked: function(mouse) { + root.inAttributeClicked(mouse, object.linkedInAttributes) + } } } @@ -196,9 +201,15 @@ RowLayout { Layout.alignment: Qt.AlignTop | Qt.AlignRight topPadding: 7 - onClicked: function(mouse) { - root.outAttributeClicked(mouse, attribute.linkedOutAttributes) + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.MiddleButton + + onClicked: function(mouse) { + root.outAttributeClicked(mouse, attribute.linkedOutAttributes) + } } + } From cc4e1a2087392493d1acdd78325f8a96ac1101ab Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 15:27:54 +0200 Subject: [PATCH 11/20] [ui, core] AttributeEditor: Fix the ListAttribute navigation buttons not displaying when connection exists --- meshroom/core/attribute.py | 36 ++++++++++++++++--- .../qml/GraphEditor/AttributeItemDelegate.qml | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/meshroom/core/attribute.py b/meshroom/core/attribute.py index ee7adc1c0b..4946d0cc50 100644 --- a/meshroom/core/attribute.py +++ b/meshroom/core/attribute.py @@ -325,10 +325,7 @@ def hasOutputConnections(self): # safety check to avoid evaluation errors if not self.node.graph or not self.node.graph.edges: return False - # if the attribute is a ListAttribute, we need to check if any of its elements has output connections - if isinstance(self, ListAttribute): - return next((edge for edge in self.node.graph.edges.values() if edge.src == self), None) is not None or \ - any(attr.hasOutputConnections for attr in self._value if hasattr(attr, 'hasOutputConnections')) + return next((edge for edge in self.node.graph.edges.values() if edge.src == self), None) is not None def getInputConnections(self) -> list["Edge"]: @@ -733,12 +730,43 @@ def isLinkNested(self): return self.isLink \ or self.node.graph and self.isInput and self.node.graph._edges \ and any(v in self.node.graph._edges.keys() for v in self._value) + + # override + @property + def hasOutputConnections(self): + """ Whether the attribute has output connections, i.e is the source of at least one edge. """ + + # safety check to avoid evaluation errors + if not self.node.graph or not self.node.graph.edges: + return False + + return next((edge for edge in self.node.graph.edges.values() if edge.src in self._value), None) is not None or \ + any(attr.hasOutputConnections for attr in self._value if hasattr(attr, 'hasOutputConnections')) + + # override + def getInputConnections(self) -> list["Edge"]: + + if not self.node.graph or not self.node.graph.edges: + return [] + + return [edge for edge in self.node.graph.edges.values() if edge.dst in self._value] + + # override + def getOutputConnections(self) -> list["Edge"]: + + if not self.node.graph or not self.node.graph.edges: + return [] + + return [edge for edge in self.node.graph.edges.values() if edge.src in self._value] + # Override value property setter value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged) isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged) baseType = Property(str, getBaseType, constant=True) isLinkNested = Property(bool, isLinkNested.fget) + hasOutputConnections = Property(bool, hasOutputConnections.fget, notify=Attribute.hasOutputConnectionsChanged) + class GroupAttribute(Attribute): diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index cc2a9e08ae..10a94a2bb0 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -60,7 +60,7 @@ RowLayout { // In connection MaterialToolButton { - property var shouldBeVisible: (object != undefined && object.isLink) + property var shouldBeVisible: (object != undefined && object.isLinkNested) text: shouldBeVisible ? MaterialIcons.login : " " enabled: shouldBeVisible From 3de447130bb6a504800259c257288c05dcb80f41 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 15:29:09 +0200 Subject: [PATCH 12/20] [ui] AttributeItemDelegate: Add navButton ids for clarity and future testing --- meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 10a94a2bb0..a9c82288d0 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -60,6 +60,7 @@ RowLayout { // In connection MaterialToolButton { + id: navButtonIn property var shouldBeVisible: (object != undefined && object.isLinkNested) text: shouldBeVisible ? MaterialIcons.login : " " @@ -193,6 +194,7 @@ RowLayout { } MaterialToolButton { + id: navButtonOut property var shouldBeVisible: (attribute != undefined && attribute.hasOutputConnections) text: shouldBeVisible ? MaterialIcons.logout : "" From 812cc653739a3afee681bbf3c93ba2808c21d4df Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 18:24:03 +0200 Subject: [PATCH 13/20] [ui] NodeEditor: add the listing of connected attributes for rightClick on attributesNavButtons --- meshroom/ui/qml/Application.qml | 56 +++++++++++++++++-- .../ui/qml/GraphEditor/AttributeEditor.qml | 12 ++-- .../qml/GraphEditor/AttributeItemDelegate.qml | 23 ++++---- meshroom/ui/qml/GraphEditor/NodeEditor.qml | 12 ++-- 4 files changed, 75 insertions(+), 28 deletions(-) diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml index 545dc115f4..7619e84fc4 100644 --- a/meshroom/ui/qml/Application.qml +++ b/meshroom/ui/qml/Application.qml @@ -1335,7 +1335,9 @@ Page { SplitView.minimumWidth: 80 node: _reconstruction ? _reconstruction.selectedNode : null - property bool computing: _reconstruction ? _reconstruction.computing : false + property bool computing: _reconstruction ? _reconstruction.computing : false + property var currentAttributes: [] + // Make NodeEditor readOnly when computing readOnly: node ? node.locked : false @@ -1344,8 +1346,22 @@ Page { _reconstruction.selectedNode = n } - onInAttributeClicked: function(mouse, inAttributes) { - selectNodesFromAttributes(inAttributes) + onInAttributeClicked: function(srcItem, mouse, inAttributes) { + + nodeEditor.currentAttributes = inAttributes + + if (mouse.button === Qt.RightButton) { + + const srcGlobal = srcItem.mapToGlobal(0, 0) + const nodeEditorGlobal = nodeEditor.mapToGlobal(0, 0) + contextMenu.x = srcGlobal.x - nodeEditorGlobal.x + contextMenu.y = srcGlobal.y - nodeEditorGlobal.y - 14 // TODO: Couldn't found a way to avoid padding in position. 14 = navButtonIn.paddingTop * 2 + contextMenu.open() + + return + } + + nodeEditor.selectNodesFromAttributes(nodeEditor.currentAttributes) if (mouse.button === Qt.MiddleButton) { graphEditor.fit() @@ -1353,8 +1369,22 @@ Page { } - onOutAttributeClicked: function(mouse, outAttributes) { - selectNodesFromAttributes(outAttributes) + onOutAttributeClicked: function(srcItem, mouse, outAttributes) { + + nodeEditor.currentAttributes = outAttributes + + if (mouse.button === Qt.RightButton) { + + const srcGlobal = srcItem.mapToGlobal(0, 0) + const nodeEditorGlobal = nodeEditor.mapToGlobal(0, 0) + contextMenu.x = srcGlobal.x - nodeEditorGlobal.x + contextMenu.y = srcGlobal.y - nodeEditorGlobal.y - 14 // TODO: Couldn't found a way to avoid padding in position. 14 = navButtonOut.paddingTop * 2 + contextMenu.open() + + return + } + + nodeEditor.selectNodesFromAttributes(nodeEditor.currentAttributes) if (mouse.button === Qt.MiddleButton) { graphEditor.fit() @@ -1362,6 +1392,22 @@ Page { } + Menu { + id: contextMenu + + Repeater { + model: nodeEditor.currentAttributes + + delegate: MenuItem { + text: `${modelData.node.label}.${modelData.label}` + onTriggered: { + nodeEditor.selectNodesFromAttributes([nodeEditor.currentAttributes[index]]) + } + } + } + + } + function selectNodesFromAttributes(attributes) { /* Retrieve the nodes from given attributes, and select its diff --git a/meshroom/ui/qml/GraphEditor/AttributeEditor.qml b/meshroom/ui/qml/GraphEditor/AttributeEditor.qml index 2702c0994d..8b4b0103be 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeEditor.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeEditor.qml @@ -16,8 +16,8 @@ ListView { signal upgradeRequest() signal attributeDoubleClicked(var mouse, var attribute) - signal inAttributeClicked(var mouse, var inAttributes) - signal outAttributeClicked(var mouse, var outAttributes) + signal inAttributeClicked(var srcItem, var mouse, var inAttributes) + signal outAttributeClicked(var srcItem, var mouse, var outAttributes) implicitHeight: contentHeight @@ -46,11 +46,11 @@ ListView { onDoubleClicked: function(mouse, attr) { root.attributeDoubleClicked(mouse, attr) } - onInAttributeClicked: function(mouse, inAttributes) { - root.inAttributeClicked(mouse, inAttributes) + onInAttributeClicked: function(srcItem, mouse, inAttributes) { + root.inAttributeClicked(srcItem, mouse, inAttributes) } - onOutAttributeClicked: function(mouse, outAttributes) { - root.outAttributeClicked(mouse, outAttributes) + onOutAttributeClicked: function(srcItem, mouse, outAttributes) { + root.outAttributeClicked(srcItem, mouse, outAttributes) } } diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index a9c82288d0..ba5b4dc401 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -26,8 +26,8 @@ RowLayout { readonly property bool editable: !attribute.isOutput && !attribute.isLink && !readOnly signal doubleClicked(var mouse, var attr) - signal inAttributeClicked(var mouse, var inAttributes) - signal outAttributeClicked(var mouse, var outAttributes) + signal inAttributeClicked(var srcItem, var mouse, var inAttributes) + signal outAttributeClicked(var srcItem, var mouse, var outAttributes) spacing: 2 @@ -61,6 +61,7 @@ RowLayout { // In connection MaterialToolButton { id: navButtonIn + property var shouldBeVisible: (object != undefined && object.isLinkNested) text: shouldBeVisible ? MaterialIcons.login : " " @@ -68,17 +69,16 @@ RowLayout { font.pointSize: 8 Layout.alignment: Qt.AlignTop | Qt.AlignLeft topPadding: 7 - ToolTip.text: shouldBeVisible ? object.linkParam.label : "" MouseArea { anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.MiddleButton + acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton onClicked: function(mouse) { - root.inAttributeClicked(mouse, object.linkedInAttributes) + root.inAttributeClicked(navButtonIn, mouse, object.linkedInAttributes) } } - + } Label { @@ -195,6 +195,7 @@ RowLayout { MaterialToolButton { id: navButtonOut + property var shouldBeVisible: (attribute != undefined && attribute.hasOutputConnections) text: shouldBeVisible ? MaterialIcons.logout : "" @@ -205,10 +206,10 @@ RowLayout { MouseArea { anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.MiddleButton + acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton - onClicked: function(mouse) { - root.outAttributeClicked(mouse, attribute.linkedOutAttributes) + onClicked: function(mouse) { + root.outAttributeClicked(navButtonOut, mouse, attribute.linkedOutAttributes) } } @@ -714,8 +715,8 @@ RowLayout { obj.label.horizontalAlignment = Text.AlignHCenter obj.label.verticalAlignment = Text.AlignVCenter obj.doubleClicked.connect(function(attr) { root.doubleClicked(attr) }) - obj.inAttributeClicked.connect(function(mouse, inAttributes) { root.inAttributeClicked(mouse, inAttributes) }) - obj.outAttributeClicked.connect(function(mouse, outAttributes) { root.outAttributeClicked(mouse, outAttributes) }) + obj.inAttributeClicked.connect(function(srcItem, mouse, inAttributes) { root.inAttributeClicked(srcItem, mouse, inAttributes) }) + obj.outAttributeClicked.connect(function(srcItem, mouse, outAttributes) { root.outAttributeClicked(srcItem, mouse, outAttributes) }) } ToolButton { enabled: root.editable diff --git a/meshroom/ui/qml/GraphEditor/NodeEditor.qml b/meshroom/ui/qml/GraphEditor/NodeEditor.qml index 8500317714..00d4509053 100644 --- a/meshroom/ui/qml/GraphEditor/NodeEditor.qml +++ b/meshroom/ui/qml/GraphEditor/NodeEditor.qml @@ -21,8 +21,8 @@ Panel { property string nodeStartDateTime: "" signal attributeDoubleClicked(var mouse, var attribute) - signal inAttributeClicked(var mouse, var inAttributes) - signal outAttributeClicked(var mouse, var outAttributes) + signal inAttributeClicked(var srcItem, var mouse, var inAttributes) + signal outAttributeClicked(var srcItem, var mouse, var outAttributes) signal upgradeRequest() title: "Node" + (node !== null ? " - " + node.label + "" + (node.label !== node.defaultLabel ? " (" + node.defaultLabel + ")" : "") : "") @@ -304,11 +304,11 @@ Panel { onUpgradeRequest: root.upgradeRequest() filterText: searchBar.text - onInAttributeClicked: function(mouse, inAttributes) { - root.inAttributeClicked(mouse, inAttributes) + onInAttributeClicked: function(srcItem, mouse, inAttributes) { + root.inAttributeClicked(srcItem, mouse, inAttributes) } - onOutAttributeClicked: function(mouse, outAttributes) { - root.outAttributeClicked(mouse, outAttributes) + onOutAttributeClicked: function(srcItem, mouse, outAttributes) { + root.outAttributeClicked(srcItem, mouse, outAttributes) } } From cc2d485c886a4766882a40a1ac990ab9a752d3de Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 18:33:44 +0200 Subject: [PATCH 14/20] [ui] NodeEditor: Factorize attributeNavButton click behavior --- meshroom/ui/qml/Application.qml | 73 +++++++++++++-------------------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml index 7619e84fc4..90902ceca6 100644 --- a/meshroom/ui/qml/Application.qml +++ b/meshroom/ui/qml/Application.qml @@ -1346,50 +1346,12 @@ Page { _reconstruction.selectedNode = n } - onInAttributeClicked: function(srcItem, mouse, inAttributes) { - - nodeEditor.currentAttributes = inAttributes - - if (mouse.button === Qt.RightButton) { - - const srcGlobal = srcItem.mapToGlobal(0, 0) - const nodeEditorGlobal = nodeEditor.mapToGlobal(0, 0) - contextMenu.x = srcGlobal.x - nodeEditorGlobal.x - contextMenu.y = srcGlobal.y - nodeEditorGlobal.y - 14 // TODO: Couldn't found a way to avoid padding in position. 14 = navButtonIn.paddingTop * 2 - contextMenu.open() - - return - } - - nodeEditor.selectNodesFromAttributes(nodeEditor.currentAttributes) - - if (mouse.button === Qt.MiddleButton) { - graphEditor.fit() - } - + onInAttributeClicked: function(srcItem, mouse, inAttributes) { + _handleNavButtonClick(srcItem, mouse, inAttributes) } - onOutAttributeClicked: function(srcItem, mouse, outAttributes) { - - nodeEditor.currentAttributes = outAttributes - - if (mouse.button === Qt.RightButton) { - - const srcGlobal = srcItem.mapToGlobal(0, 0) - const nodeEditorGlobal = nodeEditor.mapToGlobal(0, 0) - contextMenu.x = srcGlobal.x - nodeEditorGlobal.x - contextMenu.y = srcGlobal.y - nodeEditorGlobal.y - 14 // TODO: Couldn't found a way to avoid padding in position. 14 = navButtonOut.paddingTop * 2 - contextMenu.open() - - return - } - - nodeEditor.selectNodesFromAttributes(nodeEditor.currentAttributes) - - if (mouse.button === Qt.MiddleButton) { - graphEditor.fit() - } - + onOutAttributeClicked: function(srcItem, mouse, outAttributes) { + _handleNavButtonClick(srcItem, mouse, outAttributes) } Menu { @@ -1401,14 +1363,14 @@ Page { delegate: MenuItem { text: `${modelData.node.label}.${modelData.label}` onTriggered: { - nodeEditor.selectNodesFromAttributes([nodeEditor.currentAttributes[index]]) + nodeEditor._selectNodesFromAttributes([nodeEditor.currentAttributes[index]]) } } } } - function selectNodesFromAttributes(attributes) { + function _selectNodesFromAttributes(attributes) { /* Retrieve the nodes from given attributes, and select its */ @@ -1425,6 +1387,29 @@ Page { graphEditor.uigraph.selectNodes(nodes) } + function _openLinkAttributesContextMenu(srcItem, mouse, attributes) { + nodeEditor.currentAttributes = attributes + const srcGlobal = srcItem.mapToGlobal(0, 0) + const nodeEditorGlobal = nodeEditor.mapToGlobal(0, 0) + navButtonContextMenu.x = srcGlobal.x - nodeEditorGlobal.x + navButtonContextMenu.y = srcGlobal.y - nodeEditorGlobal.y - 14 // TODO: Couldn't found a way to avoid padding in position. 14 = navButtonOut.paddingTop * 2 + navButtonContextMenu.open() + } + + function _handleNavButtonClick(srcItem, mouse, attributes) { + + if (mouse.button === Qt.RightButton) { + nodeEditor._openLinkAttributesContextMenu(srcItem, mouse, attributes) + return + } + + nodeEditor._selectNodesFromAttributes(nodeEditor.currentAttributes) + + if (mouse.button === Qt.MiddleButton) { + graphEditor.fit() + } + } + } } } From 81f692468fb8f215d9e81f474981d6b280822dd8 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Fri, 2 May 2025 19:19:44 +0200 Subject: [PATCH 15/20] [core] attributes: Add tests for coverage --- tests/test_attributes.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index c4648c2007..d8abd4d8dc 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -17,6 +17,10 @@ def test_attribute_retrieve_linked_input_and_output_attributes(): n3 = g.addNewNode('AppendFiles', input=n1.output, input2=n2.output) # check that the attribute can retrieve its linked input attributes + + assert n0.output.hasOutputConnections + assert not n3.output.hasOutputConnections + assert len(n0.input.getLinkedInAttributes()) == 0 assert len(n1.input.getLinkedInAttributes()) == 1 assert n1.input.getLinkedInAttributes()[0] == n0.output @@ -26,4 +30,10 @@ def test_attribute_retrieve_linked_input_and_output_attributes(): assert n1.output.getLinkedOutAttributes()[0] == n2.input assert n1.output.getLinkedOutAttributes()[1] == n3.input + n0.graph = None + + # Bounding cases + assert not n0.output.hasOutputConnections + assert len(n0.input.getLinkedInAttributes()) == 0 + assert len(n0.output.getLinkedOutAttributes()) == 0 \ No newline at end of file From 03c745a69b0494a973bd29681a27a0520d29a4ee Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Mon, 5 May 2025 09:34:41 +0200 Subject: [PATCH 16/20] [ui] AttributeEditor: Fix navButton left click behavior. (Wasn't selecting the connected nodes anymore) --- meshroom/ui/qml/Application.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml index 90902ceca6..cecd619b0a 100644 --- a/meshroom/ui/qml/Application.qml +++ b/meshroom/ui/qml/Application.qml @@ -1403,7 +1403,7 @@ Page { return } - nodeEditor._selectNodesFromAttributes(nodeEditor.currentAttributes) + nodeEditor._selectNodesFromAttributes(attributes) if (mouse.button === Qt.MiddleButton) { graphEditor.fit() From 3da2918c2458e30eaa504c54f5d0dbaf78d565b1 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Mon, 5 May 2025 11:16:06 +0200 Subject: [PATCH 17/20] [ui] AttributeEditor: navButton context show truncated text if the label is too long. navbutton contextMenu has specific id now --- meshroom/ui/qml/Application.qml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml index cecd619b0a..85f04520e9 100644 --- a/meshroom/ui/qml/Application.qml +++ b/meshroom/ui/qml/Application.qml @@ -1354,14 +1354,21 @@ Page { _handleNavButtonClick(srcItem, mouse, outAttributes) } + // NavButtonContextMenu Menu { - id: contextMenu + id: navButtonContextMenu Repeater { model: nodeEditor.currentAttributes delegate: MenuItem { - text: `${modelData.node.label}.${modelData.label}` + + contentItem: Text { + text: `${modelData.node.label}.${modelData.label}` + elide: Text.ElideLeft + color: Colors.sysPalette.text + } + onTriggered: { nodeEditor._selectNodesFromAttributes([nodeEditor.currentAttributes[index]]) } From 5d2c85bf50df1e7b44c00ceb0d98e9f58ac58a41 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Mon, 5 May 2025 11:18:22 +0200 Subject: [PATCH 18/20] [core] Attribute: Fix ListAttributes input / output connections retrieving, by handling bundary cases --- meshroom/core/attribute.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/meshroom/core/attribute.py b/meshroom/core/attribute.py index 4946d0cc50..aed962e5d3 100644 --- a/meshroom/core/attribute.py +++ b/meshroom/core/attribute.py @@ -749,7 +749,7 @@ def getInputConnections(self) -> list["Edge"]: if not self.node.graph or not self.node.graph.edges: return [] - return [edge for edge in self.node.graph.edges.values() if edge.dst in self._value] + return [edge for edge in self.node.graph.edges.values() if edge.dst == self or edge.dst in self._value] # override def getOutputConnections(self) -> list["Edge"]: @@ -757,9 +757,8 @@ def getOutputConnections(self) -> list["Edge"]: if not self.node.graph or not self.node.graph.edges: return [] - return [edge for edge in self.node.graph.edges.values() if edge.src in self._value] + return [edge for edge in self.node.graph.edges.values() if edge.src == self or edge.src in self._value] - # Override value property setter value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged) isDefault = Property(bool, _isDefault, notify=Attribute.valueChanged) From d1b434de1e436e99d8b46854e805d0af36915905 Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Tue, 6 May 2025 09:08:41 +0200 Subject: [PATCH 19/20] [ui] Attribute: Use bool type for shouldBeVisible property --- meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index ba5b4dc401..b0e4e821c6 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -62,7 +62,7 @@ RowLayout { MaterialToolButton { id: navButtonIn - property var shouldBeVisible: (object != undefined && object.isLinkNested) + property bool shouldBeVisible: (object != undefined && object.isLinkNested) text: shouldBeVisible ? MaterialIcons.login : " " enabled: shouldBeVisible @@ -196,7 +196,7 @@ RowLayout { MaterialToolButton { id: navButtonOut - property var shouldBeVisible: (attribute != undefined && attribute.hasOutputConnections) + property bool shouldBeVisible: (attribute != undefined && attribute.hasOutputConnections) text: shouldBeVisible ? MaterialIcons.logout : "" font.pointSize: 8 From 7881fa1bcb584a5101c210581a75fb01da5477ac Mon Sep 17 00:00:00 2001 From: nicolas-lambert-tc Date: Tue, 6 May 2025 09:11:19 +0200 Subject: [PATCH 20/20] [ui] Attribute: Align non linked attributes label (in/out) with the linked one --- meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index b0e4e821c6..baff5ab3ac 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -64,7 +64,7 @@ RowLayout { property bool shouldBeVisible: (object != undefined && object.isLinkNested) - text: shouldBeVisible ? MaterialIcons.login : " " + text: shouldBeVisible ? MaterialIcons.login : " " enabled: shouldBeVisible font.pointSize: 8 Layout.alignment: Qt.AlignTop | Qt.AlignLeft @@ -198,7 +198,7 @@ RowLayout { property bool shouldBeVisible: (attribute != undefined && attribute.hasOutputConnections) - text: shouldBeVisible ? MaterialIcons.logout : "" + text: shouldBeVisible ? MaterialIcons.logout : " " font.pointSize: 8 enabled: shouldBeVisible Layout.alignment: Qt.AlignTop | Qt.AlignRight