diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js
index 39673b8f16..84ad59df36 100644
--- a/scripts/system/html/js/entityList.js
+++ b/scripts/system/html/js/entityList.js
@@ -153,6 +153,9 @@ const ICON_FOR_TYPE = {
Text: "l",
};
+const DOUBLE_CLICK_TIMEOUT = 300; // ms
+const RENAME_COOLDOWN = 400; // ms
+
// List of all entities
let entities = [];
// List of all entities, indexed by Entity ID
@@ -181,6 +184,10 @@ let currentResizeEl = null;
let startResizeEvent = null;
let resizeColumnIndex = 0;
let startThClick = null;
+let renameTimeout = null;
+let renameLastBlur = null;
+let renameLastEntityID = null;
+let isRenameFieldBeingMoved = false;
let elEntityTable,
elEntityTableHeader,
@@ -204,7 +211,8 @@ let elEntityTable,
elNoEntitiesMessage,
elColumnsMultiselectBox,
elColumnsOptions,
- elToggleSpaceMode;
+ elToggleSpaceMode,
+ elRenameInput;
const ENABLE_PROFILING = false;
let profileIndent = '';
@@ -388,19 +396,20 @@ function loaded() {
elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th");
- entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow,
- createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT);
+ entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow,
+ clearRow, preRefresh, postRefresh, preRefresh, WINDOW_NONVARIABLE_HEIGHT);
entityListContextMenu = new EntityListContextMenu();
function startRenamingEntity(entityID) {
+ renameLastEntityID = entityID;
let entity = entitiesByID[entityID];
if (!entity || entity.locked || !entity.elRow) {
return;
}
let elCell = entity.elRow.childNodes[getColumnIndex("name")];
- let elRenameInput = document.createElement("input");
+ elRenameInput = document.createElement("input");
elRenameInput.setAttribute('class', 'rename-entity');
elRenameInput.value = entity.name;
let ignoreClicks = function(event) {
@@ -415,6 +424,9 @@ function loaded() {
};
elRenameInput.onblur = function(event) {
+ if (isRenameFieldBeingMoved) {
+ return;
+ }
let value = elRenameInput.value;
EventBridge.emitWebEvent(JSON.stringify({
type: 'rename',
@@ -422,7 +434,10 @@ function loaded() {
name: value
}));
entity.name = value;
- elCell.innerText = value;
+ elRenameInput.parentElement.innerText = value;
+
+ renameLastBlur = Date.now();
+ elRenameInput = null;
};
elCell.innerHTML = "";
@@ -431,6 +446,32 @@ function loaded() {
elRenameInput.select();
}
+ function preRefresh() {
+ // move the rename input to the body
+ if (!isRenameFieldBeingMoved && elRenameInput) {
+ isRenameFieldBeingMoved = true;
+ document.body.appendChild(elRenameInput);
+ // keep the focus
+ elRenameInput.select();
+ }
+ }
+
+ function postRefresh() {
+ if (!elRenameInput || !isRenameFieldBeingMoved) {
+ return;
+ }
+ let entity = entitiesByID[renameLastEntityID];
+ if (!entity || entity.locked || !entity.elRow) {
+ return;
+ }
+ let elCell = entity.elRow.childNodes[getColumnIndex("name")];
+ elCell.innerHTML = "";
+ elCell.appendChild(elRenameInput);
+ // keep the focus
+ elRenameInput.select();
+ isRenameFieldBeingMoved = false;
+ }
+
entityListContextMenu.setOnSelectedCallback(function(optionName, selectedEntityID) {
switch (optionName) {
case "Cut":
@@ -455,6 +496,11 @@ function loaded() {
});
function onRowContextMenu(clickEvent) {
+ if (elRenameInput) {
+ // disallow the context menu from popping up while renaming
+ return;
+ }
+
let entityID = this.dataset.entityID;
if (!selectedEntities.includes(entityID)) {
@@ -478,6 +524,13 @@ function loaded() {
entityListContextMenu.open(clickEvent, entityID, enabledContextMenuItems);
}
+ let clearRenameTimeout = () => {
+ if (renameTimeout !== null) {
+ window.clearTimeout(renameTimeout);
+ renameTimeout = null;
+ }
+ };
+
function onRowClicked(clickEvent) {
let entityID = this.dataset.entityID;
let selection = [entityID];
@@ -516,7 +569,15 @@ function loaded() {
} else if (!clickEvent.ctrlKey && !clickEvent.shiftKey && selectedEntities.length === 1) {
// if reselecting the same entity then start renaming it
if (selectedEntities[0] === entityID) {
- startRenamingEntity(entityID);
+ if (renameLastBlur && renameLastEntityID === entityID && (Date.now() - renameLastBlur) < RENAME_COOLDOWN) {
+
+ return;
+ }
+ clearRenameTimeout();
+ renameTimeout = window.setTimeout(() => {
+ renameTimeout = null;
+ startRenamingEntity(entityID);
+ }, DOUBLE_CLICK_TIMEOUT);
}
}
@@ -530,6 +591,8 @@ function loaded() {
}
function onRowDoubleClicked() {
+ clearRenameTimeout();
+
let selection = [this.dataset.entityID];
updateSelectedEntities(selection, false);
@@ -1100,12 +1163,12 @@ function loaded() {
startResizeEvent = ev;
}
}
- }
+ };
document.onmouseup = function(ev) {
startResizeEvent = null;
ev.stopPropagation();
- }
+ };
function setSpaceMode(spaceMode) {
if (spaceMode === "local") {
diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js
index f775a4cb24..49a91388a5 100644
--- a/scripts/system/html/js/listView.js
+++ b/scripts/system/html/js/listView.js
@@ -13,8 +13,8 @@ debugPrint = function (message) {
console.log(message);
};
-function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction,
- updateRowFunction, clearRowFunction, WINDOW_NONVARIABLE_HEIGHT) {
+function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, updateRowFunction, clearRowFunction,
+ preRefreshFunction, postRefreshFunction, preResizeFunction, WINDOW_NONVARIABLE_HEIGHT) {
this.elTableBody = elTableBody;
this.elTableScroll = elTableScroll;
this.elTableHeaderRow = elTableHeaderRow;
@@ -25,6 +25,9 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio
this.createRowFunction = createRowFunction;
this.updateRowFunction = updateRowFunction;
this.clearRowFunction = clearRowFunction;
+ this.preRefreshFunction = preRefreshFunction;
+ this.postRefreshFunction = postRefreshFunction;
+ this.preResizeFunction = preResizeFunction;
// the list of row elements created in the table up to max viewable height plus SCROLL_ROWS rows for scrolling buffer
this.elRows = [];
@@ -173,6 +176,7 @@ ListView.prototype = {
},
refresh: function() {
+ this.preRefreshFunction();
// block refreshing before rows are initialized
let numRows = this.getNumRows();
if (numRows === 0) {
@@ -211,6 +215,7 @@ ListView.prototype = {
this.lastRowShiftScrollTop = 0;
}
}
+ this.postRefreshFunction();
},
refreshBuffers: function() {
@@ -230,7 +235,7 @@ ListView.prototype = {
refreshRowOffset: function() {
// make sure the row offset isn't causing visible rows to pass the end of the item list and is clamped to 0
- var numRows = this.getNumRows();
+ let numRows = this.getNumRows();
if (this.rowOffset + numRows > this.itemData.length) {
this.rowOffset = this.itemData.length - numRows;
}
@@ -244,7 +249,7 @@ ListView.prototype = {
debugPrint("ListView.resize - no valid table body or table scroll element");
return;
}
-
+ this.preResizeFunction();
let prevScrollTop = this.elTableScroll.scrollTop;
// take up available window space
diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js
index 8942c84f33..eeb16fd60d 100644
--- a/scripts/system/libraries/entityList.js
+++ b/scripts/system/libraries/entityList.js
@@ -17,7 +17,7 @@ var profileIndent = '';
const PROFILE_NOOP = function(_name, fn, args) {
fn.apply(this, args);
};
-PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) {
+const PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) {
console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin");
var previousIndent = profileIndent;
profileIndent += ' ';