├── www ├── screenshot.png ├── version.json ├── Titillium-Light.otf ├── Titillium-Thin.otf ├── Titillium-ThinItalic.otf ├── qtdesignstudioviewer-128.png ├── qtdesignstudioviewer-16.png ├── qtdesignstudioviewer-256.png ├── qtdesignstudioviewer-32.png ├── qtdesignstudioviewer-384.png ├── qtdesignstudioviewer-48.png ├── qtdesignstudioviewer-512.png ├── qmlprojects │ └── index.json ├── uploadIcon.svg └── index.html ├── android ├── res │ ├── mipmap-hdpi │ │ └── app_icon.png │ ├── mipmap-mdpi │ │ └── app_icon.png │ ├── mipmap-xhdpi │ │ └── app_icon.png │ ├── mipmap-xxhdpi │ │ └── app_icon.png │ └── mipmap-xxxhdpi │ │ └── app_icon.png └── AndroidManifest.xml ├── importdummy_wasm.qml ├── CMakeLists.txt ├── README.md ├── importdummy.qml ├── .github └── workflows │ ├── build-wasm.yml │ └── build-apks.yml └── main.cpp /www/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/screenshot.png -------------------------------------------------------------------------------- /www/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.8", 3 | "buildNumber": "#buildnumber#" 4 | } 5 | -------------------------------------------------------------------------------- /www/Titillium-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/Titillium-Light.otf -------------------------------------------------------------------------------- /www/Titillium-Thin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/Titillium-Thin.otf -------------------------------------------------------------------------------- /www/Titillium-ThinItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/Titillium-ThinItalic.otf -------------------------------------------------------------------------------- /www/qtdesignstudioviewer-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/qtdesignstudioviewer-128.png -------------------------------------------------------------------------------- /www/qtdesignstudioviewer-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/qtdesignstudioviewer-16.png -------------------------------------------------------------------------------- /www/qtdesignstudioviewer-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/qtdesignstudioviewer-256.png -------------------------------------------------------------------------------- /www/qtdesignstudioviewer-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/qtdesignstudioviewer-32.png -------------------------------------------------------------------------------- /www/qtdesignstudioviewer-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/qtdesignstudioviewer-384.png -------------------------------------------------------------------------------- /www/qtdesignstudioviewer-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/qtdesignstudioviewer-48.png -------------------------------------------------------------------------------- /www/qtdesignstudioviewer-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/www/qtdesignstudioviewer-512.png -------------------------------------------------------------------------------- /android/res/mipmap-hdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/android/res/mipmap-hdpi/app_icon.png -------------------------------------------------------------------------------- /android/res/mipmap-mdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/android/res/mipmap-mdpi/app_icon.png -------------------------------------------------------------------------------- /android/res/mipmap-xhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/android/res/mipmap-xhdpi/app_icon.png -------------------------------------------------------------------------------- /android/res/mipmap-xxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/android/res/mipmap-xxhdpi/app_icon.png -------------------------------------------------------------------------------- /android/res/mipmap-xxxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtdesignviewer/dev/android/res/mipmap-xxxhdpi/app_icon.png -------------------------------------------------------------------------------- /www/qmlprojects/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ 3 | { "file": "ClusterTutorial.qmlrc"}, 4 | { "file": "CoffeeMachine.qmlrc"}, 5 | { "file": "EBikeDesign.qmlrc"}, 6 | { "file": "SideMenu.qmlrc"}, 7 | { "file": "WebinarDemo.qmlrc"} 8 | ] 9 | } -------------------------------------------------------------------------------- /importdummy_wasm.qml: -------------------------------------------------------------------------------- 1 | // Hack to force the qml plugins to be linked statically 2 | 3 | import QtQuick 4 | import QtQuick.Controls 5 | 6 | import QtQml 7 | import QtQml.Models 8 | import QtQml.StateMachine 9 | 10 | import QtQuick3D 11 | import QtQuick3D.Effects 12 | import QtQuick3D.Particles3D 13 | 14 | import QtQuick.Studio.Components 15 | import QtQuick.Studio.Effects 16 | import QtQuick.Studio.EventSimulator 17 | import QtQuick.Studio.EventSystem 18 | import QtQuick.Studio.LogicHelper 19 | import QtQuick.Studio.MultiText 20 | 21 | ApplicationWindow { 22 | visible: true 23 | width: 640 24 | height: 480 25 | } 26 | -------------------------------------------------------------------------------- /www/uploadIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(qtdesignviewer LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | set(CMAKE_AUTOUIC ON) 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | 11 | set(CMAKE_CXX_STANDARD 11) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | 14 | find_package(QT NAMES Qt6 COMPONENTS Core REQUIRED) 15 | find_package(Qt6 COMPONENTS 16 | Core 17 | Widgets 18 | Quick 19 | Gui 20 | Qml 21 | REQUIRED) 22 | 23 | 24 | set(QT_MINIMUM_VERSION 6.3.0) 25 | if(QT_VERSION VERSION_LESS QT_MINIMUM_VERSION) 26 | MESSAGE(FATAL_ERROR "Minimum supported Qt version: ${QT_MINIMUM_VERSION}") 27 | endif() 28 | 29 | qt_add_executable(${CMAKE_PROJECT_NAME} 30 | main.cpp 31 | importdummy_wasm.qml 32 | ) 33 | 34 | set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY QT_WASM_INITIAL_MEMORY "50MB") 35 | 36 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE 37 | Qt6::Core 38 | Qt6::Widgets 39 | Qt6::Quick 40 | Qt6::Gui 41 | Qt6::Qml 42 | Qt6::GuiPrivate 43 | ) 44 | 45 | qt6_import_qml_plugins(${CMAKE_PROJECT_NAME}) 46 | 47 | file(COPY ${CMAKE_SOURCE_DIR}/www/ DESTINATION ${CMAKE_BINARY_DIR}/) 48 | 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qt Design Viewer 2 | 3 | ## About 4 | 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/). 5 | 6 | Qt Design Viewer works in a variety of web browsers which support WebAssembly, on Desktop and even on mobile. 7 | 8 | ## Usage 9 | 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. 10 | 11 | The Qt Design Viewer lets You drop the _.zip_/_.qmlrc_, or load it via a file selector. 12 | 13 | ![Qt Design Viewer in action](screenshot.png "Qt Design Viewer in action") 14 | 15 | 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: 16 | * https://qt-webassembly.io/designviewer/#ClusterTutorial.qmlrc 17 | * https://qt-webassembly.io/designviewer/#CoffeeMachine.qmlrc 18 | * https://qt-webassembly.io/designviewer/#SideMenu.qmlrc 19 | 20 | ## Note 21 | 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. 22 | 23 | ## Live version 24 | Check out the [live version](https://qt-webassembly.io/designviewer/) of the Qt Design Viewer. 25 | -------------------------------------------------------------------------------- /android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 14 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 40 | 41 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /importdummy.qml: -------------------------------------------------------------------------------- 1 | // Hack to force the qml plugins to be linked statically 2 | // TODO: Find out how to do that in .pro or .cpp file 3 | 4 | import QtQuick 5 | import QtQuick.Controls 6 | import QtQuick.Dialogs 7 | import QtQuick.Extras 8 | import QtQuick.Layouts 9 | import QtQuick.Particles 10 | import QtQuick.PrivateWidgets 11 | import QtQuick.Templates 12 | import QtQuick.VirtualKeyboard 13 | import QtQuick.VirtualKeyboard.Settings 14 | import QtQuick.Window 15 | import QtQuick.XmlListModel 16 | import QtQuick.Timeline 17 | import QtQuick.Shapes 18 | 19 | import Qt.labs.calendar 20 | import Qt.labs.folderlistmodel 21 | // import Qt.labs.lottieqt 22 | import Qt.labs.platform 23 | import Qt.labs.qmlmodels 24 | import Qt.labs.settings 25 | import Qt.labs.wavefrontmesh 26 | import Qt.labs.sharedimage 27 | 28 | import QtBluetooth 29 | import QtCanvas3D 30 | import QtCharts 31 | import QtDataVisualization 32 | // import QtFeedback 33 | import QtGamepad 34 | import QtGraphicalEffects 35 | import QtLocation 36 | import QtMultimedia 37 | import QtNfc 38 | import QtPositioning 39 | import QtPurchasing 40 | import QtRemoteObjects 41 | import QtScxml 42 | import QtSensors 43 | import QtTest 44 | import QtWebChannel 45 | import QtWebSockets 46 | import QtWebView 47 | 48 | import QtQml 49 | import QtQml.Models 50 | import QtQml.RemoteObjects 51 | import QtQml.StateMachine 52 | 53 | import QtQuick.Scene3D 54 | import QtQuick.Scene2D 55 | import QtQuick.LocalStorage 56 | 57 | import QtQuick.Controls.Styles 58 | import QtQuick.Controls.Material 59 | import QtQuick.Controls.Universal 60 | import QtQuick.Controls.Imagine 61 | 62 | import Qt3D.Extras 63 | import Qt3D.Input 64 | import Qt3D.Logic 65 | import Qt3D.Core 66 | import Qt3D.Render 67 | import Qt3D.Animation 68 | 69 | // TODO: Find out how to build that 70 | import Qt.SafeRenderer 71 | 72 | // Will QtStudio3D.OpenGL or its successor support WebAssembly? 73 | import QtStudio3D.OpenGL 74 | 75 | import Qt5Compat.GraphicalEffects 76 | 77 | import FlowView 78 | 79 | import Components 80 | import QtQuick.Studio.Components 81 | import QtQuick.Studio.Effects 82 | import QtQuick.Studio.EventSimulator 83 | import QtQuick.Studio.EventSystem 84 | import QtQuick.Studio.LogicHelper 85 | import QtQuick.Studio.MultiText 86 | 87 | import QtQuick3D 88 | import QtQuick3D.Effects 89 | import QtQuick3D.Particles3D 90 | 91 | ApplicationWindow { 92 | visible: true 93 | width: 640 94 | height: 480 95 | } 96 | -------------------------------------------------------------------------------- /.github/workflows/build-wasm.yml: -------------------------------------------------------------------------------- 1 | name: Build WebAssembly Packages 2 | on: 3 | push: 4 | branches: 5 | - dev 6 | 7 | env: 8 | QT_VERSION: 6.3.0 9 | APP_DIR: qtdesignviewer 10 | APP_NAME: qtdesignviewer 11 | 12 | jobs: 13 | build: 14 | name: Ubuntu Latest 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | ref: dev 21 | path: ${{ env.APP_DIR }} 22 | 23 | - name: Setup cmake 24 | uses: jwlawson/actions-setup-cmake@v1.12 25 | with: 26 | cmake-version: '3.21.x' 27 | 28 | - name: Download Qt 29 | id: qt 30 | shell: cmake -P {0} 31 | run: | 32 | set(qt_version "$ENV{QT_VERSION}") 33 | string(REPLACE "." "" qt_version_dotless "${qt_version}") 34 | set(qt_package_arch_suffix "wasm_32") 35 | 36 | set(qt_host_base_url "https://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt6_630") 37 | set(qt_host_package_suffix "-Linux-RHEL_8_4-GCC-Linux-RHEL_8_4-X86_64.7z") 38 | set(qt_host_package_dir_url "${qt_host_base_url}/qt.qt6.${qt_version_dotless}.gcc_64") 39 | 40 | # Prepare directories 41 | 42 | set(working_dir $ENV{GITHUB_WORKSPACE}) 43 | file(MAKE_DIRECTORY ${working_dir}) 44 | 45 | 46 | function(downloadAndExtract url download_dir archive) 47 | message("Downloading ${url} to ${download_dir}/${archive}") 48 | file(DOWNLOAD "${url}" ${download_dir}/${archive} ) 49 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${download_dir}/${archive} WORKING_DIRECTORY ${working_dir}) 50 | endfunction() 51 | 52 | 53 | message("Downloading host packages from : ${qt_host_package_dir_url}") 54 | message("Downloading ${qt_host_base_url}/Updates.xml") 55 | 56 | file(DOWNLOAD "${qt_host_base_url}/Updates.xml" ./Updates.xml SHOW_PROGRESS) 57 | file(READ ./Updates.xml updates_xml) 58 | string(REGEX MATCH "qt.qt6.*([0-9+-.]+)" updates_xml_output "${updates_xml}") 59 | set(qt_host_package_version ${CMAKE_MATCH_1}) 60 | 61 | set(download_dir $ENV{GITHUB_WORKSPACE}/qt6-download-${qt_host_package_version}) 62 | file(MAKE_DIRECTORY ${download_dir}) 63 | file(RENAME ./Updates.xml ${download_dir}/Updates.xml) 64 | 65 | foreach(package qtbase qtdeclarative) 66 | downloadAndExtract( 67 | "${qt_host_package_dir_url}/${qt_host_package_version}${package}${qt_host_package_suffix}" 68 | ${download_dir} 69 | ${package}.7z 70 | ) 71 | endforeach() 72 | 73 | # and rcc depends on libicu56.so 74 | downloadAndExtract( 75 | "${qt_host_package_dir_url}/${qt_host_package_version}icu-linux-Rhel7.2-x64.7z" 76 | ${download_dir} 77 | icu.7z 78 | ) 79 | 80 | message("Downloading WebAssembly packages from : ${qt_package_dir_url}") 81 | message("Downloading ${qt_base_url}/Updates.xml") 82 | 83 | set(qt_base_url "https://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt6_630_wasm") 84 | set(qt_package_suffix "-Linux-openSUSE_15_3-GCC-Linux-WebAssembly-X86_64.7z") 85 | set(qt_package_dir_url "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}") 86 | 87 | 88 | file(DOWNLOAD "${qt_base_url}/Updates.xml" ./Updates.xml SHOW_PROGRESS) 89 | file(READ ./Updates.xml updates_xml) 90 | string(REGEX MATCH "qt.qt6.*([0-9+-.]+)" updates_xml_output "${updates_xml}") 91 | set(qt_package_version ${CMAKE_MATCH_1}) 92 | 93 | set(download_dir $ENV{GITHUB_WORKSPACE}/qt6-download-${qt_package_version}) 94 | file(MAKE_DIRECTORY ${download_dir}) 95 | file(RENAME ./Updates.xml ${download_dir}/Updates.xml) 96 | 97 | # core packages 98 | foreach(package qttranslations qttools qtsvg qtdeclarative qtbase) 99 | downloadAndExtract( 100 | "${qt_package_dir_url}/${qt_package_version}${package}${qt_package_suffix}" 101 | ${download_dir} 102 | ${package}.7z 103 | ) 104 | endforeach() 105 | 106 | # additional packages 107 | # to be added: qtshadertools qtquick3d 108 | foreach(package qtquicktimeline qt5compat) 109 | downloadAndExtract( 110 | "${qt_base_url}/qt.qt6.${qt_version_dotless}.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}" 111 | ${download_dir} 112 | ${package}.7z 113 | ) 114 | endforeach() 115 | 116 | # addons packages 117 | foreach(package qtwebsockets qtwebchannel qtvirtualkeyboard qtlottie qtimageformats) 118 | downloadAndExtract( 119 | "${qt_base_url}/qt.qt6.${qt_version_dotless}.addons.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}" 120 | ${download_dir} 121 | ${package}.7z 122 | ) 123 | endforeach() 124 | 125 | set(qt_dir ${working_dir}/${qt_version}/wasm_32) 126 | 127 | file(READ "${qt_dir}/mkspecs/qconfig.pri" qtconfig) 128 | string(REPLACE "Enterprise" "OpenSource" qtconfig "${qtconfig}") 129 | string(REPLACE "licheck.exe" "" qtconfig "${qtconfig}") 130 | string(REPLACE "licheck64" "" qtconfig "${qtconfig}") 131 | string(REPLACE "licheck_mac" "" qtconfig "${qtconfig}") 132 | file(WRITE "${qt_dir}/mkspecs/qconfig.pri" "${qtconfig}") 133 | 134 | # Save the path for other steps 135 | message("::set-output name=qt_dir::${qt_dir}") 136 | 137 | - name: Checkout emsdk 138 | uses: actions/checkout@v2 139 | with: 140 | repository: emscripten-core/emsdk 141 | ref: main 142 | path: emsdk 143 | 144 | - name: Install emsdk 145 | run: | 146 | echo "Current path" 147 | pwd 148 | echo "Contents:" 149 | ls -al 150 | 151 | cd emsdk 152 | ./emsdk install 3.0.0 153 | ./emsdk activate 3.0.0 154 | source "emsdk_env.sh" 155 | env 156 | 157 | - name: Checkout qtquickdesigner-components 158 | uses: actions/checkout@v2 159 | with: 160 | repository: qt-labs/qtquickdesigner-components 161 | ref: dev 162 | path: qtquickdesigner-components 163 | 164 | - name: Configure, build and install qtquickdesigner-components 165 | run: | 166 | source "emsdk/emsdk_env.sh" 167 | cd qtquickdesigner-components 168 | cmake -DCMAKE_BUILD_TYPE:STRING=Release -DQT_QMAKE_EXECUTABLE:STRING=${GITHUB_WORKSPACE}/6.3.0/wasm_32/bin/qmake -DCMAKE_PREFIX_PATH:STRING=${GITHUB_WORKSPACE}/6.3.0/wasm_32 -DCMAKE_C_COMPILER:STRING=${GITHUB_WORKSPACE}/emsdk/upstream/emscripten/emcc -DCMAKE_CXX_COMPILER:STRING=${GITHUB_WORKSPACE}/emsdk/upstream/emscripten/em++ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${GITHUB_WORKSPACE}/6.3.0/wasm_32/lib/cmake/Qt6/qt.toolchain.cmake -DQT_HOST_PATH:STRING=${GITHUB_WORKSPACE}/6.3.0/gcc_64 . 169 | 170 | cmake --build . 171 | cmake --install . 172 | 173 | - name: Configure and build qtdesignviewer 174 | run: | 175 | source "emsdk/emsdk_env.sh" 176 | cd qtdesignviewer 177 | cmake -DCMAKE_BUILD_TYPE:STRING=Release -DQT_QMAKE_EXECUTABLE:STRING=${GITHUB_WORKSPACE}/6.3.0/wasm_32/bin/qmake -DCMAKE_PREFIX_PATH:STRING=${GITHUB_WORKSPACE}/6.3.0/wasm_32 -DCMAKE_C_COMPILER:STRING=${GITHUB_WORKSPACE}/emsdk/upstream/emscripten/emcc -DCMAKE_CXX_COMPILER:STRING=${GITHUB_WORKSPACE}/emsdk/upstream/emscripten/em++ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${GITHUB_WORKSPACE}/6.3.0/wasm_32/lib/cmake/Qt6/qt.toolchain.cmake -DQT_HOST_PATH:STRING=${GITHUB_WORKSPACE}/6.3.0/gcc_64 . 178 | 179 | cmake --build . 180 | 181 | - name: Create artifact package 182 | run: | 183 | cd qtdesignviewer 184 | 185 | # APPLY BUILD NUMBER 186 | sed -i 's/#buildnumber#/${{github.run_number}}/' version.json 187 | 188 | # BUILD PACKAGE 189 | mkdir build 190 | mv *.wasm build 191 | mv *.js build 192 | mv *.json build 193 | mv *.otf build 194 | mv *.png build 195 | mv *.svg build 196 | mv index.html build 197 | 198 | - uses: actions/upload-artifact@v2 199 | with: 200 | name: qtdesignviewer_wasm 201 | path: qtdesignviewer/build/* 202 | 203 | -------------------------------------------------------------------------------- /.github/workflows/build-apks.yml: -------------------------------------------------------------------------------- 1 | name: Build Android Packages 2 | on: 3 | push: 4 | branches: 5 | - dev 6 | 7 | env: 8 | QT_VERSION: 6.2.1 9 | APP_PRO: qtdesignviewer.pro 10 | APP_DIR: qtdesignviewer 11 | APP_NAME: qtdesignviewer 12 | 13 | jobs: 14 | build: 15 | name: Ubuntu Latest 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | abi: 20 | - { arch: "x86_64", suffix: "X86_64", name: "x86_64" } 21 | - { arch: "x86", suffix: "X86", name: "x86" } 22 | - { arch: "armv7", suffix: "ARMv7", name: "armeabi-v7a" } 23 | - { arch: "arm64_v8a", suffix: "ARM64", name: "arm64-v8a" } 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | with: 28 | ref: dev 29 | path: ${{ env.APP_DIR }} 30 | 31 | - name: Download Qt 32 | id: qt 33 | shell: cmake -P {0} 34 | run: | 35 | set(qt_version "$ENV{QT_VERSION}") 36 | string(REPLACE "." "" qt_version_dotless "${qt_version}") 37 | 38 | set(qt_package_arch_suffix "gcc_64") 39 | set(qt_package_suffix_desktop "-Linux-RHEL_8_2-GCC-Linux-RHEL_8_2-X86_64") 40 | 41 | set(abi ${{ matrix.abi.arch }}) 42 | set(qt_package_suffix_android "-Linux-RHEL_8_2-Clang-Android-Android_ANY-${{ matrix.abi.suffix }}") 43 | 44 | set(qt_base_url "https://download.qt.io/online/qtsdkrepository/linux_x64") 45 | 46 | # Prepare directories 47 | set(working_dir $ENV{GITHUB_WORKSPACE}/Qt) 48 | file(MAKE_DIRECTORY ${working_dir}) 49 | 50 | function(downloadAndExtract url download_dir archive) 51 | message("Downloading ${url}") 52 | file(DOWNLOAD "${url}" ./${download_dir}/${archive} SHOW_PROGRESS) 53 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../${download_dir}/${archive} WORKING_DIRECTORY ${working_dir}) 54 | endfunction() 55 | 56 | # Download Desktop Qt 57 | set(qt_base_url_desktop "${qt_base_url}/desktop/qt6_${qt_version_dotless}") 58 | message("Downloading ${qt_base_url_desktop}/Updates.xml") 59 | 60 | file(DOWNLOAD "${qt_base_url_desktop}/Updates.xml" ./Updates.xml SHOW_PROGRESS) 61 | file(READ ./Updates.xml updates_xml) 62 | string(REGEX MATCH "qt.qt6.*([0-9+-.]+)" updates_xml_output "${updates_xml}") 63 | set(qt_package_version ${CMAKE_MATCH_1}) 64 | 65 | set(download_dir $ENV{GITHUB_WORKSPACE}/qt6-download-desktop-${qt_package_version}) 66 | file(MAKE_DIRECTORY ${download_dir}) 67 | 68 | file(RENAME ./Updates.xml ${download_dir}/Updates.xml) 69 | 70 | # Needs qtdeclarative for qmlimportscanner 71 | foreach(package qtbase qtdeclarative) 72 | downloadAndExtract( 73 | "${qt_base_url_desktop}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix_desktop}.7z" 74 | ${download_dir} 75 | ${package}.7z 76 | ) 77 | endforeach() 78 | 79 | # uic depends on libicu56.so 80 | if (NOT WIN32 AND NOT APPLE) 81 | downloadAndExtract( 82 | "${qt_base_url_desktop}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-Rhel7.2-x64.7z" 83 | ${download_dir} 84 | icu.7z 85 | ) 86 | endif() 87 | 88 | set(qt_dir_desktop ${working_dir}/${qt_version}/gcc_64) 89 | 90 | file(READ "${qt_dir_desktop}/mkspecs/qconfig.pri" qtconfig) 91 | string(REPLACE "Enterprise" "OpenSource" qtconfig "${qtconfig}") 92 | string(REPLACE "licheck.exe" "" qtconfig "${qtconfig}") 93 | string(REPLACE "licheck64" "" qtconfig "${qtconfig}") 94 | string(REPLACE "licheck_mac" "" qtconfig "${qtconfig}") 95 | file(WRITE "${qt_dir_desktop}/mkspecs/qconfig.pri" "${qtconfig}") 96 | 97 | # Save the path for other steps 98 | message("::set-output name=qt_dir_desktop::${qt_dir_desktop}") 99 | 100 | # Download Android Qt 101 | set(qt_base_url_android "${qt_base_url}/android/qt6_${qt_version_dotless}_${abi}") 102 | message("Downloading ${qt_base_url_android}/Updates.xml") 103 | 104 | file(DOWNLOAD "${qt_base_url_android}/Updates.xml" ./Updates.xml SHOW_PROGRESS) 105 | file(READ ./Updates.xml updates_xml) 106 | string(REGEX MATCH "qt.qt6.*([0-9+-.]+)" updates_xml_output "${updates_xml}") 107 | set(qt_package_version ${CMAKE_MATCH_1}) 108 | 109 | set(download_dir $ENV{GITHUB_WORKSPACE}/qt6-download-android-${qt_package_version}) 110 | file(MAKE_DIRECTORY ${download_dir}) 111 | 112 | file(RENAME ./Updates.xml ${download_dir}/Updates.xml) 113 | 114 | foreach(package qttranslations qttools qtsvg qtdeclarative qtbase) 115 | downloadAndExtract( 116 | "${qt_base_url_android}/qt.qt6.${qt_version_dotless}.android_${abi}/${qt_package_version}${package}${qt_package_suffix_android}.7z" 117 | ${download_dir} 118 | ${package}.7z 119 | ) 120 | endforeach() 121 | 122 | foreach(package qtwebview qtwebsockets qtwebchannel qtvirtualkeyboard qtsensors qtscxml qtmultimedia qtimageformats qtdatavis3d qtconnectivity qtcharts qt3d) 123 | downloadAndExtract( 124 | "${qt_base_url_android}/qt.qt6.${qt_version_dotless}.addons.${package}.android_${abi}/${qt_package_version}${package}${qt_package_suffix_android}.7z" 125 | ${download_dir} 126 | ${package}.7z 127 | ) 128 | endforeach() 129 | 130 | # Special case for qtpositioning as the package is called qtlocation 131 | set(url "${qt_base_url_android}/qt.qt6.${qt_version_dotless}.addons.qtpositioning.android_${abi}/${qt_package_version}qtlocation${qt_package_suffix_android}.7z") 132 | message("Downloading ${url}") 133 | file(DOWNLOAD "${url}" ./${download_dir}/qtpositioning.7z SHOW_PROGRESS) 134 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../${download_dir}/qtpositioning.7z WORKING_DIRECTORY ${working_dir}) 135 | 136 | foreach(package qtshadertools qtquicktimeline qtquick3d qt5compat) 137 | downloadAndExtract( 138 | "${qt_base_url_android}/qt.qt6.${qt_version_dotless}.${package}.android_${abi}/${qt_package_version}${package}${qt_package_suffix_android}.7z" 139 | ${download_dir} 140 | ${package}.7z 141 | ) 142 | endforeach() 143 | 144 | set(qt_dir_android ${working_dir}/${qt_version}/android_${abi}) 145 | 146 | # Save the path for other steps 147 | message("::set-output name=qt_dir_android::${qt_dir_android}") 148 | 149 | # Modify target_qt.conf 150 | set(conf_content "\ 151 | [Paths] 152 | Prefix=../ 153 | HostPrefix=../../${qt_package_arch_suffix} 154 | HostData=../android_${abi} 155 | Sysroot= 156 | SysrootifyPrefix=false 157 | TargetSpec=android-clang 158 | HostSpec=linux-g++ 159 | ") 160 | 161 | file(WRITE ${qt_dir_android}/bin/target_qt.conf ${conf_content}) 162 | 163 | - name: Checkout qtquickdesigner-components 164 | uses: actions/checkout@v2 165 | with: 166 | repository: qt-labs/qtquickdesigner-components 167 | ref: dev 168 | path: qtquickdesigner-components 169 | 170 | - name: Configure, build and install qtquickdesigner-components 171 | run: | 172 | cd qtquickdesigner-components 173 | ${{ steps.qt.outputs.qt_dir_desktop }}/bin/qmake -qtconf ${{ steps.qt.outputs.qt_dir_android }}/bin/target_qt.conf && make && make install 174 | 175 | - name: Configure 176 | shell: cmake -P {0} 177 | run: | 178 | execute_process( 179 | COMMAND ${{ steps.qt.outputs.qt_dir_desktop }}/bin/qmake 180 | -qtconf ${{ steps.qt.outputs.qt_dir_android }}/bin/target_qt.conf 181 | $ENV{APP_PRO} 182 | -spec android-clang 183 | CONFIG+=debug 184 | CONFIG+=qml_debug 185 | WORKING_DIRECTORY $ENV{APP_DIR} 186 | RESULT_VARIABLE result 187 | ) 188 | if (NOT result EQUAL 0) 189 | message(FATAL_ERROR "Bad exit status") 190 | endif() 191 | 192 | # https://github.com/actions/virtual-environments/issues/3757 193 | - name: Fix Android Build Tools 194 | run: | 195 | ln -s ${ANDROID_HOME}/build-tools/31.0.0/d8 ${ANDROID_HOME}/build-tools/31.0.0/dx 196 | ln -s ${ANDROID_HOME}/build-tools/31.0.0/lib/d8.jar ${ANDROID_HOME}/build-tools/31.0.0/lib/dx.jar 197 | 198 | - name: Build 199 | id: build 200 | shell: cmake -P {0} 201 | run: | 202 | include(ProcessorCount) 203 | ProcessorCount(N) 204 | 205 | execute_process( 206 | COMMAND $ENV{ANDROID_NDK_HOME}/prebuilt/linux-x86_64/bin/make 207 | -f Makefile 208 | qmake_all 209 | WORKING_DIRECTORY $ENV{APP_DIR} 210 | RESULT_VARIABLE result 211 | ) 212 | if (NOT result EQUAL 0) 213 | message(FATAL_ERROR "Bad exit status") 214 | endif() 215 | 216 | execute_process( 217 | COMMAND $ENV{ANDROID_NDK_HOME}/prebuilt/linux-x86_64/bin/make 218 | -j ${N} 219 | WORKING_DIRECTORY $ENV{APP_DIR} 220 | RESULT_VARIABLE result 221 | ) 222 | if (NOT result EQUAL 0) 223 | message(FATAL_ERROR "Bad exit status") 224 | endif() 225 | 226 | execute_process( 227 | COMMAND $ENV{ANDROID_NDK_HOME}/prebuilt/linux-x86_64/bin/make 228 | INSTALL_ROOT=android-build install 229 | WORKING_DIRECTORY $ENV{APP_DIR} 230 | RESULT_VARIABLE result 231 | ) 232 | if (NOT result EQUAL 0) 233 | message(FATAL_ERROR "Bad exit status") 234 | endif() 235 | 236 | execute_process( 237 | COMMAND ${{ steps.qt.outputs.qt_dir_desktop }}/bin/androiddeployqt 238 | --input android-qtdesignviewer-deployment-settings.json 239 | --output android-build 240 | --android-platform android-31 241 | --jdk $ENV{JAVA_HOME_11_X64} 242 | --gradle 243 | WORKING_DIRECTORY $ENV{APP_DIR} 244 | RESULT_VARIABLE result 245 | ) 246 | if (NOT result EQUAL 0) 247 | message(FATAL_ERROR "Bad exit status") 248 | endif() 249 | 250 | # Rename APK 251 | set(apk_dir $ENV{APP_DIR}/android-build/build/outputs/apk/debug) 252 | set(apk_path ${apk_dir}/$ENV{APP_NAME}_${{ matrix.abi.name }}.apk) 253 | 254 | file( 255 | RENAME ${apk_dir}/android-build-debug.apk ${apk_path} 256 | RESULT result 257 | ) 258 | if (NOT result EQUAL 0) 259 | message(FATAL_ERROR "Bad exit status") 260 | endif() 261 | 262 | message("::set-output name=apk_path::${apk_path}") 263 | 264 | - uses: actions/upload-artifact@v2 265 | with: 266 | name: ${{ env.APP_NAME }}_${{ matrix.abi.name }} 267 | path: ${{ steps.build.outputs.apk_path }} 268 | 269 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Qt Design Viewer 14 | 48 | 49 | 50 |
51 | Qt Design Viewer 52 | 53 |
54 |
55 |
56 | 57 |
Qt Design Viewer
58 |
powered by web assembly
59 |
60 | 61 |
62 |
63 |
64 |
drop your qmlrc file or zip file here
65 |

(package with a pure Qml project, containing either a .qmlproject file or a main.qml)

66 |
67 | 68 |
69 |
70 |
71 |

EXAMPLES

72 |
    73 |
74 |
75 | 76 | 77 | 88 | 89 | 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2019 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Design Viewer of the Qt Toolkit. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #if defined(Q_OS_ANDROID) 47 | #include 48 | #include 49 | #endif 50 | 51 | #ifdef Q_OS_WASM 52 | #include 53 | 54 | std::function g_setFileDataCallback; 55 | extern "C" EMSCRIPTEN_KEEPALIVE void qt_callSetFileData(char *content, size_t contentSize, 56 | char *fileName) 57 | { 58 | if (g_setFileDataCallback == nullptr) 59 | return; 60 | 61 | g_setFileDataCallback(content, contentSize, fileName); 62 | g_setFileDataCallback = nullptr; 63 | } 64 | 65 | void fetchProject(QByteArray *data, QString *fileName) 66 | { 67 | // Call qt_callSetFileData to make sure the emscripten linker does not 68 | // optimize it away, which may happen if the function is called from JavaScript 69 | // only. Set g_setFileDataCallback to null to make it a no-op. 70 | ::g_setFileDataCallback = nullptr; 71 | ::qt_callSetFileData(nullptr, 0, nullptr); 72 | 73 | auto setFileDataCallback = [data, fileName](char *content, size_t contentSize, char *projectFilename){ 74 | *data->setRawData(content, contentSize); 75 | *fileName = QString::fromUtf8(projectFilename); 76 | }; 77 | g_setFileDataCallback = setFileDataCallback; 78 | 79 | EM_ASM_({ 80 | // Copy the file file content to the C++ heap. 81 | // Note: this could be simplified by passing the content as an 82 | // "array" type to ccall and then let it copy to C++ memory. 83 | // However, this built-in solution does not handle files larger 84 | // than ~15M (Chrome). Instead, allocate memory manually and 85 | // pass a pointer to the C++ side (which will free() it when done). 86 | 87 | // TODO: consider slice()ing the file to read it picewise and 88 | // then assembling it in a QByteArray on the C++ side. 89 | 90 | const contentSize = contentArray.byteLength; 91 | const heapPointer = _malloc(contentSize); 92 | const heapBytes = new Uint8Array(Module.HEAPU8.buffer, heapPointer, contentSize); 93 | heapBytes.set(contentArray); 94 | 95 | // Null out the first data copy to enable GC 96 | reader = null; 97 | contentArray = null; 98 | 99 | // Call the C++ file data ready callback 100 | ccall("qt_callSetFileData", null, 101 | ["number", "number", "string"], [heapPointer, contentSize, projectfileName]); 102 | }); 103 | } 104 | 105 | void printWarning(const QString &error) 106 | { 107 | QString escaped = error; 108 | escaped.replace("'", "\'"); 109 | escaped.replace("\n", "\\n"); 110 | emscripten_run_script("alert('" + escaped.toUtf8() + "');"); 111 | } 112 | 113 | void printError(const QString &error) 114 | { 115 | printWarning(error); 116 | emscripten_run_script("location.hash = ''; location.reload();"); 117 | } 118 | 119 | void setScreenSize(const QSize &size) 120 | { 121 | const QString command = 122 | QString::fromLatin1("setScreenSize(%1, %2);").arg(size.width()).arg(size.height()); 123 | emscripten_run_script(command.toUtf8()); 124 | } 125 | #elif defined(Q_OS_ANDROID) 126 | std::function showFatalMessageAndDie; 127 | 128 | void fetchProject(QByteArray *data, QString *fileName) 129 | { 130 | *fileName = QCoreApplication::arguments().at(1); 131 | QFile file(*fileName); 132 | if (file.open(QIODevice::ReadOnly)) 133 | *data = file.readAll(); 134 | } 135 | 136 | void printError(const QString &error) 137 | { 138 | qDebug() << error; 139 | } 140 | 141 | void setScreenSize(const QSize &size) 142 | { 143 | qDebug() << "Screen size: " << size.width() << "x" << size.height(); 144 | } 145 | #else 146 | 147 | void fetchProject(QByteArray *data, QString *fileName) 148 | { 149 | if (QCoreApplication::arguments().count() < 2) { 150 | qWarning() << "Pass a package file name as argument"; 151 | return; 152 | } 153 | 154 | *fileName = QCoreApplication::arguments().at(1); 155 | QFile file(*fileName); 156 | if (file.open(QIODevice::ReadOnly)) 157 | *data = file.readAll(); 158 | } 159 | 160 | void printError(const QString &error) 161 | { 162 | fprintf(stderr, "%s\n", qPrintable(error)); 163 | } 164 | 165 | void setScreenSize(const QSize &size) 166 | { 167 | fprintf(stderr, "Screen size: %d x %d\n", size.width(), size.height()); 168 | } 169 | #endif // Q_OS_WASM 170 | 171 | QString unpackProject(const QByteArray &project, const QString &targetDir, bool skipExtract = false) 172 | { 173 | QDir().mkpath(targetDir); 174 | 175 | QBuffer buffer; 176 | buffer.setData(project); 177 | buffer.open(QIODevice::ReadOnly); 178 | 179 | if (!skipExtract) { 180 | QZipReader reader(&buffer); 181 | reader.extractAll(targetDir); 182 | } 183 | 184 | QDir projectLocationDir(targetDir); 185 | // maybe it was not a zip file so try it as resource binary 186 | if (projectLocationDir.isEmpty()) { 187 | if (!skipExtract) 188 | qDebug() << "File could not be extracted. Trying to open it as a resource file."; 189 | const uchar* data = reinterpret_cast(project.data()); 190 | const QString resourcePath("/qtdesignviewer"); 191 | const QFileInfo sourceInfo(resourcePath); 192 | const QDir sourceDir(sourceInfo.dir()); 193 | if (QResource::registerResource(data, resourcePath)) { 194 | return ":" + resourcePath; 195 | } else { 196 | printError("Can not load the resource data."); 197 | } 198 | } 199 | return targetDir; 200 | } 201 | 202 | QString findFile(const QString &dir, const QString &filter) 203 | { 204 | QDirIterator it(dir, {filter}, QDir::NoFilter, QDirIterator::Subdirectories); 205 | return it.next(); 206 | } 207 | 208 | void parseQmlprojectFile(const QString &fileName, QString *mainFile, QStringList *importPaths) 209 | { 210 | /* if filename comes from a resource qml need qrc:/ at the mainfile and importPaths, 211 | * but all other c++ call like QFileInfo::exists do not understand that, there we 212 | * need to keep the : only at the beginning 213 | */ 214 | QFile file(fileName); 215 | if (!file.open(QIODevice::ReadOnly)) { 216 | printError("Could not open " + fileName + ": " + file.errorString()); 217 | return; 218 | } 219 | const QString text = QString::fromUtf8(file.readAll()); 220 | const QRegularExpression mainFileRegExp("mainFile:\\s*\"(.*)\""); 221 | const QRegularExpressionMatch mainFileMatch = mainFileRegExp.match(text); 222 | if (mainFileMatch.hasMatch()) { 223 | QString basePath = QFileInfo(fileName).path() + "/"; 224 | 225 | *mainFile = basePath + mainFileMatch.captured(1); 226 | if (mainFile->startsWith(QLatin1String(":/"))) 227 | 228 | *mainFile = "qrc:" + mainFile->mid(1); 229 | 230 | const QRegularExpression qt6ProjectRegExp("qt6Project:\\s*true"); 231 | const QRegularExpressionMatch qt6ProjectMatch = qt6ProjectRegExp.match(text); 232 | if (!qt6ProjectMatch.hasMatch()) { 233 | printWarning("This is not a Qt6 project.\nQt5 projects might work, but they are not officially supported."); 234 | } 235 | 236 | const QRegularExpression importPathsRegExp("importPaths:\\s*\\[\\s*(.*)\\s*\\]"); 237 | const QRegularExpressionMatch importPathsMatch = importPathsRegExp.match(text); 238 | if (importPathsMatch.hasMatch()) { 239 | for (const QString &path : importPathsMatch.captured(1).split(",")) { 240 | QString cleanedPath = path.trimmed(); 241 | cleanedPath = basePath + cleanedPath.mid(1, cleanedPath.length() - 2); 242 | if (QFileInfo::exists(cleanedPath)) { 243 | if (cleanedPath.startsWith(QLatin1String(":/"))) 244 | cleanedPath = "qrc:" + cleanedPath.mid(1); 245 | importPaths->append(cleanedPath); 246 | } 247 | } 248 | } 249 | } else { 250 | return; 251 | } 252 | } 253 | 254 | int main(int argc, char *argv[]) 255 | { 256 | qDebug().noquote() << QString("Built on %1 %2\n").arg(__DATE__, __TIME__); 257 | 258 | #if defined(Q_OS_ANDROID) 259 | QString mainQml; 260 | 261 | showFatalMessageAndDie = [&argc,&argv](QStringList messages) { 262 | QApplication *app = static_cast(QApplication::instance()); 263 | const bool createdLocally = (app == nullptr); 264 | if (createdLocally) 265 | app = new QApplication(argc, argv); 266 | messages.append("\nThis app is supposed to be run from Qt Design Studio."); 267 | QMessageBox msg(QMessageBox::Critical, "Fatal error", messages.join("\n"), QMessageBox::Ok); 268 | QObject::connect(&msg, &QMessageBox::finished, [&app](int){app->exit(-1);}); 269 | msg.show(); 270 | int retVal = app->exec(); 271 | if (createdLocally) { 272 | delete app; 273 | } 274 | return retVal; 275 | }; 276 | if (argc < 2) { 277 | return showFatalMessageAndDie({QString("Qml project to show has not been defined.")}); 278 | } 279 | #endif 280 | QSurfaceFormat format = QSurfaceFormat::defaultFormat(); 281 | format.setVersion(3,0); 282 | QSurfaceFormat::setDefaultFormat(format); 283 | 284 | #if defined(Q_OS_ANDROID) 285 | QApplication app(argc, argv); 286 | QApplication::setApplicationName(QStringLiteral("Qt Design Viewer")); 287 | #else 288 | QGuiApplication app(argc, argv); 289 | #endif 290 | QString projectFileName; 291 | QByteArray projectData; 292 | fetchProject(&projectData, &projectFileName); 293 | 294 | #ifdef Q_OS_WASM 295 | QString projectLocation = "/home/web_user/"; 296 | #else // Q_OS_WASM 297 | QTemporaryDir tempDir("qmlprojector"); 298 | QString projectLocation = tempDir.path(); 299 | #endif // Q_OS_WASM 300 | 301 | #if defined(Q_OS_ANDROID) 302 | const bool skipZipOpenAttempt = projectFileName.endsWith(".qmlrc"); 303 | projectLocation = unpackProject(projectData, projectLocation, skipZipOpenAttempt); 304 | #else 305 | projectLocation = unpackProject(projectData, projectLocation); 306 | #endif 307 | QString mainQmlFile; 308 | QStringList importPaths; 309 | const QString qmlProjectFile = findFile(projectLocation, "*.qmlproject"); 310 | if (!qmlProjectFile.isEmpty()) { 311 | parseQmlprojectFile(qmlProjectFile, &mainQmlFile, &importPaths); 312 | } else { 313 | mainQmlFile = findFile(projectLocation, "main.qml"); 314 | if (mainQmlFile.isEmpty()) 315 | mainQmlFile = findFile(projectLocation, QFileInfo(projectFileName).baseName() + ".qml"); 316 | } 317 | if (mainQmlFile.isEmpty()) { 318 | printError("No \"*.qmlproject\", \"main.qml\" or \"" + QFileInfo(projectFileName).baseName() 319 | + ".qml\" found in \"" + projectFileName + "\"."); 320 | return -1; 321 | } 322 | QUrl mainQmlUrl = QUrl::fromUserInput(mainQmlFile); 323 | 324 | const QString qtquickcontrols2File = findFile(projectLocation, "qtquickcontrols2.conf"); 325 | if (!qtquickcontrols2File.isEmpty()) 326 | qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1()); 327 | 328 | QQmlEngine engine; 329 | for (const QString &importPath : importPaths) 330 | engine.addImportPath(importPath); 331 | QPointer component = new QQmlComponent(&engine); 332 | component->loadUrl(mainQmlUrl); 333 | while (component->isLoading()) 334 | QCoreApplication::processEvents(); 335 | if (!component->isReady()) { 336 | printError(component->errorString()); 337 | #if defined(Q_OS_ANDROID) 338 | showFatalMessageAndDie({"Error while loading Qml component.", component->errorString()}); 339 | #endif 340 | return -1; 341 | } 342 | QObject *topLevel = component->create(); 343 | if (!topLevel && component->isError()) { 344 | printError("Create error"); 345 | #if defined(Q_OS_ANDROID) 346 | showFatalMessageAndDie({"Error while creating Qml component.", component->errorString()}); 347 | #endif 348 | return -1; 349 | } 350 | QScopedPointer window(qobject_cast(topLevel)); 351 | if (window) { 352 | engine.setIncubationController(window->incubationController()); 353 | setScreenSize(QSize()); // Full browser size. TODO: Find out initial ApplicationWindow size 354 | } else { 355 | QQuickItem *contentItem = qobject_cast(topLevel); 356 | if (contentItem) { 357 | QQuickView* view = new QQuickView(&engine, nullptr); 358 | window.reset(view); 359 | view->setContent(mainQmlUrl, component, contentItem); 360 | view->setResizeMode(QQuickView::SizeViewToRootObject); 361 | setScreenSize(QSize(contentItem->width(), contentItem->height())); 362 | } 363 | } 364 | 365 | window->show(); 366 | 367 | const int exitcode = app.exec(); 368 | delete component; 369 | return exitcode; 370 | } 371 | --------------------------------------------------------------------------------