From f50101db8a717ae97e736d8e0a2df99fd861fdd0 Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:04:24 -0400 Subject: [PATCH 1/2] Add "Import" Tab Add "Import" Tab --- scripts/system/create/qml/EditTabView.qml | 19 +++++++++++++++++ .../system/create/qml/EditToolsTabView.qml | 21 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml index 96e66c109ed..2db23ec659d 100644 --- a/scripts/system/create/qml/EditTabView.qml +++ b/scripts/system/create/qml/EditTabView.qml @@ -301,6 +301,22 @@ TabBar { } } + EditTabButton { + title: "IMPORT" + active: true + enabled: true + property string originalUrl: "" + + property Component visualItem: Component { + WebView { + id: advancedImportWebView + url: Qt.resolvedUrl("../importEntities/html/importEntities.html") + enabled: true + blurOnCtrlShift: false + } + } + } + function fromScript(message) { switch (message.method) { case 'selectTab': @@ -333,6 +349,9 @@ TabBar { case 'grid': editTabView.currentIndex = 3; break; + case 'import': + editTabView.currentIndex = 4; + break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/scripts/system/create/qml/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml index 998c3a3aac3..1000724458a 100644 --- a/scripts/system/create/qml/EditToolsTabView.qml +++ b/scripts/system/create/qml/EditToolsTabView.qml @@ -291,6 +291,22 @@ TabBar { } } + EditTabButton { + title: "IMPORT" + active: true + enabled: true + property string originalUrl: "" + + property Component visualItem: Component { + WebView { + id: advancedImportWebView + url: Qt.resolvedUrl("../importEntities/html/importEntities.html") + enabled: true + blurOnCtrlShift: false + } + } + } + function fromScript(message) { switch (message.method) { case 'selectTab': @@ -304,7 +320,7 @@ TabBar { // Changes the current tab based on tab index or title as input function selectTab(id) { if (typeof id === 'number') { - if (id >= tabIndex.create && id <= tabIndex.grid) { + if (id >= tabIndex.create && id <= tabIndex.import) { editTabView.currentIndex = id; } else { console.warn('Attempt to switch to invalid tab:', id); @@ -320,6 +336,9 @@ TabBar { case 'grid': editTabView.currentIndex = tabIndex.grid; break; + case 'import': + editTabView.currentIndex = tabIndex.import; + break; default: console.warn('Attempt to switch to invalid tab:', id); } From a509f84a42013e0135db3f14452085fea97e98fc Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:05:14 -0400 Subject: [PATCH 2/2] Add "Import" Tab Add "Import" Tab --- scripts/system/create/edit.js | 81 ++++++- .../html/css/importEntities.css | 160 +++++++++++++ .../importEntities/html/importEntities.html | 77 +++++++ .../html/js/importEntitiesUi.js | 217 ++++++++++++++++++ 4 files changed, 533 insertions(+), 2 deletions(-) create mode 100644 scripts/system/create/importEntities/html/css/importEntities.css create mode 100644 scripts/system/create/importEntities/html/importEntities.html create mode 100644 scripts/system/create/importEntities/html/js/importEntitiesUi.js diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 5d3e924ccfd..1d7f4fc05e5 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -121,6 +121,16 @@ var copiedPosition; var copiedRotation; + var importUiPersistedData = { + "elJsonUrl": "", + "elImportAtAvatar": true, + "elImportAtSpecificPosition": false, + "elPositionX": 0, + "elPositionY": 0, + "elPositionZ": 0, + "elEntityHostTypeDomain": true, + "elEntityHostTypeAvatar": false + }; var cameraManager = new CameraManager(); @@ -2009,7 +2019,8 @@ return position; } - function importSVO(importURL) { + function importSVO(importURL, importEntityHostType) { + importEntityHostType = importEntityHostType || "domain"; if (!Entities.canRez() && !Entities.canRezTmp()) { Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -2032,7 +2043,7 @@ position = createApp.getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2); } if (position !== null && position !== undefined) { - var pastedEntityIDs = Clipboard.pasteEntities(position); + var pastedEntityIDs = Clipboard.pasteEntities(position, importEntityHostType); if (!isLargeImport) { // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move // entities after they're imported so that they're all the correct distance in front of and with geometric mean @@ -2792,6 +2803,72 @@ type: 'zoneListRequest', zones: getExistingZoneList() }); + } else if (data.type === "importUiBrowse") { + let fileToImport = Window.browse("Select .json to Import", "", "*.json"); + if (fileToImport !== null) { + emitScriptEvent({ + type: 'importUi_SELECTED_FILE', + file: fileToImport + }); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiImport") { + if ((data.entityHostType === "domain" && Entities.canAdjustLocks() && Entities.canRez()) || + (data.entityHostType === "avatar" && Entities.canRezAvatarEntities())) { + if (data.positioningMode === "avatar") { + importSVO(data.jsonURL, data.entityHostType); + } else { + if (Clipboard.importEntities(data.jsonURL)) { + let importedPastedEntities = Clipboard.pasteEntities(data.position, data.entityHostType); + if (importedPastedEntities.length === 0) { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "No Entity has been imported." + }); + } else { + if (isActive) { + selectionManager.setSelections(importedPastedEntities, this); + } + emitScriptEvent({type: 'importUi_IMPORT_CONFIRMATION'}); + } + } else { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "Import Entities has failed." + }); + } + } + } else { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "You don't have permission to create in this domain." + }); + } + } else if (data.type === "importUiGoBack") { + if (location.canGoBack()) { + location.goBack(); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiGoTutorial") { + Window.location = "file:///~/serverless/tutorial.json"; + } else if (data.type === "importUiGetCopiedPosition") { + if (copiedPosition !== undefined) { + emitScriptEvent({ + type: 'importUi_POSITION_TO_PASTE', + position: copiedPosition + }); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiPersistData") { + importUiPersistedData = data.importUiPersistedData; + } else if (data.type === "importUiGetPersistData") { + emitScriptEvent({ + type: 'importUi_LOAD_DATA', + importUiPersistedData: importUiPersistedData + }); } }; diff --git a/scripts/system/create/importEntities/html/css/importEntities.css b/scripts/system/create/importEntities/html/css/importEntities.css new file mode 100644 index 00000000000..61c75dabb35 --- /dev/null +++ b/scripts/system/create/importEntities/html/css/importEntities.css @@ -0,0 +1,160 @@ +/* +// importEntities.css +// +// Created by Alezia Kurdis on March 13th, 2024 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +*/ + +@font-face { + font-family: FiraSans-SemiBold; + src: url(../../../../../../resources/fonts/FiraSans-SemiBold.ttf), /* Windows production */ + url(../../../../../../fonts/FiraSans-SemiBold.ttf); /* OSX production */ +} + +@font-face { + font-family: FiraSans-Regular; + src: url(../../../../../../resources/fonts/FiraSans-Regular.ttf), /* Windows production */ + url(../../../../../../fonts/FiraSans-Regular.ttf); /* OSX production */ +} + +@font-face { + font-family: Raleway-Bold; + src: url(../../../../../../resources/fonts/Raleway-Bold.ttf), /* Windows production */ + url(../../../../../../fonts/Raleway-Bold.ttf); /* OSX production */ +} + +html { + width: 100%; + height: 100%; +} +input[type="text"] { + font-family: FiraSans-SemiBold; + color: #BBBBBB; + background-color: #222222; + border: 0; + padding: 4px; + margin: 1px; +} + +input[type="number"] { + font-family: FiraSans-SemiBold; + color: #BBBBBB; + background-color: #222222; + border: 0; + padding: 4px; + margin: 1px; + width: 90px; +} + +h2 { + font-size: 18px; + color: #FFFFFF; +} +body { + background: #404040; + font-family: FiraSans-Regular; + font-size: 14px; + color: #BBBBBB; + text-decoration: none; + font-style: normal; + font-variant: normal; + text-transform: none; +} + +#importAtSpecificPositionContainer { + display: none; + width: 100%; +} + +#jsonUrl { + width:90%; +} +#browseBtn { + font-family: FiraSans-SemiBold; +} +#browseBtn:hover { + +} + +label { + font-family: FiraSans-SemiBold; + color: #DDDDDD; +} +font.red { + font-family: FiraSans-SemiBold; + color: #e83333; +} +font.green { + font-family: FiraSans-SemiBold; + color: #0db518; +} +font.blue { + font-family: FiraSans-SemiBold; + color: #447ef2; +} +#importBtn { + color: #ffffff; + background-color: #1080b8; + background: linear-gradient(#00b4ef 20%, #1080b8 100%); + font-family: Raleway-Bold; + font-size: 13px; + text-transform: uppercase; + vertical-align: top; + height: 28px; + min-width: 70px; + padding: 0 18px; + margin: 3px 3px 12px 3px; + border-radius: 5px; + border: 0; + cursor: pointer; +} +#importBtn:hover { + background: linear-gradient(#00b4ef, #00b4ef); + border: none; +} +input:focus { + outline: none; + color: #FFFFFF; +} +button:focus { + outline: none; +} +div.explicative { + width: 96%; + padding: 7px; + font-family: FiraSans-SemiBold; + font-size: 12px; + text-decoration: none; + color: #BBBBBB; +} +button.black { + font-family: Raleway-Bold; + font-size: 10px; + text-transform: uppercase; + vertical-align: top; + height: 18px; + min-width: 60px; + padding: 0 14px; + margin: 5px; + border-radius: 4px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 20%, #000 100%); + cursor: pointer; +} +button.black:hover { + background: linear-gradient(#000, #000); + border: none; +} +#messageContainer { + font-family: FiraSans-SemiBold; + width: 100%; +} +#testContainer { + border: 1px solid #AAAAAA; + padding: 0px; +} diff --git a/scripts/system/create/importEntities/html/importEntities.html b/scripts/system/create/importEntities/html/importEntities.html new file mode 100644 index 00000000000..a1550a642ef --- /dev/null +++ b/scripts/system/create/importEntities/html/importEntities.html @@ -0,0 +1,77 @@ + + +
+
+ Position: + + + |
+
+
+ X
+ Y
+ Z
+ + +
+ Note: If you import a "serverless" json file, such data include positions.
+ It this case, the "Position" will act as an offset.
+
+ |
+
+ Entity Host Type: + + + |
+ + + | +
+
+ For large import, it can be wise to test it in a serverless environment before doing it in your real domain.
+
+ |
+
+
+
+
+
+
+ |
+