[outliner] Keep outliner selection in sync with workspace

This commit is contained in:
Nick O'Leary 2020-05-14 22:08:25 +01:00
parent f1bd3e1711
commit fbd911ed27
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
9 changed files with 168 additions and 80 deletions

View File

@ -40,7 +40,8 @@
* label: 'Local', // label for the item
* sublabel: 'Local', // a sub-label for the item
* icon: 'fa fa-rocket', // (optional) icon for the item
* selected: true/false, // (optional) if present, display checkbox accordingly
* checkbox: true/false, // (optional) if present, display checkbox accordingly
* selected: true/false, // (optional) whether the item is selected or not
* children: [] | function(done,item) // (optional) an array of child items, or a function
* // that will call the `done` callback with an array
* // of child items
@ -163,6 +164,7 @@
});
this._data = [];
this._items = {};
this._selected = new Set();
this._topList = $('<ol class="red-ui-treeList-list">').css({
position:'absolute',
top: 0,
@ -318,6 +320,7 @@
item.parent.children.splice(index,1)
that._trigger("sort",null,item.parent);
}
delete item.treeList;
delete(that._items[item.id]);
}
item.treeList.insertChildAt = function(newItem,position,select) {
@ -564,7 +567,7 @@
item.treeList.makeParent();
}
if (item.hasOwnProperty('selected')) {
if (item.checkbox) {
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);
@ -573,6 +576,11 @@
});
cb.on('change', function(e) {
item.selected = this.checked;
if (item.selected) {
that._selected.add(item);
} else {
that._selected.delete(item);
}
label.toggleClass("selected",this.checked);
that._trigger("select",e,item);
})
@ -590,9 +598,11 @@
} else {
label.on("click", function(e) {
if (!that.options.multi) {
that._topList.find(".selected").removeClass("selected");
that.clearSelection();
}
label.addClass("selected");
that._selected.add(item);
that._trigger("select",e,item)
})
label.on("dblclick", function(e) {
@ -602,14 +612,21 @@
})
item.treeList.select = function(v) {
if (!that.options.multi) {
that._topList.find(".selected").removeClass("selected");
that.clearSelection();
}
label.toggleClass("selected",v);
if (v) {
that._selected.add(item);
that._trigger("select",null,item)
} else {
that._selected.delete(item);
}
that.reveal(item);
}
if (item.selected) {
that._selected.add(item);
}
label.toggleClass("selected",!!item.selected);
}
if (item.icon) {
if (typeof item.icon === "string") {
@ -663,7 +680,7 @@
return this._data;
}
},
show: function(item) {
show: function(item, done) {
if (typeof item === "string") {
item = this._items[item]
}
@ -684,6 +701,7 @@
if (stack.length === 0) {
setTimeout(function() {
that.reveal(item);
if (done) { done(); }
},isOpening?200:0);
} else {
item.treeList.expand(handleStack)
@ -710,36 +728,53 @@
this._topList.parent().scrollTop(scrollTop+((itemOffset+2.5*itemHeight)-treeHeight));
}
},
select: function(item, triggerEvent) {
select: function(item, triggerEvent, deselectExisting) {
var that = this;
if (!this.options.multi && deselectExisting !== false) {
this.clearSelection();
}
if (Array.isArray(item)) {
item.forEach(function(i) {
that.select(i,triggerEvent,false);
})
return;
}
if (typeof item === "string") {
item = this._items[item]
}
if (!this.options.multi) {
this._topList.find(".selected").removeClass("selected");
}
if (!item) {
return;
}
this.show(item.id);
item.treeList.label.addClass("selected");
// this.show(item.id);
item.selected = true;
this._selected.add(item);
if (item.treeList.label) {
item.treeList.label.addClass("selected");
}
if (triggerEvent !== false) {
this._trigger("select",null,item)
}
},
clearSelection: function() {
this._topList.find(".selected").removeClass("selected");
this._selected.forEach(function(item) {
item.selected = false;
if (item.treeList.label) {
item.treeList.label.removeClass("selected")
}
});
this._selected.clear();
},
selected: function() {
var s = this._topList.find(".selected");
var selected = [];
this._selected.forEach(function(item) {
selected.push(item);
})
if (this.options.multi) {
var res = [];
s.each(function() {
res.push($(this).parent().data('data'));
})
return res;
return selected;
}
if (s.length) {
return s.parent().data('data');
if (selected.length) {
return selected[0]
} else {
// TODO: This may be a bug.. it causes the call to return itself
// not undefined.

View File

@ -1092,6 +1092,7 @@ RED.editor = (function() {
var defaultIcon;
var nodeInfoEditor;
var finishedBuilding = false;
var skipInfoRefreshOnClose = false;
editStack.push(node);
RED.view.state(RED.state.EDITING);
@ -1532,9 +1533,6 @@ RED.editor = (function() {
collapsible: true,
menu: false
});
if (editing_node) {
RED.sidebar.info.refresh(editing_node);
}
var ns;
if (node._def.set.module === "node-red") {
ns = "node-red";
@ -1614,7 +1612,7 @@ RED.editor = (function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
RED.view.state(RED.state.DEFAULT);
}
if (editing_node) {
if (editing_node && !skipInfoRefreshOnClose) {
RED.sidebar.info.refresh(editing_node);
}
RED.workspaces.refresh();
@ -1642,6 +1640,7 @@ RED.editor = (function() {
text: RED._("subflow.edit"),
click: function() {
RED.workspaces.show(id);
skipInfoRefreshOnClose = true;
$("#node-dialog-ok").trigger("click");
}
});

View File

@ -504,7 +504,9 @@ RED.group = (function() {
}
}
}
if (g) {
RED.events.emit("groups:change",group)
}
markDirty(group);
}
function removeFromGroup(group, nodes, reparent) {

View File

@ -59,7 +59,7 @@ RED.sidebar.help = (function() {
panels = RED.panels.create({
container: stackContainer
})
panels.ratio(0.5);
panels.ratio(0.3);
helpSearch = $('<input type="text" data-i18n="[placeholder]sidebar.help.search">').appendTo(toolbar).searchBox({
delay: 100,
@ -255,6 +255,7 @@ RED.sidebar.help = (function() {
if (ratio > 0.7) {
panels.ratio(0.7)
}
treeList.treeList("show","node-type:"+nodeType)
treeList.treeList("select","node-type:"+nodeType, false);
}

View File

@ -61,7 +61,7 @@ RED.sidebar.info.outliner = (function() {
}
function getNodeLabelText(n) {
var label = n.name || n.id;
var label = n.name || n.type+": "+n.id;
if (n._def.label) {
try {
label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||"";
@ -131,11 +131,11 @@ RED.sidebar.info.outliner = (function() {
RED.view.clickNodeButton(n);
})
}
$('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-eye"></i></button>').appendTo(controls).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.view.reveal(n.id);
})
// $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-eye"></i></button>').appendTo(controls).on("click",function(evt) {
// evt.preventDefault();
// evt.stopPropagation();
// RED.view.reveal(n.id);
// })
if (n.type !== 'group' && n.type !== 'subflow') {
$('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) {
evt.preventDefault();
@ -226,11 +226,16 @@ RED.sidebar.info.outliner = (function() {
treeList = $("<div>").css({width: "100%"}).appendTo(container).treeList({
data:getFlowData()
})
// treeList.on('treelistselect', function(e,item) {
// console.log(item)
// RED.view.reveal(item.id);
// })
// treeList.treeList('data',[ ... ] )
treeList.on('treelistselect', function(e,item) {
var node = RED.nodes.node(item.id) || RED.nodes.group(item.id);
if (node) {
if (node.type === 'group' || node._def.category !== "config") {
RED.view.select({nodes:[node]})
} else {
RED.view.select({nodes:[]})
}
}
})
treeList.on('treelistconfirm', function(e,item) {
var node = RED.nodes.node(item.id);
if (node) {
@ -276,7 +281,8 @@ RED.sidebar.info.outliner = (function() {
element: getFlowLabel(ws),
children:[getEmptyItem(ws.id)],
deferBuild: true,
icon: "red-ui-icons red-ui-icons-flow"
icon: "red-ui-icons red-ui-icons-flow",
gutter: getGutter(ws)
}
flowList.treeList.addChild(objects[ws.id])
objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
@ -307,7 +313,8 @@ RED.sidebar.info.outliner = (function() {
id: sf.id,
element: getNodeLabel(sf),
children:[getEmptyItem(sf.id)],
deferBuild: true
deferBuild: true,
gutter: getGutter(sf)
}
subflowList.treeList.addChild(objects[sf.id])
}
@ -353,11 +360,20 @@ RED.sidebar.info.outliner = (function() {
parent.treeList.addChild(getEmptyItem(parent.id));
}
}
function getGutter(n) {
var span = $("<span>",{class:"red-ui-info-outline-gutter"});
$('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-search"></i></button>').appendTo(span).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.view.reveal(n.id);
})
return span;
}
function onNodeAdd(n) {
objects[n.id] = {
id: n.id,
element: getNodeLabel(n)
element: getNodeLabel(n),
gutter: getGutter(n)
}
if (n.type === "group") {
objects[n.id].children = [];
@ -383,7 +399,7 @@ RED.sidebar.info.outliner = (function() {
}
function onSelectionChanged(selection) {
treeList.treeList('clearSelection');
// treeList.treeList('clearSelection');
}
return {
@ -391,8 +407,20 @@ RED.sidebar.info.outliner = (function() {
search: function(val) {
searchInput.searchBox('value',val)
},
select: function(node) {
if (node) {
if (Array.isArray(node)) {
treeList.treeList('select', node.map(function(n) { return objects[n.id] }), false)
} else {
treeList.treeList('select', objects[node.id], false)
}
} else {
treeList.treeList('clearSelection')
}
},
reveal: function(node) {
treeList.treeList('select', objects[node.id])
treeList.treeList('show', objects[node.id])
}
}
})();

View File

@ -70,12 +70,12 @@ RED.sidebar.info = (function() {
propertiesPanelHeaderHelp = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({
position: 'absolute',
top: '12px',
right: '38px'
right: '32px'
}).on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (selectedObject) {
RED.sidebar.help.show(selectedObject.type)
RED.sidebar.help.show(selectedObject.type);
}
}).appendTo(propertiesPanelHeader);
RED.popover.tooltip(propertiesPanelHeaderHelp,RED._("sidebar.help.showHelp"));
@ -90,6 +90,7 @@ RED.sidebar.info = (function() {
evt.stopPropagation();
if (selectedObject) {
RED.sidebar.info.outliner.reveal(selectedObject);
RED.view.reveal(selectedObject.id);
}
}).appendTo(propertiesPanelHeader);
RED.popover.tooltip(propertiesPanelHeaderReveal,RED._("sidebar.help.showInOutline"));
@ -176,10 +177,12 @@ RED.sidebar.info = (function() {
var subflowUserCount;
if (node === null) {
RED.sidebar.info.outliner.select(null);
return;
} else if (Array.isArray(node)) {
// Multiple things selected
// - hide help and info sections
RED.sidebar.info.outliner.select(node);
propertiesPanelHeaderIcon.empty();
RED.utils.createNodeIcon({type:"_selection_"}).appendTo(propertiesPanelHeaderIcon);
@ -226,6 +229,8 @@ RED.sidebar.info = (function() {
} else {
// A single 'thing' selected.
RED.sidebar.info.outliner.select(node);
// Check to see if this is a subflow or subflow instance
var subflowRegex = /^subflow(:(.+))?$/.exec(node.type);
if (subflowRegex) {
@ -246,17 +251,30 @@ RED.sidebar.info = (function() {
propertiesPanelHeaderIcon.empty();
RED.utils.createNodeIcon(node).appendTo(propertiesPanelHeaderIcon);
propertiesPanelHeaderLabel.text(RED.utils.getNodeLabel(node, node.type+" "+node.id));
propertiesPanelHeaderLabel.text(RED.utils.getNodeLabel(node, node.type+": "+node.id));
propertiesPanelHeaderReveal.show();
selectedObject = node;
propRow = $('<tr class="red-ui-help-info-row"><td></td><td></td></tr>').appendTo(tableBody);
var objectType = "node";
if (node.type === "subflow" || subflowRegex) {
objectType = "subflow";
} else if (node.type === "tab") {
objectType = "flow";
}else if (node.type === "group") {
objectType = "group";
}
$(propRow.children()[0]).text(RED._("sidebar.info."+objectType))
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
if (node.type === "tab" || node.type === "subflow") {
// If nothing is selected, but we're on a flow or subflow tab.
propertiesPanelHeaderHelp.hide();
} else if (node.type === "group") {
propertiesPanelHeaderHelp.hide();
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.group")+'</td><td></td></tr>').appendTo(tableBody);
propRow = $('<tr class="red-ui-help-info-row"><td>&nbsp;</td><td></td></tr>').appendTo(tableBody);
var typeCounts = {
nodes:0,
@ -280,16 +298,7 @@ RED.sidebar.info = (function() {
} else {
propertiesPanelHeaderHelp.show ();
propRow = $('<tr class="red-ui-help-info-row"><td></td><td></td></tr>').appendTo(tableBody);
if (!subflowRegex) {
$(propRow.children()[0]).text(RED._("sidebar.info.node"))
} else {
$(propRow.children()[0]).text(RED._("sidebar.info.subflow"))
}
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
propertiesPanelHeaderHelp.show();
if (!subflowRegex) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.type")+'</td><td></td></tr>').appendTo(tableBody);

View File

@ -4665,45 +4665,51 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
},
getGroupAtPoint: getGroupAt,
getActiveGroup: function() { return activeGroup },
reveal: function(id) {
reveal: function(id,triggerHighlight) {
if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) {
RED.workspaces.show(id);
} else {
var node = RED.nodes.node(id) || RED.nodes.group(id);
if (node) {
if (node.z && (node.type === "group" || node._def.category !== 'config')) {
node.highlighted = true;
node.dirty = true;
RED.workspaces.show(node.z);
var screenSize = [chart.width()/scaleFactor,chart.height()/scaleFactor];
var scrollPos = [chart.scrollLeft()/scaleFactor,chart.scrollTop()/scaleFactor];
if (node.x < scrollPos[0] || node.y < scrollPos[1] || node.x > screenSize[0]+scrollPos[0] || node.y > screenSize[1]+scrollPos[1]) {
var deltaX = '-='+(((scrollPos[0] - node.x) + screenSize[0]/2)*scaleFactor);
var deltaY = '-='+(((scrollPos[1] - node.y) + screenSize[1]/2)*scaleFactor);
var cx = node.x;
var cy = node.y;
if (node.type === "group") {
cx += node.w/2;
cy += node.h/2;
}
if (cx < scrollPos[0] || cy < scrollPos[1] || cx > screenSize[0]+scrollPos[0] || cy > screenSize[1]+scrollPos[1]) {
var deltaX = '-='+(((scrollPos[0] - cx) + screenSize[0]/2)*scaleFactor);
var deltaY = '-='+(((scrollPos[1] - cy) + screenSize[1]/2)*scaleFactor);
chart.animate({
scrollLeft: deltaX,
scrollTop: deltaY
},200);
}
if (!node._flashing) {
node._flashing = true;
var flash = 22;
var flashFunc = function() {
flash--;
node.dirty = true;
if (flash >= 0) {
node.highlighted = !node.highlighted;
setTimeout(flashFunc,100);
} else {
node.highlighted = false;
delete node._flashing;
if (triggerHighlight !== false) {
node.highlighted = true;
if (!node._flashing) {
node._flashing = true;
var flash = 22;
var flashFunc = function() {
flash--;
node.dirty = true;
if (flash >= 0) {
node.highlighted = !node.highlighted;
setTimeout(flashFunc,100);
} else {
node.highlighted = false;
delete node._flashing;
}
RED.view.redraw();
}
RED.view.redraw();
flashFunc();
}
flashFunc();
}
} else if (node._def.category === 'config') {
RED.sidebar.config.show(id);

View File

@ -387,7 +387,7 @@ RED.workspaces = (function() {
RED.nodes.dirty(true);
RED.sidebar.config.refresh();
var selection = RED.view.selection();
if (!selection.nodes && !selection.links) {
if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) {
RED.sidebar.info.refresh(workspace);
}
if (changes.hasOwnProperty('disabled')) {

View File

@ -391,7 +391,15 @@ div.red-ui-info-table {
display: inline-block;
width: 23px;
}
.red-ui-info-outline-gutter {
display:none;
position: absolute;
top: 2px;
left: 2px;
.red-ui-treeList-label:hover & {
display: inline;
}
}
.red-ui-info-outline-item-controls {
position: absolute;
top:0;