From dd7da65b181e374ba76812120d99e8c06948e4f1 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:55:19 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Frontend:=20Expose=20``inputs=20req?= =?UTF-8?q?uired``=20property=20(#5899)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../class/osparc/dashboard/StudyBrowser.js | 4 +- .../source/class/osparc/data/model/Node.js | 57 +++++-------- .../class/osparc/data/model/Workbench.js | 11 +-- .../class/osparc/desktop/StudyEditor.js | 4 +- .../source/class/osparc/form/PortInfoHint.js | 2 +- .../class/osparc/form/renderer/PropForm.js | 79 +++++++++++++------ .../osparc/form/renderer/PropFormBase.js | 34 +++++++- .../source/class/osparc/widget/NodeOptions.js | 2 + 8 files changed, 119 insertions(+), 74 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 1f4260bbf29..b1a87f56194 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -285,8 +285,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, _reloadCards: function() { - this.__addNewStudyButtons(); - const fetching = this._loadingResourcesBtn ? this._loadingResourcesBtn.getFetching() : false; const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; @@ -294,6 +292,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const cards = this._resourcesContainer.reloadCards("studiesList"); this.__configureCards(cards); + this.__addNewStudyButtons(); + const loadMoreBtn = this.__createLoadMoreButton(); loadMoreBtn.set({ fetching, diff --git a/services/static-webserver/client/source/class/osparc/data/model/Node.js b/services/static-webserver/client/source/class/osparc/data/model/Node.js index afca119c2c9..488c1b54f6f 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Node.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Node.js @@ -53,7 +53,6 @@ qx.Class.define("osparc.data.model.Node", { this.setOutputs({}); this.__inputNodes = []; - this.__exposedNodes = []; if (study) { this.setStudy(study); @@ -136,6 +135,12 @@ qx.Class.define("osparc.data.model.Node", { event: "changeInputs" }, + inputsRequired: { + check: "Array", + init: [], + event: "changeInputsRequired" + }, + outputs: { check: "Object", nullable: false, @@ -228,7 +233,8 @@ qx.Class.define("osparc.data.model.Node", { "fileUploaded": "qx.event.type.Event", "showInLogger": "qx.event.type.Data", "outputListChanged": "qx.event.type.Event", - "changeInputNodes": "qx.event.type.Event" + "changeInputNodes": "qx.event.type.Event", + "changeInputsRequired": "qx.event.type.Event" }, statics: { @@ -331,7 +337,6 @@ qx.Class.define("osparc.data.model.Node", { members: { __metaData: null, __inputNodes: null, - __exposedNodes: null, __settingsForm: null, __posX: null, __posY: null, @@ -512,7 +517,8 @@ qx.Class.define("osparc.data.model.Node", { } this.setOutputData(nodeData.outputs); this.addInputNodes(nodeData.inputNodes); - this.addOutputNodes(nodeData.outputNodes); + // backwards compatible + this.setInputsRequired(nodeData.inputsRequired || []); }, populateStates: function(nodeData) { @@ -875,43 +881,17 @@ qx.Class.define("osparc.data.model.Node", { }, // !---- Input Nodes ----- - // ----- Output Nodes ----- - getOutputNodes: function() { - return this.__exposedNodes; - }, - - addOutputNodes: function(outputNodes) { - if (outputNodes) { - outputNodes.forEach(outputNode => { - this.addOutputNode(outputNode); - }); - } - }, - - addOutputNode: function(outputNodeId) { - if (!this.__exposedNodes.includes(outputNodeId)) { - this.__exposedNodes.push(outputNodeId); - this.fireEvent("outputListChanged"); - return true; - } - return false; - }, - - removeOutputNode: function(outputNodeId) { - const index = this.__exposedNodes.indexOf(outputNodeId); + toggleInputRequired: function(portId) { + const inputsRequired = this.getInputsRequired(); + const index = inputsRequired.indexOf(portId); if (index > -1) { - // remove node connection - this.__exposedNodes.splice(index, 1); - this.fireEvent("outputListChanged"); + inputsRequired.splice(index, 1); + } else { + inputsRequired.push(portId); } - return false; - }, - - isOutputNode: function(outputNodeId) { - const index = this.__exposedNodes.indexOf(outputNodeId); - return (index > -1); + this.setInputsRequired(inputsRequired); + this.fireEvent("changeInputsRequired"); }, - // !---- Output Nodes ----- canNodeStart: function() { return this.isDynamic() && ["idle", "failed"].includes(this.getStatus().getInteractive()); @@ -1532,6 +1512,7 @@ qx.Class.define("osparc.data.model.Node", { inputsUnits: this.__getInputUnits(), inputAccess: this.getInputAccess(), inputNodes: this.getInputNodes(), + inputsRequired: this.getInputsRequired(), thumbnail: this.getThumbnail(), bootOptions: this.getBootOptions() }; diff --git a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js index 072913795d4..01da1153297 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js @@ -676,12 +676,11 @@ qx.Class.define("osparc.data.model.Workbench", { if (node === null) { continue; } - this.__addInputOutputNodesAndEdges(node, nodeData.inputNodes, true); - this.__addInputOutputNodesAndEdges(node, nodeData.outputNodes, false); + this.__addInputOutputNodesAndEdges(node, nodeData.inputNodes); } }, - __addInputOutputNodesAndEdges: function(node, inputOutputNodeIds, isInput) { + __addInputOutputNodesAndEdges: function(node, inputOutputNodeIds) { if (inputOutputNodeIds) { inputOutputNodeIds.forEach(inputOutputNodeId => { const node1 = this.getNode(inputOutputNodeId); @@ -690,11 +689,7 @@ qx.Class.define("osparc.data.model.Workbench", { } const edge = new osparc.data.model.Edge(null, node1, node); this.addEdge(edge); - if (isInput) { - node.addInputNode(inputOutputNodeId); - } else { - node.addOutputNode(inputOutputNodeId); - } + node.addInputNode(inputOutputNodeId); }); } }, diff --git a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js index 9ad894e029a..4b0848288d2 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js +++ b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js @@ -169,7 +169,7 @@ qx.Class.define("osparc.desktop.StudyEditor", { study.openStudy() .then(() => { - this.__lastSavedStudy = study.serialize(); + this.__lastSavedStudy = osparc.utils.Utils.deepCloneObject(study.serialize()); this.__workbenchView.setStudy(study); this.__slideshowView.setStudy(study); @@ -761,7 +761,7 @@ qx.Class.define("osparc.desktop.StudyEditor", { const newObj = this.getStudy().serialize(); return this.getStudy().updateStudy(newObj, run) .then(() => { - this.__lastSavedStudy = osparc.wrapper.JsonDiffPatch.getInstance().clone(newObj); + this.__lastSavedStudy = osparc.utils.Utils.deepCloneObject(newObj); }) .catch(error => { if ("status" in error && error.status === 409) { diff --git a/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js b/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js index 9c1bf89f9b6..70ddcf32bf0 100644 --- a/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js +++ b/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js @@ -38,7 +38,7 @@ qx.Class.define("osparc.form.PortInfoHint", { const color = qx.theme.manager.Color.getInstance().resolve("failed-red"); text += `

${errorMsg}`; } - this._hint.setText(text); + this.setHintText(text); this.set({ source: errorMsg ? this.self().ERROR_ICON : osparc.ui.hint.InfoHint.INFO_ICON, textColor: errorMsg ? "failed-red" : "text" diff --git a/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js b/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js index 733be9bd936..07fa01cb1f2 100644 --- a/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js +++ b/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js @@ -286,6 +286,12 @@ qx.Class.define("osparc.form.renderer.PropForm", { }); }); } + + if (optionsMenu.getChildren().length) { + optionsMenu.addSeparator(); + } + const inputRequiredButton = this.__getInputRequiredButton(field.key); + optionsMenu.add(inputRequiredButton); }, __connectToInputNode: function(targetPortId, inputNodeId, outputKey) { @@ -342,6 +348,26 @@ qx.Class.define("osparc.form.renderer.PropForm", { return null; }, + __populateInputNodePortsMenu: function(inputNodeId, targetPortId, menu, menuBtn) { + menuBtn.exclude(); + menu.removeAll(); + + const inputNode = this.getStudy().getWorkbench().getNode(inputNodeId); + if (inputNode) { + for (const outputKey in inputNode.getOutputs()) { + osparc.utils.Ports.arePortsCompatible(inputNode, outputKey, this.getNode(), targetPortId) + .then(compatible => { + if (compatible) { + const paramButton = new qx.ui.menu.Button(inputNode.getOutput(outputKey).label); + paramButton.addListener("execute", () => this.__connectToInputNode(targetPortId, inputNodeId, outputKey), this); + menu.add(paramButton); + menuBtn.show(); + } + }); + } + } + }, + __getSelectFileButton: function(portId) { const selectFileButton = new qx.ui.menu.Button(this.tr("Select File")); selectFileButton.addListener("execute", () => this.fireDataEvent("filePickerRequested", { @@ -366,26 +392,6 @@ qx.Class.define("osparc.form.renderer.PropForm", { return existingParamBtn; }, - __populateInputNodePortsMenu: function(inputNodeId, targetPortId, menu, menuBtn) { - menuBtn.exclude(); - menu.removeAll(); - - const inputNode = this.getStudy().getWorkbench().getNode(inputNodeId); - if (inputNode) { - for (const outputKey in inputNode.getOutputs()) { - osparc.utils.Ports.arePortsCompatible(inputNode, outputKey, this.getNode(), targetPortId) - .then(compatible => { - if (compatible) { - const paramButton = new qx.ui.menu.Button(inputNode.getOutput(outputKey).label); - paramButton.addListener("execute", () => this.__connectToInputNode(targetPortId, inputNodeId, outputKey), this); - menu.add(paramButton); - menuBtn.show(); - } - }); - } - } - }, - __populateExistingParamsMenu: function(targetPortId, menu, menuBtn) { menuBtn.exclude(); menu.removeAll(); @@ -410,6 +416,26 @@ qx.Class.define("osparc.form.renderer.PropForm", { }); }, + __getInputRequiredButton: function(portId) { + const node = this.getNode(); + const inputRequiredBtn = new qx.ui.menu.Button(this.tr("Required Input")); + const evalButton = () => { + if (node.getInputsRequired().includes(portId)) { + inputRequiredBtn.set({ + icon: "@FontAwesome5Regular/check-square/12" + }); + } else { + inputRequiredBtn.set({ + icon: "@FontAwesome5Regular/square/12" + }); + } + } + node.addListener("changeInputsRequired", () => evalButton(), this); + inputRequiredBtn.addListener("execute", () => node.toggleInputRequired(portId), this); + evalButton(); + return inputRequiredBtn; + }, + // overridden addItems: function(items, names, title, itemOptions, headerOptions) { this.base(arguments, items, names, title, itemOptions, headerOptions); @@ -419,6 +445,7 @@ qx.Class.define("osparc.form.renderer.PropForm", { for (let i = 0; i < items.length; i++) { const item = items[i]; + const portId = item.key; const fieldOpts = this.__createLinkUnlinkStack(item); if (fieldOpts) { @@ -428,10 +455,10 @@ qx.Class.define("osparc.form.renderer.PropForm", { }); } - this.__createDropMechanism(item, item.key); + this.__createDropMechanism(item, portId); // Notify focus and focus out - const msgDataFn = (nodeId, portId) => this.__arePortsCompatible(nodeId, portId, this.getNode().getNodeId(), item.key); + const msgDataFn = (nodeId, pId) => this.__arePortsCompatible(nodeId, pId, this.getNode().getNodeId(), item.key); item.addListener("focus", () => { if (this.getNode()) { @@ -447,6 +474,14 @@ qx.Class.define("osparc.form.renderer.PropForm", { row++; } + const evalRequired = () => { + for (const portId in this.__ctrlLinkMap) { + this.evalFieldRequired(portId); + } + } + this.getNode().addListener("changeInputsRequired", () => evalRequired()); + evalRequired(); + // add port button const addPortButton = this.__addInputPortButton = new qx.ui.form.Button().set({ label: this.tr("Input"), diff --git a/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js b/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js index f6110e38204..bda6c498aed 100644 --- a/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js +++ b/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js @@ -125,7 +125,7 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { const label = this._createLabel(names[i], item); label.set({ - rich: false, // override, required for showing the vut off ellipses + rich: false, // override, required for showing the cut off ellipses toolTipText: names[i] }); label.setBuddy(item); @@ -191,6 +191,38 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { return filteredData; }, + evalFieldRequired: function(portId) { + const label = this._getLabelFieldChild(portId).child; + const inputsRequired = this.getNode().getInputsRequired(); + + // add star (*) to the label + const requiredSuffix = " *"; + let newLabel = label.getValue(); + newLabel = newLabel.replace(requiredSuffix, ""); + if (inputsRequired.includes(portId)) { + newLabel += requiredSuffix; + } + label.setValue(newLabel); + + // add "required" text to the label's tooltip + const toolTipSuffix = "
" + this.tr("Required input: without it, the service will not start/run."); + let newToolTip = label.getToolTipText(); + newToolTip = newToolTip.replace(toolTipSuffix, ""); + if (inputsRequired.includes(portId)) { + newToolTip += toolTipSuffix; + } + label.setToolTipText(newToolTip); + + // add "required" text to the description + const infoButton = this._getInfoFieldChild(portId).child; + let newHintText = infoButton.getHintText(); + newHintText = newHintText.replace(toolTipSuffix, ""); + if (inputsRequired.includes(portId)) { + newHintText += toolTipSuffix; + } + infoButton.setHintText(newHintText); + }, + getChangedXUnits: function() { const xUnits = {}; const ctrls = this._form.getControls(); diff --git a/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js b/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js index 2aafc7f2dd6..180de5bb2cb 100644 --- a/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js +++ b/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js @@ -113,6 +113,8 @@ qx.Class.define("osparc.widget.NodeOptions", { const startStopButton = new osparc.node.StartStopButton(); startStopButton.setNode(node); this._add(startStopButton); + + startStopButton.getChildControl("stop-button").bind("visibility", instructions, "visibility"); } sections.forEach(section => this._add(section));