Add RED.view.selectNodes api for node selection whilst editing

This commit is contained in:
Nick O'Leary 2019-05-23 16:39:06 +01:00
parent b9b900e908
commit cae003d4fa
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
12 changed files with 290 additions and 30 deletions

View File

@ -28,7 +28,8 @@
"status": "Status",
"enabled": "Enabled",
"disabled":"Disabled",
"info": "Description"
"info": "Description",
"selectNodes": "Select nodes"
},
"menu": {
"label": {

View File

@ -18,6 +18,8 @@
/**
* options:
* - data : array - initial items to display in tree
* - multi : boolean - if true, .selected will return an array of results
* otherwise, returns the first selected item
*
* methods:
* - data(items) - clears existing items and replaces with new data
@ -289,13 +291,26 @@
if (item.hasOwnProperty('selected')) {
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
var cb = $('<input class="red-ui-treeList-checkbox" type="checkbox">').prop('checked',item.selected).appendTo(selectWrapper);
label.toggleClass("selected",item.selected);
cb.on('click', function(e) {
e.stopPropagation();
});
cb.on('change', function(e) {
item.selected = this.checked;
label.toggleClass("selected",this.checked);
that._trigger("select",e,item);
})
if (!item.children) {
label.on("click", function(e) {
e.stopPropagation();
cb.trigger("click");
})
}
item.treeList.select = function(v) {
if (v !== item.selected) {
cb.trigger("click");
}
}
} else {
label.on("click", function(e) {
that._topList.find(".selected").removeClass("selected");
@ -311,8 +326,14 @@
if (item.icon) {
$('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label);
}
if (item.label) {
$('<span class="red-ui-treeList-label-text"></span>').text(item.label).appendTo(label);
if (item.label || item.sublabel) {
if (item.label) {
$('<span class="red-ui-treeList-label-text"></span>').text(item.label).appendTo(label);
}
if (item.sublabel) {
$('<span class="red-ui-treeList-sublabel-text"></span>').text(item.sublabel).appendTo(label);
}
} else if (item.element) {
$(item.element).appendTo(label);
}
@ -355,8 +376,15 @@
},
selected: function() {
var s = this._topList.find(".selected").parent().data('data');
return s;
var s = this._topList.find(".selected");
if (this.options.multi) {
var res = [];
s.each(function() {
res.push($(this).parent().data('data'));
})
return res;
}
return s.parent().data('data');
}
});

View File

@ -24,7 +24,7 @@ RED.notifications = (function() {
var myNotification = RED.notify("This is the message to display",{
modal: true,
fixed: true,
type: 'warning',
type: 'warning', // 'compact', 'success', 'warning', 'error'
buttons: [
{
text: "cancel",
@ -103,7 +103,7 @@ RED.notifications = (function() {
$(n).append(msg);
}
if (options.buttons) {
var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(n)
var buttonSet = $('<div class="ui-dialog-buttonset"></div>').appendTo(n)
options.buttons.forEach(function(buttonDef) {
var b = $('<button>').html(buttonDef.text).on("click", buttonDef.click).appendTo(buttonSet);
if (buttonDef.id) {

View File

@ -24,5 +24,6 @@ RED.state = {
IMPORT: 7,
IMPORT_DRAGGING: 8,
QUICK_JOINING: 9,
PANNING: 10
PANNING: 10,
SELECTING_NODE: 11
}

View File

@ -18,6 +18,7 @@ RED.tray = (function() {
var stack = [];
var editorStack;
var openingTray = false;
var stackHidden = false;
function resize() {
@ -217,7 +218,19 @@ RED.tray = (function() {
});
},
show: function show(options) {
if (stack.length > 0 && !options.overlay) {
if (!options) {
if (stack.length > 0) {
var tray = stack[stack.length-1];
tray.tray.css({right:0});
$("#red-ui-header-shade").show();
$("#red-ui-editor-shade").show();
$("#red-ui-palette-shade").show();
$(".red-ui-sidebar-shade").show();
stackHidden = false;
}
} else if (stackHidden) {
throw new Error("Cannot add to stack whilst hidden");
} else if (stack.length > 0 && !options.overlay) {
var oldTray = stack[stack.length-1];
if (options.width === "inherit") {
options.width = oldTray.tray.width();
@ -235,6 +248,19 @@ RED.tray = (function() {
}
},
hide: function hide() {
if (stack.length > 0) {
var tray = stack[stack.length-1];
tray.tray.css({
right: -(tray.tray.width()+10)+"px"
});
$("#red-ui-header-shade").hide();
$("#red-ui-editor-shade").hide();
$("#red-ui-palette-shade").hide();
$(".red-ui-sidebar-shade").hide();
stackHidden = true;
}
},
resize: handleWindowResize,
close: function close(done) {
if (stack.length > 0) {

View File

@ -77,6 +77,8 @@ RED.view = (function() {
quickAddLink = null,
showAllLinkPorts = -1;
var selectNodesOptions;
var clipboard = "";
// Note: these are the permitted status colour aliases. The actual RGB values
@ -640,6 +642,7 @@ RED.view = (function() {
nn.w = node_width;
nn.h = Math.max(node_height,(nn.outputs||0) * 15);
nn.resize = true;
var historyEvent = {
t:"add",
@ -665,6 +668,10 @@ RED.view = (function() {
function canvasMouseDown() {
var point;
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
if (d3.event.button === 1) {
// Middle Click pan
@ -1026,6 +1033,11 @@ RED.view = (function() {
return;
}
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && selected_link == null) {
return;
}
@ -1231,6 +1243,10 @@ RED.view = (function() {
resetMouseVars();
return
}
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
if (mouse_mode === RED.state.QUICK_JOINING) {
return;
}
@ -1805,7 +1821,10 @@ RED.view = (function() {
if (d3.event.button === 1) {
return;
}
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
mousedown_node = d;
mousedown_port_type = portType;
mousedown_port_index = portIndex || 0;
@ -1823,6 +1842,10 @@ RED.view = (function() {
}
function portMouseUp(d,portType,portIndex) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
var i;
if (mouse_mode === RED.state.QUICK_JOINING && drag_lines.length > 0) {
if (drag_lines[0].node === d) {
@ -2105,6 +2128,10 @@ RED.view = (function() {
}
function portMouseOver(port,d,portType,portIndex) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
clearTimeout(portLabelHoverTimeout);
var active = (mouse_mode!=RED.state.JOINING && mouse_mode != RED.state.QUICK_JOINING) || // Not currently joining - all ports active
(
@ -2136,6 +2163,10 @@ RED.view = (function() {
port.classed("red-ui-flow-port-hovered",active);
}
function portMouseOut(port,d,portType,portIndex) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
clearTimeout(portLabelHoverTimeout);
if (portLabelHover) {
portLabelHover.remove();
@ -2145,6 +2176,10 @@ RED.view = (function() {
}
function nodeMouseUp(d) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < 750) {
mouse_mode = RED.state.DEFAULT;
if (d.type != "subflow") {
@ -2219,6 +2254,28 @@ RED.view = (function() {
} else if (mouse_mode == RED.state.QUICK_JOINING) {
d3.event.stopPropagation();
return;
} else if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
if (d.selected) {
d.selected = false;
for (i=0;i<moving_set.length;i+=1) {
if (moving_set[i].n === d) {
moving_set.splice(i,1);
break;
}
}
} else {
if (!selectNodesOptions.filter || selectNodesOptions.filter(d)) {
d.selected = true;
moving_set.push({n:d});
}
}
d.dirty = true;
redraw();
// if (selectNodesOptions && selectNodesOptions.onselect) {
// selectNodesOptions.onselect(moving_set.map(function(n) { return n.n;}))
// }
return;
}
mousedown_node = d;
var now = Date.now();
@ -2298,6 +2355,10 @@ RED.view = (function() {
}
function nodeButtonClicked(d) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
var activeWorkspace = RED.workspaces.active();
var ws = RED.nodes.workspace(activeWorkspace);
if (ws && !ws.disabled) {
@ -2524,7 +2585,7 @@ RED.view = (function() {
subflowOutputs.each(function(d,i) {
if (d.dirty) {
var output = d3.select(this);
output.selectAll(".red-ui-flow-subflow-port").classed("red-ui-flow-node-selected",function(d) { return d.selected; })
output.classed("red-ui-flow-node-selected",function(d) { return d.selected; })
output.selectAll(".red-ui-flow-port-index").text(function(d){ return d.i+1});
output.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
dirtyNodes[d.id] = d;
@ -2534,7 +2595,7 @@ RED.view = (function() {
subflowInputs.each(function(d,i) {
if (d.dirty) {
var input = d3.select(this);
input.selectAll(".red-ui-flow-subflow-port").classed("red-ui-flow-node-selected",function(d) { return d.selected; })
input.classed("red-ui-flow-node-selected",function(d) { return d.selected; })
input.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
dirtyNodes[d.id] = d;
d.dirty = false;
@ -2543,7 +2604,7 @@ RED.view = (function() {
subflowStatus.each(function(d,i) {
if (d.dirty) {
var output = d3.select(this);
output.selectAll(".red-ui-flow-subflow-port").classed("red-ui-flow-node-selected",function(d) { return d.selected; })
output.classed("red-ui-flow-node-selected",function(d) { return d.selected; })
output.selectAll(".red-ui-flow-port-index").text(function(d){ return d.i+1});
output.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
dirtyNodes[d.id] = d;
@ -2571,7 +2632,7 @@ RED.view = (function() {
var hideLabel = d.hasOwnProperty('l')?!d.l : isLink;
node.attr("id",d.id);
var l = RED.utils.getNodeLabel(d);
if (d.w === undefined) {
if (d.resize || d.w === undefined) {
if (hideLabel) {
d.w = node_height;
} else {
@ -2653,9 +2714,14 @@ RED.view = (function() {
nodeMouseUp.call(this,d);
})
.on("mouseover",function(d) {
if (mouse_mode === 0) {
var nodeBody = d3.select(this);
nodeBody.classed("node_hovered",true);
if (mouse_mode === 0 || mouse_mode === RED.state.SELECTING_NODE) {
if (mouse_mode === RED.state.SELECTING_NODE && selectNodesOptions && selectNodesOptions.filter) {
if (selectNodesOptions.filter(d)) {
node.classed("red-ui-flow-node-hovered",true);
}
} else {
node.classed("red-ui-flow-node-hovered",true);
}
clearTimeout(portLabelHoverTimeout);
if (d.hasOwnProperty('l')?!d.l : (d.type === "link in" || d.type === "link out")) {
portLabelHoverTimeout = setTimeout(function() {
@ -2697,8 +2763,7 @@ RED.view = (function() {
}
})
.on("mouseout",function(d) {
var nodeBody = d3.select(this);
nodeBody.classed("node_hovered",false);
node.classed("red-ui-flow-node-hovered",false);
clearTimeout(portLabelHoverTimeout);
if (portLabelHover) {
portLabelHover.remove();
@ -2833,10 +2898,10 @@ RED.view = (function() {
//thisNode.selectAll(".centerDot").attr({"cx":function(d) { return d.w/2;},"cy":function(d){return d.h/2}});
thisNode.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
if (mouse_mode != RED.state.MOVING_ACTIVE) {
thisNode.classed("red-ui-flow-node-selected",function(d) { return d.selected })
thisNode.selectAll(".red-ui-flow-node")
.attr("width",function(d){return d.w})
.attr("height",function(d){return d.h})
.classed("red-ui-flow-node-selected",function(d) { return d.selected; })
.classed("red-ui-flow-node-highlighted",function(d) { return d.highlighted; })
;
if ((!d._def.align && d.inputs !== 0 && d.outputs === 0) || "right" === d._def.align) {
@ -3081,6 +3146,10 @@ RED.view = (function() {
l.append("svg:path").attr("class","red-ui-flow-link-background red-ui-flow-link-path")
.classed("red-ui-flow-link-link", function(d) { return d.link })
.on("mousedown",function(d) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
mousedown_link = d;
clearSelection();
selected_link = mousedown_link;
@ -3090,6 +3159,10 @@ RED.view = (function() {
d3.event.stopPropagation();
})
.on("touchstart",function(d) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
}
mousedown_link = d;
clearSelection();
selected_link = mousedown_link;
@ -3580,6 +3653,68 @@ RED.view = (function() {
},
getActiveNodes: function() {
return activeNodes;
},
selectNodes: function(options) {
$("#red-ui-workspace-tabs-shade").show();
$("#red-ui-palette-shade").show();
$("#red-ui-sidebar-shade").show();
$("#red-ui-header-shade").show();
$("#red-ui-workspace").addClass("red-ui-workspace-select-mode");
mouse_mode = RED.state.SELECTING_NODE;
clearSelection();
if (options.selected) {
options.selected.forEach(function(id) {
var n = RED.nodes.node(id);
if (n) {
n.selected = true;
n.dirty = true;
moving_set.push({n:n});
}
})
}
redraw();
selectNodesOptions = options||{};
var closeNotification = function() {
clearSelection();
$("#red-ui-workspace-tabs-shade").hide();
$("#red-ui-palette-shade").hide();
$("#red-ui-sidebar-shade").hide();
$("#red-ui-header-shade").hide();
$("#red-ui-workspace").removeClass("red-ui-workspace-select-mode");
resetMouseVars();
notification.close();
}
var notification = RED.notify(selectNodesOptions.prompt || RED._("workspace.selectNodes"),{
modal: false,
fixed: true,
type: "compact",
buttons: [
{
text: RED._("common.label.cancel"),
click: function(e) {
closeNotification();
if (selectNodesOptions.oncancel) {
selectNodesOptions.oncancel();
}
}
},
{
text: RED._("common.label.done"),
class: "primary",
click: function(e) {
var selection = moving_set.map(function(n) { return n.n;});
closeNotification();
if (selectNodesOptions.onselect) {
selectNodesOptions.onselect(selection);
}
}
}
]
})
}
};
})();

View File

@ -307,6 +307,7 @@ RED.workspaces = (function() {
function init() {
$('<ul id="red-ui-workspace-tabs"></ul>').appendTo("#red-ui-workspace");
$('<div id="red-ui-workspace-tabs-shade" class="hide"></div>').appendTo("#red-ui-workspace");
$('<div id="red-ui-workspace-chart" tabindex="1"></div>').appendTo("#red-ui-workspace");
$('<div id="red-ui-workspace-toolbar"></div>').appendTo("#red-ui-workspace");
$('<div id="red-ui-workspace-footer" class="red-ui-component-footer"></div>').appendTo("#red-ui-workspace");

View File

@ -90,6 +90,7 @@ $event-log-selection-background: #999;
$list-item-color: $primary-text-color;
$list-item-secondary-color: $secondary-text-color;
$list-item-background: $secondary-background;
$list-item-background-disabled: $secondary-background-inactive;
$list-item-background-hover: $secondary-background-hover;
@ -185,6 +186,7 @@ $view-lasso-stroke: #ff7f0e;
$view-lasso-fill: rgba(20,125,255,0.1);
$view-background: $secondary-background;
$view-select-mode-background: $secondary-background-selected;
$view-grid-color: #eee;
$node-label-color: #333;

View File

@ -54,6 +54,23 @@
cursor: move;
stroke-width: 1;
}
.red-ui-workspace-select-mode {
g.red-ui-flow-node.red-ui-flow-node-hovered * {
cursor: pointer !important
}
g.red-ui-flow-node, g.red-ui-flow-link {
opacity: 0.5;
}
g.red-ui-flow-node.red-ui-flow-node-hovered:not(.red-ui-flow-node-selected) {
opacity: 0.9;
.red-ui-flow-node {
stroke-width: 2;
stroke: $node-selected-color !important;
stroke-dasharray: 10, 4;
}
}
}
.red-ui-flow-node-unknown {
stroke-dasharray:10,4;
stroke: $node-border-unknown;
@ -115,9 +132,14 @@
stroke-linecap: round;
}
.red-ui-flow-node-selected {
stroke-width: 2;
stroke: $node-selected-color !important;
g.red-ui-flow-node-selected {
.red-ui-workspace-select-mode & {
opacity: 1;
}
.red-ui-flow-node,.red-ui-flow-subflow-port {
stroke-width: 2;
stroke: $node-selected-color !important;
}
}
.red-ui-flow-node-highlighted {
border-color: $node-selected-color !important;

View File

@ -33,6 +33,9 @@
border: 1px solid $notification-border-default;
border-left-width: 16px;
overflow: hidden;
.ui-dialog-buttonset {
margin-top: 20px;
}
}
.red-ui-notification p:first-child {
font-size: 1.1em;
@ -55,6 +58,18 @@
border-color: $notification-border-error;
}
.red-ui-notification-compact {
p {
margin: 0;
}
.ui-dialog-buttonset {
margin-top: 0;
position: absolute;
top: 8px;
right: 10px;
}
}
.red-ui-notification-shake-horizontal {
-webkit-animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;
animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;

View File

@ -77,18 +77,18 @@
margin: 0;
position: relative;
// &:hover {
// background: $list-item-background-hover;
// color: $list-item-color;
// text-decoration: none;
// }
&:focus {
&:hover, &:hover .red-ui-treeList-sublabel-text {
background: $list-item-background-hover;
color: $list-item-color;
text-decoration: none;
}
&:focus, &:focus .red-ui-treeList-sublabel-text {
background: $list-item-background-hover;
outline: none;
color: $list-item-color;
text-decoration: none;
}
&.selected {
&.selected, &.selected .red-ui-treeList-sublabel-text {
background: $list-item-background-selected;
outline: none;
color: $list-item-color;
@ -101,6 +101,19 @@
.red-ui-treeList-label-text {
margin-left: 4px;
}
.red-ui-treeList-sublabel-text {
top: 0;
bottom: 0;
right: 0;
padding: 0 10px 0 5px;
line-height: 32px;
font-size: 0.9em;
color: $list-item-secondary-color;
position: absolute;
background: $list-item-background;
}
.red-ui-treeList-icon {
display: inline-block;
width: 20px;

View File

@ -39,6 +39,12 @@
outline: none;
}
}
#red-ui-workspace-tabs-shade {
@include shade;
z-index: 2;
bottom: auto;
height: 35px;
}
.red-ui-workspace-chart-background {
fill: $view-background;
@ -49,6 +55,15 @@
stroke: $view-grid-color;
stroke-width: 1px;
}
.red-ui-workspace-select-mode {
.red-ui-workspace-chart-background {
opacity: 0.7;
// fill: $view-select-mode-background;
}
.red-ui-workspace-chart-grid line {
opacity: 0.8;
}
}
.red-ui-palette-closed #red-ui-workspace {
left: 7px;
@ -92,6 +107,7 @@
}
}
#red-ui-navigator-canvas {
position: absolute;
bottom: 0;