mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 00:44:38 +02:00
Merge pull request #14410 from thoys/fix/create/entityListDoubleClick
CreateApp Fix entity list double click rename / highlight and virtual keyboard issue
This commit is contained in:
commit
7701be0742
3 changed files with 81 additions and 13 deletions
|
@ -153,6 +153,9 @@ const ICON_FOR_TYPE = {
|
||||||
Text: "l",
|
Text: "l",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DOUBLE_CLICK_TIMEOUT = 300; // ms
|
||||||
|
const RENAME_COOLDOWN = 400; // ms
|
||||||
|
|
||||||
// List of all entities
|
// List of all entities
|
||||||
let entities = [];
|
let entities = [];
|
||||||
// List of all entities, indexed by Entity ID
|
// List of all entities, indexed by Entity ID
|
||||||
|
@ -181,6 +184,10 @@ let currentResizeEl = null;
|
||||||
let startResizeEvent = null;
|
let startResizeEvent = null;
|
||||||
let resizeColumnIndex = 0;
|
let resizeColumnIndex = 0;
|
||||||
let startThClick = null;
|
let startThClick = null;
|
||||||
|
let renameTimeout = null;
|
||||||
|
let renameLastBlur = null;
|
||||||
|
let renameLastEntityID = null;
|
||||||
|
let isRenameFieldBeingMoved = false;
|
||||||
|
|
||||||
let elEntityTable,
|
let elEntityTable,
|
||||||
elEntityTableHeader,
|
elEntityTableHeader,
|
||||||
|
@ -204,7 +211,8 @@ let elEntityTable,
|
||||||
elNoEntitiesMessage,
|
elNoEntitiesMessage,
|
||||||
elColumnsMultiselectBox,
|
elColumnsMultiselectBox,
|
||||||
elColumnsOptions,
|
elColumnsOptions,
|
||||||
elToggleSpaceMode;
|
elToggleSpaceMode,
|
||||||
|
elRenameInput;
|
||||||
|
|
||||||
const ENABLE_PROFILING = false;
|
const ENABLE_PROFILING = false;
|
||||||
let profileIndent = '';
|
let profileIndent = '';
|
||||||
|
@ -388,19 +396,20 @@ function loaded() {
|
||||||
|
|
||||||
elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th");
|
elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th");
|
||||||
|
|
||||||
entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow,
|
entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow,
|
||||||
createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT);
|
clearRow, preRefresh, postRefresh, preRefresh, WINDOW_NONVARIABLE_HEIGHT);
|
||||||
|
|
||||||
entityListContextMenu = new EntityListContextMenu();
|
entityListContextMenu = new EntityListContextMenu();
|
||||||
|
|
||||||
function startRenamingEntity(entityID) {
|
function startRenamingEntity(entityID) {
|
||||||
|
renameLastEntityID = entityID;
|
||||||
let entity = entitiesByID[entityID];
|
let entity = entitiesByID[entityID];
|
||||||
if (!entity || entity.locked || !entity.elRow) {
|
if (!entity || entity.locked || !entity.elRow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let elCell = entity.elRow.childNodes[getColumnIndex("name")];
|
let elCell = entity.elRow.childNodes[getColumnIndex("name")];
|
||||||
let elRenameInput = document.createElement("input");
|
elRenameInput = document.createElement("input");
|
||||||
elRenameInput.setAttribute('class', 'rename-entity');
|
elRenameInput.setAttribute('class', 'rename-entity');
|
||||||
elRenameInput.value = entity.name;
|
elRenameInput.value = entity.name;
|
||||||
let ignoreClicks = function(event) {
|
let ignoreClicks = function(event) {
|
||||||
|
@ -415,6 +424,9 @@ function loaded() {
|
||||||
};
|
};
|
||||||
|
|
||||||
elRenameInput.onblur = function(event) {
|
elRenameInput.onblur = function(event) {
|
||||||
|
if (isRenameFieldBeingMoved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let value = elRenameInput.value;
|
let value = elRenameInput.value;
|
||||||
EventBridge.emitWebEvent(JSON.stringify({
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
type: 'rename',
|
type: 'rename',
|
||||||
|
@ -422,7 +434,10 @@ function loaded() {
|
||||||
name: value
|
name: value
|
||||||
}));
|
}));
|
||||||
entity.name = value;
|
entity.name = value;
|
||||||
elCell.innerText = value;
|
elRenameInput.parentElement.innerText = value;
|
||||||
|
|
||||||
|
renameLastBlur = Date.now();
|
||||||
|
elRenameInput = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
elCell.innerHTML = "";
|
elCell.innerHTML = "";
|
||||||
|
@ -431,6 +446,32 @@ function loaded() {
|
||||||
elRenameInput.select();
|
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) {
|
entityListContextMenu.setOnSelectedCallback(function(optionName, selectedEntityID) {
|
||||||
switch (optionName) {
|
switch (optionName) {
|
||||||
case "Cut":
|
case "Cut":
|
||||||
|
@ -455,6 +496,11 @@ function loaded() {
|
||||||
});
|
});
|
||||||
|
|
||||||
function onRowContextMenu(clickEvent) {
|
function onRowContextMenu(clickEvent) {
|
||||||
|
if (elRenameInput) {
|
||||||
|
// disallow the context menu from popping up while renaming
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let entityID = this.dataset.entityID;
|
let entityID = this.dataset.entityID;
|
||||||
|
|
||||||
if (!selectedEntities.includes(entityID)) {
|
if (!selectedEntities.includes(entityID)) {
|
||||||
|
@ -478,6 +524,13 @@ function loaded() {
|
||||||
entityListContextMenu.open(clickEvent, entityID, enabledContextMenuItems);
|
entityListContextMenu.open(clickEvent, entityID, enabledContextMenuItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let clearRenameTimeout = () => {
|
||||||
|
if (renameTimeout !== null) {
|
||||||
|
window.clearTimeout(renameTimeout);
|
||||||
|
renameTimeout = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function onRowClicked(clickEvent) {
|
function onRowClicked(clickEvent) {
|
||||||
let entityID = this.dataset.entityID;
|
let entityID = this.dataset.entityID;
|
||||||
let selection = [entityID];
|
let selection = [entityID];
|
||||||
|
@ -516,7 +569,15 @@ function loaded() {
|
||||||
} else if (!clickEvent.ctrlKey && !clickEvent.shiftKey && selectedEntities.length === 1) {
|
} else if (!clickEvent.ctrlKey && !clickEvent.shiftKey && selectedEntities.length === 1) {
|
||||||
// if reselecting the same entity then start renaming it
|
// if reselecting the same entity then start renaming it
|
||||||
if (selectedEntities[0] === entityID) {
|
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() {
|
function onRowDoubleClicked() {
|
||||||
|
clearRenameTimeout();
|
||||||
|
|
||||||
let selection = [this.dataset.entityID];
|
let selection = [this.dataset.entityID];
|
||||||
updateSelectedEntities(selection, false);
|
updateSelectedEntities(selection, false);
|
||||||
|
|
||||||
|
@ -1100,12 +1163,12 @@ function loaded() {
|
||||||
startResizeEvent = ev;
|
startResizeEvent = ev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
document.onmouseup = function(ev) {
|
document.onmouseup = function(ev) {
|
||||||
startResizeEvent = null;
|
startResizeEvent = null;
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
}
|
};
|
||||||
|
|
||||||
function setSpaceMode(spaceMode) {
|
function setSpaceMode(spaceMode) {
|
||||||
if (spaceMode === "local") {
|
if (spaceMode === "local") {
|
||||||
|
|
|
@ -13,8 +13,8 @@ debugPrint = function (message) {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction,
|
function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, updateRowFunction, clearRowFunction,
|
||||||
updateRowFunction, clearRowFunction, WINDOW_NONVARIABLE_HEIGHT) {
|
preRefreshFunction, postRefreshFunction, preResizeFunction, WINDOW_NONVARIABLE_HEIGHT) {
|
||||||
this.elTableBody = elTableBody;
|
this.elTableBody = elTableBody;
|
||||||
this.elTableScroll = elTableScroll;
|
this.elTableScroll = elTableScroll;
|
||||||
this.elTableHeaderRow = elTableHeaderRow;
|
this.elTableHeaderRow = elTableHeaderRow;
|
||||||
|
@ -25,6 +25,9 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio
|
||||||
this.createRowFunction = createRowFunction;
|
this.createRowFunction = createRowFunction;
|
||||||
this.updateRowFunction = updateRowFunction;
|
this.updateRowFunction = updateRowFunction;
|
||||||
this.clearRowFunction = clearRowFunction;
|
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
|
// the list of row elements created in the table up to max viewable height plus SCROLL_ROWS rows for scrolling buffer
|
||||||
this.elRows = [];
|
this.elRows = [];
|
||||||
|
@ -173,6 +176,7 @@ ListView.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
|
this.preRefreshFunction();
|
||||||
// block refreshing before rows are initialized
|
// block refreshing before rows are initialized
|
||||||
let numRows = this.getNumRows();
|
let numRows = this.getNumRows();
|
||||||
if (numRows === 0) {
|
if (numRows === 0) {
|
||||||
|
@ -211,6 +215,7 @@ ListView.prototype = {
|
||||||
this.lastRowShiftScrollTop = 0;
|
this.lastRowShiftScrollTop = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.postRefreshFunction();
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshBuffers: function() {
|
refreshBuffers: function() {
|
||||||
|
@ -230,7 +235,7 @@ ListView.prototype = {
|
||||||
|
|
||||||
refreshRowOffset: function() {
|
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
|
// 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) {
|
if (this.rowOffset + numRows > this.itemData.length) {
|
||||||
this.rowOffset = this.itemData.length - numRows;
|
this.rowOffset = this.itemData.length - numRows;
|
||||||
}
|
}
|
||||||
|
@ -244,7 +249,7 @@ ListView.prototype = {
|
||||||
debugPrint("ListView.resize - no valid table body or table scroll element");
|
debugPrint("ListView.resize - no valid table body or table scroll element");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.preResizeFunction();
|
||||||
let prevScrollTop = this.elTableScroll.scrollTop;
|
let prevScrollTop = this.elTableScroll.scrollTop;
|
||||||
|
|
||||||
// take up available window space
|
// take up available window space
|
||||||
|
|
|
@ -17,7 +17,7 @@ var profileIndent = '';
|
||||||
const PROFILE_NOOP = function(_name, fn, args) {
|
const PROFILE_NOOP = function(_name, fn, args) {
|
||||||
fn.apply(this, 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");
|
console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin");
|
||||||
var previousIndent = profileIndent;
|
var previousIndent = profileIndent;
|
||||||
profileIndent += ' ';
|
profileIndent += ' ';
|
||||||
|
|
Loading…
Reference in a new issue