[ui] GraphEditor: move node context menu to a Loader

Avoid having the node context menu always evaluating the current
state of the selected nodes for its own display, by dynamically
creating it on demand with a Loader.
Use callbacks for recomputing/resubmitting actions, instead of
storing state in the UI components.
This commit is contained in:
Yann Lanthony 2024-12-06 10:14:50 +01:00
parent 686927a92d
commit b3a8c6a1f2

View file

@ -31,8 +31,6 @@ Item {
signal computeRequest(var nodes) signal computeRequest(var nodes)
signal submitRequest(var nodes) signal submitRequest(var nodes)
signal dataDeleted()
property int nbMeshroomScenes: 0 property int nbMeshroomScenes: 0
property int nbDraggedFiles: 0 property int nbDraggedFiles: 0
signal filesDropped(var drop, var mousePosition) // Files have been dropped signal filesDropped(var drop, var mousePosition) // Files have been dropped
@ -70,17 +68,6 @@ Item {
} }
} }
onDataDeleted: {
if (computeMenuItem.recompute) {
computeRequest(uigraph.selectedNodes)
computeMenuItem.recompute = false
}
else if (submitMenuItem.resubmit) {
submitRequest(uigraph.selectedNodes)
submitMenuItem.resubmit = false
}
}
/// Duplicate a node and optionally all the following ones /// Duplicate a node and optionally all the following ones
function duplicateNode(duplicateFollowingNodes) { function duplicateNode(duplicateFollowingNodes) {
var nodes var nodes
@ -549,12 +536,44 @@ Item {
} }
} }
Loader {
id: nodeMenuLoader
property var currentNode: null
active: currentNode != null
sourceComponent: nodeMenuComponent
function load(node) {
currentNode = node;
}
function unload() {
currentNode = null;
}
function showDataDeletionDialog(deleteFollowing: bool, callback) {
uigraph.forceNodesStatusUpdate();
const dialog = deleteDataDialog.createObject(
root,
{
"node": currentNode,
"deleteFollowing": deleteFollowing
}
);
dialog.open();
if(callback)
dialog.dataDeleted.connect(callback);
}
}
Component {
id: nodeMenuComponent
Menu { Menu {
id: nodeMenu id: nodeMenu
property var currentNode: null
property bool canComputeNode: currentNode != null && uigraph.graph.canComputeTopologically(currentNode) property var currentNode: nodeMenuLoader.currentNode
property bool canComputeNode: uigraph.graph.canComputeTopologically(currentNode)
// canSubmitOrCompute: return int n : 0 >= n <= 3 | n=0 cannot submit or compute | n=1 can compute | n=2 can submit | n=3 can compute & submit // canSubmitOrCompute: return int n : 0 >= n <= 3 | n=0 cannot submit or compute | n=1 can compute | n=2 can submit | n=3 can compute & submit
property int canSubmitOrCompute: currentNode != null && uigraph.graph.canSubmitOrCompute(currentNode) property int canSubmitOrCompute: uigraph.graph.canSubmitOrCompute(currentNode)
property bool isComputed: { property bool isComputed: {
var count = 0 var count = 0
for (var i = 0; i < uigraph.selectedNodes.count; ++i) { for (var i = 0; i < uigraph.selectedNodes.count; ++i) {
@ -567,12 +586,14 @@ Item {
} }
return count > 0 return count > 0
} }
width: 220 width: 220
onClosed: currentNode = null
Component.onCompleted: popup()
onClosed: nodeMenuLoader.unload()
MenuItem { MenuItem {
id: computeMenuItem id: computeMenuItem
property bool recompute: false
text: nodeMenu.isComputed ? "Recompute" : "Compute" text: nodeMenu.isComputed ? "Recompute" : "Compute"
visible: { visible: {
var count = 0 var count = 0
@ -607,10 +628,14 @@ Item {
onTriggered: { onTriggered: {
if (nodeMenu.isComputed) { if (nodeMenu.isComputed) {
recompute = true nodeMenuLoader.showDataDeletionDialog(
deleteDataMenuItem.showConfirmationDialog(false) false,
function(request, uigraph) {
request(uigraph.selectedNodes);
}.bind(null, computeRequest, uigraph)
);
} else { } else {
computeRequest(uigraph.selectedNodes) computeRequest(uigraph.selectedNodes);
} }
} }
} }
@ -648,35 +673,39 @@ Item {
} }
onTriggered: { onTriggered: {
if (nodeMenu.isComputed) { if (nodeMenu.isComputed) {
resubmit = true nodeMenuLoader.showDataDeletionDialog(
deleteDataMenuItem.showConfirmationDialog(false) false,
function(request, uigraph) {
request(uigraph.selectedNodes);
}.bind(null, submitRequest, uigraph)
);
} else { } else {
submitRequest(uigraph.selectedNodes) submitRequest(uigraph.selectedNodes);
} }
} }
} }
MenuItem { MenuItem {
text: "Stop Computation" text: "Stop Computation"
enabled: nodeMenu.currentNode ? nodeMenu.currentNode.canBeStopped() : false enabled: nodeMenu.currentNode.canBeStopped()
visible: enabled visible: enabled
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: uigraph.stopNodeComputation(nodeMenu.currentNode) onTriggered: uigraph.stopNodeComputation(nodeMenu.currentNode)
} }
MenuItem { MenuItem {
text: "Cancel Computation" text: "Cancel Computation"
enabled: nodeMenu.currentNode ? nodeMenu.currentNode.canBeCanceled() : false enabled: nodeMenu.currentNode.canBeCanceled()
visible: enabled visible: enabled
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: uigraph.cancelNodeComputation(nodeMenu.currentNode) onTriggered: uigraph.cancelNodeComputation(nodeMenu.currentNode)
} }
MenuItem { MenuItem {
text: "Open Folder" text: "Open Folder"
visible: nodeMenu.currentNode ? nodeMenu.currentNode.isComputable : false visible: nodeMenu.currentNode.isComputable
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: Qt.openUrlExternally(Filepath.stringToUrl(nodeMenu.currentNode.internalFolder)) onTriggered: Qt.openUrlExternally(Filepath.stringToUrl(nodeMenu.currentNode.internalFolder))
} }
MenuSeparator { MenuSeparator {
visible: nodeMenu.currentNode ? nodeMenu.currentNode.isComputable : false visible: nodeMenu.currentNode.isComputable
} }
MenuItem { MenuItem {
text: "Cut Node(s)" text: "Cut Node(s)"
@ -725,7 +754,7 @@ Item {
} }
MenuItem { MenuItem {
text: "Remove Node(s)" + (removeFollowingButton.hovered ? " From Here" : "") text: "Remove Node(s)" + (removeFollowingButton.hovered ? " From Here" : "")
enabled: nodeMenu.currentNode ? !nodeMenu.currentNode.locked : false enabled: !nodeMenu.currentNode.locked
onTriggered: uigraph.removeSelectedNodes() onTriggered: uigraph.removeSelectedNodes()
MaterialToolButton { MaterialToolButton {
id: removeFollowingButton id: removeFollowingButton
@ -742,12 +771,12 @@ Item {
} }
} }
MenuSeparator { MenuSeparator {
visible: nodeMenu.currentNode ? nodeMenu.currentNode.isComputable : false visible: nodeMenu.currentNode.isComputable
} }
MenuItem { MenuItem {
id: deleteDataMenuItem id: deleteDataMenuItem
text: "Delete Data" + (deleteFollowingButton.hovered ? " From Here" : "" ) + "..." text: "Delete Data" + (deleteFollowingButton.hovered ? " From Here" : "" ) + "..."
visible: nodeMenu.currentNode ? nodeMenu.currentNode.isComputable : false visible: nodeMenu.currentNode.isComputable
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
enabled: { enabled: {
if (!nodeMenu.currentNode) if (!nodeMenu.currentNode)
@ -763,18 +792,7 @@ Item {
return true return true
} }
function showConfirmationDialog(deleteFollowing) { onTriggered: nodeMenuLoader.showDataDeletionDialog(false)
uigraph.forceNodesStatusUpdate()
var obj = deleteDataDialog.createObject(root,
{
"node": nodeMenu.currentNode,
"deleteFollowing": deleteFollowing
})
obj.open()
nodeMenu.close()
}
onTriggered: showConfirmationDialog(false)
MaterialToolButton { MaterialToolButton {
id: deleteFollowingButton id: deleteFollowingButton
@ -784,35 +802,41 @@ Item {
} }
height: parent.height height: parent.height
text: MaterialIcons.fast_forward text: MaterialIcons.fast_forward
onClicked: parent.showConfirmationDialog(true) onClicked: {
} nodeMenuLoader.showDataDeletionDialog(true);
nodeMenu.close();
// Confirmation dialog for node cache deletion
Component {
id: deleteDataDialog
MessageDialog {
property var node
property bool deleteFollowing: false
focus: true
modal: false
header.visible: false
text: "Delete Data of '" + node.label + "'" + (uigraph.selectedNodes.count > 1 ? " and other selected Nodes" : "") + (deleteFollowing ? " and following Nodes?" : "?")
helperText: "Warning: This operation cannot be undone."
standardButtons: Dialog.Yes | Dialog.Cancel
onAccepted: {
if (deleteFollowing)
uigraph.clearDataFrom(uigraph.selectedNodes)
else
uigraph.clearData(uigraph.selectedNodes)
root.dataDeleted()
}
onClosed: destroy()
} }
} }
}
}
}
// Confirmation dialog for node cache deletion
Component {
id: deleteDataDialog
MessageDialog {
property var node
property bool deleteFollowing: false
signal dataDeleted()
focus: true
modal: false
header.visible: false
text: "Delete Data of '" + node.label + "'" + (uigraph.selectedNodes.count > 1 ? " and other selected Nodes" : "") + (deleteFollowing ? " and following Nodes?" : "?")
helperText: "Warning: This operation cannot be undone."
standardButtons: Dialog.Yes | Dialog.Cancel
onAccepted: {
if (deleteFollowing)
uigraph.clearDataFrom(uigraph.selectedNodes);
else
uigraph.clearData(uigraph.selectedNodes);
dataDeleted();
}
onClosed: destroy()
} }
} }
@ -880,8 +904,7 @@ Item {
// Keep the full selection when right-clicking on a node. // Keep the full selection when right-clicking on a node.
nodeRepeater.updateSelectionOnClick = false; nodeRepeater.updateSelectionOnClick = false;
} }
nodeMenu.currentNode = node nodeMenuLoader.load(node)
nodeMenu.popup()
} }
if(selectionMode != ItemSelectionModel.NoUpdate) { if(selectionMode != ItemSelectionModel.NoUpdate) {