Skip to content

Commit 01a377a

Browse files
authored
Link ports from drop down menu (ITISFoundation#2575)
1 parent a7f08c2 commit 01a377a

File tree

5 files changed

+175
-87
lines changed

5 files changed

+175
-87
lines changed

services/web/client/source/class/osparc/component/form/renderer/PropForm.js

Lines changed: 154 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ qx.Class.define("osparc.component.form.renderer.PropForm", {
2020
* @param study {osparc.data.model.Study} Study owning the node
2121
*/
2222
construct: function(form, node, study) {
23-
this.base(arguments, form, node);
24-
2523
if (study) {
2624
this.setStudy(study);
2725
}
28-
2926
this.__ctrlLinkMap = {};
3027
this.__ctrlParamMap = {};
28+
this.__fieldOptsBtnMap = {};
29+
30+
this.base(arguments, form, node);
3131

3232
this.__addLinkCtrls();
3333
this.__addParamCtrls();
@@ -92,80 +92,177 @@ qx.Class.define("osparc.component.form.renderer.PropForm", {
9292

9393
__ctrlLinkMap: null,
9494
__ctrlParamMap: null,
95+
__fieldOptsBtnMap: null,
9596

9697
__createFieldOpts: function(field) {
98+
const optionsMenu = new qx.ui.menu.Menu().set({
99+
position: "bottom-right"
100+
});
101+
const fieldOptsBtn = new qx.ui.form.MenuButton().set({
102+
menu: optionsMenu,
103+
icon: "@FontAwesome5Solid/ellipsis-v/14",
104+
focusable: false,
105+
allowGrowX: false,
106+
alignX: "center"
107+
});
108+
this.__fieldOptsBtnMap[field.key] = fieldOptsBtn;
109+
// populaten the button/menu when the it appears
110+
fieldOptsBtn.addListenerOnce("appear", () => {
111+
if (this.getStudy()) {
112+
this.__populateFieldOptionsMenu(optionsMenu, field);
113+
this.getStudy().getWorkbench().addListener("pipelineChanged", () => this.__populateFieldOptionsMenu(optionsMenu, field), this);
114+
}
115+
});
116+
return fieldOptsBtn;
117+
},
118+
119+
__populateFieldOptionsMenu: function(optionsMenu, field) {
120+
optionsMenu.removeAll();
121+
122+
this.__addInputsMenuButtons(field.key, optionsMenu);
123+
124+
if (optionsMenu.getChildren().length) {
125+
optionsMenu.addSeparator();
126+
}
127+
97128
if (["FileButton"].includes(field.widgetType)) {
98-
return this.__getSelectFileButton(field.key);
129+
const menuButton = this.__getSelectFileButton(field.key);
130+
optionsMenu.add(menuButton);
99131
}
100132
if (this.self().isFieldParametrizable(field)) {
101-
const paramsMenuBtn = this.__getParamsMenuButton(field.key).set({
102-
visibility: "excluded"
103-
});
133+
const newParamBtn = this.__getNewParamButton(field.key);
134+
newParamBtn.exclude();
135+
optionsMenu.add(newParamBtn);
136+
const paramsMenuBtn = this.__getParamsMenuButton(field.key);
137+
paramsMenuBtn.exclude();
138+
optionsMenu.add(paramsMenuBtn);
104139
osparc.utils.Utils.isDevelopmentPlatform()
105140
.then(areParamsEnabled => {
106-
field.bind("visibility", paramsMenuBtn, "visibility", {
107-
converter: visibility => (visibility === "visible" && areParamsEnabled) ? "visible" : "excluded"
141+
[
142+
newParamBtn,
143+
paramsMenuBtn
144+
].forEach(btn => {
145+
field.bind("visibility", btn, "visibility", {
146+
converter: visibility => (visibility === "visible" && areParamsEnabled) ? "visible" : "excluded"
147+
});
108148
});
109149
});
110-
return paramsMenuBtn;
150+
}
151+
},
152+
153+
__addInputsMenuButtons: function(targetPortId, menu) {
154+
const study = this.getStudy();
155+
const thisNode = this.getNode();
156+
if (study && thisNode) {
157+
const inputNodeIDs = thisNode.getInputNodes();
158+
inputNodeIDs.forEach(inputNodeId => {
159+
const inputNode = this.getStudy().getWorkbench().getNode(inputNodeId);
160+
if (inputNode) {
161+
for (const outputKey in inputNode.getOutputs()) {
162+
const paramButton = new qx.ui.menu.Button();
163+
inputNode.bind("label", paramButton, "label", {
164+
converter: val => val + " : " + inputNode.getOutput(outputKey).label
165+
});
166+
paramButton.addListener("execute", () => {
167+
this.getNode().addInputNode(inputNodeId);
168+
this.getNode().addPortLink(targetPortId, inputNodeId, outputKey);
169+
}, this);
170+
menu.add(paramButton);
171+
osparc.utils.Ports.arePortsCompatible(inputNode, outputKey, this.getNode(), targetPortId)
172+
.then(compatible => {
173+
if (compatible === false) {
174+
paramButton.exclude();
175+
}
176+
});
177+
}
178+
}
179+
});
180+
}
181+
},
182+
183+
__getInputMenuButton: function(inputNodeId, targetPortId) {
184+
const study = this.getStudy();
185+
const thisNode = this.getNode();
186+
if (study && thisNode) {
187+
const node = study.getWorkbench().getNode(inputNodeId);
188+
189+
const inputNodePortsMenu = new qx.ui.menu.Menu();
190+
const inputMenuBtn = new qx.ui.menu.Button(null, null, null, inputNodePortsMenu);
191+
node.bind("label", inputMenuBtn, "label");
192+
if (this.getStudy()) {
193+
this.__populateInputNodePortsMenu(inputNodeId, targetPortId, inputNodePortsMenu, inputMenuBtn);
194+
}
195+
return inputMenuBtn;
111196
}
112197
return null;
113198
},
114199

115200
__getSelectFileButton: function(portId) {
116-
const selectFileButton = new qx.ui.form.Button((this.tr("Select File")));
117-
selectFileButton.addListener("execute", () => {
118-
this.fireDataEvent("filePickerRequested", portId);
119-
}, this);
201+
const selectFileButton = new qx.ui.menu.Button(this.tr("Select File"));
202+
selectFileButton.addListener("execute", () => this.fireDataEvent("filePickerRequested", portId), this);
120203
return selectFileButton;
121204
},
122205

123-
__getParamsMenuButton: function(portId) {
124-
const paramsMenu = new qx.ui.menu.Menu().set({
125-
position: "bottom-right"
126-
});
127-
206+
__getNewParamButton: function(portId) {
128207
const newParamBtn = new qx.ui.menu.Button(this.tr("Set new parameter"));
129-
newParamBtn.addListener("execute", () => {
130-
this.fireDataEvent("parameterNodeRequested", portId);
131-
}, this);
132-
paramsMenu.add(newParamBtn);
208+
newParamBtn.addListener("execute", () => this.fireDataEvent("parameterNodeRequested", portId), this);
209+
return newParamBtn;
210+
},
133211

212+
__getParamsMenuButton: function(portId) {
134213
const existingParamMenu = new qx.ui.menu.Menu();
135-
const study = this.getStudy();
136-
if (study) {
137-
this.__populateExistingParamsMenu(portId, existingParamMenu);
138-
study.addListener("changeParameters", () => {
139-
this.__populateExistingParamsMenu(portId, existingParamMenu);
140-
}, this);
214+
const existingParamBtn = new qx.ui.menu.Button(this.tr("Set existing parameter"), null, null, existingParamMenu);
215+
if (this.getStudy()) {
216+
this.__populateExistingParamsMenu(portId, existingParamMenu, existingParamBtn);
141217
}
218+
return existingParamBtn;
219+
},
142220

143-
const existingParamBtn = new qx.ui.menu.Button(this.tr("Set existing parameter"), null, null, existingParamMenu);
144-
paramsMenu.add(existingParamBtn);
221+
__populateInputNodePortsMenu: function(inputNodeId, targetPortId, menu, menuBtn) {
222+
menuBtn.exclude();
223+
menu.removeAll();
145224

146-
const menuBtn = new qx.ui.form.MenuButton().set({
147-
menu: paramsMenu,
148-
icon: "@FontAwesome5Solid/ellipsis-v/14",
149-
focusable: false,
150-
allowGrowX: false,
151-
alignX: "center"
152-
});
153-
return menuBtn;
225+
const inputNode = this.getStudy().getWorkbench().getNode(inputNodeId);
226+
if (inputNode) {
227+
for (const outputKey in inputNode.getOutputs()) {
228+
osparc.utils.Ports.arePortsCompatible(inputNode, outputKey, this.getNode(), targetPortId)
229+
.then(compatible => {
230+
if (compatible) {
231+
const paramButton = new qx.ui.menu.Button(inputNode.getOutput(outputKey).label);
232+
paramButton.addListener("execute", () => {
233+
this.getNode().addInputNode(inputNodeId);
234+
this.getNode().addPortLink(targetPortId, inputNodeId, outputKey);
235+
}, this);
236+
menu.add(paramButton);
237+
menuBtn.show();
238+
}
239+
});
240+
}
241+
}
154242
},
155243

156-
__populateExistingParamsMenu: function(fieldKey, existingParamMenu) {
157-
existingParamMenu.removeAll();
158-
this.getStudy().getParameters().forEach(paramNode => {
159-
osparc.utils.Ports.arePortsCompatible(paramNode, "out_1", this.getNode(), fieldKey)
244+
__populateExistingParamsMenu: function(targetPortId, menu, menuBtn) {
245+
menuBtn.exclude();
246+
menu.removeAll();
247+
248+
const params = this.getStudy().getParameters();
249+
params.forEach(paramNode => {
250+
const inputNodeId = paramNode.getNodeId();
251+
const outputKey = "out_1";
252+
osparc.utils.Ports.arePortsCompatible(paramNode, outputKey, this.getNode(), targetPortId)
160253
.then(compatible => {
161254
if (compatible) {
162255
const paramButton = new qx.ui.menu.Button();
256+
paramButton.nodeId = inputNodeId;
163257
paramNode.bind("label", paramButton, "label");
164258
paramButton.addListener("execute", () => {
165-
this.getNode().addInputNode(paramNode.getNodeId());
166-
this.getNode().addPortLink(fieldKey, paramNode.getNodeId(), "out_1");
259+
this.getNode().addInputNode(inputNodeId);
260+
this.getNode().addPortLink(targetPortId, inputNodeId, outputKey);
167261
}, this);
168-
existingParamMenu.add(paramButton);
262+
if (!menu.getChildren().some(child => child.nodeId === paramButton.nodeId)) {
263+
menu.add(paramButton);
264+
menuBtn.show();
265+
}
169266
}
170267
});
171268
});
@@ -517,7 +614,9 @@ qx.Class.define("osparc.component.form.renderer.PropForm", {
517614
flex: 1
518615
});
519616

520-
const unlinkBtn = new qx.ui.form.Button(this.tr("Unlink"), "@FontAwesome5Solid/unlink/14");
617+
const unlinkBtn = new qx.ui.form.Button(null, "@FontAwesome5Solid/unlink/14").set({
618+
toolTipText: this.tr("Unlink")
619+
});
521620
unlinkBtn.addListener("execute", function() {
522621
this.removePortLink(portId);
523622
}, this);
@@ -530,6 +629,10 @@ qx.Class.define("osparc.component.form.renderer.PropForm", {
530629
column: this.self().gridPos.ctrlField
531630
});
532631

632+
if (portId in this.__fieldOptsBtnMap) {
633+
this.__fieldOptsBtnMap[portId].setEnabled(false);
634+
}
635+
533636
const linkFieldModified = {
534637
portId,
535638
fromNodeId,
@@ -548,6 +651,10 @@ qx.Class.define("osparc.component.form.renderer.PropForm", {
548651
fieldOpts.child.setEnabled(true);
549652
}
550653

654+
if (portId in this.__fieldOptsBtnMap) {
655+
this.__fieldOptsBtnMap[portId].setEnabled(true);
656+
}
657+
551658
const linkFieldModified = {
552659
portId,
553660
added: false
@@ -635,34 +742,6 @@ qx.Class.define("osparc.component.form.renderer.PropForm", {
635742
});
636743
},
637744

638-
__parameterAdded: function(portId) {
639-
let data = this._getCtrlFieldChild(portId);
640-
if (data) {
641-
let child = data.child;
642-
const idx = data.idx;
643-
const layoutProps = child.getLayoutProperties();
644-
this._remove(child);
645-
646-
const hBox = new qx.ui.container.Composite(new qx.ui.layout.HBox(5));
647-
hBox.add(this.getControlParam(portId), {
648-
flex: 1
649-
});
650-
651-
const unparamBtn = new qx.ui.form.Button(this.tr("Remove parameter"), "@FontAwesome5Solid/unlink/14");
652-
unparamBtn.addListener("execute", function() {
653-
this.removeParameter(portId);
654-
}, this);
655-
hBox.add(unparamBtn);
656-
657-
hBox.key = portId;
658-
659-
this._addAt(hBox, idx, {
660-
row: layoutProps.row,
661-
column: this.self().gridPos.ctrlField
662-
});
663-
}
664-
},
665-
666745
__parameterRemoved: function(portId) {
667746
this.__resetCtrlField(portId);
668747
},

services/web/client/source/class/osparc/component/form/renderer/PropFormBase.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ qx.Class.define("osparc.component.form.renderer.PropFormBase", {
4444
fl.setColumnFlex(this.self().gridPos.info, 0);
4545
fl.setColumnAlign(this.self().gridPos.info, "left", "middle");
4646
fl.setColumnFlex(this.self().gridPos.ctrlField, 1);
47-
fl.setColumnMinWidth(this.self().gridPos.ctrlField, 130);
47+
fl.setColumnMinWidth(this.self().gridPos.ctrlField, 100);
4848
},
4949

5050
properties: {

services/web/client/source/class/osparc/component/task/TasksButton.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ qx.Class.define("osparc.component.task.TasksButton", {
2626
this.set({
2727
width: 30,
2828
alignX: "center",
29-
cursor: "pointer"
29+
cursor: "pointer",
30+
visibility: "excluded"
3031
});
3132

3233
const tasks = osparc.component.task.Tasks.getInstance();

services/web/client/source/class/osparc/component/widget/NodeTreeItem.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
4141
extend: qx.ui.tree.VirtualTreeItem,
4242

4343
construct: function(study) {
44+
this.__study = study;
45+
4446
this.base(arguments);
4547

46-
this.__study = study;
4748
this.__setNotHoveredStyle();
4849
this.__attachEventHandlers();
4950
},
@@ -81,7 +82,7 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
8182
const part = this.getChildControl("buttons");
8283
control = new qx.ui.form.Button().set({
8384
backgroundColor: "transparent",
84-
toolTipText: this.tr("openNode"),
85+
toolTipText: this.tr("Open"),
8586
icon: "@FontAwesome5Solid/edit/9"
8687
});
8788
control.addListener("execute", () => this.fireDataEvent("openNode", this.getNodeId()));
@@ -92,7 +93,7 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
9293
const part = this.getChildControl("buttons");
9394
control = new qx.ui.form.Button().set({
9495
backgroundColor: "transparent",
95-
toolTipText: this.tr("renameNode"),
96+
toolTipText: this.tr("Rename"),
9697
icon: "@FontAwesome5Solid/i-cursor/9"
9798
});
9899
control.addListener("execute", () => this.fireDataEvent("renameNode", this.getNodeId()));
@@ -103,7 +104,7 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
103104
const part = this.getChildControl("buttons");
104105
control = new qx.ui.form.Button().set({
105106
backgroundColor: "transparent",
106-
toolTipText: this.tr("deleteNode"),
107+
toolTipText: this.tr("Delete"),
107108
icon: "@FontAwesome5Solid/trash/9"
108109
});
109110
control.addListener("execute", () => this.fireDataEvent("deleteNode", this.getNodeId()));
@@ -150,8 +151,10 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
150151
});
151152

152153
this.getChildControl("open-btn");
153-
this.getChildControl("rename-btn");
154-
this.getChildControl("delete-btn");
154+
const studyId = this.__study.getUuid();
155+
const readOnly = this.__study.isReadOnly();
156+
this.getChildControl("rename-btn").setEnabled(!readOnly);
157+
this.getChildControl("delete-btn").setEnabled(!readOnly && studyId !== this.getNodeId());
155158
this.getChildControl("node-id");
156159
},
157160

@@ -199,7 +202,6 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
199202

200203
if (state === "selected") {
201204
this.getChildControl("buttons").show();
202-
this.getChildControl("delete-btn").setEnabled(false);
203205
}
204206
},
205207

@@ -210,6 +212,7 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
210212
this.getChildControl("buttons").exclude();
211213
const studyId = this.__study.getUuid();
212214
const readOnly = this.__study.isReadOnly();
215+
this.getChildControl("rename-btn").setEnabled(!readOnly);
213216
// disable delete button if the study is read only or if it's the study node item
214217
this.getChildControl("delete-btn").setEnabled(!readOnly && studyId !== this.getNodeId());
215218
}

0 commit comments

Comments
 (0)