mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Touch: add long-touch menu
This commit is contained in:
parent
863ceb065a
commit
e524393d87
@ -325,5 +325,6 @@
|
|||||||
<script src="red/ui/editor.js"></script>
|
<script src="red/ui/editor.js"></script>
|
||||||
<script src="red/ui/library.js"></script>
|
<script src="red/ui/library.js"></script>
|
||||||
<script src="red/ui/notifications.js"></script>
|
<script src="red/ui/notifications.js"></script>
|
||||||
|
<script src="red/ui/touch/radialMenu.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -23,6 +23,9 @@ RED.history = function() {
|
|||||||
undo_history[i].dirty = true;
|
undo_history[i].dirty = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
depth: function() {
|
||||||
|
return undo_history.length;
|
||||||
|
},
|
||||||
push: function(ev) {
|
push: function(ev) {
|
||||||
undo_history.push(ev);
|
undo_history.push(ev);
|
||||||
},
|
},
|
||||||
|
186
public/red/ui/touch/radialMenu.js
Normal file
186
public/red/ui/touch/radialMenu.js
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
RED.touch = RED.touch||{};
|
||||||
|
RED.touch.radialMenu = function() {
|
||||||
|
|
||||||
|
|
||||||
|
var touchMenu = null;
|
||||||
|
var isActive = false;
|
||||||
|
var isOutside = false;
|
||||||
|
var activeOption = null;
|
||||||
|
|
||||||
|
|
||||||
|
function createRadial(obj,pos,options) {
|
||||||
|
isActive = true;
|
||||||
|
try {
|
||||||
|
var w = $("body").width();
|
||||||
|
var h = $("body").height();
|
||||||
|
|
||||||
|
touchMenu = d3.select("body").append("div")
|
||||||
|
.style({
|
||||||
|
position:"absolute",
|
||||||
|
top: 0,
|
||||||
|
left:0,
|
||||||
|
bottom:0,
|
||||||
|
right:0,
|
||||||
|
"z-index": 1000
|
||||||
|
})
|
||||||
|
.on('touchstart',function() {
|
||||||
|
hide();
|
||||||
|
d3.event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var menu = touchMenu.append("div")
|
||||||
|
.style({
|
||||||
|
position: "absolute",
|
||||||
|
top: (pos[1]-80)+"px",
|
||||||
|
left:(pos[0]-80)+"px",
|
||||||
|
"border-radius": "80px",
|
||||||
|
width: "160px",
|
||||||
|
height: "160px",
|
||||||
|
background: "rgba(255,255,255,0.6)",
|
||||||
|
border: "1px solid #666"
|
||||||
|
});
|
||||||
|
|
||||||
|
var menuOpts = [];
|
||||||
|
function createMenuOpt(x,y,opt) {
|
||||||
|
opt.el = menu.append("div")
|
||||||
|
.style({
|
||||||
|
position: "absolute",
|
||||||
|
top: (y+80-25)+"px",
|
||||||
|
left:(x+80-25)+"px",
|
||||||
|
"border-radius": "20px",
|
||||||
|
width: "50px",
|
||||||
|
height: "50px",
|
||||||
|
background: "#fff",
|
||||||
|
border: "2px solid #666",
|
||||||
|
"text-align": "center",
|
||||||
|
"line-height":"50px"
|
||||||
|
});
|
||||||
|
if (opt.icon) {
|
||||||
|
opt.el.append("i").attr("class","icon "+opt.icon)
|
||||||
|
} else {
|
||||||
|
opt.el.html(opt.name);
|
||||||
|
}
|
||||||
|
if (opt.disabled) {
|
||||||
|
opt.el.style({"border-color":"#ccc",color:"#ccc"});
|
||||||
|
}
|
||||||
|
opt.x = x;
|
||||||
|
opt.y = y;
|
||||||
|
menuOpts.push(opt);
|
||||||
|
|
||||||
|
opt.el.on('touchstart',function() {
|
||||||
|
opt.el.style("background","#999");
|
||||||
|
d3.event.preventDefault();
|
||||||
|
d3.event.stopPropagation();
|
||||||
|
});
|
||||||
|
opt.el.on('touchend',function() {
|
||||||
|
hide();
|
||||||
|
opt.onselect();
|
||||||
|
d3.event.preventDefault();
|
||||||
|
d3.event.stopPropagation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = options.length;
|
||||||
|
var dang = Math.max(Math.PI/(n-1),Math.PI/4);
|
||||||
|
var ang = Math.PI;
|
||||||
|
for (var i=0;i<n;i++) {
|
||||||
|
var x = Math.floor(Math.cos(ang)*80);
|
||||||
|
var y = Math.floor(Math.sin(ang)*80);
|
||||||
|
if (options[i].name) {
|
||||||
|
createMenuOpt(x,y,options[i]);
|
||||||
|
}
|
||||||
|
ang += dang;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
isActive = false;
|
||||||
|
activeOption = null;
|
||||||
|
touchMenu.remove();
|
||||||
|
touchMenu = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.on('touchend.radial',function() {
|
||||||
|
obj.on('touchend.radial',null);
|
||||||
|
obj.on('touchmenu.radial',null);
|
||||||
|
|
||||||
|
if (activeOption) {
|
||||||
|
try {
|
||||||
|
activeOption.onselect();
|
||||||
|
} catch(err) {
|
||||||
|
RED._debug(err);
|
||||||
|
}
|
||||||
|
hide();
|
||||||
|
} else if (isOutside) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
obj.on('touchmove.radial',function() {
|
||||||
|
try {
|
||||||
|
var touch0 = d3.event.touches.item(0);
|
||||||
|
var p = [touch0.pageX - pos[0],touch0.pageY-pos[1]];
|
||||||
|
for (var i=0;i<menuOpts.length;i++) {
|
||||||
|
var opt = menuOpts[i];
|
||||||
|
if (!opt.disabled) {
|
||||||
|
if (p[0]>opt.x-30 && p[0]<opt.x+30 && p[1]>opt.y-30 && p[1]<opt.y+30) {
|
||||||
|
if (opt !== activeOption) {
|
||||||
|
opt.el.style("background","#999");
|
||||||
|
activeOption = opt;
|
||||||
|
}
|
||||||
|
} else if (opt === activeOption) {
|
||||||
|
opt.el.style("background","#fff");
|
||||||
|
activeOption = null;
|
||||||
|
} else {
|
||||||
|
opt.el.style("background","#fff");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!activeOption) {
|
||||||
|
var d = Math.abs((p[0]*p[0])+(p[1]*p[1]));
|
||||||
|
isOutside = (d > 80*80);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
RED._debug(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
RED._debug(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
show: createRadial,
|
||||||
|
active: function() {
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}();
|
||||||
|
|
@ -23,6 +23,13 @@ RED.view = function() {
|
|||||||
node_width = 100,
|
node_width = 100,
|
||||||
node_height = 30;
|
node_height = 30;
|
||||||
|
|
||||||
|
var touchLongPressTimeout = 1000,
|
||||||
|
startTouchDistance = 0,
|
||||||
|
startTouchCenter = [],
|
||||||
|
moveTouchCenter = [],
|
||||||
|
touchStartTime = 0;
|
||||||
|
|
||||||
|
|
||||||
var activeWorkspace = 0;
|
var activeWorkspace = 0;
|
||||||
var workspaceScrollPositions = {};
|
var workspaceScrollPositions = {};
|
||||||
|
|
||||||
@ -39,13 +46,8 @@ RED.view = function() {
|
|||||||
dirty = false,
|
dirty = false,
|
||||||
lasso = null,
|
lasso = null,
|
||||||
showStatus = false,
|
showStatus = false,
|
||||||
touchStartTime = 0,
|
|
||||||
clickTime = 0,
|
clickTime = 0,
|
||||||
clickElapsed = 0,
|
clickElapsed = 0;
|
||||||
startTouchDistance = 0,
|
|
||||||
startTouchCenter = [],
|
|
||||||
moveTouchCenter = [],
|
|
||||||
touches = 0;
|
|
||||||
|
|
||||||
var clipboard = "";
|
var clipboard = "";
|
||||||
|
|
||||||
@ -74,13 +76,16 @@ RED.view = function() {
|
|||||||
.on("touchend", function() {
|
.on("touchend", function() {
|
||||||
clearTimeout(touchStartTime);
|
clearTimeout(touchStartTime);
|
||||||
touchStartTime = null;
|
touchStartTime = null;
|
||||||
|
if (RED.touch.radialMenu.active()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (lasso) {
|
if (lasso) {
|
||||||
outer_background.attr("fill","#fff");
|
outer_background.attr("fill","#fff");
|
||||||
}
|
}
|
||||||
canvasMouseUp.call(this);
|
canvasMouseUp.call(this);
|
||||||
})
|
})
|
||||||
.on("touchcancel", canvasMouseUp)
|
.on("touchcancel", canvasMouseUp)
|
||||||
.on("touchstart", function(){
|
.on("touchstart", function() {
|
||||||
if (d3.event.touches.length>1) {
|
if (d3.event.touches.length>1) {
|
||||||
d3.event.preventDefault();
|
d3.event.preventDefault();
|
||||||
var touch0 = d3.event.touches.item(0);
|
var touch0 = d3.event.touches.item(0);
|
||||||
@ -100,35 +105,41 @@ RED.view = function() {
|
|||||||
]
|
]
|
||||||
startTouchDistance = Math.sqrt((a*a)+(b*b));
|
startTouchDistance = Math.sqrt((a*a)+(b*b));
|
||||||
} else {
|
} else {
|
||||||
|
var obj = d3.select(document.body);
|
||||||
var touch0 = d3.event.touches.item(0);
|
var touch0 = d3.event.touches.item(0);
|
||||||
|
var pos = [touch0.pageX,touch0.pageY];
|
||||||
startTouchCenter = [touch0.pageX,touch0.pageY];
|
startTouchCenter = [touch0.pageX,touch0.pageY];
|
||||||
startTouchDistance = 0;
|
startTouchDistance = 0;
|
||||||
var point = d3.touches(this)[0];
|
var point = d3.touches(this)[0];
|
||||||
touchStartTime = setTimeout(function() {
|
touchStartTime = setTimeout(function() {
|
||||||
lasso = vis.append('rect')
|
|
||||||
.attr("ox",point[0])
|
|
||||||
.attr("oy",point[1])
|
|
||||||
.attr("rx",2)
|
|
||||||
.attr("ry",2)
|
|
||||||
.attr("x",point[0])
|
|
||||||
.attr("y",point[1])
|
|
||||||
.attr("width",0)
|
|
||||||
.attr("height",0)
|
|
||||||
.attr("class","lasso");
|
|
||||||
touchStartTime = null;
|
touchStartTime = null;
|
||||||
outer_background.attr("fill","#f3f3f3");
|
showTouchMenu(obj,pos);
|
||||||
},1000);
|
//lasso = vis.append('rect')
|
||||||
canvasMouseDown();
|
// .attr("ox",point[0])
|
||||||
|
// .attr("oy",point[1])
|
||||||
|
// .attr("rx",2)
|
||||||
|
// .attr("ry",2)
|
||||||
|
// .attr("x",point[0])
|
||||||
|
// .attr("y",point[1])
|
||||||
|
// .attr("width",0)
|
||||||
|
// .attr("height",0)
|
||||||
|
// .attr("class","lasso");
|
||||||
|
//outer_background.attr("fill","#e3e3f3");
|
||||||
|
},touchLongPressTimeout);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("touchmove", function(){
|
.on("touchmove", function(){
|
||||||
|
if (RED.touch.radialMenu.active()) {
|
||||||
|
d3.event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (d3.event.touches.length<2) {
|
if (d3.event.touches.length<2) {
|
||||||
if (touchStartTime) {
|
if (touchStartTime) {
|
||||||
var touch0 = d3.event.touches.item(0);
|
var touch0 = d3.event.touches.item(0);
|
||||||
var dx = (touch0.pageX-startTouchCenter[0]);
|
var dx = (touch0.pageX-startTouchCenter[0]);
|
||||||
var dy = (touch0.pageY-startTouchCenter[1]);
|
var dy = (touch0.pageY-startTouchCenter[1]);
|
||||||
var d = Math.sqrt(dx*dx+dy*dy);
|
var d = Math.abs(dx*dx+dy*dy);
|
||||||
if (d > 7) {
|
if (d > 64) {
|
||||||
clearTimeout(touchStartTime);
|
clearTimeout(touchStartTime);
|
||||||
touchStartTime = null;
|
touchStartTime = null;
|
||||||
}
|
}
|
||||||
@ -311,22 +322,19 @@ RED.view = function() {
|
|||||||
$( "#node-dialog-delete-workspace" ).dialog('open');
|
$( "#node-dialog-delete-workspace" ).dialog('open');
|
||||||
}
|
}
|
||||||
|
|
||||||
//d3.select(window).on("keydown", keydown);
|
|
||||||
|
|
||||||
function canvasMouseDown() {
|
function canvasMouseDown() {
|
||||||
|
|
||||||
if (!mousedown_node && !mousedown_link) {
|
if (!mousedown_node && !mousedown_link) {
|
||||||
selected_link = null;
|
selected_link = null;
|
||||||
updateSelection();
|
updateSelection();
|
||||||
//vis.call(d3.behavior.zoom().on("zoom"), rescale);
|
|
||||||
}
|
}
|
||||||
if (mouse_mode == 0) {
|
if (mouse_mode == 0) {
|
||||||
if (lasso) {
|
if (lasso) {
|
||||||
lasso.remove();
|
lasso.remove();
|
||||||
lasso = null;
|
lasso = null;
|
||||||
}
|
}
|
||||||
var point = d3.touches(this)[0]||d3.mouse(this);
|
|
||||||
if (d3.touches(this).length === 0) {
|
if (!touchStartTime) {
|
||||||
|
var point = d3.mouse(this);
|
||||||
lasso = vis.append('rect')
|
lasso = vis.append('rect')
|
||||||
.attr("ox",point[0])
|
.attr("ox",point[0])
|
||||||
.attr("oy",point[1])
|
.attr("oy",point[1])
|
||||||
@ -823,6 +831,9 @@ RED.view = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function nodeMouseDown(d) {
|
function nodeMouseDown(d) {
|
||||||
|
//var touch0 = d3.event;
|
||||||
|
//var pos = [touch0.pageX,touch0.pageY];
|
||||||
|
//RED.touch.radialMenu.show(d3.select(this),pos);
|
||||||
if (mouse_mode == RED.state.IMPORT_DRAGGING) {
|
if (mouse_mode == RED.state.IMPORT_DRAGGING) {
|
||||||
RED.keyboard.remove(/* ESCAPE */ 27);
|
RED.keyboard.remove(/* ESCAPE */ 27);
|
||||||
updateSelection();
|
updateSelection();
|
||||||
@ -899,6 +910,20 @@ RED.view = function() {
|
|||||||
d3.event.preventDefault();
|
d3.event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showTouchMenu(obj,pos) {
|
||||||
|
var mdn = mousedown_node;
|
||||||
|
var options = [];
|
||||||
|
options.push({name:"delete",disabled:(moving_set.length==0),onselect:function() {deleteSelection();}});
|
||||||
|
options.push({name:"cut",disabled:(moving_set.length==0),onselect:function() {copySelection();deleteSelection();}});
|
||||||
|
options.push({name:"copy",disabled:(moving_set.length==0),onselect:function() {copySelection();}});
|
||||||
|
options.push({name:"paste",disabled:(clipboard.length==0),onselect:function() {importNodes(clipboard,true);}});
|
||||||
|
options.push({name:"edit",disabled:(moving_set.length != 1),onselect:function() { RED.editor.edit(mdn);}});
|
||||||
|
options.push({name:"select",onselect:function() {selectAll();}});
|
||||||
|
options.push({name:"undo",disabled:(RED.history.depth() == 0),onselect:function() {RED.history.pop();}});
|
||||||
|
|
||||||
|
RED.touch.radialMenu.show(obj,pos,options);
|
||||||
|
resetMouseVars();
|
||||||
|
}
|
||||||
function redraw() {
|
function redraw() {
|
||||||
vis.attr("transform","scale("+scaleFactor+")");
|
vis.attr("transform","scale("+scaleFactor+")");
|
||||||
outer.attr("width", space_width*scaleFactor).attr("height", space_height*scaleFactor);
|
outer.attr("width", space_width*scaleFactor).attr("height", space_height*scaleFactor);
|
||||||
@ -967,8 +992,28 @@ RED.view = function() {
|
|||||||
.attr("rx", 6)
|
.attr("rx", 6)
|
||||||
.attr("ry", 6)
|
.attr("ry", 6)
|
||||||
.attr("fill",function(d) { return d._def.color;})
|
.attr("fill",function(d) { return d._def.color;})
|
||||||
|
.on("mouseup",nodeMouseUp)
|
||||||
.on("mousedown",nodeMouseDown)
|
.on("mousedown",nodeMouseDown)
|
||||||
.on("touchstart",nodeMouseDown)
|
.on("touchstart",function(d) {
|
||||||
|
var obj = d3.select(this);
|
||||||
|
var touch0 = d3.event.touches.item(0);
|
||||||
|
var pos = [touch0.pageX,touch0.pageY];
|
||||||
|
startTouchCenter = [touch0.pageX,touch0.pageY];
|
||||||
|
startTouchDistance = 0;
|
||||||
|
touchStartTime = setTimeout(function() {
|
||||||
|
showTouchMenu(obj,pos);
|
||||||
|
},touchLongPressTimeout);
|
||||||
|
nodeMouseDown.call(this,d)
|
||||||
|
})
|
||||||
|
.on("touchend", function(d) {
|
||||||
|
clearTimeout(touchStartTime);
|
||||||
|
touchStartTime = null;
|
||||||
|
if (RED.touch.radialMenu.active()) {
|
||||||
|
d3.event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nodeMouseUp.call(this,d);
|
||||||
|
})
|
||||||
.on("mouseover",function(d) {
|
.on("mouseover",function(d) {
|
||||||
if (mouse_mode == 0) {
|
if (mouse_mode == 0) {
|
||||||
var node = d3.select(this);
|
var node = d3.select(this);
|
||||||
@ -983,10 +1028,6 @@ RED.view = function() {
|
|||||||
//node.append("rect").attr("class", "node-gradient-top").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-top)").style("pointer-events","none");
|
//node.append("rect").attr("class", "node-gradient-top").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-top)").style("pointer-events","none");
|
||||||
//node.append("rect").attr("class", "node-gradient-bottom").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-bottom)").style("pointer-events","none");
|
//node.append("rect").attr("class", "node-gradient-bottom").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-bottom)").style("pointer-events","none");
|
||||||
|
|
||||||
mainRect.on("mouseup",nodeMouseUp);
|
|
||||||
mainRect.on("touchend",nodeMouseUp);
|
|
||||||
//mainRect.on("touchend",nodeMouseUp);
|
|
||||||
|
|
||||||
if (d._def.icon) {
|
if (d._def.icon) {
|
||||||
var icon = node.append("image")
|
var icon = node.append("image")
|
||||||
.attr("xlink:href","icons/"+d._def.icon)
|
.attr("xlink:href","icons/"+d._def.icon)
|
||||||
@ -1281,7 +1322,7 @@ RED.view = function() {
|
|||||||
* - all 'selected'
|
* - all 'selected'
|
||||||
* - attached to mouse for placing - 'IMPORT_DRAGGING'
|
* - attached to mouse for placing - 'IMPORT_DRAGGING'
|
||||||
*/
|
*/
|
||||||
function importNodes(newNodesStr) {
|
function importNodes(newNodesStr,touchImport) {
|
||||||
try {
|
try {
|
||||||
var result = RED.nodes.import(newNodesStr,true);
|
var result = RED.nodes.import(newNodesStr,true);
|
||||||
if (result) {
|
if (result) {
|
||||||
@ -1320,7 +1361,9 @@ RED.view = function() {
|
|||||||
node.dx -= minX;
|
node.dx -= minX;
|
||||||
node.dy -= minY;
|
node.dy -= minY;
|
||||||
}
|
}
|
||||||
|
if (!touchImport) {
|
||||||
mouse_mode = RED.state.IMPORT_DRAGGING;
|
mouse_mode = RED.state.IMPORT_DRAGGING;
|
||||||
|
}
|
||||||
|
|
||||||
RED.keyboard.add(/* ESCAPE */ 27,function(){
|
RED.keyboard.add(/* ESCAPE */ 27,function(){
|
||||||
RED.keyboard.remove(/* ESCAPE */ 27);
|
RED.keyboard.remove(/* ESCAPE */ 27);
|
||||||
|
@ -62,6 +62,10 @@ a.brand img {
|
|||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-fixed-top {
|
||||||
|
position: absolute !important;
|
||||||
|
z-index: 100 !important;
|
||||||
|
}
|
||||||
.navbar-inner > .container-fluid {
|
.navbar-inner > .container-fluid {
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user