diff --git a/README.md b/README.md index 249afcfbd9f5ee0244c37b0f58af47dab4193e11..b2e098870541acbe79ff0abaaba2e34c94e7d114 100644 --- a/README.md +++ b/README.md @@ -8,62 +8,19 @@ Qt Design Viewer works in a variety of web browsers which support WebAssembly, o ## Prerequisites * CMake 3.16 or newer -* Android - * Qt6.5.0 or newer - * OpenSSL (<https://github.com/KDAB/android_openssl>) - * Android SDK and NDK (<https://developer.android.com/studio>) * WebAssembly * Qt6.4.3 or newer * EMSDK (<https://emscripten.org/docs/getting_started/downloads.html>) ## Building -Build instructions are provided for Linux, macOS hosts and for Android and WebAssembly targets. Windows should work as well but is not tested. -> Note: +> Note 1: +> Build instructions are provided for Linux, macOS hosts and for WebAssembly target. Windows should work as well but is not tested. +> +> Note 2: > If you're building in a Docker container and you've mounted the source directory into the container, you may need to change the build path pointing out somewhere out of the source directory. Otherwise you may get errors like the following; > `CMake Error at /opt/qt-v.6.5.0/android-arm64-v8a/lib/cmake/Qt6/QtSyncQtHelpers.cmake:235 (message): syncqt.cpp failed for module QtQuickStudioApplication: Unable to remove file ...` -### Android - -> Note: -> Android build instructions can also be used on desktop (or host) for testing purposes. Just replace the `CMAKE_TOOLCHAIN_FILE` path with the path to your Qt host installation. - -First build and install QtQuickDesigner Components for Android: - -```bash -cd qtquickdesigner-components -cmake \ - -S . \ - -B build \ - -G Ninja \ - -DCMAKE_TOOLCHAIN_FILE=<qt-android-path>/lib/cmake/Qt6/qt.toolchain.cmake \ - -DCMAKE_INSTALL_PREFIX=<qt-android-path> \ - -DANDROID_SDK_ROOT=<android-sdk-path> \ - -DANDROID_NDK_ROOT=<android-sdk-path>/ndk/<ndk-version> \ - -DANDROID_OPENSSL_PATH=<openssl-path> - -cmake --build build -cmake --install build -cd .. -``` - -Then build the Qt Design Viewer: - -```bash -cmake \ - -S . \ - -B build \ - -G Ninja \ - -DCMAKE_TOOLCHAIN_FILE=<qt-android-path>/lib/cmake/Qt6/qt.toolchain.cmake \ - -DANDROID_SDK_ROOT=<android-sdk-path> \ - -DANDROID_NDK_ROOT=<android-sdk-path>/ndk/<ndk-version> \ - -DANDROID_OPENSSL_PATH=<openssl-path> - -cmake --build build -``` - -### WebAssembly - First activate the correct EMSDK version: ```bash @@ -82,8 +39,9 @@ cmake \ -S . \ -B build \ -G Ninja \ - -DCMAKE_TOOLCHAIN_FILE=<qt-wasm-path>/wasm/lib/cmake/Qt6/qt.toolchain.cmake \ - -DCMAKE_INSTALL_PREFIX=<qt-wasm-path> + -DCMAKE_PREFIX_PATH=<qt-wasm-path> \ + -DCMAKE_INSTALL_PREFIX=<qt-wasm-path> \ + -DCMAKE_TOOLCHAIN_FILE=<qt-wasm-path>/wasm/lib/cmake/Qt6/qt.toolchain.cmake cmake --build build cmake --install build @@ -97,11 +55,33 @@ cmake \ -S . \ -B build \ -G Ninja \ + -DCMAKE_PREFIX_PATH=<qt-wasm-path> \ + -DCMAKE_INSTALL_PREFIX=build/install \ -DCMAKE_TOOLCHAIN_FILE=<qt-wasm-path>/wasm/lib/cmake/Qt6/qt.toolchain.cmake cmake --build build +cmake --install build +``` + +## Running + +You can use 'qtwasmserver' package to run the Qt Design Viewer: + +To install the package: + +```bash +python -m pip install qtwasmserver ``` +And then run the server: + +```bash +cd build/install +qtwasmserver . +``` + +Then open <http://localhost:8000> in Your web browser. + ## Usage Compress Your Qml project as a _.zip_ file, ideally including a _.qmlproject_ file. Or use the _Build -> Generate Resource File_ feature of Qt Design Studio 1.3+ to pack Your project in a _.qmlrc_ file. diff --git a/cicd/gitlab-ci.yml b/cicd/gitlab-ci.yml index 33058bb34899e7b37da01d0a407f1b589818e7e8..1299a52056a8d675fcaae1ad858d57e3277afd3b 100644 --- a/cicd/gitlab-ci.yml +++ b/cicd/gitlab-ci.yml @@ -1,9 +1,6 @@ variables: QDS_CI_QT_VERSION: - value: "643" - options: - - "643" - - "652" + value: "6.6.1" description: "Qt version for build" QDS_CI_ARTIFACTS_PATH: "${CI_PROJECT_DIR}/artifacts" DEBIAN_FRONTEND: non-interactive diff --git a/cicd/stages/build.yml b/cicd/stages/build.yml index fbcdc4d3c8b1f277cd23041d079c749d1eb9c82a..d8a7abe3243d73f20f150b2bc9b92c160c5b4173 100644 --- a/cicd/stages/build.yml +++ b/cicd/stages/build.yml @@ -1,27 +1,14 @@ # QDS_CI_BUILD_QT_VERSION_ANDROID and QDS_CI_BUILD_QT_VERSION_WASM are the tags for the docker images. # https://git.qt.io/design-studio/maintenance/docker-images/container_registry - -.build-base: - rules: - - if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web" - -build-wasm-multiarch: +build-wasm: stage: build - extends: .build-base tags: - linux-blade rules: - - if: $QDS_CI_QT_VERSION == "643" && $QDS_CI_JOB_TARGET_ARCH == "32" - - if: $QDS_CI_QT_VERSION != "643" && $QDS_CI_JOB_TARGET_ARCH == "singlethread" - - if: $QDS_CI_QT_VERSION != "643" && $QDS_CI_JOB_TARGET_ARCH == "multithread" - parallel: - matrix: - - QDS_CI_JOB_TARGET_ARCH: - - "32" - - "singlethread" - - "multithread" + - if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web" || $CI_PIPELINE_SOURCE == "merge_request_event" variables: - QDS_BUILD_PATH: "${CI_PROJECT_DIR}/outdir/build" + QDS_CI_BUILD_PATH: "${CI_PROJECT_DIR}/outdir/build" + QDS_CI_JOB_TARGET_ARCH: "singlethread" QDS_CI_JOB_TARGET_PLATFORM: "wasm" QDS_CI_JOB_ARTIFACTS_PATH: ${QDS_CI_ARTIFACTS_PATH}/${QDS_CI_JOB_TARGET_PLATFORM}/${QDS_CI_JOB_TARGET_ARCH} artifacts: @@ -31,46 +18,42 @@ build-wasm-multiarch: - ${QDS_CI_ARTIFACTS_PATH} reports: dotenv: build.env - image: "git.qt.io:4567/design-studio/maintenance/docker-images/${QDS_CI_JOB_TARGET_PLATFORM}:${QDS_CI_QT_VERSION}-${QDS_CI_JOB_TARGET_PLATFORM}_${QDS_CI_JOB_TARGET_ARCH}" + image: "git.qt.io:4567/design-studio/maintenance/docker-images/qt-full:${QDS_CI_QT_VERSION}" script: - mkdir -p ${QDS_CI_JOB_ARTIFACTS_PATH} - - | - if [[ ${QDS_CI_QT_VERSION} == "643" ]]; then - EMSDK_VERSION="3.1.14" - elif [[ ${QDS_CI_QT_VERSION} == "652" ]]; then - EMSDK_VERSION="3.1.25" - fi - | cd emsdk - ./emsdk install ${EMSDK_VERSION} - ./emsdk activate ${EMSDK_VERSION} + ./emsdk install 3.1.37 + ./emsdk activate 3.1.37 source emsdk_env.sh cd .. + - echo ${DOCKER_ENV_QT_PATH_WASM_SINGLETHREAD} + - echo ${DOCKER_ENV_QT_PATH_LINUX_GCC_64} - | cd qtquickdesigner-components cmake \ -S . \ -G Ninja \ - -DCMAKE_TOOLCHAIN_FILE=${QT_PATH}/${QT_VERSION}/${QDS_CI_JOB_TARGET_PLATFORM}_${QDS_CI_JOB_TARGET_ARCH}/lib/cmake/Qt6/qt.toolchain.cmake \ - -DQT_HOST_PATH=${QT_PATH}/${QT_VERSION}/gcc_64 \ + -DCMAKE_TOOLCHAIN_FILE=${DOCKER_ENV_QT_PATH_WASM_SINGLETHREAD}/lib/cmake/Qt6/qt.toolchain.cmake \ + -DQT_HOST_PATH=${DOCKER_ENV_QT_PATH_LINUX_GCC_64} \ -DCMAKE_C_COMPILER=${EMSDK}/upstream/emscripten/emcc \ -DCMAKE_CXX_COMPILER=${EMSDK}/upstream/emscripten/em++ \ - -DCMAKE_INSTALL_PREFIX=${QT_PATH}/${QT_VERSION}/${QDS_CI_JOB_TARGET_PLATFORM}_${QDS_CI_JOB_TARGET_ARCH} + -DCMAKE_INSTALL_PREFIX=${DOCKER_ENV_QT_PATH_WASM_SINGLETHREAD} - cmake --build . - cmake --install . - cd .. - | cmake \ -S . \ - -B ${QDS_BUILD_PATH} \ + -B ${QDS_CI_BUILD_PATH} \ -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_TOOLCHAIN_FILE=${QT_PATH}/${QT_VERSION}/${QDS_CI_JOB_TARGET_PLATFORM}_${QDS_CI_JOB_TARGET_ARCH}/lib/cmake/Qt6/qt.toolchain.cmake \ - -DQT_HOST_PATH=${QT_PATH}/${QT_VERSION}/gcc_64 \ + -DCMAKE_TOOLCHAIN_FILE=${DOCKER_ENV_QT_PATH_WASM_SINGLETHREAD}/lib/cmake/Qt6/qt.toolchain.cmake \ + -DQT_HOST_PATH=${DOCKER_ENV_QT_PATH_LINUX_GCC_64} \ -DCMAKE_C_COMPILER=${EMSDK}/upstream/emscripten/emcc \ -DCMAKE_CXX_COMPILER=${EMSDK}/upstream/emscripten/em++ \ -DCMAKE_INSTALL_PREFIX=${QDS_CI_JOB_ARTIFACTS_PATH} - - cmake --build ${QDS_BUILD_PATH} - - cmake --install ${QDS_BUILD_PATH} + - cmake --build ${QDS_CI_BUILD_PATH} + - cmake --install ${QDS_CI_BUILD_PATH} - echo "PREVIOUS_JOB_ID=${CI_JOB_ID}" >> build.env - echo "JOB_TARGET_ARCH=${QDS_CI_JOB_TARGET_ARCH}" >> build.env diff --git a/cicd/stages/release.yml b/cicd/stages/release.yml index 0eda911d30ab979d69265c4f0ed79742a6a42802..7f652bb5fe9d8d5970f7c18be16e1325e7cbf65c 100644 --- a/cicd/stages/release.yml +++ b/cicd/stages/release.yml @@ -6,7 +6,7 @@ create-packages: rules: - if: $CI_COMMIT_TAG needs: - - job: build-wasm-multiarch + - job: build-wasm optional: true artifacts: true variables: diff --git a/cicd/stages/trigger.yml b/cicd/stages/trigger.yml index c01011c1808cfce4d031892d108130fdc1074ba9..b097b1312eaa1d9e31b3871db5568ea2b0b64a53 100644 --- a/cicd/stages/trigger.yml +++ b/cicd/stages/trigger.yml @@ -6,7 +6,7 @@ QDS_CI_PARENT_PROJECT_ID: ${CI_PROJECT_ID} QDS_CI_PARENT_PIPELINE_ARCH: ${JOB_TARGET_ARCH} needs: - - job: build-wasm-multiarch + - job: build-wasm artifacts: true optional: true trigger: diff --git a/resources/dvicon-new.png b/resources/dvicon-new.png new file mode 100644 index 0000000000000000000000000000000000000000..554b5eee2046e491e36ea9c489a20fa602b72bd7 Binary files /dev/null and b/resources/dvicon-new.png differ diff --git a/src/designviewer.cpp b/src/designviewer.cpp index f9cd839e76d76f8a0d63daceec4a0d65ca34d2cb..7eb3fe6f8f9e474e9979874208ae9172b47e057a 100644 --- a/src/designviewer.cpp +++ b/src/designviewer.cpp @@ -24,9 +24,9 @@ ****************************************************************************/ #include "designviewer.h" -#if defined(Q_OS_WASM) #include <emscripten.h> +#include <emscripten/val.h> #include <functional> #include <QBuffer> @@ -38,7 +38,13 @@ #include <QResource> #include <QTemporaryDir> #include <QTemporaryFile> -#include <private/qzipreader_p.h> +#include <QTimer> + +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +#include <QtCore/private/qzipreader_p.h> +#else +#include <QtGui/private/qzipreader_p.h> +#endif #define printErr(x) printError(x, __FILE_NAME__, __LINE__) @@ -56,55 +62,29 @@ extern "C" EMSCRIPTEN_KEEPALIVE void qt_callSetFileData(char *content, DesignViewer::DesignViewer() { + printLog("DesignViewer constructor"); QString projectFileName; QByteArray projectData; + printLog("Fetching project"); fetchProject(&projectData, &projectFileName); - if (!runProject(projectData, projectFileName)) - printError("Failed to run project", projectFileName, 0); + printLog("Running project"); + if (!runProject(projectData, projectFileName)) { + printErr("Failed to run project"); + } + printLog("DesignViewer constructor done"); } void DesignViewer::fetchProject(QByteArray *data, QString *fileName) { - // Call qt_callSetFileData to make sure the emscripten linker does not - // optimize it away, which may happen if the function is called from JavaScript - // only. Set g_setFileDataCallback to null to make it a no-op. - ::g_setFileDataCallback = nullptr; - ::qt_callSetFileData(nullptr, 0, nullptr); - - auto setFileDataCallback = - [data, fileName](char *content, size_t contentSize, char *projectFilename) - { - data->setRawData(content, contentSize); - *fileName = QString::fromUtf8(projectFilename); - }; - g_setFileDataCallback = setFileDataCallback; - - EM_ASM_({ - // Copy the file file content to the C++ heap. - // Note: this could be simplified by passing the content as an - // "array" type to ccall and then let it copy to C++ memory. - // However, this built-in solution does not handle files larger - // than ~15M (Chrome). Instead, allocate memory manually and - // pass a pointer to the C++ side (which will free() it when done). - - // TODO: consider slice()ing the file to read it picewise and - // then assembling it in a QByteArray on the C++ side. - - const contentSize = contentArray.byteLength; - const heapPointer = _malloc(contentSize); - const heapBytes = new Uint8Array(Module.HEAPU8.buffer, heapPointer, contentSize); - heapBytes.set(contentArray); - - // Null out the first data copy to enable GC - reader = null; - contentArray = null; - - // Call the C++ file data ready callback - ccall("qt_callSetFileData", - null, - [ "number", "number", "string" ], - [ heapPointer, contentSize, projectfileName ]); - }); + printLog("Init project data"); + // Access global variables set by index.html + emscripten::val jsData = emscripten::val::global("contentArray"); + emscripten::val jsFileName = emscripten::val::global("projectfileName"); + + printLog("Copying project data"); + // Copy project data to the C++ heap and convert string + *data = QByteArray::fromEcmaUint8Array(jsData); + *fileName = QString::fromStdString(jsFileName.as<std::string>()); } QString DesignViewer::unpackProject(const QByteArray &project, bool extractZip) @@ -163,84 +143,7 @@ QString DesignViewer::unpackProject(const QByteArray &project, bool extractZip) projectLocation = ":" + resourcePath; } - // create a temporary directory and copy the project there - QTemporaryDir tempDir; - tempDir.setAutoRemove(false); - if (!tempDir.isValid()) { - printErr("Could not create a temporary directory."); - return ""; - } - - const QString tempDirPath = tempDir.path(); - printLog("Temporary directory: " + tempDirPath); - - QDir tempDirDir(tempDirPath); - if (!tempDirDir.exists()) { - printErr("Temporary directory does not exist."); - return ""; - } - - if (!QDir(projectLocation).exists()) { - printErr("Project directory does not exist."); - return ""; - } - - // copy all files and directories from projectLocation to tempDirPath - QDirIterator it(projectLocation, - QDir::AllEntries | QDir::NoDotAndDotDot, - QDirIterator::Subdirectories); - printLog("Files in project location: " + projectLocation); - while (it.hasNext()) { - const QString filePath = it.next(); - printLog("Found file: " + filePath); - const QFileInfo fileInfo(filePath); - const QString relativeFilePath = fileInfo.filePath().remove(projectLocation); - const QString newFilePath = tempDirPath + relativeFilePath; - printLog("Copying file: " + filePath + " to " + newFilePath); - if (fileInfo.isDir()) { - QDir().mkpath(newFilePath); - } else if (!QFile::copy(filePath, newFilePath)) { - printErr("Could not copy file: " + filePath + " to " + newFilePath); - return ""; - } - } - - // find all .qml files in the temporary directory and remove the Qt versions from 6.5 to 6.9 - // at the end of the import statements. example "import <modulename> 6.5" -> "import <modulename>" - QDirIterator it2(tempDirPath, {"*.qml"}, QDir::Files, QDirIterator::Subdirectories); - - printLog("Files in temporary directory: " + tempDirPath); - while (it2.hasNext()) { - const QString filePath = it2.next(); - QFile file(filePath); - // set file permissions to read and write - file.setPermissions(QFile::ReadOwner | QFile::WriteOwner); - if (!file.open(QIODevice::ReadWrite)) { - printErr("Could not open file: " + filePath); - return ""; - } - - const QRegularExpression importRegExp("import\\s+\\S+\\s+6\\.[5-9]"); - QString content = QString::fromUtf8(file.readAll()); - while (content.contains(importRegExp)) { - const QRegularExpressionMatch match = importRegExp.match(content); - const QStringList textList = match.capturedTexts(); - - for (const QString &text : textList) { - const QString newText = QString(text).remove(QRegularExpression("\\s+6\\.[5-9]")); - printLog("Found text to replace: " + text + " with " + newText + " in file " - + filePath); - content.replace(text, newText); - } - } - - file.resize(0); - file.write(content.toUtf8()); - file.close(); - } - - // return the temporary directory path - return tempDirPath; + return projectLocation; } QString DesignViewer::findFile(const QString &dir, const QString &filter) @@ -354,7 +257,7 @@ bool DesignViewer::runProject(const QByteArray &projectData, const QString &proj printLog("Found mainQmlFile: " + mainQmlFilePath); QUrl mainQmlUrl = QUrl::fromUserInput(mainQmlFilePath); - QFile file(mainQmlUrl.path()); + QFile file(mainQmlUrl.path().prepend(":")); if (!file.open(QIODevice::ReadOnly)) { printErr("Could not open mainQmlfile for reading! " + file.fileName() + ": " + file.errorString()); return false; @@ -450,21 +353,53 @@ void DesignViewer::printWarn(const QString &message) void DesignViewer::printError(const QString &message, const QString &fileName, int line) { + fprintf(stderr, "ERROR:%s\n", qPrintable(message)); QString escaped = message; escaped.replace("'", "\'"); escaped.replace("\n", "\\n"); - emscripten_run_script("alert('" + escaped.toUtf8() + "');"); - emscripten_run_script("location.hash = ''; location.reload();"); + emscripten::val location = emscripten::val::global("window")["location"]; + location.set("hash", std::string()); + location.call<void>("reload"); } void DesignViewer::showAppWindow() { printLog("Resizing the QML app window"); const QSize size; - const QString command = QString::fromLatin1("setScreenSize(%1, %2);").arg(size.width()).arg(size.height()); - emscripten_run_script(command.toUtf8()); + + printLog("Getting the QML app window container"); + emscripten::val qtContainer = emscripten::val::global("qtcontainer"); // global from index.html + printLog("Getting the QML app window style"); + emscripten::val qtContainerStyle = qtContainer["style"]; + + printLog("Setting the QML app window size"); + if (size.isEmpty()) { + printLog("Setting the QML app window size to 100%"); + qtContainerStyle.set("width", std::string("100%")); + // qtContainerStyle.set("height", + // std::string( + // "100%")); // ### FIXME: 100% height gives 0px height for some reason + } else { + printLog("Setting the QML app window size to " + + QString("%1x%2").arg(size.width()).arg(size.height())); + qtContainerStyle.set("width", QString("%1px").arg(size.width()).toStdString()); + qtContainerStyle.set("height", QString("%1px").arg(size.height()).toStdString()); + } + + printLog("Setting the QML app window position"); + + // Make Qt pick up the new container size by calling the resizeCanvasElement() + // qtloader API. This needs to be done on delay after initial setup to make + // sure qtloader is initialized. + // QTimer::singleShot(0, [&]() { + // emscripten::val instance = emscripten::val::global("instance"); + // if (instance.isNull()) { + // printErr("instance is null"); + // return; + // } + // instance.call<void>("qtResizeContainerElement", qtContainer); + // printLog("QML app window position set"); + // }); printLog("Showing the QML app window"); m_quickWindow->show(); } - -#endif // Q_OS_WASM diff --git a/src/designviewer.h b/src/designviewer.h index f7f28c9d8d80baaea3bbede7831f57590876569f..1736277c4455a90f5ca59341cb9b68f8769285b5 100644 --- a/src/designviewer.h +++ b/src/designviewer.h @@ -47,6 +47,8 @@ private: QQmlEngine m_qmlEngine; QQmlComponent m_qmlComponent{&m_qmlEngine}; + QWidget *m_appWindow = nullptr; + void printLog(const QString &message); void printWarn(const QString &message); void printError(const QString &message, const QString &fileName, int line); diff --git a/www/index.html b/www/index.html index f63216846c29a0fd269a6f4b07ddc84fef3be767..962aba26082c1bc43c5527395100b62e4c322071 100644 --- a/www/index.html +++ b/www/index.html @@ -61,6 +61,7 @@ > </center> </figure> + <div id="qtcontainer"></div> <div id="dropzone"> <div id="instruction">Drop your qmlrc file or zip file here</div> @@ -119,9 +120,10 @@ </div> <canvas - id="qtcanvas" + id="canvas" oncontextmenu="event.preventDefault()" contenteditable="true" + style="display: none" ></canvas> <div id="footer"> <div id="alertBox" class="alert"> @@ -148,7 +150,7 @@ </p> <p id="versioninfo_main">Version: 1.0.0</p> </div> - + <script src="qtdesignviewer.js"></script> <script src="scripts/script.js" type="text/javascript"></script> <script src="qtloader.js" type="text/javascript"></script> </body> diff --git a/www/scripts/script.js b/www/scripts/script.js index 25576dae959e6d98f762f82700d4e6c824145584..4f3e9e6e40880890f4be3815fa78f6287a8ad061 100644 --- a/www/scripts/script.js +++ b/www/scripts/script.js @@ -1,8 +1,8 @@ var contentArray; var projectfileName; var reader; -var spinner = document.querySelector("#qtspinner"); -var canvas = document.querySelector("#qtcanvas"); + +var canvas = document.querySelector("#canvas"); var uploadform = document.querySelector("#uploadform"); var fileinput = document.querySelector("#fileinput"); var dropzone = document.querySelector("#dropzone"); @@ -17,8 +17,8 @@ var alertBox = document.querySelector("#alertBox"); var passwordDialog = document.querySelector("#passwordDialog"); var alertText = document.querySelector("#alertText"); var passwordInput = document.querySelector("#passwordInput"); -var qtLoader; var versionInfo = undefined; +var instance; function showAlert(text) { alertText.innerHTML = text; @@ -206,63 +206,56 @@ function init() { }; } -function restart() { - qtLoader.loadEmscriptenModule("qtdesignviewer"); -} - -function loadProjector() { - qtLoader = QtLoader({ - canvasElements: [canvas], - showLoader: function (loaderStatus) { - spinner.style.display = "block"; - canvas.style.display = "none"; - if (loaderStatus === "Downloading/Compiling") - loaderStatus = "Starting"; - launchstatustext.innerHTML = loaderStatus; - }, - showError: function (errorText) { - launchstatustext.innerHTML = errorText; - spinner.style.display = "block"; - canvas.style.display = "none"; - }, - showExit: function () { - launchstatustext.innerHTML = "Application exit"; - if (qtLoader.exitCode !== undefined) - launchstatustext.innerHTML += " with code " + qtLoader.exitCode; - if (qtLoader.exitText !== undefined) - launchstatustext.innerHTML += " (" + qtLoader.exitText + ")"; - spinner.style.display = "block"; - canvas.style.display = "none"; - }, - showCanvas: function () { - spinner.style.display = "none"; - canvas.style.display = "block"; - launchstatus.style.display = "none"; - appheader.style.display = "block"; - }, - }); +async function loadProjector() { + const spinner = document.querySelector("#qtspinner"); + const container = document.querySelector('#qtcontainer'); + const status = document.querySelector('#qtstatus'); - // workaround for making sure that self.module is set up before - // running setScreenSize - self.moduleConfig.preRun = []; - self.moduleConfig.preRun.push(function (module) { - self.module = module; - }); + const showUi = (ui) => { + [spinner, container].forEach(element => element.style.display = 'none'); + if (container === ui) + container.style.position = 'default'; + ui.style.display = 'block'; + } - qtLoader.loadEmscriptenModule("qtdesignviewer"); + try { + showUi(spinner); + status.innerHTML = 'Loading...'; + instance = await qtLoad({ + qt: { + onLoaded: () => { + setContainerSize(container, window.innerWidth, window.innerHeight); + showUi(container); + }, + onExit: exitData => { + console.log("onExit"); + status.innerHTML = 'Application exit'; + status.innerHTML += + exitData.code !== undefined ? ` with code ` : ''; + status.innerHTML += + exitData.text !== undefined ? ` ()` : ''; + showUi(spinner); + }, + entryFunction: window.createQtAppInstance, + containerElements: [container] + } + }); + } catch (e) { + console.error(e); + console.error(e.stack); + } } -function setScreenSize(width, height) { +function setContainerSize(container, width, height) { if (width > 1 && height > 1) { - canvas.style.width = `${width}px`; - canvas.style.height = `${height}px`; + container.style.width = `${width}px`; + container.style.height = `${height}px`; } else { // undefined root size - canvas.style.width = canvas.style.height = "100%"; + container.style.width = container.style.height = "100%"; document.documentElement.style.height = document.body.style.height = "100%"; document.documentElement.style.overflow = document.body.style.overflow = "hidden"; } - qtLoader.resizeCanvasElement(canvas); }