diff --git a/.gitmodules b/.gitmodules index 94655d16ce5d54d6be692cf3f54a3ea5ebbc0523..c35f9a3353da85f26a68bbffdf87e3a099da99c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "qtquickdesigner-components"] -path = qtquickdesigner-components -url = https://codereview.qt-project.org/qt-labs/qtquickdesigner-components + path = qtquickdesigner-components + url = https://codereview.qt-project.org/qt-labs/qtquickdesigner-components [submodule "emsdk"] -path = emsdk -url = https://github.com/emscripten-core/emsdk.git + path = emsdk + url = https://github.com/emscripten-core/emsdk.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 724bb5547caa46f54d2c6e9b13b48d3d383ccf83..cc0065e899088655c2303f5951a3f89e0bdbd4b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ find_package( REQUIRED ) -set(QT_MINIMUM_VERSION 6.6.0) +set(QT_MINIMUM_VERSION 6.7.0) if(QT_VERSION VERSION_LESS QT_MINIMUM_VERSION) message(FATAL_ERROR "Minimum supported Qt version: ${QT_MINIMUM_VERSION}") endif() @@ -49,13 +49,13 @@ target_link_options(${PROJECT_NAME} PRIVATE # -fwasm-exceptions # -sEXPORT_EXCEPTION_HANDLING_HELPERS=1 # -sEXCEPTION_STACK_TRACES=1 - -sEXCEPTION_DEBUG=1 - -sFULL_ES3=1 + # -sEXCEPTION_DEBUG=1 + # -sFULL_ES3=1 -sABORT_ON_WASM_EXCEPTIONS=0 ) set_target_properties(${PROJECT_NAME} PROPERTIES - QT_WASM_INITIAL_MEMORY 2048MB + QT_WASM_INITIAL_MEMORY 256MB ) qt6_import_qml_plugins(${PROJECT_NAME}) @@ -73,6 +73,14 @@ install( DIRECTORY ${CMAKE_SOURCE_DIR}/www/ DESTINATION ${CMAKE_INSTALL_PREFIX} ) + +## here we provide a couple of examples in case the server doesn't provide any +if(DEPLOY_EXAMPLES) + install( + DIRECTORY ${CMAKE_SOURCE_DIR}/resources/examples/ + DESTINATION ${CMAKE_INSTALL_PREFIX}/examples + ) +endif() # END --install configuration execute_process(COMMAND git describe --always --tags OUTPUT_VARIABLE GIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/README.md b/README.md index b2e098870541acbe79ff0abaaba2e34c94e7d114..08c542be5d9a947071fa057282ccf40a36c9b258 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,23 @@ # Qt Design Viewer ## About -Launch [Design Studio](https://www.qt.io/ui-design-tools) projects in Your web browser. Or share Your design on the web. The viewer that helps you do this is done with [Qt for WebAssembly](https://doc.qt.io/qt-5/wasm.html). The underlying technologies are [WebAssembly](https://webassembly.org/) & [emscripten](https://emscripten.org/). + +Launch [Qt Design Studio](https://www.qt.io/ui-design-tools) projects in your web browser. Or share your design on the web. The viewer that helps you do this is done with [Qt for WebAssembly](https://doc.qt.io/qt-5/wasm.html). The underlying technologies are [WebAssembly](https://webassembly.org/) & [emscripten](https://emscripten.org/). Qt Design Viewer works in a variety of web browsers which support WebAssembly, on Desktop and even on mobile. ## Prerequisites -* CMake 3.16 or newer -* WebAssembly - * Qt6.4.3 or newer - * EMSDK (<https://emscripten.org/docs/getting_started/downloads.html>) +* CMake +* Qt WebAssembly support +* EMSDK (<https://emscripten.org/docs/getting_started/downloads.html>) + +Please see the [CMakeLists.txt](CMakeLists.txt) file for the required CMake and Qt version. For corresponding EMSDK version, please see the [Qt for WebAssembly](https://doc.qt.io/qt-6/wasm.html) directory. ## Building > Note 1: -> Build instructions are provided for Linux, macOS hosts and for WebAssembly target. Windows should work as well but is not tested. +> Build instructions are provided (and tested) for Linux and macOS hosts. > > 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; @@ -80,26 +82,16 @@ 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. - -The Qt Design Viewer lets You drop the _.zip_/_.qmlrc_, or load it via a file selector. - - - -You can also select one of the premade examples. If you host the Qt Design Viewer on Your web server, You can offer your own examples. Direct links to hosted examples can be sent like this: +Then open <http://localhost:8000> in your web browser and upload the QMLRC file you want to view. -* <https://qt-webassembly.io/designviewer/#ClusterTutorial.qmlrc> -* <https://qt-webassembly.io/designviewer/#CoffeeMachine.qmlrc> -* <https://qt-webassembly.io/designviewer/#SideMenu.qmlrc> +## Creating QMLRC package -## Note +1. Run Qt Design Studio version 3.0 or newer. +2. Open your project. +3. Go to `File` -> `Export Project` -> `Generate Deployable Package`. -This is a static web page. The Qml application that You load runs and remains locally in Your browser, nothing gets uploaded into the cloud. +This will create a `<project-name>.qmlrc` file in the project folder that you can upload to the Qt Design Viewer. ## Live version -Check out the [live version](https://qt-webassembly.io/designviewer/) of the Qt Design Viewer. +Check out the live version of the Qt Design Viewer on <https://designviewer.qt.io>. diff --git a/cicd/gitlab-ci.yml b/cicd/gitlab-ci.yml index 39080a183c9ae42a0ac1978e98d3bcf4cccfec41..93259b9f5862ddf9e5ac1c7e55d6199d9fbc1539 100644 --- a/cicd/gitlab-ci.yml +++ b/cicd/gitlab-ci.yml @@ -9,6 +9,10 @@ variables: workflow: name: 'Qt-${QDS_CI_QT_VERSION} - ${CI_COMMIT_MESSAGE}' + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' + when: never stages: - build diff --git a/cicd/stages/build.yml b/cicd/stages/build.yml index 2ce16859c848bb6ec1e70fd574fb3b09c0f9db34..fa3e21815d6287d09661ae7e821e18b73bbd0f3a 100644 --- a/cicd/stages/build.yml +++ b/cicd/stages/build.yml @@ -65,7 +65,8 @@ build-wasm: -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_ARTIFACTS_PATH} + -DCMAKE_INSTALL_PREFIX=${QDS_CI_ARTIFACTS_PATH} \ + -DDEPLOY_EXAMPLES=ON - cmake --build ${QDS_CI_BUILD_PATH} - cmake --install ${QDS_CI_BUILD_PATH} - echo "PREVIOUS_JOB_ID=${CI_JOB_ID}" >> build.env @@ -82,6 +83,8 @@ pages: - gem install bundler - bundle install - bundle exec jekyll build -d ${CI_PROJECT_DIR}/public + - ls -l ${CI_PROJECT_DIR}/public + - ls -l ${CI_PROJECT_DIR}/public/examples artifacts: paths: - public diff --git a/cicd/stages/release.yml b/cicd/stages/release.yml index bddb8d49b95a8e4aeeb9ab217cbcc38760922443..4a79fd67c60f174b3e1fa8811abfe90cb755b20c 100644 --- a/cicd/stages/release.yml +++ b/cicd/stages/release.yml @@ -14,6 +14,7 @@ create-packages: script: - apk add tar curl - cd ${QDS_CI_ARTIFACTS_PATH} + - rm -rf examples - export package_name="design_viewer_wasm-${CI_COMMIT_TAG}-qt${QDS_CI_QT_VERSION}.tar.gz" - | tar -czf "${package_name}" * diff --git a/emsdk b/emsdk index e2627e265d940db5ea58dffa63e490375bfc92e5..772828321afe0761c836bbbad3992ba392b9b708 160000 --- a/emsdk +++ b/emsdk @@ -1 +1 @@ -Subproject commit e2627e265d940db5ea58dffa63e490375bfc92e5 +Subproject commit 772828321afe0761c836bbbad3992ba392b9b708 diff --git a/qtquickdesigner-components b/qtquickdesigner-components index 39f8401bdb821aa6609fbb6baadd503f57aa82d1..c765d12cbdf676c94f415fcbe4d2db3237553f6d 160000 --- a/qtquickdesigner-components +++ b/qtquickdesigner-components @@ -1 +1 @@ -Subproject commit 39f8401bdb821aa6609fbb6baadd503f57aa82d1 +Subproject commit c765d12cbdf676c94f415fcbe4d2db3237553f6d diff --git a/resources/examples/ClusterTutorial.qmlrc b/resources/examples/ClusterTutorial.qmlrc new file mode 100644 index 0000000000000000000000000000000000000000..93a665e68569f851d70d96e7455cff6851a15c64 Binary files /dev/null and b/resources/examples/ClusterTutorial.qmlrc differ diff --git a/resources/examples/EBikeDesign.qmlrc b/resources/examples/EBikeDesign.qmlrc new file mode 100644 index 0000000000000000000000000000000000000000..cd51a926041368dc3b1d7448b0e1eadcc8f204e2 Binary files /dev/null and b/resources/examples/EBikeDesign.qmlrc differ diff --git a/resources/examples/MaterialBundleDemo.qmlrc b/resources/examples/MaterialBundleDemo.qmlrc new file mode 100644 index 0000000000000000000000000000000000000000..36c5a8ae640c2f8d7c2d2b46491301bed5aa992f Binary files /dev/null and b/resources/examples/MaterialBundleDemo.qmlrc differ diff --git a/www/index.html b/www/index.html index ea7a1ad3ce720c55117b3e0146517975ff87b0b4..a800882574bb131703dd4c9b80031328e7eacc64 100644 --- a/www/index.html +++ b/www/index.html @@ -75,7 +75,6 @@ </center> </figure> <div id="qtcontainer"></div> - <div id="dropzone"> <div id="instruction">Drop your qmlrc file or zip file here</div> <p> @@ -101,7 +100,6 @@ <p>EXAMPLES</p> <ul id="projectsmenulist"></ul> </div> - <div id="launchstatus"> <p id="launchstatustext"></p> <div class="progress"> @@ -167,14 +165,6 @@ <p id="versioninfo_main">Version: 1.0.0</p> </div> - <div id="err_user_feedback"> - <span - class="closebtn" - onclick="this.parentElement.style.display='none';" - >×</span - > - <div id="err_user_feedback_text"></div> - </div> <script src="qtdesignviewer.js"></script> <script src="scripts/script.js" type="text/javascript"></script> <script src="qtloader.js" type="text/javascript"></script> diff --git a/www/scripts/script.js b/www/scripts/script.js index 2a44c5a488eb6ef45d6e33a3e702d8519e30c20d..208ebbd4b4dc7db0a67dfa35fb8341eb9bdee1af 100644 --- a/www/scripts/script.js +++ b/www/scripts/script.js @@ -20,50 +20,6 @@ var passwordInput = document.querySelector("#passwordInput"); var versionInfo = undefined; var instance; -function showAlert(text) { - alertText.innerHTML = text; - alertBox.style.display = "block"; -} - -function hideMainPage() { - uploadform.style.display = - dropzone.style.display = - projectsmenu.style.display = - footer.style.display = - "none"; -} - -function showDownloader() { - hideMainPage(); - launchstatustext.innerHTML = "Downloading application"; -} - -function baseName(str) { - var base = new String(str).substring(str.lastIndexOf("/") + 1); - if (base.lastIndexOf(".") != -1) - base = base.substring(0, base.lastIndexOf(".")); - return base; -} - -function loadFileInHash() { - var filename = location.hash.split("#")[1]; - appname.innerHTML = baseName(filename); - launchstatus.style.display = "block"; - loadFromServer(filename, passwordInput.value); -} - -window.addEventListener( - "hashchange", - function () { - if (location.hash == "") { - location.reload(); // E.g. when browsing back - return; - } - loadFileInHash(); - }, - false -); - function logError(error) { const spinner = document.querySelector("#qtspinner"); const container = document.querySelector('#qtcontainer'); @@ -96,21 +52,71 @@ function logError(error) { bugreport.innerHTML = '<br><br>You can easily report this issue at <a href="' + reportLink.href + '">Qt Bug Reports.</a>'; } -function handleFileSelection(event) { - reader = new FileReader(); - var file = fileinput.files[0]; - reader.onload = function () { - contentArray = new Uint8Array(reader.result); - projectfileName = file.name; - hideMainPage(); +window.addEventListener( + "hashchange", + function () { + if (location.hash == "") { + location.reload(); // E.g. when browsing back + return; + } + loadFileInHash(); + }, + false +); + +function showAlert(text) { + alertText.innerHTML = text; + alertBox.style.display = "block"; +} + +function hideMainPage() { + uploadform.style.display = + dropzone.style.display = + projectsmenu.style.display = + footer.style.display = + "none"; +} + +function showDownloader() { + launchstatustext.innerHTML = "Downloading application"; +} + +function baseName(str) { + var base = new String(str).substring(str.lastIndexOf("/") + 1); + if (base.lastIndexOf(".") != -1) + base = base.substring(0, base.lastIndexOf(".")); + return base; +} + +function loadFileInHash() { + var filename = location.hash.split("#")[1]; + appname.innerHTML = baseName(filename); + launchstatus.style.display = "block"; + console.log("Loading file: " + filename); + if (filename.startsWith("examples/")) { + loadFromLocalFileSystem(filename); + } else { + loadFromServer(filename, passwordInput.value); + } +} + +function loadFromLocalFileSystem(fileName) { + console.log("Loading file: " + fileName); + var request = new XMLHttpRequest(); + request.responseType = "arraybuffer"; + request.onload = function () { + if (this.status == 404 || request.response.byteLength == 0) { + alert("Example is not found"); + return; + } + console.log("File loaded"); + contentArray = new Uint8Array(request.response); + projectfileName = fileName; loadProjector(); }; - try { - reader.readAsArrayBuffer(file); - } catch (e) { - logError(e); - } + request.open("GET", fileName, true); + request.send(null); } function loadFromServer(fileName, password) { @@ -150,30 +156,65 @@ function loadFromServer(fileName, password) { request.send(null); } -function listExamples() { +function listLocalExamples() { + var projectsmenulist = document.querySelector("#projectsmenulist"); + var files = [ + "examples/ClusterTutorial.qmlrc", + "examples/EBikeDesign.qmlrc", + "examples/MaterialBundleDemo.qmlrc" + ]; + for (var i = 0; i < files.length; i++) { + var li = document.createElement("li"); + var a = document.createElement("a"); + var filename = files[i].split("/").pop(); + a.setAttribute("href", "#" + files[i]); + a.innerHTML = filename; + li.appendChild(a); + projectsmenulist.appendChild(li); + } + projectsmenu.style.display = "block"; +} + +function listServerExamples() { var request = new XMLHttpRequest(); request.responseType = "json"; request.onload = function (oEvent) { var json = request.response; - // check if json is not null - if (json !== null && json.length > 0) { - var projectsmenulist = document.querySelector("#projectsmenulist"); - for (project in json) { - var li = document.createElement("li"); - var a = document.createElement("a"); - var filename = json[project].name; - a.setAttribute("href", "#demos/" + filename); - a.innerHTML = filename; - li.appendChild(a); - projectsmenulist.appendChild(li); - } - projectsmenu.style.display = "block"; + if (json === null || json.length <= 0) { + console.error("Unable to load examples."); + return; } + + var projectsmenulist = document.querySelector("#projectsmenulist"); + for (project in json) { + var li = document.createElement("li"); + var a = document.createElement("a"); + var filename = json[project].name; + a.setAttribute("href", "#demos/" + filename); + a.innerHTML = filename; + li.appendChild(a); + projectsmenulist.appendChild(li); + } + projectsmenu.style.display = "block"; }; + request.open("GET", "api/v1/demos", true); request.send(null); } +function listExamples() { + listLocalExamples(); + return; + console.log("Location hostname: " + location.hostname); + if (location.hostname === "localhost" || location.hostname === "127.0.0.1" || location.hostname === "" || location.hostname.includes("gitlab-pages.qt.io")) { + console.log("Showing local examples"); + listLocalExamples(); + } else { + console.log("Loading server examples"); + listServerExamples(); + } +} + function showVersionInfo() { var request = new XMLHttpRequest(); request.responseType = "json"; @@ -223,6 +264,22 @@ function verifyWebGL() { } } +function handleFileSelection(event) { + reader = new FileReader(); + var file = fileinput.files[0]; + reader.onload = function () { + contentArray = new Uint8Array(reader.result); + projectfileName = file.name; + loadProjector(); + }; + + try { + reader.readAsArrayBuffer(file); + } catch (e) { + logError(e); + } +} + function init() { if (!verifyWebGL()) { return; @@ -249,8 +306,10 @@ async function loadProjector() { const container = document.querySelector('#qtcontainer'); const status = document.querySelector('#qtstatus'); + hideMainPage(); + const showUi = (ui) => { - [spinner, container].forEach(element => element.style.display = 'none'); + [spinner, container, dropzone].forEach(element => element.style.display = 'none'); if (container === ui) container.style.position = 'default'; ui.style.display = 'block';