Skip to content

Commit 7816bb6

Browse files
authored
Pie context menu (ITISFoundation#2640)
1 parent 9a8767c commit 7816bb6

File tree

13 files changed

+1551
-25
lines changed

13 files changed

+1551
-25
lines changed

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ qx.Class.define("osparc.component.widget.StudyTitleOnlyTree", {
2525
this.setDelegate({
2626
...this.getDelegate(),
2727
createItem: () => {
28-
const nodeTreeItem = new osparc.component.widget.NodeTreeItem();
29-
nodeTreeItem.addListener("renameNode", e => this.__openItemRenamer(e.getData()));
30-
nodeTreeItem.addListener("infoNode", e => this.__openStudyInfo());
31-
return nodeTreeItem;
28+
const studyTreeItem = new osparc.component.widget.NodeTreeItem();
29+
studyTreeItem.addListener("renameNode", e => this.__openItemRenamer(e.getData()));
30+
studyTreeItem.addListener("infoNode", e => this.__openStudyInfo());
31+
return studyTreeItem;
3232
}
3333
});
3434
},
@@ -39,6 +39,11 @@ qx.Class.define("osparc.component.widget.StudyTitleOnlyTree", {
3939
const width = 500;
4040
const height = 500;
4141
osparc.ui.window.Window.popUpInWindow(studyDetails, title, width, height);
42+
},
43+
44+
selectStudyItem: function() {
45+
this.setSelection(new qx.data.Array([this.getModel()]));
46+
this.fireDataEvent("nodeSelected", this.getModel().getNodeId());
4247
}
4348
}
4449
});

services/web/client/source/class/osparc/component/workbench/WorkbenchUI.js

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,15 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", {
305305
return inputOutputNodesLayout;
306306
},
307307

308+
__openServiceCatalog: function(e) {
309+
if (this.getStudy().isReadOnly()) {
310+
return;
311+
}
312+
const winPos = this.__pointerEventToScreenPos(e);
313+
const nodePos = this.__pointerEventToWorkbenchPos(e);
314+
this.openServiceCatalog(winPos, nodePos);
315+
},
316+
308317
openServiceCatalog: function(winPos, nodePos) {
309318
const srvCat = new osparc.component.workbench.ServiceCatalog();
310319
const maxLeft = this.getBounds().width - osparc.component.workbench.ServiceCatalog.Width;
@@ -810,7 +819,7 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", {
810819
return topOffset;
811820
},
812821

813-
__pointerEventToWinPos: function(e) {
822+
__pointerEventToScreenPos: function(e) {
814823
const leftOffset = this.__getLeftOffset();
815824
const inputNodesLayoutWidth = this.__inputNodesLayout && this.__inputNodesLayout.isVisible() ? this.__inputNodesLayout.getWidth() : 0;
816825
return {
@@ -823,7 +832,7 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", {
823832
const {
824833
x,
825834
y
826-
} = this.__pointerEventToWinPos(e);
835+
} = this.__pointerEventToScreenPos(e);
827836
const scaledPos = this.__scaleCoordinates(x, y);
828837
const scrollX = this._workbenchLayoutScroll.getScrollX();
829838
const scrollY = this._workbenchLayoutScroll.getScrollY();
@@ -1091,7 +1100,7 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", {
10911100
if (this.__isSelectedItemAnEdge()) {
10921101
const edge = this.__getEdgeUI(newID);
10931102
edge.setSelected(true);
1094-
} else if (newID) {
1103+
} else {
10951104
this.fireDataEvent("changeSelectedNode", newID);
10961105
}
10971106

@@ -1118,10 +1127,72 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", {
11181127
};
11191128
},
11201129

1130+
__openContextMenu: function(e) {
1131+
const radialMenuWrapper = osparc.wrapper.RadialMenu.getInstance();
1132+
if (radialMenuWrapper.getLibReady()) {
1133+
this.__doOpenContextMenu(e);
1134+
} else {
1135+
radialMenuWrapper.init()
1136+
.then(loaded => {
1137+
if (loaded) {
1138+
this.__doOpenContextMenu(e);
1139+
}
1140+
});
1141+
}
1142+
},
1143+
1144+
__doOpenContextMenu: function(e) {
1145+
if (this.__contextMenu) {
1146+
this.__contextMenu.hide();
1147+
}
1148+
const buttons = [{
1149+
"text": "\uf067", // plus
1150+
"action": () => {
1151+
this.__openServiceCatalog(e);
1152+
}
1153+
}, {
1154+
"text": "\uf00e", // search-plus
1155+
"action": () => {
1156+
this.__pointerPos = this.__pointerEventToWorkbenchPos(e);
1157+
this.__zoom(true);
1158+
}
1159+
}, {
1160+
"text": "\uf002", // search
1161+
"action": () => {
1162+
this.setScale(1);
1163+
}
1164+
}, {
1165+
"text": "\uf010", // search-minus
1166+
"action": () => {
1167+
this.__pointerPos = this.__pointerEventToWorkbenchPos(e);
1168+
this.__zoom(false);
1169+
}
1170+
}];
1171+
let rotation = 3 * Math.PI / 2;
1172+
rotation -= (2/buttons.length) * (Math.PI / 2);
1173+
const radialMenuWrapper = osparc.wrapper.RadialMenu.getInstance();
1174+
const contextMenu = this.__contextMenu = radialMenuWrapper.createMenu({rotation});
1175+
contextMenu.addButtons(buttons);
1176+
contextMenu.setPos(e.getDocumentLeft() - contextMenu.w2, e.getDocumentTop() - contextMenu.h2);
1177+
contextMenu.show();
1178+
/*
1179+
const tapListener = ev => {
1180+
if (osparc.utils.Utils.isMouseOnElement(contextMenu, ev)) {
1181+
return;
1182+
}
1183+
contextMenu.hide();
1184+
document.removeEventListener("mousedown", tapListener);
1185+
};
1186+
document.addEventListener("mousedown", tapListener);
1187+
*/
1188+
},
1189+
11211190
__mouseDown: function(e) {
1122-
if (e.isMiddlePressed()) {
1123-
this.__panning = true;
1191+
if (e.isRightPressed()) {
1192+
this.__openContextMenu(e);
1193+
} else if (e.isMiddlePressed()) {
11241194
this.__pointerPos = this.__pointerEventToWorkbenchPos(e);
1195+
this.__panning = true;
11251196
this.set({
11261197
cursor: "move"
11271198
});
@@ -1407,12 +1478,7 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", {
14071478
}, this);
14081479

14091480
this.__workbenchLayout.addListener("dbltap", e => {
1410-
if (this.getStudy().isReadOnly()) {
1411-
return;
1412-
}
1413-
const winPos = this.__pointerEventToWinPos(e);
1414-
const nodePos = this.__pointerEventToWorkbenchPos(e);
1415-
this.openServiceCatalog(winPos, nodePos);
1481+
this.__openServiceCatalog(e);
14161482
}, this);
14171483

14181484
this.__workbenchLayout.addListener("resize", () => this.__updateHint(), this);

services/web/client/source/class/osparc/dashboard/Dashboard.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ qx.Class.define("osparc.dashboard.Dashboard", {
4747
osparc.wrapper.JsonDiffPatch.getInstance().init();
4848
osparc.wrapper.JsonTreeViewer.getInstance().init();
4949
osparc.wrapper.DOMPurify.getInstance().init();
50+
osparc.wrapper.RadialMenu.getInstance().init()
51+
.then(loaded => {
52+
if (loaded) {
53+
// hack to trigger the fonts loading
54+
const menu = osparc.wrapper.RadialMenu.getInstance().createMenu();
55+
menu.show();
56+
menu.hide();
57+
}
58+
});
5059
this.__createMainViewLayout();
5160
},
5261

services/web/client/source/class/osparc/desktop/WorkbenchView.js

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ qx.Class.define("osparc.desktop.WorkbenchView", {
234234
study.getUi().addListener("changeMode", () => this.__evalSlidesButtons());
235235
this.__evalSlidesButtons();
236236
this.evalSnapshotsButtons();
237+
238+
// if there are no nodes, preselect the study item (show study info)
239+
const nodes = study.getWorkbench().getNodes(true);
240+
if (Object.values(nodes).length === 0) {
241+
this.__studyTreeItem.selectStudyItem();
242+
}
237243
}
238244
this.__workbenchPanel.getToolbar().setStudy(study);
239245
},
@@ -523,14 +529,19 @@ qx.Class.define("osparc.desktop.WorkbenchView", {
523529
}
524530
});
525531
workbenchUI.addListener("changeSelectedNode", e => {
526-
studyTreeItem.resetSelection();
527532
const nodeId = e.getData();
528-
this.__nodesTree.nodeSelected(nodeId);
529-
const workbench = this.getStudy().getWorkbench();
530-
const node = workbench.getNode(nodeId);
531-
this.__populateSecondPanel(node);
532-
this.__evalIframe(node);
533-
this.__loggerView.setCurrentNodeId(nodeId);
533+
if (nodeId) {
534+
studyTreeItem.resetSelection();
535+
this.__nodesTree.nodeSelected(nodeId);
536+
const workbench = this.getStudy().getWorkbench();
537+
const node = workbench.getNode(nodeId);
538+
this.__populateSecondPanel(node);
539+
this.__evalIframe(node);
540+
this.__loggerView.setCurrentNodeId(nodeId);
541+
} else {
542+
// empty selection
543+
this.__studyTreeItem.selectStudyItem();
544+
}
534545
});
535546
workbenchUI.addListener("nodeSelected", e => {
536547
studyTreeItem.resetSelection();
@@ -679,7 +690,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", {
679690

680691
nodeSelected: function(nodeId) {
681692
const study = this.getStudy();
682-
if (nodeId === null) {
693+
if (nodeId === null || nodeId === undefined) {
683694
nodeId = study.getUuid();
684695
}
685696

@@ -690,8 +701,12 @@ qx.Class.define("osparc.desktop.WorkbenchView", {
690701
this.__nodesTree.nodeSelected(nodeId);
691702
}
692703

693-
const node = study.getWorkbench().getNode(nodeId);
694-
this.__populateSecondPanel(node);
704+
if (nodeId === study.getUuid()) {
705+
this.__studyTreeItem.selectStudyItem();
706+
} else {
707+
const node = study.getWorkbench().getNode(nodeId);
708+
this.__populateSecondPanel(node);
709+
}
695710
},
696711

697712
__evalIframe: function(node) {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2021 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
/* global RadialMenu */
19+
20+
/**
21+
* @asset(radialMenu/RadialMenuES5.js)
22+
* @asset(radialMenu/font-awesome.css)
23+
* @asset(radialMenu/fontawesome.*)
24+
* @asset(radialMenu/FontAwesome.otf)
25+
* @ignore(RadialMenu)
26+
*/
27+
28+
/**
29+
* A qooxdoo wrapper for
30+
* <a href='https://github.com/victorqribeiro/radialMenu' target='_blank'>RadialMenu</a>
31+
*/
32+
33+
qx.Class.define("osparc.wrapper.RadialMenu", {
34+
extend: qx.core.Object,
35+
type: "singleton",
36+
37+
statics: {
38+
NAME: "RadialMenu",
39+
VERSION: "1.2.0",
40+
URL: "https://github.com/victorqribeiro/radialMenu"
41+
},
42+
43+
properties: {
44+
libReady: {
45+
nullable: false,
46+
init: false,
47+
check: "Boolean"
48+
}
49+
},
50+
51+
members: {
52+
init: function() {
53+
return new Promise((resolve, reject) => {
54+
// initialize the script loading
55+
const radialMenuPath = "radialMenu/RadialMenuES5.js";
56+
const radialMenuFontsCss = "radialMenu/font-awesome.css";
57+
const radialMenuFontsCssUri = qx.util.ResourceManager.getInstance().toUri(radialMenuFontsCss);
58+
qx.module.Css.includeStylesheet(radialMenuFontsCssUri);
59+
const dynLoader = new qx.util.DynamicScriptLoader([
60+
radialMenuPath
61+
]);
62+
63+
dynLoader.addListenerOnce("ready", e => {
64+
this.setLibReady(true);
65+
resolve(true);
66+
}, this);
67+
68+
dynLoader.addListener("failed", e => {
69+
let data = e.getData();
70+
console.error("failed to load " + data.script);
71+
reject(data);
72+
}, this);
73+
74+
dynLoader.start();
75+
});
76+
},
77+
78+
createMenu: function(settings = {}) {
79+
const defSettings = this.__getDefaultSettings();
80+
const radialMenu = new RadialMenu(Object.assign(defSettings, settings));
81+
// give id to canvas
82+
return radialMenu;
83+
},
84+
85+
__getDefaultSettings: function() {
86+
return {
87+
fontSize: 16,
88+
textColor: qx.theme.manager.Color.getInstance().resolve("text"),
89+
backgroundColor: qx.theme.manager.Color.getInstance().resolve("background-main-lighter+"),
90+
hoverBackgroundColor: qx.theme.manager.Color.getInstance().resolve("contrasted-background+"),
91+
borderColor: "transparent",
92+
innerCircle: 20,
93+
outerCircle: 60,
94+
rotation: 0, // radians
95+
buttonGap: 0.01 // radians
96+
// isFixed: true // we will handle the events
97+
};
98+
}
99+
}
100+
});
132 KB
Binary file not shown.

0 commit comments

Comments
 (0)